Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH] mtd: rawnand: lpc32xx_mlc: fail DMA transfers on timeout
From: Vladimir Zapolskiy @ 2026-06-25 13:42 UTC (permalink / raw)
  To: Pengpeng Hou, Miquel Raynal
  Cc: Richard Weinberger, Vignesh Raghavendra, Piotr Wojtaszczyk,
	linux-mtd, linux-arm-kernel, linux-kernel
In-Reply-To: <20260625003327.11060-1-pengpeng@iscas.ac.cn>

On 6/25/26 03:33, Pengpeng Hou wrote:
> lpc32xx_xmit_dma() starts a DMA transfer and waits up to one second for
> its completion, but it ignores the wait result and returns success after
> unmapping the buffer.
> 
> A timed out read can therefore return success with incomplete data, and a
> timed out write can continue the NAND operation without proof that the DMA
> payload reached the controller.
> 
> Terminate the DMA channel on timeout, unmap the scatterlist through the
> existing cleanup path, and return -ETIMEDOUT to the NAND read/write
> callers.
> 
> Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
> ---
>   drivers/mtd/nand/raw/lpc32xx_mlc.c | 12 ++++++++++--
>   1 file changed, 10 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/mtd/nand/raw/lpc32xx_mlc.c b/drivers/mtd/nand/raw/lpc32xx_mlc.c
> index 19b13ae53..8f6a89d9b 100644
> --- a/drivers/mtd/nand/raw/lpc32xx_mlc.c
> +++ b/drivers/mtd/nand/raw/lpc32xx_mlc.c
> @@ -396,6 +396,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
>   	struct lpc32xx_nand_host *host = nand_get_controller_data(chip);
>   	struct dma_async_tx_descriptor *desc;
>   	int flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT;
> +	unsigned long time_left;
>   	int res;
>   
>   	sg_init_one(&host->sgl, mem, len);
> @@ -410,6 +411,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
>   				       flags);
>   	if (!desc) {
>   		dev_err(mtd->dev.parent, "Failed to prepare slave sg\n");
> +		res = -ENXIO;
>   		goto out1;
>   	}
>   
> @@ -420,7 +422,13 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
>   	dmaengine_submit(desc);
>   	dma_async_issue_pending(host->dma_chan);
>   
> -	wait_for_completion_timeout(&host->comp_dma, msecs_to_jiffies(1000));
> +	time_left = wait_for_completion_timeout(&host->comp_dma,
> +						msecs_to_jiffies(1000));
> +	if (!time_left) {
> +		dmaengine_terminate_sync(host->dma_chan);
> +		res = -ETIMEDOUT;
> +		goto out1;
> +	}
>   
>   	dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
>   		     DMA_BIDIRECTIONAL);
> @@ -428,7 +436,7 @@ static int lpc32xx_xmit_dma(struct mtd_info *mtd, void *mem, int len,
>   out1:
>   	dma_unmap_sg(host->dma_chan->device->dev, &host->sgl, 1,
>   		     DMA_BIDIRECTIONAL);
> -	return -ENXIO;
> +	return res;
>   }
>   
>   static int lpc32xx_read_page(struct nand_chip *chip, uint8_t *buf,

Thank you for the change.

Reviewed-by: Vladimir Zapolskiy <vz@kernel.org>

-- 
Best wishes,
Vladimir


^ permalink raw reply

* Re: [PATCH v5 1/7] KVM: arm64: Enforce strict SBZ checks in the FF-A proxy
From: Will Deacon @ 2026-06-25 13:16 UTC (permalink / raw)
  To: Sebastian Ene
  Cc: catalin.marinas, maz, oupton, joey.gouly, korneld, kvmarm,
	linux-arm-kernel, linux-kernel, android-kvm, mrigendra.chaubey,
	perlarsen, suzuki.poulose, vdonnefort, yuzenghui
In-Reply-To: <20260623115354.632361-2-sebastianene@google.com>

Hi all,

On Tue, Jun 23, 2026 at 11:53:48AM +0000, Sebastian Ene wrote:
> Introduce a helper method ffa_check_unused_args_sbz to enforce strict
> arguments checking when the hypervisor acts as a relayer between the
> host and Trustzone.
> 
> Signed-off-by: Sebastian Ene <sebastianene@google.com>
> Reviewed-by: Vincent Donnefort <vdonnefort@google.com>
> ---
>  arch/arm64/kvm/hyp/nvhe/ffa.c | 54 +++++++++++++++++++++++++++++++++++
>  1 file changed, 54 insertions(+)
> 
> diff --git a/arch/arm64/kvm/hyp/nvhe/ffa.c b/arch/arm64/kvm/hyp/nvhe/ffa.c
> index 1af722771178..78bb043b33ee 100644
> --- a/arch/arm64/kvm/hyp/nvhe/ffa.c
> +++ b/arch/arm64/kvm/hyp/nvhe/ffa.c
> @@ -71,6 +71,20 @@ static u32 hyp_ffa_version;
>  static bool has_version_negotiated;
>  static hyp_spinlock_t version_lock;
>  
> +static bool ffa_check_unused_args_sbz(struct kvm_cpu_context *ctxt, int first_reg)
> +{
> +	DECLARE_REG(u32, func_id, ctxt, 0);
> +	int reg, end_reg;
> +
> +	end_reg = ARM_SMCCC_IS_64(func_id) ? 17 : 7;
> +	for (reg = first_reg; reg <= end_reg; reg++) {
> +		if (cpu_reg(ctxt, reg))
> +			return true;
> +	}
> +
> +	return false;
> +}

Seb and I tried taking this for a spin on some Android devices and, sadly,
it leads to fireworks. The reason is that the FF-A spec quietly changed
the list of unused parameter registers for 64-bit SMCs from v1.1 to v1.2
of the spec so that pre-existing calls were affected.

For example, in v1.1 a 64-bit RXTX_MAP only has x4-x7 as MBZ, whereas in
v1.2 the same call has x4-x17 as SBZ.

We can follow the spec by predicating the additional check on the FF-A
version being >= 1.2, but I'm not hopeful that existing drivers are
compliant. I also suggest moving this patch to the end of the series in
case we need to revert it.

Cheers,

Will


^ permalink raw reply

* [PATCH v1] KVM: Ignore MMU notifiers for guest_memfd-only memslots
From: Alexandru Elisei @ 2026-06-25 13:09 UTC (permalink / raw)
  To: pbonzini, kvm, linux-kernel, maz, oupton, suzuki.poulose, kvmarm,
	linux-arm-kernel, seanjc, david.hildenbrand, mark.rutland,
	ackerleytng

For guest_memfd-only memslots (kvm_memslot_is_gmem_only() is true), the
memory provider for the virtual machine is the guest_memfd file, not the
userspace mapping. Mappings in the secondary MMU are established by
obtaining folios from guest_memfd directly, not by looking the folios up
through the page tables through GUP. Consequently, there is no relationship
between the page tables and the secondary MMU: MMU notifiers do not apply.

Despite this, KVM's MMU notifiers still modify the secondary MMU page
tables, only for the same memory to be remapped the next time a guest
accesses it. Make the disconnect between the user mapping and the secondary
MMU page tables explicit by ignoring the MMU notifiers for guest_memfd-only
memslots.

Suggested-by: Sean Christopherson <seanjc@google.com>
Signed-off-by: Alexandru Elisei <alexandru.elisei@arm.com>
---
RFC can be found here [1].

The only theoretical instance where the MMU notifiers are invoked for the
userspace mapping of a guest_memfd-only memslot that I was able to find was
automatic NUMA balancing with a non-NULL NUMA policy for the guest_memfd
file. I wasn't able to test it in practice. Ackerley Tng also mentioned
that this change would fix double unmap on a shared to private in-place
conversion of the guest_memfd memory [2].

When and if it happens, having memory unmapped from the seconday MMU in the
case of a guest_memfd-only memslot is at most a performance issue (it
causes unnecessary guest faults), but having memory that stays mapped at
stage 2 (unless userspace explicitly unmaps it from the VM) is needed for a
Arm feature (called SPE, Statistical Profiling Extension) that I'm working
to upstream. This patch aims to provide the guarantee that memory won't be
unmapped from the secondary MMU without the VMM explicitely triggering it
(by punching a hole or closing the guest_memfd file).

Ran a basic test by hacking KVM_PRE_FAULT_MEMORY for arm64, and modifying
kvmtool to apply it on the entire VM memory, then munmap the same memory
from its page tables. Also hacked guest_memfd + GUEST_MEMFD_FLAG_MMAP
support in kvmtool. Put traces in the arm64 fault handling code and printfs
in kvm_mmu_unmap_gfn_range(). When running a guest, KVM doesn't unmap the
memory from the secondary MMU when kvmtool munmaps it; all the faults
triggered by the guest on the guest_memfd backed memslots are instruction
faults; and KVM unmaps the guest memory from the secondary MMU when the
guest_memfd file is closed by userspace. Looks correct to me.

Changes in RFC -> v1:

* Dropped the RFC tag.
* Fix unbalanced invalidation reported by sashiko by implementing Sean's
  approach; I've expanded it to page ageing.
* Modified the commit message as per DavidH comment.

[1] https://lore.kernel.org/kvm/20260615155244.183044-1-alexandru.elisei@arm.com/
[2] https://lore.kernel.org/kvm/CAEvNRgE9cLfjDbXuR5wq3fEWZyHxYPxdExxNjXUFO1nT5m==1A@mail.gmail.com/

 include/linux/kvm_host.h |  1 +
 virt/kvm/kvm_main.c      | 50 ++++++++++++++++++++++++++++++++++++----
 2 files changed, 47 insertions(+), 4 deletions(-)

diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h
index 4c14aee1fb06..483ad9fe8fb7 100644
--- a/include/linux/kvm_host.h
+++ b/include/linux/kvm_host.h
@@ -260,6 +260,7 @@ union kvm_mmu_notifier_arg {
 enum kvm_gfn_range_filter {
 	KVM_FILTER_SHARED		= BIT(0),
 	KVM_FILTER_PRIVATE		= BIT(1),
+	KVM_FILTER_USERSPACE_MAPPINGS	= BIT(2),
 };
 
 struct kvm_gfn_range {
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index 881f92d7a469..204e7faa325a 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -607,8 +607,13 @@ static __always_inline kvm_mn_ret_t kvm_handle_hva_range(struct kvm *kvm,
 			/*
 			 * HVA-based notifications aren't relevant to private
 			 * mappings as they don't have a userspace mapping.
+			 *
+			 * Memslots where guest_memfd is the only memory
+			 * provider can also safely ignore changes to the
+			 * userspace mapping.
 			 */
-			gfn_range.attr_filter = KVM_FILTER_SHARED;
+			gfn_range.attr_filter = KVM_FILTER_SHARED |
+						KVM_FILTER_USERSPACE_MAPPINGS;
 
 			/*
 			 * {gfn(page) | page intersects with [hva_start, hva_end)} =
@@ -715,6 +720,21 @@ void kvm_mmu_invalidate_range_add(struct kvm *kvm, gfn_t start, gfn_t end)
 bool kvm_mmu_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range)
 {
 	kvm_mmu_invalidate_range_add(kvm, range->start, range->end);
+
+	/*
+	 * When reacting to changes in userspace mappings, don't unmap memslots
+	 * that are guest_memfd-only, in which case KVM's MMU mappings are
+	 * pulled directly from guest_memfd, i.e. don't depend on the userspace
+	 * mappings.
+	 *
+	 * TODO: Skip gmem-only memslots on mmu_notifier events entirely, once
+	 * gfn_to_pfn_cache is also wired up to directly pull from guest_memfd.
+	 */
+	if (range->attr_filter & KVM_FILTER_USERSPACE_MAPPINGS &&
+	    kvm_slot_has_gmem(range->slot) &&
+	    kvm_memslot_is_gmem_only(range->slot))
+		return false;
+
 	return kvm_unmap_gfn_range(kvm, range);
 }
 
@@ -825,12 +845,23 @@ static void kvm_mmu_notifier_invalidate_range_end(struct mmu_notifier *mn,
 		rcuwait_wake_up(&kvm->mn_memslots_update_rcuwait);
 }
 
+static bool kvm_mmu_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+	/* See comment in kvm_mmu_unmap_gfn_range() */
+	if (range->attr_filter & KVM_FILTER_USERSPACE_MAPPINGS &&
+	    kvm_slot_has_gmem(range->slot) &&
+	    kvm_memslot_is_gmem_only(range->slot))
+		return false;
+
+	return kvm_age_gfn(kvm, range);
+}
+
 static bool kvm_mmu_notifier_clear_flush_young(struct mmu_notifier *mn,
 		struct mm_struct *mm, unsigned long start, unsigned long end)
 {
 	trace_kvm_age_hva(start, end);
 
-	return kvm_age_hva_range(mn, start, end, kvm_age_gfn,
+	return kvm_age_hva_range(mn, start, end, kvm_mmu_age_gfn,
 				 !IS_ENABLED(CONFIG_KVM_ELIDE_TLB_FLUSH_IF_YOUNG));
 }
 
@@ -852,7 +883,18 @@ static bool kvm_mmu_notifier_clear_young(struct mmu_notifier *mn,
 	 * cadence. If we find this inaccurate, we might come up with a
 	 * more sophisticated heuristic later.
 	 */
-	return kvm_age_hva_range_no_flush(mn, start, end, kvm_age_gfn);
+	return kvm_age_hva_range_no_flush(mn, start, end, kvm_mmu_age_gfn);
+}
+
+static bool kvm_mmu_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range)
+{
+	/* See comment in kvm_mmu_unmap_gfn_range() */
+	if (range->attr_filter & KVM_FILTER_USERSPACE_MAPPINGS &&
+	    kvm_slot_has_gmem(range->slot) &&
+	    kvm_memslot_is_gmem_only(range->slot))
+		return false;
+
+	return kvm_test_age_gfn(kvm, range);
 }
 
 static bool kvm_mmu_notifier_test_young(struct mmu_notifier *mn,
@@ -861,7 +903,7 @@ static bool kvm_mmu_notifier_test_young(struct mmu_notifier *mn,
 	trace_kvm_test_age_hva(address);
 
 	return kvm_age_hva_range_no_flush(mn, address, address + 1,
-					  kvm_test_age_gfn);
+					  kvm_mmu_test_age_gfn);
 }
 
 static void kvm_mmu_notifier_release(struct mmu_notifier *mn,

base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
-- 
2.54.0



^ permalink raw reply related

* Re: [PATCH] ARM: enable interrupts when arm_notify_die() is handling user mode errors
From: Xie Yuanbin @ 2026-06-25 12:26 UTC (permalink / raw)
  To: linux, bigeasy, rmk+kernel
  Cc: clrkwllms, rostedt, linusw, arnd, linux-arm-kernel, linux-kernel,
	linux-rt-devel, liaohua4, lilinjie8, Xie Yuanbin
In-Reply-To: <aj0BpqlaCh3cVw8Q@shell.armlinux.org.uk>

On Thu, 25 Jun 2026 11:23:34 +0100, Russell King wrote:
>> 3. enable interrupts in do_DataAbort()/do_PrefetchAbort() after
>>    `inf->fn()`, this may be ok.
>> 
>> From this perspective, arm_notify_die() also seems to be a good place?
>
> If one is happy with higher latency for preempt cases, then it may
> be, but if we want lower latency, then it ought to be earlier.
> My preference is (3).

```c
	if (!inf->fn(addr, ifsr | FSR_LNX_PF, regs))
		return;

	if (likely(user_mode(regs)))
		local_irq_enable();

	pr_alert("8<--- cut here ---\n");
```
or
```c
	if (!inf->fn(addr, ifsr | FSR_LNX_PF, regs))
		return;

	if (likely(interrupts_enabled(regs)))
		local_irq_enable();

	pr_alert("8<--- cut here ---\n");
```

Which one do you prefer? I prefer the first one, because for kernel
fault, kernel may have encountered a serious issue,
and enabling interrupts may be not appropriate.


^ permalink raw reply

* Re: mm: opaque hardware page-table entry handles
From: Muhammad Usama Anjum @ 2026-06-25 12:15 UTC (permalink / raw)
  To: Pedro Falcato
  Cc: usama.anjum, Andrew Morton, Lorenzo Stoakes, David Hildenbrand,
	Liam R. Howlett, Mike Rapoport, Ryan Roberts, Anshuman Khandual,
	Catalin Marinas, Will Deacon, Samuel Holland, linux-mm,
	linux-arm-kernel, linux-kernel
In-Reply-To: <aj0JU-AzsEQBOiQ1@pedro-suse>

On 25/06/2026 12:08 pm, Pedro Falcato wrote:
> On Thu, Jun 25, 2026 at 11:50:28AM +0100, Muhammad Usama Anjum wrote:
>> On 24/06/2026 8:25 pm, Pedro Falcato wrote:
>>> On Wed, Jun 24, 2026 at 03:09:08PM +0100, Usama Anjum wrote:
>>>> Hi all,
>>>>
>>>> This is a direction-check with the wider community before spending time on the
>>>> development. This picks up the idea that was raised and broadly agreed in the
>>>> earlier thread (Ryan Roberts, Lorenzo Stoakes, David Hildenbrand) [1].
>>>>
>>>> The problem
>>>> -----------
>>>> Core MM code reaches page-table entries by raw pointer dereference (pte_t *,
>>>> pmd_t *, *pud, ...) in places, implicitly assuming a single, uniform
>>>> representation. Sprinkling getters wouldn't solve the problem entirely. The
>>>> problem is one level up: the *pointer type* itself is overloaded. At each level
>>>> there are really three distinct things:
>>>>
>>>>   1. a page-table entry value (pte_t, pmd_t, ...)
>>>>   2. a pointer to an entry value, e.g. a pXX_t on the stack
>>>>   3. a pointer to a live entry in the hardware page table
>>>>
>>>> Today (2) and (3) share the same type - pte_t *, pmd_t *, and so on. Nothing
>>>> distinguishes a pointer into a live table from a pointer to a stack copy.
>>>>
>>>> A pointer to an on-stack entry value and a pointer to a live hardware entry have
>>>> the same type, so the compiler cannot distinguish them. Passing the stack
>>>> pointer to an arch helper that expects a hardware-entry pointer compiles fine,
>>>> but is wrong - a bug class the type system makes invisible. It also blocks
>>>> evolution: an arch helper may need to read beyond the addressed entry (e.g.
>>>> adjacent or contiguous entries), which only makes sense for a real page-table
>>>> pointer, not a stack copy.
>>>>
>>>> The idea
>>>> --------
>>>> Give (3) its own opaque type that cannot be dereferenced:
>>>>
>>>>     /* opaque handle to a HW page-table entry; not dereferenceable */
>>>>     typedef struct {
>>>> 	pte_t *ptr;
>>>>     } hw_ptep;
>>>
>>> I don't love typedefs that hide pointers.
>> Nobody likes them. This is the only way so that by mistake stack pointers
>> don't get reintroduced. Its also hard to catch such cases during review.
> 
> That's not true, you could have:
> 
> typedef struct { pteval_t pte; } sw_pte_t;
> 
> and
> 
> /* only usable by arch code and whoever wants to interpret these
>  * types */
> static inline sw_to_ptep(sw_pte_t *swptep)
> {
> 	return (pte_t *) swptep;
> }
> 
> and so on... Also, see Documentation/process/coding-style.rst 5) typedefs, it
> explicitly warns against pointer typedefs.
I hear your concern, but I think the sw_pte_t inversion solves the small problem
and gives up the big one. Let me make the case for keeping the opaque hardware type.

The narrow goal is "no stack pointer reaches a table-writing API". Both schemes catch
that. But the actual reasons for this idea is broader one:
* making core independent of how a real page table entry is represented and accessed.
  It only works if hardware type is the abstract one.
* As you may have noted with pmdp_get(): on some arches the read is not a pure load
  (folding, lockless ordering, kmap of a highmem table page). pte_t * lets callers
  bypass all of that with *ptep. The handle makes the accessor the only door, so the
  barriers/folds can't be skipped by accident.
 
>>
>>>
>>>>
>>>> With this:
>>>>
>>>>   - a stack value can no longer masquerade as a hardware table entry,
>>>>   - a hardware handle can no longer be raw-dereferenced,
>>>>   - cases that genuinely operate on a value can be refactored to pass the value
>>>>     and let the caller, which knows whether it holds a handle or a stack copy,
>>>>     read it once.
>>>
>>> Just a small passing comment: how about doing it differently? like
>>>
>>> typedef struct {
>>> 	pte_t *ptep;
>>> } sw_ptep_t;
>>>
>>> or something like that. Were I to guess, referring to a pte_t on the stack
>>> is much rarer than all the pte_t references to actual page tables. But maybe
>>> reality doesn't match up with my guess :)
>> We want to fix the current usages and future usages as well. sw_ptep_t can work
>> for current usages, but it'll not force the new code to be written using correct
>> notations.
> 
> I don't understand what you mean. pte_t is a perfectly correct notation,
> it's just currently maybe too ambiguously overloaded.
Yes, this overload is what need fixing.

> 
>> Apart from different types, another benefit of hw_pXXp would be that
>> it'll become an opaque object which only architecture can manipulate. Hence
>> architecture can decide howeverever it wants to manage them in certain cases.
> 
> That's already the case. pte_t is fully opaque apart from the little fact
> that you can declare one on your stack. Introducing a different sw_pte_t
> would further reinforce that. And if you want ways to find raw derefs on
> pointers, we can simply slap on __attribute__((noderef)) (available in
> sparse and clang) on those types after sw_pte_t is introduced and pte_t
> is unambiguously a "hardware" PTE.
The pte_t iterator loops in core code prove that it isn't opaque enough.
The pointer arithmetic (ptep++) is done at several places in the core.

The sw_pte_t + deref protection only catches misues under sparse. While the
hw_ptep type is enforced by every compiler for every build. 

> 
> I dunno, I'm not convinced that changing around ~450 files is worth it, and
> _if_ we want to do something like this I would strongly prefer the way that
> is less churny.
Probably you grepped these types to come up with 450 files? But we aren't going
to update all files. Only the generic code would be converted with one or two
architectures. Its architecture opt-in. It'll be transparent to non-converted
architectures. So if arch/ is excluded, the number of files would become a
quarter?

This type change is going to localize the future churn. It is one time cost;
after that, every future representation change lives behind the accessors. 
If pte_t * stays the live type, each such change is another N files audit.
The struct buys us one choke point to evolve.

-- 
Thanks,
Usama



^ permalink raw reply

* [PATCH v2 3/3] scsi: ufs: core: Always run tx_eqtr POST_CHANGE notify
From: Can Guo @ 2026-06-25 12:13 UTC (permalink / raw)
  To: bvanassche, beanhuo, peter.wang, martin.petersen, mani
  Cc: linux-scsi, Can Guo, Alim Akhtar, Avri Altman,
	James E.J. Bottomley, Matthias Brugger,
	AngeloGioacchino Del Regno, open list,
	moderated list:ARM/Mediatek SoC support:Keyword:mediatek,
	moderated list:ARM/Mediatek SoC support:Keyword:mediatek
In-Reply-To: <20260625121306.1655467-1-can.guo@oss.qualcomm.com>

ufshcd_tx_eqtr() skips POST_CHANGE notify when __ufshcd_tx_eqtr()
fails. That can leave variant cleanup incomplete when PRE_CHANGE saved
temporary state that POST_CHANGE is expected to restore.

Always call POST_CHANGE once PRE_CHANGE has succeeded. Keep the TX EQTR
result as the primary return value, and only propagate POST_CHANGE
failure when TX EQTR itself succeeded.

Log PRE_CHANGE and POST_CHANGE notify failures to make variant callback
failures visible in TX EQTR error paths.

Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
Reviewed-by: Peter Wang <peter.wang@mediatek.com>
Signed-off-by: Can Guo <can.guo@oss.qualcomm.com>
---
 drivers/ufs/core/ufs-txeq.c | 14 ++++++++++----
 1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/drivers/ufs/core/ufs-txeq.c b/drivers/ufs/core/ufs-txeq.c
index e1302ea9f27e..7f908ea97ec3 100644
--- a/drivers/ufs/core/ufs-txeq.c
+++ b/drivers/ufs/core/ufs-txeq.c
@@ -1223,6 +1223,7 @@ static int ufshcd_tx_eqtr(struct ufs_hba *hba,
 {
 	struct ufs_pa_layer_attr old_pwr_info;
 	unsigned int noio_flag;
+	int notify_ret;
 	int ret;
 
 	/*
@@ -1252,14 +1253,19 @@ static int ufshcd_tx_eqtr(struct ufs_hba *hba,
 	}
 
 	ret = ufshcd_vops_tx_eqtr_notify(hba, PRE_CHANGE, pwr_mode);
-	if (ret)
+	if (ret) {
+		dev_err(hba->dev, "TX EQTR PRE_CHANGE notify failed: %d\n", ret);
 		goto out;
+	}
 
 	ret = __ufshcd_tx_eqtr(hba, params, pwr_mode);
-	if (ret)
-		goto out;
 
-	ret = ufshcd_vops_tx_eqtr_notify(hba, POST_CHANGE, pwr_mode);
+	notify_ret = ufshcd_vops_tx_eqtr_notify(hba, POST_CHANGE, pwr_mode);
+	if (notify_ret)
+		dev_err(hba->dev, "TX EQTR POST_CHANGE notify failed: %d\n", notify_ret);
+
+	if (!ret)
+		ret = notify_ret;
 
 out:
 	if (ret)
-- 
2.34.1



^ permalink raw reply related

* Re: [PATCH] ARM: enable interrupts when arm_notify_die() is handling user mode errors
From: Sebastian Andrzej Siewior @ 2026-06-25 12:08 UTC (permalink / raw)
  To: Russell King
  Cc: Xie Yuanbin, clrkwllms, rostedt, linusw, arnd, linux-arm-kernel,
	linux-kernel, linux-rt-devel, liaohua4, lilinjie8
In-Reply-To: <aj0A3SjMhR24e8fP@shell.armlinux.org.uk>

On 2026-06-25 11:20:13 [+0100], Russell King wrote:
> > is not worth doing it? With this I can my little testcase working.
> 
> No, it isn't, because if you enable PERF_EVENTS then BKPT breaks.
> hw_breakpoint.c claims this vector.

I see.

…
> BKPT is a total mess.

Understood.

> > it does cond_local_irq_enable() which enables the interrupts if they
> > were enabled by the "caller", sends the signal (SIGTRAP).
> 
> I'm happy with that approach as far as interrupts go, but we can't
> change the behaviour for FSR=2 again, beyond fixing LinusW's
> commit (which has recently been reported as a regression.)
> 
> Note that the change which makes this raise a SIGTRAP rather than
> SIGBUS when PERF_EVENTS=y could _also_ be reported as a regression
> that we would have to fix, and making FSR=2 raise a SIGTRAP now
> could very well invite that regression to be reported.
> 
> Essentially, I don't think we can "fix" BKPT to always raise SIGTRAP.
> The BKPT instruction is something the kernel has never _officially_
> supported.

It looked like an easy fix. You explained that it is a bigger mess with
"other features" and so on. Given that and the fact that it was never
supported, I would appreciate just to enable interrupts before the
(SIGBUS) signal is sent.

Sebastian


^ permalink raw reply

* Re: [PATCH 0/2] gpio: fix sleeping-in-atomic in shared-proxy; restore meson non-sleeping
From: Viacheslav @ 2026-06-25 12:05 UTC (permalink / raw)
  To: Bartosz Golaszewski
  Cc: linux-gpio, linux-arm-kernel, linux-amlogic, linux-kernel,
	linux-rockchip
In-Reply-To: <CAMRc=MdP8Wf6QRXGHpb3KJW2KMidSe-0LeyKKTYix=wYKZcPuA@mail.gmail.com>

Hi!

24.06.2026 10:25, Bartosz Golaszewski wrote:
> On Tue, 23 Jun 2026 17:16:44 +0200, Robin Murphy <robin.murphy@arm.com> said:
>> On 11/06/2026 9:26 am, Marek Szyprowski wrote:
>>> Hi Viachesla,
>>>
>>> On 10.06.2026 17:32, Viacheslav Bocharov wrote:
>>>> gpio-shared-proxy chooses its descriptor lock (mutex vs spinlock) from
>>>> the underlying chip's can_sleep, but under that lock it calls config and
>>>> direction ops that reach sleeping pinctrl paths. On a controller with
>>>> non-sleeping MMIO value ops the lock is a spinlock, so a sleeping call
>>>> runs from atomic context:
>>>>
> 
> ...
> 
>>>
>>> I've checked this patchset with these two reverted and no warning was reported.
>>
>> If it hadn't already been fixed (...)
>>
> 
> About that - Viacheslav, do you still plan to submit v2 of this?

Thanks for review. I prepared and sent the second version of the patch today

> 
> Bart
> 
> _______________________________________________
> linux-amlogic mailing list
> linux-amlogic@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-amlogic



^ permalink raw reply

* [PATCH v2 0/2] gpio: fix sleeping-in-atomic in shared-proxy; restore meson non-sleeping
From: Viacheslav Bocharov @ 2026-06-25 11:57 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski
  Cc: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Marek Szyprowski, Robin Murphy, Diederik de Haas, linux-gpio,
	linux-arm-kernel, linux-amlogic, linux-kernel

gpio-shared-proxy chooses its descriptor lock (mutex vs spinlock) from
the underlying chip's can_sleep, but under that lock it calls config and
direction ops that reach sleeping pinctrl paths. On a controller with
non-sleeping MMIO value ops the lock is a spinlock, so a sleeping call
runs from atomic context:

  BUG: sleeping function called from invalid context
    ... pinctrl_gpio_set_config <- gpiochip_generic_config
    <- gpio_shared_proxy_set_config (voting spinlock held)
    <- ... <- mmc_pwrseq_simple_probe

This was reported on Khadas VIM3 and worked around for Amlogic by
commit 28f240683871 ("pinctrl: meson: mark the GPIO controller as
sleeping"), which marked the whole meson controller sleeping. That
workaround broke atomic value-path consumers: w1-gpio (1-Wire bitbang)
no longer detects devices, because its IRQ-disabled read slot calls the
non-cansleep gpiod_*_value() and now hits WARN_ON(can_sleep) per bit.

Patch 1 fixes the proxy locking generically (always a sleeping mutex).
Patch 2 then restores meson can_sleep=false, fixing 1-Wire.

Patch 1 has a trade-off: a proxied GPIO becomes sleeping, so consumers
gating on gpiod_cansleep() change behaviour. No current device needs
atomic (non-cansleep) value access on a shared GPIO -- every report
(Khadas VIM3, ODROID-M1, my test on JetHub D1+) is a shared reset line
(eMMC/SDIO pwrseq or PCIe reset) driven through the cansleep accessors,
which is what the proxy exists to vote on; bit-banging that needs atomic
access cannot work through voting anyway. An alternative that keeps
atomic value access (split locking) is possible but adds a second lock
and new race windows, so this series takes the simpler mutex-only
approach.

The two are a unit: patch 2 must not be applied without patch 1,
otherwise the original VIM3 splat returns on boards that share a meson
GPIO -- please keep the order. I have not Cc'd stable; I will request
stable backports separately once both patches have landed.

Changes since v1:
- gpio: shared-proxy: open-code the descriptor mutex; drop the
  gpio_shared_desc_lock guard and the gpio_shared_lockdep_assert()
  helper, move the mutex rationale to the can_sleep assignment. No
  functional change.

v1: https://lore.kernel.org/linux-gpio/20260610153329.937833-1-v@baodeep.com/

Viacheslav Bocharov (2):
  gpio: shared-proxy: always serialize with a sleeping mutex
  pinctrl: meson: restore non-sleeping GPIO access

 drivers/gpio/gpio-shared-proxy.c      | 66 +++++++++++----------------
 drivers/gpio/gpiolib-shared.c         |  9 +---
 drivers/gpio/gpiolib-shared.h         | 28 +-----------
 drivers/pinctrl/meson/pinctrl-meson.c |  2 +-
 4 files changed, 30 insertions(+), 75 deletions(-)


base-commit: 840ef6c78e6a2f694b578ecb9063241c992aaa9e
--
2.54.0



^ permalink raw reply

* [PATCH v2 2/2] pinctrl: meson: restore non-sleeping GPIO access
From: Viacheslav Bocharov @ 2026-06-25 11:57 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski
  Cc: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Marek Szyprowski, Robin Murphy, Diederik de Haas, linux-gpio,
	linux-arm-kernel, linux-amlogic, linux-kernel
In-Reply-To: <20260625115718.1678991-1-v@baodeep.com>

Commit 28f240683871 ("pinctrl: meson: mark the GPIO controller as
sleeping") set gpio_chip.can_sleep = true to work around
gpio-shared-proxy holding a spinlock across a sleeping pinctrl config
path. That locking bug is now fixed in the shared-proxy itself ("gpio:
shared-proxy: always serialize with a sleeping mutex"), so the
controller-wide workaround is no longer needed; the meson GPIO
controller does not sleep.

meson_gpio_get/set/direction_* access MMIO through regmap. The
regmap_mmio bus uses fast I/O (spinlock) locking, so these value
callbacks do not contain sleeping operations. Since gpio_chip.can_sleep
describes the get/set value path, restore can_sleep = false.

Marking the controller sleeping also broke atomic value consumers such
as w1-gpio (1-Wire bitbang): w1_io.c runs its read time slot under
local_irq_save() and uses the non-cansleep gpiod_set_value() /
gpiod_get_value(), which with can_sleep=true trigger WARN_ON(can_sleep)
in gpiolib on every transferred bit (from w1_gpio_write_bit() /
w1_gpio_read_bit() via w1_reset_bus() and w1_search()). The printk and
stack dump inside the IRQs-off, microsecond-scale time slot destroy the
bit timing, so reset/presence detection and ROM search fail: the bus
master registers but w1_master_slave_count stays at 0 and no devices
are found. Verified on an Amlogic A113X board (DS18B20 on GPIOA_14):
with can_sleep restored to false the warnings are gone and the sensor
is detected and read again.

This must not be applied or backported without the shared-proxy locking
fix above; otherwise the original Khadas VIM3 splat returns on boards
that genuinely share a meson GPIO.

Fixes: 28f240683871 ("pinctrl: meson: mark the GPIO controller as sleeping")
Link: https://lore.kernel.org/all/20260105150509.56537-1-bartosz.golaszewski@oss.qualcomm.com/
Signed-off-by: Viacheslav Bocharov <v@baodeep.com>
---
v1: https://lore.kernel.org/linux-gpio/20260610153329.937833-3-v@baodeep.com/

 drivers/pinctrl/meson/pinctrl-meson.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/pinctrl/meson/pinctrl-meson.c b/drivers/pinctrl/meson/pinctrl-meson.c
index 4507dc8b5563..18295b15ecd9 100644
--- a/drivers/pinctrl/meson/pinctrl-meson.c
+++ b/drivers/pinctrl/meson/pinctrl-meson.c
@@ -619,7 +619,7 @@ static int meson_gpiolib_register(struct meson_pinctrl *pc)
 	pc->chip.set = meson_gpio_set;
 	pc->chip.base = -1;
 	pc->chip.ngpio = pc->data->num_pins;
-	pc->chip.can_sleep = true;
+	pc->chip.can_sleep = false;
 
 	ret = gpiochip_add_data(&pc->chip, pc);
 	if (ret) {
-- 
2.54.0



^ permalink raw reply related

* [PATCH v2 1/2] gpio: shared-proxy: always serialize with a sleeping mutex
From: Viacheslav Bocharov @ 2026-06-25 11:57 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski
  Cc: Neil Armstrong, Kevin Hilman, Jerome Brunet, Martin Blumenstingl,
	Marek Szyprowski, Robin Murphy, Diederik de Haas, linux-gpio,
	linux-arm-kernel, linux-amlogic, linux-kernel
In-Reply-To: <20260625115718.1678991-1-v@baodeep.com>

The shared GPIO descriptor used either a mutex or a spinlock, chosen at
runtime from the underlying chip's can_sleep:

	shared_desc->can_sleep = gpiod_cansleep(shared_desc->desc);
	... if (can_sleep) mutex_lock(); else spin_lock_irqsave();

can_sleep describes only the value path (->get/->set). Under the same
lock, however, the proxy may call gpiod_set_config() and
gpiod_direction_*(), which can reach pinctrl paths that take a mutex
(e.g. gpiod_set_config() -> gpiochip_generic_config() ->
pinctrl_gpio_set_config()), independent of can_sleep. On a controller
with non-sleeping MMIO value ops the descriptor lock was a spinlock, so
the sleeping pinctrl call ran from atomic context. Reproduced on an
Amlogic A113X board with the workaround from commit 28f240683871
("pinctrl: meson: mark the GPIO controller as sleeping") reverted; the
original Khadas VIM3 report hit the same path:

	BUG: sleeping function called from invalid context
	  __mutex_lock
	  pinctrl_get_device_gpio_range
	  pinctrl_gpio_set_config
	  gpiochip_generic_config
	  gpiod_set_config
	  gpio_shared_proxy_set_config   <- voting spinlock held
	  ...
	  mmc_pwrseq_simple_probe

The spinlock existed to take the value vote from atomic context, but the
vote and the (possibly sleeping) control operations share the same state
and lock, so this scheme cannot serialize config under a mutex and still
offer atomic value access. Always serialize the shared descriptor with a
mutex instead and mark the proxy a sleeping gpiochip, driving the
underlying GPIO through the cansleep value accessors: those are valid
for both sleeping and non-sleeping chips, so value access keeps working
on fast controllers, at the cost of no longer being atomic.

This is observable: consumers gating on gpiod_cansleep() take their
sleeping branch on a proxied GPIO (mmc-pwrseq-emmc skips its
emergency-restart reset handler; its normal reset is unaffected), and
consumers that reject sleeping GPIOs (pwm-gpio, ps2-gpio, ...) would
fail to probe. Such atomic users do not share a pin through the proxy,
whose purpose is voting on shared reset/enable lines. The same narrowing
already applies on Amlogic since that workaround, and rockchip
addressed the identical splat per-driver in commit 7ca497be0016 ("gpio:
rockchip: Stop calling pinctrl for set_direction"); fixing the proxy
addresses the locking error once, for every controller.

The lock type was added by commit a060b8c511ab ("gpiolib: implement
low-level, shared GPIO support"); the sleeping call under it arrived with
the proxy driver.

Fixes: e992d54c6f97 ("gpio: shared-proxy: implement the shared GPIO proxy driver")
Reported-by: Marek Szyprowski <m.szyprowski@samsung.com>
Closes: https://lore.kernel.org/all/00107523-7737-4b92-a785-14ce4e93b8cb@samsung.com/
Signed-off-by: Viacheslav Bocharov <v@baodeep.com>
---
v1 -> v2: open-code the descriptor mutex; drop the gpio_shared_desc_lock
          guard and the gpio_shared_lockdep_assert() helper, use
          guard(mutex) and lockdep_assert_held() directly; move the
          mutex rationale from the header to the can_sleep assignment in
          probe.

v1: https://lore.kernel.org/linux-gpio/20260610153329.937833-2-v@baodeep.com/

 drivers/gpio/gpio-shared-proxy.c | 66 +++++++++++++-------------------
 drivers/gpio/gpiolib-shared.c    |  9 +----
 drivers/gpio/gpiolib-shared.h    | 28 +-------------
 3 files changed, 29 insertions(+), 74 deletions(-)

diff --git a/drivers/gpio/gpio-shared-proxy.c b/drivers/gpio/gpio-shared-proxy.c
index 6941e4be6cf1..0cd52015b731 100644
--- a/drivers/gpio/gpio-shared-proxy.c
+++ b/drivers/gpio/gpio-shared-proxy.c
@@ -9,8 +9,10 @@
 #include <linux/err.h>
 #include <linux/gpio/consumer.h>
 #include <linux/gpio/driver.h>
+#include <linux/lockdep.h>
 #include <linux/mod_devicetable.h>
 #include <linux/module.h>
+#include <linux/mutex.h>
 #include <linux/string_choices.h>
 #include <linux/types.h>
 
@@ -32,7 +34,7 @@ gpio_shared_proxy_set_unlocked(struct gpio_shared_proxy_data *proxy,
 	struct gpio_desc *desc = shared_desc->desc;
 	int ret = 0;
 
-	gpio_shared_lockdep_assert(shared_desc);
+	lockdep_assert_held(&shared_desc->mutex);
 
 	if (value) {
 	       /* User wants to set value to high. */
@@ -89,7 +91,7 @@ static int gpio_shared_proxy_request(struct gpio_chip *gc, unsigned int offset)
 	struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
 	struct gpio_shared_desc *shared_desc = proxy->shared_desc;
 
-	guard(gpio_shared_desc_lock)(shared_desc);
+	guard(mutex)(&shared_desc->mutex);
 
 	proxy->shared_desc->usecnt++;
 
@@ -105,11 +107,11 @@ static void gpio_shared_proxy_free(struct gpio_chip *gc, unsigned int offset)
 	struct gpio_shared_desc *shared_desc = proxy->shared_desc;
 	int ret;
 
-	guard(gpio_shared_desc_lock)(shared_desc);
+	guard(mutex)(&shared_desc->mutex);
 
 	if (proxy->voted_high) {
 		ret = gpio_shared_proxy_set_unlocked(proxy,
-			shared_desc->can_sleep ? gpiod_set_value_cansleep : gpiod_set_value, 0);
+			gpiod_set_value_cansleep, 0);
 		if (ret)
 			dev_err(proxy->dev,
 				"Failed to unset the shared GPIO value on release: %d\n", ret);
@@ -129,7 +131,7 @@ static int gpio_shared_proxy_set_config(struct gpio_chip *gc,
 	struct gpio_desc *desc = shared_desc->desc;
 	int ret;
 
-	guard(gpio_shared_desc_lock)(shared_desc);
+	guard(mutex)(&shared_desc->mutex);
 
 	if (shared_desc->usecnt > 1) {
 		if (shared_desc->cfg != cfg) {
@@ -157,7 +159,7 @@ static int gpio_shared_proxy_direction_input(struct gpio_chip *gc,
 	struct gpio_desc *desc = shared_desc->desc;
 	int dir;
 
-	guard(gpio_shared_desc_lock)(shared_desc);
+	guard(mutex)(&shared_desc->mutex);
 
 	if (shared_desc->usecnt == 1) {
 		dev_dbg(proxy->dev,
@@ -187,7 +189,7 @@ static int gpio_shared_proxy_direction_output(struct gpio_chip *gc,
 	struct gpio_desc *desc = shared_desc->desc;
 	int ret, dir;
 
-	guard(gpio_shared_desc_lock)(shared_desc);
+	guard(mutex)(&shared_desc->mutex);
 
 	if (shared_desc->usecnt == 1) {
 		dev_dbg(proxy->dev,
@@ -222,13 +224,6 @@ static int gpio_shared_proxy_direction_output(struct gpio_chip *gc,
 	return gpio_shared_proxy_set_unlocked(proxy, gpiod_direction_output, value);
 }
 
-static int gpio_shared_proxy_get(struct gpio_chip *gc, unsigned int offset)
-{
-	struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
-
-	return gpiod_get_value(proxy->shared_desc->desc);
-}
-
 static int gpio_shared_proxy_get_cansleep(struct gpio_chip *gc,
 					  unsigned int offset)
 {
@@ -237,29 +232,15 @@ static int gpio_shared_proxy_get_cansleep(struct gpio_chip *gc,
 	return gpiod_get_value_cansleep(proxy->shared_desc->desc);
 }
 
-static int gpio_shared_proxy_do_set(struct gpio_shared_proxy_data *proxy,
-				    int (*set_func)(struct gpio_desc *desc, int value),
-				    int value)
-{
-	guard(gpio_shared_desc_lock)(proxy->shared_desc);
-
-	return gpio_shared_proxy_set_unlocked(proxy, set_func, value);
-}
-
-static int gpio_shared_proxy_set(struct gpio_chip *gc, unsigned int offset,
-				 int value)
-{
-	struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
-
-	return gpio_shared_proxy_do_set(proxy, gpiod_set_value, value);
-}
-
 static int gpio_shared_proxy_set_cansleep(struct gpio_chip *gc,
 					  unsigned int offset, int value)
 {
 	struct gpio_shared_proxy_data *proxy = gpiochip_get_data(gc);
 
-	return gpio_shared_proxy_do_set(proxy, gpiod_set_value_cansleep, value);
+	guard(mutex)(&proxy->shared_desc->mutex);
+
+	return gpio_shared_proxy_set_unlocked(proxy, gpiod_set_value_cansleep,
+					      value);
 }
 
 static int gpio_shared_proxy_get_direction(struct gpio_chip *gc,
@@ -302,20 +283,25 @@ static int gpio_shared_proxy_probe(struct auxiliary_device *adev,
 	gc->label = dev_name(dev);
 	gc->parent = dev;
 	gc->owner = THIS_MODULE;
-	gc->can_sleep = shared_desc->can_sleep;
+	/*
+	 * Under the descriptor mutex the proxy may call
+	 * gpiod_set_config()/gpiod_direction_*(), which can reach pinctrl
+	 * paths that take a mutex (e.g. gpiod_set_config() ->
+	 * gpiochip_generic_config() -> pinctrl_gpio_set_config()), independent
+	 * of the underlying chip's can_sleep. So the descriptor lock must be a
+	 * mutex and the proxy gpiochip is therefore always sleeping; drive the
+	 * underlying GPIO through the cansleep value accessors, which are valid
+	 * for both sleeping and non-sleeping chips.
+	 */
+	gc->can_sleep = true;
 
 	gc->request = gpio_shared_proxy_request;
 	gc->free = gpio_shared_proxy_free;
 	gc->set_config = gpio_shared_proxy_set_config;
 	gc->direction_input = gpio_shared_proxy_direction_input;
 	gc->direction_output = gpio_shared_proxy_direction_output;
-	if (gc->can_sleep) {
-		gc->set = gpio_shared_proxy_set_cansleep;
-		gc->get = gpio_shared_proxy_get_cansleep;
-	} else {
-		gc->set = gpio_shared_proxy_set;
-		gc->get = gpio_shared_proxy_get;
-	}
+	gc->set = gpio_shared_proxy_set_cansleep;
+	gc->get = gpio_shared_proxy_get_cansleep;
 	gc->get_direction = gpio_shared_proxy_get_direction;
 	gc->to_irq = gpio_shared_proxy_to_irq;
 
diff --git a/drivers/gpio/gpiolib-shared.c b/drivers/gpio/gpiolib-shared.c
index de72776fb154..495bd3d0ddf0 100644
--- a/drivers/gpio/gpiolib-shared.c
+++ b/drivers/gpio/gpiolib-shared.c
@@ -627,8 +627,7 @@ static void gpio_shared_release(struct kref *kref)
 
 	shared_desc = entry->shared_desc;
 	gpio_device_put(shared_desc->desc->gdev);
-	if (shared_desc->can_sleep)
-		mutex_destroy(&shared_desc->mutex);
+	mutex_destroy(&shared_desc->mutex);
 	kfree(shared_desc);
 	entry->shared_desc = NULL;
 }
@@ -659,11 +658,7 @@ gpiod_shared_desc_create(struct gpio_shared_entry *entry)
 	}
 
 	shared_desc->desc = &gdev->descs[entry->offset];
-	shared_desc->can_sleep = gpiod_cansleep(shared_desc->desc);
-	if (shared_desc->can_sleep)
-		mutex_init(&shared_desc->mutex);
-	else
-		spin_lock_init(&shared_desc->spinlock);
+	mutex_init(&shared_desc->mutex);
 
 	return shared_desc;
 }
diff --git a/drivers/gpio/gpiolib-shared.h b/drivers/gpio/gpiolib-shared.h
index 15e72a8dcdb1..bbdc0ab7b647 100644
--- a/drivers/gpio/gpiolib-shared.h
+++ b/drivers/gpio/gpiolib-shared.h
@@ -3,10 +3,7 @@
 #ifndef __LINUX_GPIO_SHARED_H
 #define __LINUX_GPIO_SHARED_H
 
-#include <linux/cleanup.h>
-#include <linux/lockdep.h>
 #include <linux/mutex.h>
-#include <linux/spinlock.h>
 
 struct gpio_device;
 struct gpio_desc;
@@ -42,35 +39,12 @@ static inline int gpio_shared_add_proxy_lookup(struct device *consumer,
 
 struct gpio_shared_desc {
 	struct gpio_desc *desc;
-	bool can_sleep;
 	unsigned long cfg;
 	unsigned int usecnt;
 	unsigned int highcnt;
-	union {
-		struct mutex mutex;
-		spinlock_t spinlock;
-	};
+	struct mutex mutex; /* serializes all proxy operations on this descriptor */
 };
 
 struct gpio_shared_desc *devm_gpiod_shared_get(struct device *dev);
 
-DEFINE_LOCK_GUARD_1(gpio_shared_desc_lock, struct gpio_shared_desc,
-	if (_T->lock->can_sleep)
-		mutex_lock(&_T->lock->mutex);
-	else
-		spin_lock_irqsave(&_T->lock->spinlock, _T->flags),
-	if (_T->lock->can_sleep)
-		mutex_unlock(&_T->lock->mutex);
-	else
-		spin_unlock_irqrestore(&_T->lock->spinlock, _T->flags),
-	unsigned long flags)
-
-static inline void gpio_shared_lockdep_assert(struct gpio_shared_desc *shared_desc)
-{
-	if (shared_desc->can_sleep)
-		lockdep_assert_held(&shared_desc->mutex);
-	else
-		lockdep_assert_held(&shared_desc->spinlock);
-}
-
 #endif /* __LINUX_GPIO_SHARED_H */
-- 
2.54.0



^ permalink raw reply related

* [PATCH] arm64: mm: refresh stale pmd snapshot after split_contpmd()
From: lirongqing @ 2026-06-25 11:39 UTC (permalink / raw)
  To: Catalin Marinas, Will Deacon, Ryan Roberts, Ard Biesheuvel,
	David Hildenbrand, Anshuman Khandual, Kevin Brodsky, Yang Shi,
	Chaitanya S Prakash, linux-arm-kernel, linux-kernel
  Cc: Li RongQing

From: Li RongQing <lirongqing@baidu.com>

split_contpmd() modifies the pmd entries in-place by clearing the CONT
bit, but the local 'pmd' variable still holds the old snapshot with CONT
set. The subsequent split_pmd() call uses this stale value to derive the
pgprot for the new PTE entries via pmd_pgprot(), causing the resulting
PTEs to be populated with incorrect protection bits.

Fix this by re-reading the pmd from memory after split_contpmd() returns
in both call sites: split_kernel_leaf_mapping_locked() and
split_to_ptes_pmd_entry().

Fixes: a166563e7ec3 ("arm64: mm: support large block mapping when rodata=full")
Signed-off-by: Li RongQing <lirongqing@baidu.com>
---
 arch/arm64/mm/mmu.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/arch/arm64/mm/mmu.c b/arch/arm64/mm/mmu.c
index 12e862c..e510336 100644
--- a/arch/arm64/mm/mmu.c
+++ b/arch/arm64/mm/mmu.c
@@ -746,8 +746,10 @@ static int split_kernel_leaf_mapping_locked(unsigned long addr)
 	if (!pmd_present(pmd))
 		goto out;
 	if (pmd_leaf(pmd)) {
-		if (pmd_cont(pmd))
+		if (pmd_cont(pmd)) {
 			split_contpmd(pmdp);
+			pmd = pmdp_get(pmdp);
+		}
 		/*
 		 * PMD: If addr is PMD aligned then addr already describes a
 		 * leaf boundary. Otherwise, split to contpte.
@@ -891,8 +893,10 @@ static int split_to_ptes_pmd_entry(pmd_t *pmdp, unsigned long addr,
 	int ret = 0;
 
 	if (pmd_leaf(pmd)) {
-		if (pmd_cont(pmd))
+		if (pmd_cont(pmd)) {
 			split_contpmd(pmdp);
+			pmd = pmdp_get(pmdp);
+		}
 		ret = split_pmd(pmdp, pmd, gfp, false);
 
 		/*
-- 
2.9.4



^ permalink raw reply related

* Re: [PATCH v15 08/11] arm64/ptrace: Define and use _TIF_SYSCALL_EXIT_WORK
From: Jinjie Ruan @ 2026-06-25 11:41 UTC (permalink / raw)
  To: Ada Couprie Diaz
  Cc: catalin.marinas, will, oleg, tglx, peterz, luto, kees, wad,
	mark.rutland, yeoreum.yun, linusw, kevin.brodsky, ldv, thuth,
	james.morse, song, anshuman.khandual, broonie, ryan.roberts,
	pengcan, liqiang01, linux-arm-kernel, linux-kernel
In-Reply-To: <073a55f2-54ff-4770-8457-bef164261a87@arm.com>



On 6/24/2026 10:53 PM, Ada Couprie Diaz wrote:
> On 11/05/2026 10:21, Jinjie Ruan wrote:
>> Introduce _TIF_SYSCALL_EXIT_WORK to filter out entry-only flags
>> during the syscall exit path. This aligns arm64 with the generic
>> entry framework's SYSCALL_WORK_EXIT semantics.
>>
>> [Rationale]
>> The current syscall exit path uses _TIF_SYSCALL_WORK to decide whether
>> to invoke syscall_exit_work(). However, _TIF_SYSCALL_WORK includes
>> flags that are only relevant during syscall entry:
>>
>> 1. _TIF_SECCOMP: Seccomp filtering (__secure_computing) only runs
>>    on entry. There is no seccomp callback for syscall exit.
>>
>> 2. _TIF_SYSCALL_EMU: In PTRACE_SYSEMU mode, the syscall is
>>    intercepted and skipped on entry. Since the syscall is never
>>    executed, reporting a syscall exit stop is unnecessary.
>>
>> [Changes]
>> - Define _TIF_SYSCALL_EXIT_WORK: A new mask containing only flags
>>    requiring exit processing: _TIF_SYSCALL_TRACE, _TIF_SYSCALL_AUDIT,
>>    and _TIF_SYSCALL_TRACEPOINT.
>>
>> - Update exit path: Use _TIF_SYSCALL_EXIT_WORK in
>>    syscall_exit_to_user_mode_work() to avoid redundant calls to
>>    audit and ptrace reporting when only entry-flags are set.
>>
>> - Cleanup: Remove the has_syscall_work() helper as it is no longer
>>    needed. Direct flag comparison is now used to distinguish between
>>    entry and exit work requirements.
>>
>> [Impact]
>> audit_syscall_exit() and report_syscall_exit() will no longer be
>> triggered for seccomp-only or emu-only syscalls. This matches the
>> generic entry behavior and improves efficiency by skipping unnecessary
>> exit processing.
>>
>> Cc: Mark Rutland <mark.rutland@arm.com>
>> Cc: Will Deacon <will@kernel.org>
>> Cc: Catalin Marinas <catalin.marinas@arm.com>
>> Reviewed-by: Linus Walleij <linusw@kernel.org>
>> Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
>> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
>> ---
> 
> Reviewed-by: Ada Couprie Diaz <ada.coupriediaz@arm.com>
> 
> Definitely not related to this series, but it feels like this brings us
> quite close to being able to switch to generic TIF flags as well :
> only TIF_FOREIGN_PSTATE and TIF_MTE_ASYNC_FAULT
> would need to be moved to the upper 16 bits,
> with TIF_RESTORE_SIGMASK and TIF_MEMDIE freeing two slots there.
> 
> Not sure how important the bit number changes would be and if any
> of the extra generic bits require any arch support (TIF_POLLING_NRFLAG,
> TIF_USER_RETURN_NOTIFY, TIF_RSEQ, TIF_HRTIMER_REARM)...
> 
> But again, just thinking out loud !

Hi Ada,

You have incredible foresight! You are absolutely right that this series
lays the perfect groundwork for switching arm64 to generic TIF flags
(HAVE_GENERIC_TIF_BITS).

I am happy to share that I have already implemented exactly what you
described—including migrating the architecture-specific flags to the
upper 16 bits and enabling the generic TIF infrastructure—in a separate,
dedicated patch series.

You can find the implementation and discussion here:

https://lore.kernel.org/all/20260320104222.1381274-1-ruanjinjie@huawei.com/

Since that series directly builds on top of the cleanups and
infrastructure introduced here, I plan to actively push it forward right
after we get this core generic entry conversion landed.

Thanks again for looking so far ahead and validating the direction!

Best regards,
Jinjie

> Thanks,
> Ada
> 
> 



^ permalink raw reply

* Re: [PATCH v15 01/11] entry: Fix potential syscall truncation in syscall_trace_enter()
From: Jinjie Ruan @ 2026-06-25 11:33 UTC (permalink / raw)
  To: Thomas Gleixner, catalin.marinas, will, oleg, peterz, luto, kees,
	wad, mark.rutland, yeoreum.yun, linusw, kevin.brodsky, ldv, thuth,
	james.morse, song, ada.coupriediaz, anshuman.khandual, broonie,
	ryan.roberts, pengcan, liqiang01, linux-arm-kernel, linux-kernel
In-Reply-To: <874iirlu50.ffs@fw13>



On 6/25/2026 1:34 AM, Thomas Gleixner wrote:
> On Mon, May 11 2026 at 17:20, Jinjie Ruan wrote:
>> In syscall_trace_enter(), the current logic returns "ret ? : syscall".
>> While __secure_computing() currently only returns 0 (allow) or -1 (kill),
>> this "ret ? : syscall" pattern is conceptually flawed.
>>
>> If __secure_computing() were to return a non-zero value that isn't -1, it
>> would unintentionally override the actual system call number. This logic
>> is redundant because if seccomp denies the syscall, the execution path
>> should already be handled by the caller based on the error return, rather
>> than conflating the return code with the syscall number.
>>
>> Fix it by explicitly returning the syscall number. This ensures
>> the syscall register remains untainted by the trace return values and
>> aligns with the expectation that seccomp-related interceptions are
>> handled via the -1 return status.
>>
>> Cc: Thomas Gleixner <tglx@kernel.org>
>> Fixes: 142781e108b1 ("entry: Provide generic syscall entry functionality")
> 
> What's fixed here?
> 
> A potential future change of the __secure_computing() return value
> requires that _ALL_ callsites of that function have to be audited
> and fixed up, no?
> 
> So the change is not a fix it's an optimization to get rid of the extra
> conditional.
> 
> If you really want to sanitize this, then change the
> __secure_computing() return type to boolean (true = allow, false =
> fail) and fixup the two dozen or so call sites.

Hi, Thomas

That's an excellent suggestion. Changing __secure_computing() to a
boolean return type makes the seccomp boundary much more intuitive and
cleans up a lot of historical legacy.

I am happy to take this on. I will implement this sanitization by
changing the return type to bool and fixing up all the ~24 call sites
across the tree. To keep the series clean and bisectable, I will include
this as a standalone prerequisite cleanup patch at the very beginning of
the v16 series.

Thanks for pushing for a cleaner codebase!

Best regards,
Jinjie

> 
> Thanks,
> 
>         tglx
> 



^ permalink raw reply

* [PATCH] arm64: dts: ti: k3-am62a7-sk: Add bootph-all property in cpsw_mac_syscon node
From: Chintan Vankar @ 2026-06-25 11:32 UTC (permalink / raw)
  To: Conor Dooley, Krzysztof Kozlowski, Rob Herring, Tero Kristo,
	Vignesh Raghavendra, Nishanth Menon
  Cc: c-vankar, linux-kernel, devicetree, linux-arm-kernel

Ethernet boot requires CPSW node to be present starting from R5 SPL stage.
Add "bootph-all" property in CPSW MAC's eFuse node "cpsw_mac_syscon" to
enable this node during SPL stage along with later boot stage so that CPSW
port will get static MAC address.

Signed-off-by: Chintan Vankar <c-vankar@ti.com>
---

Hello All,

This patch is based on linux-next tagged next-20260623.

 arch/arm64/boot/dts/ti/k3-am62a7-sk.dts | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts b/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts
index 821a9705bb7d..d3b3675e7a8f 100644
--- a/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts
+++ b/arch/arm64/boot/dts/ti/k3-am62a7-sk.dts
@@ -230,6 +230,10 @@ AM62AX_MCU_IOPAD(0x0030, PIN_OUTPUT, 0) /* (C8) WKUP_UART0_RTSn */
 	};
 };
 
+&cpsw_mac_syscon {
+	bootph-all;
+};
+
 /* WKUP UART0 is used for DM firmware logs */
 &wkup_uart0 {
 	pinctrl-names = "default";
-- 
2.34.1



^ permalink raw reply related

* Re: [PATCH v2] arm64: ptrace: use live x0 for seccomp and audit after ptrace
From: Yiqi Sun @ 2026-06-25 11:30 UTC (permalink / raw)
  To: catalin.marinas, linux-arm-kernel
  Cc: linux-kernel, rmk+kernel, ruanjinjie, will
In-Reply-To: <2f435bab0d61d0bf8fbaa54203525aae8e8f5371.1782384161.git.sunyiqixm@gmail.com>

On Thu, Jun 25, 2026 at 07:11:39PM +0800, Yiqi Sun wrote:
> [PATCH v2] arm64: ptrace: use live x0 for seccomp and audit after ptrace

Sorry, this patch was sent twice by mistake.
Please ignore this duplicate copy.


^ permalink raw reply

* Re: [PATCH v15 10/11] arm64: entry: Convert to generic entry
From: Jinjie Ruan @ 2026-06-25 11:27 UTC (permalink / raw)
  To: Ada Couprie Diaz
  Cc: catalin.marinas, will, oleg, tglx, peterz, luto, kees, wad,
	mark.rutland, yeoreum.yun, linusw, kevin.brodsky, ldv, thuth,
	james.morse, song, anshuman.khandual, broonie, ryan.roberts,
	pengcan, liqiang01, linux-arm-kernel, linux-kernel
In-Reply-To: <cfa07466-225b-476f-adf9-346bd1377a4d@arm.com>



On 6/24/2026 11:32 PM, Ada Couprie Diaz wrote:
> On 11/05/2026 10:21, Jinjie Ruan wrote:
>> Implement the generic entry framework for arm64 to handle system call
>> entry and exit. This follows the migration of x86, RISC-V, and LoongArch,
>> consolidating architecture-specific syscall tracing and auditing into
>> the common kernel entry infrastructure.
> If I understand correctly, as Syscall User Dispatch is gated being
> `CONFIG_GENERIC_ENTRY` only and handled via ptrace, this patch
> effectively enables Syscall User Dispatch for arm64.
> I think it would be great to mention it here explicitly !

Hi, Ada,

Yes, I mentioned it in the cover letter, will add it in next version.

>>
>> [Background]
>> Arm64 has already adopted generic IRQ entry. Completing the conversion
>> to the generic syscall entry framework reduces architectural divergence,
>> simplifies maintenance, and allows arm64 to automatically benefit from
>> improvements in the common entry code.
>>
>> [Changes]
>>
>> 1. Kconfig and Infrastructure:
>> - Select GENERIC_ENTRY and remove GENERIC_IRQ_ENTRY (now implied).
>>
>> - Migrate struct thread_info to use the syscall_work field instead
>>    of TIF flags for syscall-related tasks.
>>
>> 2. Thread Info and Flags:
>> - Remove definitions for TIF_SYSCALL_TRACE, TIF_SYSCALL_AUDIT,
>>    TIF_SYSCALL_TRACEPOINT, TIF_SECCOMP, and TIF_SYSCALL_EMU.
>>
>> - Replace _TIF_SYSCALL_WORK and _TIF_SYSCALL_EXIT_WORK with the
>>    generic SYSCALL_WORK bitmask.
>>
>> - Map single-step state to SYSCALL_EXIT_TRAP in debug-monitors.c.
>>
>> 3. Architecture-Specific Hooks (asm/entry-common.h):
>> - Implement arch_ptrace_report_syscall_entry() and _exit() by
>>    porting the existing arm64 logic to the generic interface.
>>
>> - Add arch_syscall_is_vdso_sigreturn() to asm/syscall.h to
>>    support Syscall User Dispatch (SUD).
> Related to the above : I feel this is missing an important information.
> Given that SUD is only controlled by `CONFIG_GENERIC_ENTRY`,
> converting to generic entry _requires_ supporting SUD, so we do it here.
> I think this would be important to mention, as I otherwise felt like this
> change did not belong in this patch.

Thanks for the excellent point.

I completely agree that bundling Syscall User Dispatch (SUD) support
with the core generic entry conversion makes the patch bloated and less
focused.

To address this, I will extract the Syscall User Dispatch support into a
completely standalone patch in the next version.

And I noticed that arch_syscall_is_vdso_sigreturn() returns false for
most architectures. It would make sense to refactor it to return false
by default.

> 
> General question that follows : does it make sense to require an arch
> to support Syscall User Dispatch to be able to convert to generic entry ?
> (I assume not really, given that only `arch_syscall_is_vdso_sigreturn()` is
> required on the arch side, but I am curious)

No, converting to generic entry doesn't architecturally require an arch
to force-enable SUD, but since the generic entry framework already
includes SUD support, the arch naturally gains the capability.

It is worth noting that enabling this capability has zero impact by
default. The functionality remains entirely dormant unless a userspace
application explicitly configures it via
prctl(PR_SET_SYSCALL_USER_DISPATCH, ...). Otherwise, the kernel logic
won't take effect at all.

Separating it into its own patch will make this relationship much
clearer in v16.

> 
>>
>> 4. Cleanup and Refactoring:
>> - Remove redundant arm64-specific syscall tracing functions from
>>    ptrace.c, including syscall_trace_enter(), syscall_exit_work(),
>>    and related audit/step helpers.
>>
>> - Update el0_svc_common() in syscall.c to use the generic
>>    syscall_work checks and entry/exit call sites.
>>
>> [Why this matters]
>> - Unified Interface: Aligns arm64 with the modern kernel entry standard.
>>
>> - Improved Maintainability: Bug fixes in kernel/entry/common.c now
>>    apply to arm64 automatically.
>>
>> - Feature Readiness: Simplifies the implementation of future
>>    cross-architecture syscall features.
>>
>> [Compatibility]
>> This conversion maintains full ABI compatibility with existing
>> userspace. The ptrace register-saving behavior, seccomp filtering, and
>> syscall tracing semantics remain identical to the previous
>> implementation.
> I agree, would it make sense to mention that there is no change related
> to RSEQ as arm64 does not have `HAVE_GENERIC_TIF_BITS` ? As that is
> part of generic entry, but is indeed a no-op for us.

Agreed. I will update the commit message to include this information in
v16. Thanks!

>>
>> Cc: Mark Rutland <mark.rutland@arm.com>
>> Cc: Will Deacon <will@kernel.org>
>> Cc: Catalin Marinas <catalin.marinas@arm.com>
>> Cc: Thomas Gleixner <tglx@kernel.org>
>> Cc: Peter Zijlstra <peterz@infradead.org>
>> Reviewed-by: Linus Walleij <linusw@kernel.org>
>> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org>
>> Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
>> Reviewed-by: Kevin Brodsky <kevin.brodsky@arm.com>
>> Suggested-by: Kevin Brodsky <kevin.brodsky@arm.com>
>> Suggested-by: Mark Rutland <mark.rutland@arm.com>
>> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
>> ---
>> [...]
>> diff --git a/arch/arm64/kernel/debug-monitors.c b/arch/arm64/kernel/
>> debug-monitors.c
>> index 29307642f4c9..e67643a70405 100644
>> --- a/arch/arm64/kernel/debug-monitors.c
>> +++ b/arch/arm64/kernel/debug-monitors.c
>> @@ -385,11 +385,18 @@ void user_enable_single_step(struct task_struct
>> *task)
>>         if (!test_and_set_ti_thread_flag(ti, TIF_SINGLESTEP))
>>           set_regs_spsr_ss(task_pt_regs(task));
>> +
>> +    /*
>> +     * Ensure that a trap is triggered once stepping out of a system
>> +     * call prior to executing any user instruction.
>> +     */
> I was a bit confused by the comment in isolation at first : we already
> have a signal that we are stepping and would need a trap, `TIF_SINGLESTEP`.
> Would it make sense to mention here that this is for/handled by the generic
> entry code ?
> Something along the lines of "[...], as the generic entry code does not
> check for `TIF_SINGLESTEP`.", or "Ensure that the generic entry code
> triggers a trap [...]", if you think its useful ?
>> +    set_task_syscall_work(task, SYSCALL_EXIT_TRAP);

That makes a lot of sense. The clarification is definitely useful to
prevent confusion for anyone looking at this architecture-specific bit
in the future.

I will update the comment in v16 to explicitly mention that this is
handled to ensure the generic entry code correctly triggers the trap, as
per your suggestion.

Thanks for the feedback!

Best regards,
Jinjie

>>   }
>>   NOKPROBE_SYMBOL(user_enable_single_step);
>>     void user_disable_single_step(struct task_struct *task)
>>   {
>>       clear_ti_thread_flag(task_thread_info(task), TIF_SINGLESTEP);
>> +    clear_task_syscall_work(task, SYSCALL_EXIT_TRAP);
>>   }
>>   NOKPROBE_SYMBOL(user_disable_single_step);
> 
> Apart from my minor nitpicks :
> 
> Reviewed-by: Ada Couprie Diaz <ada.coupriediaz@arm.com>
> 
> Thanks,
> Ada
> 
> 



^ permalink raw reply

* RE: [PATCH] i2c: imx: Fix slave registration error path and missing NULL check
From: Carlos Song (OSS) @ 2026-06-25 11:17 UTC (permalink / raw)
  To: Liem, Oleksij Rempel
  Cc: Andi Shyti, Pengutronix Kernel Team, Frank Li, Sascha Hauer,
	Fabio Estevam, Biwen Li, Wolfram Sang, linux-i2c@vger.kernel.org,
	imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org, stable@vger.kernel.org
In-Reply-To: <20260625071130.93544-1-liem16213@gmail.com>



> -----Original Message-----
> From: Liem <liem16213@gmail.com>
> Sent: Thursday, June 25, 2026 3:12 PM
> To: Oleksij Rempel <o.rempel@pengutronix.de>
> Cc: Andi Shyti <andi.shyti@kernel.org>; Pengutronix Kernel Team
> <kernel@pengutronix.de>; Frank Li <frank.li@nxp.com>; Sascha Hauer
> <s.hauer@pengutronix.de>; Fabio Estevam <festevam@gmail.com>; Biwen Li
> <biwen.li@nxp.com>; Wolfram Sang <wsa@kernel.org>;
> linux-i2c@vger.kernel.org; imx@lists.linux.dev;
> linux-arm-kernel@lists.infradead.org; linux-kernel@vger.kernel.org;
> stable@vger.kernel.org; Liem <liem16213@gmail.com>
> Subject: [PATCH] i2c: imx: Fix slave registration error path and missing NULL check
> 
> [You don't often get email from liem16213@gmail.com. Learn why this is
> important at https://aka.ms/LearnAboutSenderIdentification ]
> 
> There are two issues that affect the i2c-imx slave handling:
> 
> 1. In i2c_imx_reg_slave(), i2c_imx->slave is checked at the beginning
>    and the function returns -EBUSY if it is non-NULL.  If
>    pm_runtime_resume_and_get() fails later, the error path returns
>    without clearing i2c_imx->slave, leaving it non-NULL.  Subsequent
>    attempts to register a slave will then immediately fail with
>    -EBUSY, making it impossible to register the slave again.  Fix
>    by setting i2c_imx->slave = NULL on the error path.
> 
> 2. In i2c_imx_unreg_slave(), the slave pointer is set to NULL after
>    disabling interrupts.  However, a pending interrupt might already
>    have started a timer (e.g. for slave event processing) before
>    the pointer was cleared.  The timer callback
>    i2c_imx_slave_event() dereferences i2c_imx->slave without a
>    NULL check, which results in a use-after-free / NULL pointer
>    dereference.  Prevent this by checking that i2c_imx->slave is
>    valid before calling i2c_slave_event() and updating the
>    last_slave_event field.
> 
> Both issues can trigger a kernel oops or permanent slave registration failure under
> certain race conditions.  Add the missing NULL assignment and the missing NULL
> check to harden the slave path.
> 
> Fixes: f7414cd6923f ("i2c: imx: support slave mode for imx I2C driver")
> Cc: stable@vger.kernel.org
> Signed-off-by: Liem <liem16213@gmail.com>
> ---
>  drivers/i2c/busses/i2c-imx.c | 7 +++++--
>  1 file changed, 5 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-imx.c b/drivers/i2c/busses/i2c-imx.c index
> 28313d0fad37..4f7bcbeecfd0 100644
> --- a/drivers/i2c/busses/i2c-imx.c
> +++ b/drivers/i2c/busses/i2c-imx.c


Hi, Liem 

Thank you very much for this fix!
Looks like you are catching a corner bug and try to fix this for i2c-imx target mode.

Have you meet the issue on one real platform?

> @@ -775,8 +775,10 @@ static void i2c_imx_enable_bus_idle(struct
> imx_i2c_struct *i2c_imx)  static void i2c_imx_slave_event(struct imx_i2c_struct
> *i2c_imx,
>                                 enum i2c_slave_event event, u8 *val)  {
> -       i2c_slave_event(i2c_imx->slave, event, val);
> -       i2c_imx->last_slave_event = event;
> +       if (i2c_imx->slave) {
> +               i2c_slave_event(i2c_imx->slave, event, val);
> +               i2c_imx->last_slave_event = event;
> +       }

From i2c-imx.c driver, I notice this call trace is:

1. IRQ-> i2c_imx_slave_handle-> i2c_imx_slave_timeout(if in progress)-> i2c_imx_slave_finish_op
2. IRQ->i2c_imx_slave_finish_op

'''
In i2c_imx_unreg_slave(), the slave pointer is set to NULL after
disabling interrupts. However, a pending interrupt might already
have started a timer (e.g. for slave event processing) before
the pointer was cleared.
...

Yes, this may happen, then trigger slave hrtimer running. 

Go into i2c_imx_slave_finish_op().

you add a judgement in i2c_slave_event():

if (i2c_imx->slave) {
	i2c_slave_event(i2c_imx->slave, event, val);
	i2c_imx->last_slave_event = event;
}

At this time i2c_imx->slave= NULL, 
so i2c_slave_event(i2c_imx->slave, event, val) and i2c_imx->last_slave_event = event; won't run again.

in i2c_imx_slave_finish_op(),

Fall into" while (i2c_imx->last_slave_event != I2C_SLAVE_STOP)" the loop. So system maybe hang.

My idea is just cancel the slave timer and wait it finished after disabled IRQ.
If I am wrong please correct me. Thank you again.

static int i2c_imx_unreg_slave(struct i2c_client *client)
{
    imx_i2c_write_reg(0, i2c_imx, IMX_I2C_IADR);

    i2c_imx_reset_regs(i2c_imx);

+	hrtimer_cancel(&i2c_imx->slave_timer);

	i2c_imx->slave = NULL;
}

Carlos

> 
>  static void i2c_imx_slave_finish_op(struct imx_i2c_struct *i2c_imx) @@ -936,6
> +938,7 @@ static int i2c_imx_reg_slave(struct i2c_client *client)
>         /* Resume */
>         ret = pm_runtime_resume_and_get(i2c_imx->adapter.dev.parent);
>         if (ret < 0) {
> +               i2c_imx->slave = NULL;

This a good fix. I agree with this.

>                 dev_err(&i2c_imx->adapter.dev, "failed to resume i2c
> controller");
>                 return ret;
>         }
> --
> 2.34.1
> 



^ permalink raw reply

* Re: [PATCH 1/2] KVM: arm64: Fix sign-extension of MMIO loads
From: Fuad Tabba @ 2026-06-25 11:13 UTC (permalink / raw)
  To: Marc Zyngier
  Cc: Oliver Upton, Joey Gouly, Suzuki K Poulose, Zenghui Yu,
	Steffen Eiden, Catalin Marinas, Will Deacon, Shuah Khan,
	linux-arm-kernel, kvmarm, linux-kernel
In-Reply-To: <86fr2bq6b6.wl-maz@kernel.org>

On Thu, 25 Jun 2026 at 11:10, Marc Zyngier <maz@kernel.org> wrote:
>
> On Tue, 23 Jun 2026 14:51:49 +0100,
> Fuad Tabba <fuad.tabba@linux.dev> wrote:
> >
> > My reading of the ARM ARM is that the byte reversal is keyed on the access
> > size there too. It lives in Mem{size}, with the register width handled
> > separately by SignExtend(regsize):
> >
> >     data = Mem[address, 2];        // byte-reversed by the access size, BE
> >     X[t] = SignExtend(data, regsize);
> >
> > So vcpu_data_host_to_guest(..., len) swapping by len matches the Mem-side
> > reversal. Swapping by the register width would reorder bytes that were never
> > loaded. An LDRSH into Wt reads 2 bytes but would bswap 4: the halfword
> > reaches the helper as 0x0180 host-native, cpu_to_be32 turns it into
> > 0x80010000 instead of the 0x8001 cpu_to_be16 gives, and it never sign-extends
> > to 0xffff8001.
> >
> > If that reading holds, none of the helper's ops are individually wrong, and
> > the only bug was the order, with the sign-extend running before the swap and
> > the width mask then dropping it. But I've gone round in circles on endianness
> > before (to say the least), so please say if I've done it again.
>
> That's quite convincing.
>
> And the quoted pseudocode is much easier to reason about than the
> current blurb in the commit message. For reference, J1.2.3.111 Mem{}()
> is the relevant bit of the M.b spec and clearly shows that the access
> is done LE, and only then byteswapped. Can you please repaint the
> commit log to describe things in those terms?

Will do.

>
> Also, can you augment your test to cover for BE accesses from the
> guest if the HW supports it?

I'll see what I can come up with...

Cheers,
/fuad



>
> Thanks,
>
>         M.
>
> --
> Without deviation from the norm, progress is not possible.


^ permalink raw reply

* [PATCH v2] arm64: ptrace: use live x0 for seccomp and audit after ptrace
From: Yiqi Sun @ 2026-06-25 11:11 UTC (permalink / raw)
  To: sunyiqixm
  Cc: catalin.marinas, linux-arm-kernel, linux-kernel, rmk+kernel,
	ruanjinjie, will
In-Reply-To: <20260529065444.1336608-1-sunyiqixm@gmail.com>

On arm64, seccomp obtains syscall arguments via
syscall_get_arguments(), where arg0 is currently read from
regs->orig_x0. audit_syscall_entry() in syscall_trace_enter() also
takes arg0 from regs->orig_x0. However, the syscall wrapper consumes
live arguments from regs->regs[0..5].

A ptracer can modify x0 on syscall-enter stop before seccomp and audit
run, but cannot update orig_x0 through the native syscall-stop
interface. This can leave seccomp and audit checking stale arg0 while
the syscall executes with updated live x0.

Make both paths read arg0 from regs->regs[0], matching the actual
dispatch arguments and keeping seccomp and audit aligned after ptrace
updates.

Fixes: f27bb139c387 ("arm64: Miscellaneous library functions")
Signed-off-by: Yiqi Sun <sunyiqixm@gmail.com>
---
Changes in v2:
- Also switch the arm64 audit entry path to use live x0
- Clarify the orig_x0 synchronization comment in syscall_set_arguments()
---
 arch/arm64/include/asm/syscall.h | 7 +++----
 arch/arm64/kernel/ptrace.c       | 2 +-
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/asm/syscall.h
index 5e4c7fc44f73..0a44db425522 100644
--- a/arch/arm64/include/asm/syscall.h
+++ b/arch/arm64/include/asm/syscall.h
@@ -81,7 +81,7 @@ static inline void syscall_get_arguments(struct task_struct *task,
 					 struct pt_regs *regs,
 					 unsigned long *args)
 {
-	args[0] = regs->orig_x0;
+	args[0] = regs->regs[0];
 	args[1] = regs->regs[1];
 	args[2] = regs->regs[2];
 	args[3] = regs->regs[3];
@@ -101,9 +101,8 @@ static inline void syscall_set_arguments(struct task_struct *task,
 	regs->regs[5] = args[5];
 
 	/*
-	 * Also copy the first argument into orig_x0
-	 * so that syscall_get_arguments() would return it
-	 * instead of the previous value.
+	 * Keep orig_x0 in sync so syscall_rollback() and compat
+	 * register views see the updated first argument.
 	 */
 	regs->orig_x0 = regs->regs[0];
 }
diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
index 4d08598e2891..35dd86f9e87a 100644
--- a/arch/arm64/kernel/ptrace.c
+++ b/arch/arm64/kernel/ptrace.c
@@ -2426,7 +2426,7 @@ int syscall_trace_enter(struct pt_regs *regs)
 	if (test_thread_flag(TIF_SYSCALL_TRACEPOINT))
 		trace_sys_enter(regs, regs->syscallno);
 
-	audit_syscall_entry(regs->syscallno, regs->orig_x0, regs->regs[1],
+	audit_syscall_entry(regs->syscallno, regs->regs[0], regs->regs[1],
 			    regs->regs[2], regs->regs[3]);
 
 	return regs->syscallno;
-- 
2.34.1



^ permalink raw reply related

* Re: mm: opaque hardware page-table entry handles
From: Pedro Falcato @ 2026-06-25 11:08 UTC (permalink / raw)
  To: Muhammad Usama Anjum
  Cc: Andrew Morton, Lorenzo Stoakes, David Hildenbrand,
	Liam R. Howlett, Mike Rapoport, Ryan Roberts, Anshuman Khandual,
	Catalin Marinas, Will Deacon, Samuel Holland, linux-mm,
	linux-arm-kernel, linux-kernel
In-Reply-To: <66310292-f618-4497-bcaa-2a4b1240566c@arm.com>

On Thu, Jun 25, 2026 at 11:50:28AM +0100, Muhammad Usama Anjum wrote:
> On 24/06/2026 8:25 pm, Pedro Falcato wrote:
> > On Wed, Jun 24, 2026 at 03:09:08PM +0100, Usama Anjum wrote:
> >> Hi all,
> >>
> >> This is a direction-check with the wider community before spending time on the
> >> development. This picks up the idea that was raised and broadly agreed in the
> >> earlier thread (Ryan Roberts, Lorenzo Stoakes, David Hildenbrand) [1].
> >>
> >> The problem
> >> -----------
> >> Core MM code reaches page-table entries by raw pointer dereference (pte_t *,
> >> pmd_t *, *pud, ...) in places, implicitly assuming a single, uniform
> >> representation. Sprinkling getters wouldn't solve the problem entirely. The
> >> problem is one level up: the *pointer type* itself is overloaded. At each level
> >> there are really three distinct things:
> >>
> >>   1. a page-table entry value (pte_t, pmd_t, ...)
> >>   2. a pointer to an entry value, e.g. a pXX_t on the stack
> >>   3. a pointer to a live entry in the hardware page table
> >>
> >> Today (2) and (3) share the same type - pte_t *, pmd_t *, and so on. Nothing
> >> distinguishes a pointer into a live table from a pointer to a stack copy.
> >>
> >> A pointer to an on-stack entry value and a pointer to a live hardware entry have
> >> the same type, so the compiler cannot distinguish them. Passing the stack
> >> pointer to an arch helper that expects a hardware-entry pointer compiles fine,
> >> but is wrong - a bug class the type system makes invisible. It also blocks
> >> evolution: an arch helper may need to read beyond the addressed entry (e.g.
> >> adjacent or contiguous entries), which only makes sense for a real page-table
> >> pointer, not a stack copy.
> >>
> >> The idea
> >> --------
> >> Give (3) its own opaque type that cannot be dereferenced:
> >>
> >>     /* opaque handle to a HW page-table entry; not dereferenceable */
> >>     typedef struct {
> >> 	pte_t *ptr;
> >>     } hw_ptep;
> > 
> > I don't love typedefs that hide pointers.
> Nobody likes them. This is the only way so that by mistake stack pointers
> don't get reintroduced. Its also hard to catch such cases during review.

That's not true, you could have:

typedef struct { pteval_t pte; } sw_pte_t;

and

/* only usable by arch code and whoever wants to interpret these
 * types */
static inline sw_to_ptep(sw_pte_t *swptep)
{
	return (pte_t *) swptep;
}

and so on... Also, see Documentation/process/coding-style.rst 5) typedefs, it
explicitly warns against pointer typedefs.

> 
> > 
> >>
> >> With this:
> >>
> >>   - a stack value can no longer masquerade as a hardware table entry,
> >>   - a hardware handle can no longer be raw-dereferenced,
> >>   - cases that genuinely operate on a value can be refactored to pass the value
> >>     and let the caller, which knows whether it holds a handle or a stack copy,
> >>     read it once.
> > 
> > Just a small passing comment: how about doing it differently? like
> > 
> > typedef struct {
> > 	pte_t *ptep;
> > } sw_ptep_t;
> > 
> > or something like that. Were I to guess, referring to a pte_t on the stack
> > is much rarer than all the pte_t references to actual page tables. But maybe
> > reality doesn't match up with my guess :)
> We want to fix the current usages and future usages as well. sw_ptep_t can work
> for current usages, but it'll not force the new code to be written using correct
> notations.

I don't understand what you mean. pte_t is a perfectly correct notation,
it's just currently maybe too ambiguously overloaded.

> Apart from different types, another benefit of hw_pXXp would be that
> it'll become an opaque object which only architecture can manipulate. Hence
> architecture can decide howeverever it wants to manage them in certain cases.

That's already the case. pte_t is fully opaque apart from the little fact
that you can declare one on your stack. Introducing a different sw_pte_t
would further reinforce that. And if you want ways to find raw derefs on
pointers, we can simply slap on __attribute__((noderef)) (available in
sparse and clang) on those types after sw_pte_t is introduced and pte_t
is unambiguously a "hardware" PTE.

I dunno, I'm not convinced that changing around ~450 files is worth it, and
_if_ we want to do something like this I would strongly prefer the way that
is less churny.

-- 
Pedro


^ permalink raw reply

* [PATCH 2/2] iio: adc: Add Nuvoton MA35D1 EADC driver
From: Chi-Wen Weng @ 2026-06-25 11:06 UTC (permalink / raw)
  To: jic23, robh, krzk+dt, conor+dt
  Cc: dlechner, nuno.sa, andy, linux-arm-kernel, linux-iio, devicetree,
	linux-kernel, cwweng, cwweng.linux
In-Reply-To: <20260625110638.38438-1-cwweng.linux@gmail.com>

From: Chi-Wen Weng <cwweng@nuvoton.com>

Add an IIO driver for the Nuvoton MA35D1 Enhanced ADC controller.

The driver supports direct raw reads and triggered buffered capture. The
controller end-of-conversion interrupt is exposed as the device trigger
and is used to push samples into the IIO buffer.

Channels are described by firmware child nodes and can be configured as
single-ended or differential inputs. Since the differential enable bit is
global, mixed single-ended and differential buffered scans are rejected.

DMA support is intentionally not included in this initial upstream driver;
conversions are handled through the interrupt-driven path.

Signed-off-by: Chi-Wen Weng <cwweng@nuvoton.com>
---
 drivers/iio/adc/Kconfig       |  10 +
 drivers/iio/adc/Makefile      |   1 +
 drivers/iio/adc/ma35d1_eadc.c | 636 ++++++++++++++++++++++++++++++++++
 3 files changed, 647 insertions(+)
 create mode 100644 drivers/iio/adc/ma35d1_eadc.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 1c663c98c6c9..43409999a94b 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -981,6 +981,16 @@ config LTC2497
 	  To compile this driver as a module, choose M here: the module will be
 	  called ltc2497.
 
+config MA35D1_EADC
+	tristate "MA35D1 EADC driver"
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	help
+	  Say yes here to build support for MA35D1 EADC.
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called ma35d1.
+
 config MAX1027
 	tristate "Maxim max1027 ADC driver"
 	depends on SPI
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 707dd708912f..7b9b38688223 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -85,6 +85,7 @@ obj-$(CONFIG_LTC2471) += ltc2471.o
 obj-$(CONFIG_LTC2485) += ltc2485.o
 obj-$(CONFIG_LTC2496) += ltc2496.o ltc2497-core.o
 obj-$(CONFIG_LTC2497) += ltc2497.o ltc2497-core.o
+obj-$(CONFIG_MA35D1_EADC) += ma35d1_eadc.o
 obj-$(CONFIG_MAX1027) += max1027.o
 obj-$(CONFIG_MAX11100) += max11100.o
 obj-$(CONFIG_MAX1118) += max1118.o
diff --git a/drivers/iio/adc/ma35d1_eadc.c b/drivers/iio/adc/ma35d1_eadc.c
new file mode 100644
index 000000000000..0c075126e139
--- /dev/null
+++ b/drivers/iio/adc/ma35d1_eadc.c
@@ -0,0 +1,636 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Nuvoton MA35D1 EADC driver
+ *
+ * Copyright (c) 2026 Nuvoton Technology Corp.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/bitmap.h>
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/property.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define MA35D1_EADC_DAT(n)		(0x00 + (n) * 0x04)
+#define MA35D1_EADC_CTL			0x50
+#define MA35D1_EADC_SWTRG		0x54
+#define MA35D1_EADC_SCTL(n)		(0x80 + (n) * 0x04)
+#define MA35D1_EADC_INTSRC0		0xd0
+#define MA35D1_EADC_STATUS2		0xf8
+#define MA35D1_EADC_SELSMP0		0x140
+#define MA35D1_EADC_REFADJCTL		0x150
+
+#define MA35D1_EADC_CTL_ADCEN		BIT(0)
+#define MA35D1_EADC_CTL_ADCIEN0		BIT(2)
+#define MA35D1_EADC_CTL_DIFFEN		BIT(8)
+
+#define MA35D1_EADC_SCTL_CHSEL_MASK	GENMASK(3, 0)
+#define MA35D1_EADC_SCTL_TRGDLY_MASK	GENMASK(15, 8)
+#define MA35D1_EADC_SCTL_TRGSEL_MASK	GENMASK(21, 16)
+#define MA35D1_EADC_SCTL_TRGSEL_ADINT0	\
+	FIELD_PREP(MA35D1_EADC_SCTL_TRGSEL_MASK, 2)
+
+#define MA35D1_EADC_DAT_MASK		GENMASK(11, 0)
+#define MA35D1_EADC_STATUS2_ADIF0	BIT(0)
+#define MA35D1_EADC_INTSRC0_ADINT0	BIT(0)
+#define MA35D1_EADC_REFADJCTL_EXT_VREF	BIT(0)
+
+#define MA35D1_EADC_MAX_CHANNELS	9
+#define MA35D1_EADC_MAX_SAMPLE_MODULES	16
+#define MA35D1_EADC_CHAN_NAME_LEN	16
+#define MA35D1_EADC_TIMEOUT		msecs_to_jiffies(1000)
+
+struct ma35d1_adc {
+	struct device *dev;
+	void __iomem *regs;
+	struct clk *clk;
+	struct completion completion;
+	/* Protects direct conversions against concurrent register access. */
+	struct mutex lock;
+	struct iio_trigger *trig;
+	unsigned int scan_chancnt;
+	bool scan_differential;
+	char chan_name[MA35D1_EADC_MAX_CHANNELS][MA35D1_EADC_CHAN_NAME_LEN];
+	struct {
+		u16 channels[MA35D1_EADC_MAX_SAMPLE_MODULES];
+		aligned_s64 timestamp;
+	} scan;
+};
+
+static inline u32 ma35d1_adc_read(struct ma35d1_adc *adc, u32 reg)
+{
+	return readl(adc->regs + reg);
+}
+
+static inline void ma35d1_adc_write(struct ma35d1_adc *adc, u32 reg, u32 val)
+{
+	writel(val, adc->regs + reg);
+}
+
+static void ma35d1_adc_rmw(struct ma35d1_adc *adc, u32 reg, u32 mask, u32 val)
+{
+	u32 tmp;
+
+	tmp = ma35d1_adc_read(adc, reg);
+	tmp &= ~mask;
+	tmp |= val;
+	ma35d1_adc_write(adc, reg, tmp);
+}
+
+static void ma35d1_adc_set_diff(struct ma35d1_adc *adc, bool differential)
+{
+	ma35d1_adc_rmw(adc, MA35D1_EADC_CTL, MA35D1_EADC_CTL_DIFFEN,
+		       differential ? MA35D1_EADC_CTL_DIFFEN : 0);
+}
+
+static void ma35d1_adc_config_sample(struct ma35d1_adc *adc,
+				     unsigned int sample, unsigned int channel)
+{
+	u32 reg = MA35D1_EADC_SCTL(sample);
+
+	ma35d1_adc_rmw(adc, reg,
+		       MA35D1_EADC_SCTL_CHSEL_MASK |
+		       MA35D1_EADC_SCTL_TRGSEL_MASK,
+		       FIELD_PREP(MA35D1_EADC_SCTL_CHSEL_MASK, channel) |
+		       MA35D1_EADC_SCTL_TRGSEL_ADINT0);
+}
+
+static void ma35d1_adc_disable_irq(struct ma35d1_adc *adc)
+{
+	ma35d1_adc_rmw(adc, MA35D1_EADC_CTL, MA35D1_EADC_CTL_ADCIEN0, 0);
+}
+
+static void ma35d1_adc_hw_init(struct ma35d1_adc *adc)
+{
+	ma35d1_adc_disable_irq(adc);
+	ma35d1_adc_rmw(adc, MA35D1_EADC_CTL,
+		       MA35D1_EADC_CTL_ADCEN, MA35D1_EADC_CTL_ADCEN);
+	ma35d1_adc_write(adc, MA35D1_EADC_STATUS2, MA35D1_EADC_STATUS2_ADIF0);
+	ma35d1_adc_rmw(adc, MA35D1_EADC_INTSRC0,
+		       MA35D1_EADC_INTSRC0_ADINT0,
+		       MA35D1_EADC_INTSRC0_ADINT0);
+	ma35d1_adc_rmw(adc, MA35D1_EADC_REFADJCTL,
+		       MA35D1_EADC_REFADJCTL_EXT_VREF,
+		       MA35D1_EADC_REFADJCTL_EXT_VREF);
+	ma35d1_adc_rmw(adc, MA35D1_EADC_SELSMP0, GENMASK(1, 0), 3);
+}
+
+static void ma35d1_adc_hw_disable(void *data)
+{
+	struct ma35d1_adc *adc = data;
+
+	ma35d1_adc_disable_irq(adc);
+	ma35d1_adc_rmw(adc, MA35D1_EADC_CTL, MA35D1_EADC_CTL_ADCEN, 0);
+}
+
+static irqreturn_t ma35d1_adc_isr(int irq, void *data)
+{
+	struct iio_dev *indio_dev = data;
+	struct ma35d1_adc *adc = iio_priv(indio_dev);
+	u32 status;
+
+	status = ma35d1_adc_read(adc, MA35D1_EADC_STATUS2);
+	if (!(status & MA35D1_EADC_STATUS2_ADIF0))
+		return IRQ_NONE;
+
+	ma35d1_adc_write(adc, MA35D1_EADC_STATUS2, MA35D1_EADC_STATUS2_ADIF0);
+
+	if (iio_buffer_enabled(indio_dev)) {
+		ma35d1_adc_disable_irq(adc);
+		iio_trigger_poll(adc->trig);
+	} else {
+		complete(&adc->completion);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t ma35d1_adc_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct ma35d1_adc *adc = iio_priv(indio_dev);
+	int i;
+
+	for (i = 0; i < adc->scan_chancnt; i++)
+		adc->scan.channels[i] =
+			ma35d1_adc_read(adc, MA35D1_EADC_DAT(i)) &
+			MA35D1_EADC_DAT_MASK;
+
+	iio_push_to_buffers_with_timestamp(indio_dev, &adc->scan, pf->timestamp);
+	iio_trigger_notify_done(adc->trig);
+
+	ma35d1_adc_rmw(adc, MA35D1_EADC_CTL, MA35D1_EADC_CTL_ADCIEN0,
+		       MA35D1_EADC_CTL_ADCIEN0);
+	ma35d1_adc_write(adc, MA35D1_EADC_SWTRG, 1);
+
+	return IRQ_HANDLED;
+}
+
+static int ma35d1_adc_read_conversion(struct iio_dev *indio_dev,
+				      const struct iio_chan_spec *chan,
+				      int *val)
+{
+	struct ma35d1_adc *adc = iio_priv(indio_dev);
+	long timeout;
+
+	reinit_completion(&adc->completion);
+
+	ma35d1_adc_write(adc, MA35D1_EADC_STATUS2, MA35D1_EADC_STATUS2_ADIF0);
+	ma35d1_adc_rmw(adc, MA35D1_EADC_SCTL(0),
+		       MA35D1_EADC_SCTL_CHSEL_MASK |
+		       MA35D1_EADC_SCTL_TRGSEL_MASK,
+		       FIELD_PREP(MA35D1_EADC_SCTL_CHSEL_MASK,
+				  chan->channel));
+	ma35d1_adc_set_diff(adc, chan->differential);
+	ma35d1_adc_rmw(adc, MA35D1_EADC_CTL, MA35D1_EADC_CTL_ADCIEN0,
+		       MA35D1_EADC_CTL_ADCIEN0);
+	ma35d1_adc_write(adc, MA35D1_EADC_SWTRG, 1);
+
+	timeout = wait_for_completion_interruptible_timeout(&adc->completion,
+							    MA35D1_EADC_TIMEOUT);
+	ma35d1_adc_disable_irq(adc);
+
+	if (timeout < 0)
+		return timeout;
+	if (!timeout)
+		return -ETIMEDOUT;
+
+	*val = ma35d1_adc_read(adc, MA35D1_EADC_DAT(0)) & MA35D1_EADC_DAT_MASK;
+
+	return 0;
+}
+
+static int ma35d1_adc_read_raw(struct iio_dev *indio_dev,
+			       const struct iio_chan_spec *chan,
+			       int *val, int *val2, long mask)
+{
+	struct ma35d1_adc *adc = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		if (!iio_device_claim_direct(indio_dev))
+			return -EBUSY;
+
+		mutex_lock(&adc->lock);
+		ret = ma35d1_adc_read_conversion(indio_dev, chan, val);
+		mutex_unlock(&adc->lock);
+
+		iio_device_release_direct(indio_dev);
+		if (ret)
+			return ret;
+
+		return IIO_VAL_INT;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int ma35d1_adc_validate_scan(struct iio_dev *indio_dev,
+				    const unsigned long *scan_mask)
+{
+	const struct iio_chan_spec *chan;
+	bool have_single = false;
+	bool have_diff = false;
+	unsigned int count = 0;
+	unsigned long bit;
+
+	for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
+		chan = &indio_dev->channels[bit];
+
+		if (chan->type == IIO_TIMESTAMP)
+			continue;
+		count++;
+		if (chan->differential)
+			have_diff = true;
+		else
+			have_single = true;
+	}
+
+	if (!count || count > MA35D1_EADC_MAX_SAMPLE_MODULES)
+		return -EINVAL;
+
+	if (have_single && have_diff)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ma35d1_adc_update_scan_mode(struct iio_dev *indio_dev,
+				       const unsigned long *scan_mask)
+{
+	struct ma35d1_adc *adc = iio_priv(indio_dev);
+	const struct iio_chan_spec *chan;
+	unsigned int sample = 0;
+	unsigned long bit;
+	bool differential = false;
+	int ret;
+
+	ret = ma35d1_adc_validate_scan(indio_dev, scan_mask);
+	if (ret)
+		return ret;
+
+	for_each_set_bit(bit, scan_mask, indio_dev->masklength) {
+		chan = &indio_dev->channels[bit];
+		if (chan->type == IIO_TIMESTAMP)
+			continue;
+
+		if (!sample)
+			differential = chan->differential;
+
+		ma35d1_adc_config_sample(adc, sample, chan->channel);
+		sample++;
+	}
+
+	adc->scan_chancnt = sample;
+	adc->scan_differential = differential;
+
+	return 0;
+}
+
+static int ma35d1_adc_buffer_postenable(struct iio_dev *indio_dev)
+{
+	struct ma35d1_adc *adc = iio_priv(indio_dev);
+	int i;
+
+	if (!adc->scan_chancnt)
+		return -EINVAL;
+
+	ma35d1_adc_write(adc, MA35D1_EADC_STATUS2, MA35D1_EADC_STATUS2_ADIF0);
+	ma35d1_adc_rmw(adc, MA35D1_EADC_INTSRC0,
+		       MA35D1_EADC_INTSRC0_ADINT0,
+		       MA35D1_EADC_INTSRC0_ADINT0);
+	ma35d1_adc_rmw(adc, MA35D1_EADC_REFADJCTL,
+		       MA35D1_EADC_REFADJCTL_EXT_VREF,
+		       MA35D1_EADC_REFADJCTL_EXT_VREF);
+	ma35d1_adc_rmw(adc, MA35D1_EADC_SELSMP0, GENMASK(1, 0), 3);
+	ma35d1_adc_set_diff(adc, adc->scan_differential);
+
+	for (i = 0; i < adc->scan_chancnt; i++)
+		ma35d1_adc_rmw(adc, MA35D1_EADC_SCTL(i),
+			       MA35D1_EADC_SCTL_TRGDLY_MASK,
+			       MA35D1_EADC_SCTL_TRGDLY_MASK);
+
+	ma35d1_adc_rmw(adc, MA35D1_EADC_CTL, MA35D1_EADC_CTL_ADCIEN0,
+		       MA35D1_EADC_CTL_ADCIEN0);
+	ma35d1_adc_write(adc, MA35D1_EADC_SWTRG, 1);
+
+	return 0;
+}
+
+static int ma35d1_adc_buffer_predisable(struct iio_dev *indio_dev)
+{
+	struct ma35d1_adc *adc = iio_priv(indio_dev);
+	int i;
+
+	ma35d1_adc_disable_irq(adc);
+	for (i = 0; i < adc->scan_chancnt; i++)
+		ma35d1_adc_rmw(adc, MA35D1_EADC_SCTL(i),
+			       MA35D1_EADC_SCTL_TRGSEL_MASK, 0);
+
+	return 0;
+}
+
+static const struct iio_buffer_setup_ops ma35d1_adc_buffer_ops = {
+	.postenable = ma35d1_adc_buffer_postenable,
+	.predisable = ma35d1_adc_buffer_predisable,
+};
+
+static const struct iio_info ma35d1_adc_info = {
+	.read_raw = ma35d1_adc_read_raw,
+	.update_scan_mode = ma35d1_adc_update_scan_mode,
+};
+
+static const struct iio_trigger_ops ma35d1_adc_trigger_ops = {
+	.validate_device = iio_trigger_validate_own_device,
+};
+
+static void ma35d1_adc_init_channel(struct ma35d1_adc *adc,
+				    struct iio_chan_spec *chan, u32 vinp,
+				    u32 vinn, int scan_index, bool differential)
+{
+	char *name = adc->chan_name[vinp];
+
+	chan->type = IIO_VOLTAGE;
+	chan->indexed = 1;
+	chan->channel = vinp;
+	chan->address = vinp;
+	chan->scan_index = scan_index;
+	chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+	chan->scan_type.sign = 'u';
+	chan->scan_type.realbits = 12;
+	chan->scan_type.storagebits = 16;
+	chan->scan_type.endianness = IIO_CPU;
+
+	if (differential) {
+		chan->differential = 1;
+		chan->channel2 = vinn;
+		snprintf(name, MA35D1_EADC_CHAN_NAME_LEN, "in%d-in%d", vinp,
+			 vinn);
+	} else {
+		snprintf(name, MA35D1_EADC_CHAN_NAME_LEN, "in%d", vinp);
+	}
+
+	chan->datasheet_name = name;
+}
+
+static int ma35d1_adc_parse_channels(struct iio_dev *indio_dev,
+				     struct device *dev)
+{
+	struct ma35d1_adc *adc = iio_priv(indio_dev);
+	DECLARE_BITMAP(used_channels, MA35D1_EADC_MAX_CHANNELS);
+	struct fwnode_handle *child;
+	struct iio_chan_spec *channels;
+	int num_channels;
+	int scan_index = 0;
+	int ret;
+
+	bitmap_zero(used_channels, MA35D1_EADC_MAX_CHANNELS);
+
+	num_channels = device_get_child_node_count(dev);
+	if (!num_channels)
+		return dev_err_probe(dev, -ENODATA,
+				     "no ADC channels configured\n");
+
+	if (num_channels > MA35D1_EADC_MAX_CHANNELS)
+		return dev_err_probe(dev, -EINVAL, "too many ADC channels\n");
+
+	channels = devm_kcalloc(dev, num_channels + 1, sizeof(*channels),
+				GFP_KERNEL);
+	if (!channels)
+		return -ENOMEM;
+
+	device_for_each_child_node(dev, child) {
+		u32 diff[2];
+		u32 reg;
+		bool differential = false;
+
+		ret = fwnode_property_read_u32(child, "reg", &reg);
+		if (ret) {
+			fwnode_handle_put(child);
+			return dev_err_probe(dev, ret,
+					     "missing channel reg property\n");
+		}
+
+		if (reg >= MA35D1_EADC_MAX_CHANNELS) {
+			fwnode_handle_put(child);
+			return dev_err_probe(dev, -EINVAL,
+					     "invalid ADC channel %u\n", reg);
+		}
+
+		if (test_and_set_bit(reg, used_channels)) {
+			fwnode_handle_put(child);
+			return dev_err_probe(dev, -EINVAL,
+					     "duplicate ADC channel %u\n", reg);
+		}
+
+		if (fwnode_property_present(child, "diff-channels")) {
+			ret = fwnode_property_read_u32_array(child,
+							     "diff-channels",
+							     diff,
+							     ARRAY_SIZE(diff));
+			if (ret) {
+				fwnode_handle_put(child);
+				return dev_err_probe(dev, ret,
+						     "invalid diff-channels for channel %u\n",
+						     reg);
+			}
+
+			if (diff[0] != reg ||
+			    diff[1] >= MA35D1_EADC_MAX_CHANNELS ||
+			    diff[0] == diff[1]) {
+				fwnode_handle_put(child);
+				return dev_err_probe(dev, -EINVAL,
+						     "invalid differential ADC channel %u-%u\n",
+						     diff[0], diff[1]);
+			}
+
+			if (test_and_set_bit(diff[1], used_channels)) {
+				fwnode_handle_put(child);
+				return dev_err_probe(dev, -EINVAL,
+						     "ADC channel %u already used\n",
+						     diff[1]);
+			}
+
+			differential = true;
+		}
+
+		ma35d1_adc_init_channel(adc, &channels[scan_index], reg,
+					differential ? diff[1] : 0,
+					scan_index, differential);
+		scan_index++;
+	}
+
+	channels[scan_index] = (struct iio_chan_spec)
+		IIO_CHAN_SOFT_TIMESTAMP(scan_index);
+
+	indio_dev->channels = channels;
+	indio_dev->num_channels = scan_index + 1;
+	indio_dev->masklength = indio_dev->num_channels;
+
+	return 0;
+}
+
+static int ma35d1_adc_setup_trigger(struct iio_dev *indio_dev,
+				    struct device *dev)
+{
+	struct ma35d1_adc *adc = iio_priv(indio_dev);
+	int ret;
+
+	adc->trig = devm_iio_trigger_alloc(dev, "%s-trigger", dev_name(dev));
+	if (!adc->trig)
+		return -ENOMEM;
+
+	adc->trig->ops = &ma35d1_adc_trigger_ops;
+	iio_trigger_set_drvdata(adc->trig, indio_dev);
+
+	ret = devm_iio_trigger_register(dev, adc->trig);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to register trigger\n");
+
+	ret = iio_trigger_set_immutable(indio_dev, adc->trig);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to set trigger\n");
+
+	return 0;
+}
+
+static int ma35d1_adc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct iio_dev *indio_dev;
+	struct ma35d1_adc *adc;
+	int irq;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+	if (!indio_dev)
+		return -ENOMEM;
+	adc = iio_priv(indio_dev);
+	adc->dev = dev;
+	mutex_init(&adc->lock);
+	init_completion(&adc->completion);
+
+	adc->regs = devm_platform_ioremap_resource(pdev, 0);
+	if (IS_ERR(adc->regs))
+		return dev_err_probe(dev, PTR_ERR(adc->regs),
+				     "failed to map registers\n");
+
+	adc->clk = devm_clk_get_enabled(dev, NULL);
+	if (IS_ERR(adc->clk))
+		return dev_err_probe(dev, PTR_ERR(adc->clk),
+				     "failed to get and enable ADC clock\n");
+
+	indio_dev->name = "ma35d1-eadc";
+	indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED;
+	indio_dev->info = &ma35d1_adc_info;
+
+	ret = ma35d1_adc_parse_channels(indio_dev, dev);
+	if (ret)
+		return ret;
+
+	ma35d1_adc_hw_init(adc);
+
+	ret = devm_add_action_or_reset(dev, ma35d1_adc_hw_disable, adc);
+	if (ret)
+		return ret;
+
+	ret = ma35d1_adc_setup_trigger(indio_dev, dev);
+	if (ret)
+		return ret;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0)
+		return irq;
+
+	ret = devm_request_irq(dev, irq, ma35d1_adc_isr, 0, dev_name(dev),
+			       indio_dev);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to request IRQ %d\n", irq);
+
+	ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+					      iio_pollfunc_store_time,
+					      ma35d1_adc_trigger_handler,
+					      &ma35d1_adc_buffer_ops);
+	if (ret)
+		return dev_err_probe(dev, ret,
+				     "failed to setup triggered buffer\n");
+
+	platform_set_drvdata(pdev, indio_dev);
+
+	ret = devm_iio_device_register(dev, indio_dev);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to register IIO device\n");
+
+	return 0;
+}
+
+static int ma35d1_adc_suspend(struct device *dev)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ma35d1_adc *adc = iio_priv(indio_dev);
+
+	if (iio_buffer_enabled(indio_dev))
+		return -EBUSY;
+
+	ma35d1_adc_hw_disable(adc);
+	clk_disable_unprepare(adc->clk);
+
+	return 0;
+}
+
+static int ma35d1_adc_resume(struct device *dev)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct ma35d1_adc *adc = iio_priv(indio_dev);
+	int ret;
+
+	ret = clk_prepare_enable(adc->clk);
+	if (ret)
+		return ret;
+
+	ma35d1_adc_hw_init(adc);
+
+	return 0;
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(ma35d1_adc_pm_ops,
+				ma35d1_adc_suspend, ma35d1_adc_resume);
+
+static const struct of_device_id ma35d1_adc_of_match[] = {
+	{ .compatible = "nuvoton,ma35d1-eadc" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ma35d1_adc_of_match);
+
+static struct platform_driver ma35d1_adc_driver = {
+	.probe = ma35d1_adc_probe,
+	.driver = {
+		.name = "ma35d1-eadc",
+		.of_match_table = ma35d1_adc_of_match,
+		.pm = pm_sleep_ptr(&ma35d1_adc_pm_ops),
+	},
+};
+module_platform_driver(ma35d1_adc_driver);
+
+MODULE_AUTHOR("Chi-Wen Weng <cwweng@nuvoton.com>");
+MODULE_DESCRIPTION("Nuvoton MA35D1 EADC driver");
+MODULE_LICENSE("GPL");
-- 
2.25.1



^ permalink raw reply related

* Re: [PATCH v4 0/2] tracing: Move non-trace_printk prototypes into trace_controls.h
From: Jani Nikula @ 2026-06-25 11:05 UTC (permalink / raw)
  To: Steven Rostedt, linux-kernel, linux-trace-kernel
  Cc: Masami Hiramatsu, Mark Rutland, Mathieu Desnoyers, Andrew Morton,
	Linus Torvalds, Sebastian Andrzej Siewior, John Ogness,
	Thomas Gleixner, Peter Zijlstra, Julia Lawall, Yury Norov,
	linux-doc, linux-kbuild, linuxppc-dev, dri-devel, linux-stm32,
	linux-arm-kernel, linux-rdma, linux-usb, linux-ext4, linux-nfs,
	kvm, intel-gfx
In-Reply-To: <20260625104007.041432666@kernel.org>

On Thu, 25 Jun 2026, Steven Rostedt <rostedt@kernel.org> wrote:
> Remove trace_printk.h by creating a trace_controls.h for those places that
> need access to tracing prototypes like tracing_off() and for the places that
> need trace_printk() directly, to have it included directly.
>
> Changse since v3: https://lore.kernel.org/all/20260624081806.120105649@kernel.org/
>
> - Always include trace_controls.h in rcu.h (kernel test robot)
>
>   There are other configs that may include tracing_off() in rcu.h besides
>   the one that had the include of trace_controls.h. Just always include
>   it in that header to be safe.
>
> Steven Rostedt (2):
>       tracing: Move non-trace_printk prototypes into trace_controls.h
>       tracing: Remove trace_printk.h from kernel.h
>
> ----
>  arch/powerpc/kvm/book3s_xics.c         |  1 +
>  arch/powerpc/xmon/xmon.c               |  1 +
>  arch/s390/kernel/ipl.c                 |  1 +
>  arch/s390/kernel/machine_kexec.c       |  1 +
>  drivers/gpu/drm/i915/gt/intel_gtt.h    |  1 +
>  drivers/gpu/drm/i915/i915_gem.h        |  2 ++

For the i915 parts,

Acked-by: Jani Nikula <jani.nikula@intel.com>

for merging via whichever tree.

>  drivers/hwtracing/stm/dummy_stm.c      |  1 +
>  drivers/infiniband/hw/hfi1/trace_dbg.h |  1 +
>  drivers/tty/sysrq.c                    |  1 +
>  drivers/usb/early/xhci-dbc.c           |  1 +
>  fs/ext4/inline.c                       |  1 +
>  include/linux/ftrace.h                 |  2 ++
>  include/linux/kernel.h                 |  1 -
>  include/linux/sunrpc/debug.h           |  1 +
>  include/linux/trace_controls.h         | 54 ++++++++++++++++++++++++++++++++
>  include/linux/trace_printk.h           | 56 ++--------------------------------
>  kernel/debug/debug_core.c              |  1 +
>  kernel/panic.c                         |  1 +
>  kernel/rcu/rcu.h                       |  1 +
>  kernel/rcu/rcutorture.c                |  1 +
>  kernel/trace/ring_buffer_benchmark.c   |  1 +
>  kernel/trace/trace.h                   |  1 +
>  kernel/trace/trace_benchmark.c         |  1 +
>  lib/sys_info.c                         |  1 +
>  samples/fprobe/fprobe_example.c        |  1 +
>  samples/ftrace/ftrace-direct-too.c     |  1 -
>  samples/trace_printk/trace-printk.c    |  1 +
>  27 files changed, 82 insertions(+), 55 deletions(-)
>  create mode 100644 include/linux/trace_controls.h

-- 
Jani Nikula, Intel


^ permalink raw reply

* [PATCH 1/2] dt-bindings: iio: adc: Add Nuvoton MA35D1 EADC
From: Chi-Wen Weng @ 2026-06-25 11:06 UTC (permalink / raw)
  To: jic23, robh, krzk+dt, conor+dt
  Cc: dlechner, nuno.sa, andy, linux-arm-kernel, linux-iio, devicetree,
	linux-kernel, cwweng, cwweng.linux
In-Reply-To: <20260625110638.38438-1-cwweng.linux@gmail.com>

From: Chi-Wen Weng <cwweng@nuvoton.com>

Add devicetree binding for the Enhanced ADC controller found on
Nuvoton MA35D1 SoCs.

The controller has one register region, one interrupt and one functional
clock. ADC inputs are described using standard channel child nodes,
including optional differential channel pairs.

Signed-off-by: Chi-Wen Weng <cwweng@nuvoton.com>
---
 .../bindings/iio/adc/nuvoton,ma35d1-eadc.yaml | 100 ++++++++++++++++++
 1 file changed, 100 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/nuvoton,ma35d1-eadc.yaml

diff --git a/Documentation/devicetree/bindings/iio/adc/nuvoton,ma35d1-eadc.yaml b/Documentation/devicetree/bindings/iio/adc/nuvoton,ma35d1-eadc.yaml
new file mode 100644
index 000000000000..ae7ad0f7689a
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/adc/nuvoton,ma35d1-eadc.yaml
@@ -0,0 +1,100 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/iio/adc/nuvoton,ma35d1-eadc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Nuvoton MA35D1 Enhanced Analog to Digital Converter
+
+maintainers:
+  - Chi-Wen Weng <cwweng@nuvoton.com>
+
+description: |
+  The Nuvoton MA35D1 Enhanced Analog to Digital Converter (EADC) is a
+  12-bit ADC controller integrated in the MA35D1 SoC. Each enabled ADC
+  input is described by a child channel node.
+
+properties:
+  compatible:
+    const: nuvoton,ma35d1-eadc
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  '#address-cells':
+    const: 1
+
+  '#size-cells':
+    const: 0
+
+patternProperties:
+  '^channel@[0-8]$':
+    type: object
+    $ref: adc.yaml
+    unevaluatedProperties: false
+
+    properties:
+      reg:
+        minimum: 0
+        maximum: 8
+
+      diff-channels:
+        minItems: 2
+        maxItems: 2
+        items:
+          minimum: 0
+          maximum: 8
+
+    required:
+      - reg
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - '#address-cells'
+  - '#size-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/nuvoton,ma35d1-clk.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/interrupt-controller/irq.h>
+
+    soc {
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        adc@40430000 {
+            compatible = "nuvoton,ma35d1-eadc";
+            reg = <0x0 0x40430000 0x0 0x10000>;
+            interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
+            clocks = <&clk EADC_GATE>;
+
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            channel@0 {
+                reg = <0>;
+            };
+
+            channel@1 {
+                reg = <1>;
+            };
+
+            channel@2 {
+                reg = <2>;
+                diff-channels = <2 3>;
+            };
+        };
+    };
+...
-- 
2.25.1



^ permalink raw reply related

* [PATCH 0/2] iio: adc: Add Nuvoton MA35D1 EADC support
From: Chi-Wen Weng @ 2026-06-25 11:06 UTC (permalink / raw)
  To: jic23, robh, krzk+dt, conor+dt
  Cc: dlechner, nuno.sa, andy, linux-arm-kernel, linux-iio, devicetree,
	linux-kernel, cwweng, cwweng.linux

From: Chi-Wen Weng <cwweng@nuvoton.com>

This series adds devicetree binding and IIO driver support for the
Nuvoton MA35D1 Enhanced ADC controller.

The MA35D1 EADC controller supports multiple ADC input channels. This
initial upstream driver supports direct raw reads and triggered buffered
capture using the controller end-of-conversion interrupt as the IIO
device trigger.

ADC channels are described using standard firmware child nodes. Both
single-ended and differential channels are supported. Since the
differential enable bit is global in the controller, mixed single-ended
and differential buffered scans are rejected.

DMA support is intentionally not included in this initial version. The
driver uses the interrupt-driven conversion path to keep the first
upstream submission small and easier to review.

Patch 1 adds the devicetree binding.
Patch 2 adds the MA35D1 EADC IIO driver.

Chi-Wen Weng (2):
  dt-bindings: iio: adc: Add Nuvoton MA35D1 EADC
  iio: adc: Add Nuvoton MA35D1 EADC driver

 .../bindings/iio/adc/nuvoton,ma35d1-eadc.yaml | 100 +++
 drivers/iio/adc/Kconfig                       |  10 +
 drivers/iio/adc/Makefile                      |   1 +
 drivers/iio/adc/ma35d1_eadc.c                 | 636 ++++++++++++++++++
 4 files changed, 747 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/adc/nuvoton,ma35d1-eadc.yaml
 create mode 100644 drivers/iio/adc/ma35d1_eadc.c

-- 
2.25.1



^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox