* Re: [PATCH v2 3/3] arm64: escalate smp_send_stop() to an SDEI NMI as a last resort
From: Kiryl Shutsemau @ 2026-06-11 17:47 UTC (permalink / raw)
To: Doug Anderson
Cc: Catalin Marinas, Will Deacon, James Morse, Mark Rutland,
Marc Zyngier, Petr Mladek, Thomas Gleixner, Andrew Morton,
Baoquan He, Puranjay Mohan, Usama Arif, Breno Leitao,
Julien Thierry, Lecopzer Chen, Sumit Garg, kernel-team, kexec,
linux-arm-kernel, linux-kernel
In-Reply-To: <CAD=FV=VLoJBMhDjb=3XAOCZWDBACn_=KdnkL0J6-Ch4uKrHjNA@mail.gmail.com>
On Wed, Jun 10, 2026 at 03:50:32PM -0700, Doug Anderson wrote:
> Hi,
>
> On Tue, Jun 9, 2026 at 6:58 AM Kiryl Shutsemau <kirill@shutemov.name> wrote:
> >
> > @@ -910,6 +911,35 @@ static void __noreturn ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs
> > #endif
> > }
> >
> > +#ifdef CONFIG_ARM_SDEI_NMI
> > +/*
> > + * Stop entry for the SDEI cross-CPU NMI service: its event-0 handler
> > + * lands here when this CPU was asked to stop. The bookkeeping mirrors
> > + * the IPI_CPU_STOP{,_NMI} handling; the park happens inside the SDEI
> > + * event, which is never completed -- completing it would have firmware
> > + * resume the interrupted (typically wedged) context. No PSCI CPU_OFF
> > + * either: powering off a PE that EL3 still considers mid-event invites
> > + * firmware trouble.
> > + */
> > +void __noreturn arm64_nmi_cpu_stop(struct pt_regs *regs)
> > +{
> > + unsigned int cpu = smp_processor_id();
> > +
> > + local_daif_mask();
> > +
> > + if (IS_ENABLED(CONFIG_KEXEC_CORE) && crash_stop)
> > + crash_save_cpu(regs, cpu);
> > +
> > + /* the ack the stop requester polls for */
> > + set_cpu_online(cpu, false);
> > +
> > + sdei_mask_local_cpu();
> > +
> > + cpu_park_loop();
> > +}
> > +NOKPROBE_SYMBOL(arm64_nmi_cpu_stop);
> > +#endif
>
> Can we combine everything into one function so we don't have to keep
> all the different stop functionality in sync? Like this (untested):
>
Okay. Look good to me. See the patch below.
void __noreturn arm64_nmi_cpu_stop(struct pt_regs *regs, bool die_on_crash)
The stop IPI handlers call it with die_on_crash=true, the SDEI handler
and panic_smp_self_stop() with false. Pretty much your sketch, with the
crash = IS_ENABLED(CONFIG_KEXEC_CORE) && crash_stop discriminator inside.
> FWIW, I'm not totally sure I followed the logic for why "die_on_crash"
> needs to be "false" for the SDEI case,
It's not about kexec mechanics, it's about the SDEI dispatch state.
The SDEI stop handler parks inside an SDEI event that it deliberately
never completes — completing it makes firmware resume the wedged
context, which is the opposite of what we want. PSCI CPU_OFF from inside
that not-yet-completed event silently wedges EL3 on at least one
production firmware (still root-causing on the firmware side), so the
SDEI path saves the crashed context and parks instead of powering off.
The only consequence is that an SMP capture kernel can't re-online that
CPU. The dump itself is complete. I've left "power the SDEI-stopped CPU
off too" as a follow-up and called it out in the cover letter. The IPI
crash path is unaffected and still does CPU_OFF, exactly as before.
> Perhaps when doing that you'd stop unconditionally getting the cpu in
> do_handle_IPI() and just call it for `IPI_KGDB_ROUNDUP` since it would
> now be the only consumer of that local variable.
I kept the local cpu — after the change it's still used by the
IPI_KGDB_ROUNDUP case and the default: pr_crit(), so it didn't become
single-use.
> > @@ -1263,6 +1293,29 @@ void smp_send_stop(void)
> > udelay(1);
> > }
> >
> > + /*
> > + * If CPUs are *still* online, try the SDEI cross-CPU NMI. Firmware
> > + * delivers it regardless of the target's DAIF state, so it reaches
> > + * a CPU spinning with interrupts masked, which neither rung above
> > + * could (without pseudo-NMI there is no NMI rung at all). Allow
> > + * 100ms: a firmware round-trip per CPU, with headroom.
> > + */
> > + if (num_other_online_cpus()) {
> > + /* re-snapshot after the rungs above took CPUs offline */
> > + smp_rmb();
> > + cpumask_copy(&mask, cpu_online_mask);
> > + cpumask_clear_cpu(smp_processor_id(), &mask);
> > +
> > + if (sdei_nmi_stop_cpus(&mask)) {
> > + pr_info("SMP: retry stop with SDEI NMI for CPUs %*pbl\n",
> > + cpumask_pr_args(&mask));
>
> Perhaps it's being a bit pedantic, but it's a little weird that you're
> printing a message that sounds like "I'm going to retry with SDEI"
> after you've already done it. It feels like it would be nominally
> cleaner (and more parellel with the pseudo-NMI case) if you could
> separately test if SDEI is available. Then sdei_nmi_stop_cpus() would
> just return void?
Fixed. There's now a sdei_nmi_active() predicate; the rung tests it,
prints, then calls sdei_nmi_stop_cpus(), which is now void. It mirrors
the pseudo-NMI rung's check-then-act shape.
>
>
> > @@ -59,8 +64,45 @@ static bool sdei_nmi_available;
> >
> > #define SDEI_NMI_EVENT 0
> >
> > +/*
> > + * Stop-request dispatch lives on the same SDEI event 0 as everything
> > + * else. The requesting CPU sets each target's bit in sdei_nmi_stop_mask
> > + * before signalling event 0; the target's handler test-and-clears its
> > + * bit and hands the CPU to arm64_nmi_cpu_stop(), which saves crash
> > + * state when the stop is a kdump crash-stop, marks the CPU offline
> > + * (which is what the requester polls for) and parks it.
> > + *
> > + * This mirrors the cpumask the framework's nmi_cpu_backtrace() consults
> > + * just below, and a shared mask rather than a separate SDEI event avoids
> > + * extra registrations from firmware.
> > + */
>
> Do you have any reasoning for why you don't pick a separate EVENT ID
> for "backtrace" vs. "stop". If you absolutely have to share an ID
> because they're a limited resource then I guess it's fine, but it
> would make the code easier to understand / reason about if they were
> separate IDs.
>
> If you had a separate EVENT ID, then it seems like you could
> completely eliminate the (potentially large) `sdei_nmi_stop_mask`
> variable, right? Any time a "STOP" event fires you can unconditionally
> consider it to be a stop w/ no globals needed, right?
Separate event IDs aren't available: SDEI_EVENT_SIGNAL only ever signals
event 0 — it's the one architecturally software-signalled event. Every
other event number is an interrupt-bound event that firmware has to
define and bind, which is the firmware dependency this series is
specifically trying not to add. So backtrace and stop are stuck sharing
event 0.
But you're right that the mask should go — just not via a second event. A
stop is terminal and system-wide (sdei_nmi_stop_cpus() is only reached
from smp_send_stop(), which never returns), so once a stop is requested
every later event-0 fire is a stop too. I replaced the cpumask with a
single write-once flag the handler reads; a backtrace that races in
after a stop has begun just stops that CPU, which is fine. So the
(potentially large) variable is gone.
> > @@ -115,6 +157,35 @@ bool sdei_nmi_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu)
> > return true;
> > }
> >
> > +/*
> > + * Last rung of the stop escalation in smp_send_stop() (see
> > + * arch/arm64/kernel/smp.c). The caller runs the regular stop IPI (and
> > + * the pseudo-NMI stop IPI, where available) first; @mask holds whatever
> > + * stayed online through those -- typically CPUs wedged with interrupts
> > + * masked, unreachable by an IPI. Set each target's stop-request flag and
> > + * signal event 0 at it; a target acks by marking itself offline, which
> > + * the caller polls for.
> > + *
> > + * Returns false when SDEI isn't active, so the caller can skip the wait.
> > + */
> > +bool sdei_nmi_stop_cpus(const cpumask_t *mask)
> > +{
> > + unsigned int cpu;
> > +
> > + if (!sdei_nmi_available)
> > + return false;
> > +
> > + cpumask_or(&sdei_nmi_stop_mask, &sdei_nmi_stop_mask, mask);
>
> As per above, hopefully we can get rid of `sdei_nmi_stop_mask`. ...but
> if we keep it, I'm curious why "or" and not "copy"?
It doesn't matter anymore. Mask is gone.
Thanks for the feedback! Any other comments?
--------------------------------8<-----------------------------------------------
From c25c32428c5f4fd896815acec5633240326e810c Mon Sep 17 00:00:00 2001
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
Date: Tue, 2 Jun 2026 15:28:10 +0100
Subject: [PATCHv2.1 3/3] arm64: escalate smp_send_stop() to an SDEI NMI as a last
resort
A CPU wedged with interrupts masked ignores the stop IPI, and without
pseudo-NMI there is no NMI IPI to escalate to: a reboot proceeds with
the CPU still running, and a kdump misses its registers.
Add a third rung to smp_send_stop(): once the IPI (and pseudo-NMI IPI,
if enabled) rungs have run, signal SDEI event 0 at whatever stayed
online. Firmware delivers it regardless of the target's DAIF, so it
reaches a CPU a plain IPI cannot; the target acks by going offline,
which the caller already polls for.
Fold the stop bookkeeping into one arm64_nmi_cpu_stop(regs,
die_on_crash), shared by the stop IPI handlers, panic_smp_self_stop()
and the SDEI handler, replacing the near-duplicate local_cpu_stop() and
ipi_cpu_crash_stop(). @die_on_crash is the only difference: the IPI
handlers pass true and PSCI CPU_OFF the CPU on a crash stop so a capture
kernel can reclaim it; the SDEI handler and self-stop pass false and
park. The SDEI park is required, not conservative -- its handler runs
inside an SDEI event that is never completed (completing it resumes the
wedged context), and a CPU_OFF from that unfinished-event context wedges
EL3 on some firmware (left as a follow-up). The dump is unaffected; only
re-onlining the CPU in an SMP capture kernel is lost.
Suggested-by: Doug Anderson <dianders@chromium.org>
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
---
arch/arm64/include/asm/nmi.h | 24 +++++++
arch/arm64/kernel/smp.c | 109 +++++++++++++++++++++-----------
drivers/firmware/Kconfig | 2 +
drivers/firmware/arm_sdei_nmi.c | 75 ++++++++++++++++++++++
4 files changed, 172 insertions(+), 38 deletions(-)
diff --git a/arch/arm64/include/asm/nmi.h b/arch/arm64/include/asm/nmi.h
index 9366be419d18..2e8974ff8d63 100644
--- a/arch/arm64/include/asm/nmi.h
+++ b/arch/arm64/include/asm/nmi.h
@@ -4,21 +4,45 @@
#include <linux/cpumask.h>
+struct pt_regs;
+
/*
* Cross-CPU NMI provider hooks, consulted by the arm64 arch code before
* its regular-IRQ / pseudo-NMI IPI paths. The SDEI provider in
* drivers/firmware/arm_sdei_nmi.c implements them when active; a future
* FEAT_NMI provider could slot in here too. The stubs let callers stay
* unconditional when ARM_SDEI_NMI is off.
+ *
+ * sdei_nmi_active() lets a caller test for the service before committing
+ * to (and waiting on) the SDEI stop rung; sdei_nmi_stop_cpus() then signals
+ * the targets, which ack by going offline.
*/
#ifdef CONFIG_ARM_SDEI_NMI
bool sdei_nmi_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu);
+bool sdei_nmi_active(void);
+void sdei_nmi_stop_cpus(const cpumask_t *mask);
#else
static inline bool sdei_nmi_trigger_cpumask_backtrace(const cpumask_t *mask,
int exclude_cpu)
{
return false;
}
+
+static inline bool sdei_nmi_active(void)
+{
+ return false;
+}
+
+static inline void sdei_nmi_stop_cpus(const cpumask_t *mask) { }
#endif
+/*
+ * The common "stop this CPU" entry every arm64 stop path funnels through:
+ * the regular/pseudo-NMI stop IPI handlers, panic_smp_self_stop(), and the
+ * SDEI cross-CPU NMI handler. @die_on_crash powers the CPU off on the kdump
+ * crash path (IPI handlers) instead of parking it (SDEI / self-stop).
+ * Defined in arch/arm64/kernel/smp.c.
+ */
+void __noreturn arm64_nmi_cpu_stop(struct pt_regs *regs, bool die_on_crash);
+
#endif /* __ASM_NMI_H */
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index a670434a8cae..e85a4ba18d5c 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -33,6 +33,7 @@
#include <linux/kernel_stat.h>
#include <linux/kexec.h>
#include <linux/kgdb.h>
+#include <linux/kprobes.h>
#include <linux/kvm_host.h>
#include <linux/nmi.h>
@@ -862,14 +863,58 @@ void arch_irq_work_raise(void)
}
#endif
-static void __noreturn local_cpu_stop(unsigned int cpu)
+/*
+ * Bring the local CPU to a stop, saving its register state into the vmcore
+ * on the kdump crash path first. The single point every arm64 stop path
+ * funnels through, so the bookkeeping (mask interrupts, mark offline, mask
+ * SDEI, optionally power off) lives in one place:
+ *
+ * - the regular IPI_CPU_STOP and pseudo-NMI IPI_CPU_STOP_NMI handlers;
+ * - panic_smp_self_stop(), a CPU parking itself on a parallel panic();
+ * - the SDEI cross-CPU NMI handler (drivers/firmware/arm_sdei_nmi.c),
+ * which reaches CPUs the stop IPIs could not.
+ *
+ * @regs is the register state to record in the vmcore on a crash stop; NULL
+ * means "capture the current context". @die_on_crash decides the kdump crash
+ * path: the IPI stop handlers pass true and power the CPU off (PSCI CPU_OFF,
+ * via __cpu_try_die()) so a capture kernel can reclaim it. The SDEI handler
+ * and panic_smp_self_stop() pass false and only park. For SDEI that is
+ * required, not just conservative: it runs inside an SDEI event that is
+ * deliberately never completed (completing it has firmware resume the wedged
+ * context), and a CPU_OFF from that not-yet-completed context wedges EL3 on
+ * some firmware -- a documented follow-up. Parking also matches this path's
+ * own fallback when CPU_OFF is unavailable.
+ */
+void __noreturn arm64_nmi_cpu_stop(struct pt_regs *regs, bool die_on_crash)
{
+ unsigned int cpu = smp_processor_id();
+ bool crash = IS_ENABLED(CONFIG_KEXEC_CORE) && crash_stop;
+
+ /*
+ * Use local_daif_mask() instead of local_irq_disable() to make sure
+ * that pseudo-NMIs are disabled. The "stop" code starts with an IRQ
+ * and falls back to NMI (which might be pseudo). If the IRQ finally
+ * goes through right as we're timing out then the NMI could interrupt
+ * us. It's better to prevent the NMI and let the IRQ finish since the
+ * pt_regs will be better.
+ */
+ local_daif_mask();
+
+ if (crash)
+ crash_save_cpu(regs, cpu);
+
+ /* the ack a stop requester (e.g. smp_send_stop()) polls for */
set_cpu_online(cpu, false);
- local_daif_mask();
sdei_mask_local_cpu();
+
+ if (crash && die_on_crash)
+ __cpu_try_die(cpu);
+
+ /* just in case */
cpu_park_loop();
}
+NOKPROBE_SYMBOL(arm64_nmi_cpu_stop);
/*
* We need to implement panic_smp_self_stop() for parallel panic() calls, so
@@ -878,36 +923,7 @@ static void __noreturn local_cpu_stop(unsigned int cpu)
*/
void __noreturn panic_smp_self_stop(void)
{
- local_cpu_stop(smp_processor_id());
-}
-
-static void __noreturn ipi_cpu_crash_stop(unsigned int cpu, struct pt_regs *regs)
-{
-#ifdef CONFIG_KEXEC_CORE
- /*
- * Use local_daif_mask() instead of local_irq_disable() to make sure
- * that pseudo-NMIs are disabled. The "crash stop" code starts with
- * an IRQ and falls back to NMI (which might be pseudo). If the IRQ
- * finally goes through right as we're timing out then the NMI could
- * interrupt us. It's better to prevent the NMI and let the IRQ
- * finish since the pt_regs will be better.
- */
- local_daif_mask();
-
- crash_save_cpu(regs, cpu);
-
- set_cpu_online(cpu, false);
-
- sdei_mask_local_cpu();
-
- if (IS_ENABLED(CONFIG_HOTPLUG_CPU))
- __cpu_try_die(cpu);
-
- /* just in case */
- cpu_park_loop();
-#else
- BUG();
-#endif
+ arm64_nmi_cpu_stop(NULL, false);
}
static void arm64_send_ipi(const cpumask_t *mask, unsigned int nr)
@@ -984,12 +1000,7 @@ static void do_handle_IPI(int ipinr)
case IPI_CPU_STOP:
case IPI_CPU_STOP_NMI:
- if (IS_ENABLED(CONFIG_KEXEC_CORE) && crash_stop) {
- ipi_cpu_crash_stop(cpu, get_irq_regs());
- unreachable();
- } else {
- local_cpu_stop(cpu);
- }
+ arm64_nmi_cpu_stop(get_irq_regs(), true);
break;
#ifdef CONFIG_GENERIC_CLOCKEVENTS_BROADCAST
@@ -1263,6 +1274,28 @@ void smp_send_stop(void)
udelay(1);
}
+ /*
+ * If CPUs are *still* online, try the SDEI cross-CPU NMI. Firmware
+ * delivers it regardless of the target's DAIF state, so it reaches
+ * a CPU spinning with interrupts masked, which neither rung above
+ * could (without pseudo-NMI there is no NMI rung at all). Allow
+ * 100ms: a firmware round-trip per CPU, with headroom.
+ */
+ if (num_other_online_cpus() && sdei_nmi_active()) {
+ /* re-snapshot after the rungs above took CPUs offline */
+ smp_rmb();
+ cpumask_copy(&mask, cpu_online_mask);
+ cpumask_clear_cpu(smp_processor_id(), &mask);
+
+ pr_info("SMP: retry stop with SDEI NMI for CPUs %*pbl\n",
+ cpumask_pr_args(&mask));
+
+ sdei_nmi_stop_cpus(&mask);
+ timeout = USEC_PER_MSEC * 100;
+ while (num_other_online_cpus() && timeout--)
+ udelay(1);
+ }
+
if (num_other_online_cpus()) {
smp_rmb();
cpumask_copy(&mask, cpu_online_mask);
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 6501087ff90d..ab0ee36d46e7 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -46,6 +46,8 @@ config ARM_SDEI_NMI
- arch_trigger_cpumask_backtrace() (sysrq-l, RCU stalls,
hardlockup_all_cpu_backtrace, soft-lockup secondary dumps,
hung-task auxiliary dumps)
+ - smp_send_stop() escalation (reboot/halt and the
+ panic / kdump crash stop)
The driver registers a handler for the SDEI software-signalled
event (event 0) and reaches a target CPU by signalling it with
diff --git a/drivers/firmware/arm_sdei_nmi.c b/drivers/firmware/arm_sdei_nmi.c
index a82776e7b55a..b2a69be6008f 100644
--- a/drivers/firmware/arm_sdei_nmi.c
+++ b/drivers/firmware/arm_sdei_nmi.c
@@ -29,6 +29,11 @@
* hardlockup_all_cpu_backtrace, soft-lockup/hung-task secondary
* dumps all reach interrupt-masked CPUs.
*
+ * - sdei_nmi_stop_cpus() — the last rung of smp_send_stop()'s
+ * escalation (reboot/halt and the panic/kdump crash stop alike),
+ * reaching CPUs that ignored the stop IPIs; on the kdump path the
+ * wedged context is captured into the vmcore before the CPU parks.
+ *
* Delivery uses the standard SDEI software-signalled event (event 0) and
* SDEI_EVENT_SIGNAL. We register a handler for event 0, enable it, and
* poke a target CPU with sdei_event_signal(0, mpidr): firmware makes
@@ -59,8 +64,51 @@ static bool sdei_nmi_available;
#define SDEI_NMI_EVENT 0
+/*
+ * Backtrace and stop both ride SDEI event 0. That is not a chosen economy:
+ * event 0 is the only architecturally software-signalled event -- the sole
+ * event SDEI_EVENT_SIGNAL can target at an arbitrary PE. Every other event
+ * number is a firmware/platform interrupt-bound event, not something the
+ * kernel can raise cross-CPU, so a dedicated "stop" event would need
+ * firmware to define and bind it -- exactly the firmware dependency this
+ * driver sets out to avoid.
+ *
+ * Sharing one event means the handler must tell a stop apart from a
+ * backtrace. A stop is terminal and system-wide -- sdei_nmi_stop_cpus() is
+ * only reached from smp_send_stop() (reboot/halt/panic/kdump), which never
+ * returns -- so once a stop is requested, every later event-0 fire is a
+ * stop too. A single write-once flag therefore carries as much as a
+ * per-CPU mask would: sdei_nmi_stop_cpus() sets it before signalling, and
+ * the handler reads a set flag as "stop this CPU" and a clear flag as
+ * "backtrace" (handled by nmi_cpu_backtrace(), which self-gates on the
+ * framework's backtrace mask). A backtrace fire that races in after a stop
+ * has begun just stops that CPU instead -- harmless, it is going down.
+ */
+static bool sdei_nmi_stopping;
+
static int sdei_nmi_handler(u32 event, struct pt_regs *regs, void *arg)
{
+ if (READ_ONCE(sdei_nmi_stopping)) {
+ /*
+ * Never returns, and deliberately never completes the SDEI
+ * event: SDEI_EVENT_COMPLETE has firmware restore the
+ * interrupted context, which would land the CPU back in
+ * the wedged loop (or in do_idle, which BUGs at
+ * cpuhp_report_idle_dead once it sees itself offline).
+ * Returning a modified pt_regs doesn't help --
+ * arch/arm64/kernel/sdei.c::do_sdei_event only honours a PC
+ * override via its IRQ-state heuristic and otherwise hands
+ * EL3 its own saved-context slot back.
+ *
+ * Trade-off: EL3 retains ~one saved-context slot per parked
+ * CPU until the next hardware reset (~hundreds of bytes per
+ * CPU). Recoverability is unchanged versus an IPI-stopped
+ * CPU: neither comes back without a reset.
+ */
+ arm64_nmi_cpu_stop(regs, false);
+ /* unreachable */
+ }
+
/*
* nmi_cpu_backtrace() no-ops unless this CPU's bit is set in the
* global backtrace mask (driven by nmi_trigger_cpumask_backtrace()),
@@ -115,6 +163,33 @@ bool sdei_nmi_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu)
return true;
}
+bool sdei_nmi_active(void)
+{
+ return sdei_nmi_available;
+}
+
+/*
+ * Last rung of the stop escalation in smp_send_stop() (see
+ * arch/arm64/kernel/smp.c). The caller runs the regular stop IPI (and
+ * the pseudo-NMI stop IPI, where available) first; @mask holds whatever
+ * stayed online through those -- typically CPUs wedged with interrupts
+ * masked, unreachable by an IPI. Mark the stop in progress and signal
+ * event 0 at each target; a target acks by marking itself offline, which
+ * the caller polls for. The caller has already confirmed sdei_nmi_active().
+ */
+void sdei_nmi_stop_cpus(const cpumask_t *mask)
+{
+ unsigned int cpu;
+
+ WRITE_ONCE(sdei_nmi_stopping, true);
+
+ /* Publish the flag before the SMCs make targets read it */
+ smp_wmb();
+
+ for_each_cpu(cpu, mask)
+ sdei_nmi_fire(cpu);
+}
+
/*
* device_initcall (after arch_initcall(sdei_init), so the SDEI subsystem
* is up): probe the firmware, register the event, and turn on the
--
Kiryl Shutsemau / Kirill A. Shutemov
^ permalink raw reply related
* Re: [PATCH v3] arm64: errata: Workaround NVIDIA Olympus device store/load ordering erratum
From: Jason Gunthorpe @ 2026-06-11 17:49 UTC (permalink / raw)
To: Will Deacon
Cc: Shanker Donthineni, Catalin Marinas, Vladimir Murzin,
linux-arm-kernel, Mark Rutland, linux-kernel, linux-doc,
Vikram Sethi, Jason Sequeira
In-Reply-To: <aiq5VigmtZq9GlAm@willie-the-truck>
On Thu, Jun 11, 2026 at 02:34:14PM +0100, Will Deacon wrote:
> I still reckon you should do something with the memcpy-to-io routines.
> A simple option could be to make dgh() a dmb on parts with the erratum?
> That at least moves the barrier out of the loop.
AFAIK only callers that know they are using WC memory should be
calling dgh() and in that case we know it is NORMAL-NC and we don't
need a different barrier
Other random users calling memcpy_to_io functions on real IO don't
have to do dgh(), and AFAIK it doesn't do anything on the Device
memory types?
Jason
^ permalink raw reply
* Re: iio: adc: KASAN wild-memory-access in complete() on early IRQ
From: Vladimir Zapolskiy @ 2026-06-11 17:50 UTC (permalink / raw)
To: Maxwell Doose, Jaeyoung Chung
Cc: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
Piotr Wojtaszczyk, linux-iio, linux-arm-kernel, linux-kernel,
Sangyun Kim, Kyungwook Boo
In-Reply-To: <CAKqfh0GVDpfP=TJZPQ2UYkUj=pe1KGn4j1zqbaz_19xawekdnw@mail.gmail.com>
On 6/11/26 19:07, Maxwell Doose wrote:
> On Wed, Jun 10, 2026 at 7:04 AM Jaeyoung Chung <jjy600901@snu.ac.kr> wrote:
>>
>> Hi,
>>
>> lpc32xx_adc_probe() in drivers/iio/adc/lpc32xx_adc.c and
>> spear_adc_probe() in drivers/iio/adc/spear_adc.c register their
>> interrupt handler with devm_request_irq() before they initialize
>> st->completion with init_completion(). If an interrupt arrives after
>> devm_request_irq() and before init_completion(), the handler calls
>> complete() on an uninitialized completion, causing a kernel panic.
>>
>> The probe path, in lpc32xx_adc_probe():
>>
>> iodev = devm_iio_device_alloc(&pdev->dev, sizeof(*st)); /* st kzalloc-zeroed */
>> ...
>> retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0,
>> LPC32XXAD_NAME, st); /* register handler */
>> ...
>> init_completion(&st->completion); /* initialize completion */
>>
>> spear_adc_probe() has the same ordering: devm_request_irq() for
>> spear_adc_isr() before init_completion(&st->completion).
>>
>> Both interrupt handlers, lpc32xx_adc_isr() and spear_adc_isr(), call
>> complete():
>>
>> complete(&st->completion);
>>
>> If the device raises an interrupt before init_completion() runs,
>> complete() acquires the uninitialized wait.lock and walks the zeroed
>> task_list in swake_up_locked(). The zeroed task_list makes list_empty()
>> return false, so swake_up_locked() dereferences a NULL list entry,
>> triggering a KASAN wild-memory-access.
>>
>> Suggested fix: move init_completion(&st->completion) above
>> devm_request_irq(), so the completion is valid before the handler can run.
>>
>> Reported-by: Sangyun Kim <sangyun.kim@snu.ac.kr>
>> Reported-by: Kyungwook Boo <bookyungwook@gmail.com>
>>
>
> Thanks for reporting this; I can start working on a fix shortly
> (assuming nobody else is already working on it).
>
The analysis and the proposed fix are correct, please go ahead, thank you
in advance.
--
Best wishes,
Vladimir
^ permalink raw reply
* Re: [PATCH] Bluetooth: btmtksdio: fix infinite loop in btmtksdio_txrx_work()
From: patchwork-bot+bluetooth @ 2026-06-11 17:58 UTC (permalink / raw)
To: Sergey Senozhatsky
Cc: marcel, luiz.dentz, mark-yw.chen, sean.wang, tfiga,
linux-bluetooth, linux-kernel, linux-arm-kernel, linux-mediatek,
stable
In-Reply-To: <20260609121329.1262170-1-senozhatsky@chromium.org>
Hello:
This patch was applied to bluetooth/bluetooth-next.git (master)
by Luiz Augusto von Dentz <luiz.von.dentz@intel.com>:
On Tue, 9 Jun 2026 21:10:06 +0900 you wrote:
> Every once in a while we see a hung btmtksdio_flush() task:
>
> INFO: task kworker/u17:0:189 blocked for more than 122 seconds.
> __cancel_work_timer+0x3f4/0x460
> cancel_work_sync+0x1c/0x2c
> btmtksdio_flush+0x2c/0x40
> hci_dev_open_sync+0x10c4/0x2190
> [..]
>
> [...]
Here is the summary with links:
- Bluetooth: btmtksdio: fix infinite loop in btmtksdio_txrx_work()
https://git.kernel.org/bluetooth/bluetooth-next/c/a4263306d01d
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply
* Re: [PATCH v5] soc: aspeed: lpc-snoop: Fix usercopy overflow in snoop_file_read
From: Karthikeyan KS @ 2026-06-11 18:08 UTC (permalink / raw)
To: andrew
Cc: joel, andrew, Kees Cook, linux-arm-kernel, linux-aspeed,
linux-kernel, linux-hardening
In-Reply-To: <033f2657ae6a94ad13d22f717a2900afb75d892d.camel@codeconstruct.com.au>
Hi Andrew,
Thanks Andrew. The __guarded_by annotation and context analysis integration look good, I wasn't aware of that infrastructure.
Thanks for applying the fix-up on top.
Thanks,
Karthikeyan
^ permalink raw reply
* [PATCH 0/3] ARM: dts: stm32: lxa: change stdout-path baud rate from 9600 to 115200
From: Ahmad Fatoum @ 2026-06-11 18:12 UTC (permalink / raw)
To: Alexandre Torgue, Maxime Coquelin, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Leonard Göhrs,
Marc Kleine-Budde
Cc: Alexandre Torgue, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, kernel, Ahmad Fatoum
The LXA boards are the only STM32 boards that set stdout-path = &uart*
instead of explicitly specifying a baud rate.
This would mean the default of 9600 is used, but it goes unnoticed when
booting normally as barebox fixes up a console= line that includes a
baud rate.
When EFI booting GRUB however, GRUB will not pass along the console=
line and thus the board ends up with a 9600 baud Linux console,
confusing users.
This series fixes this. As the device trees were added at different
times, they are fixed each in a separate commit with its own Fixes: tag.
---
Ahmad Fatoum (3):
ARM: dts: stm32: lxa-mc1: change stdout-path baud rate from 9600 to 115200
ARM: dts: stm32: lxa-tac: change stdout-path baud rate from 9600 to 115200
ARM: dts: stm32: fairytux2: change stdout-path baud rate from 9600 to 115200
arch/arm/boot/dts/st/stm32mp153c-lxa-fairytux2.dtsi | 2 +-
arch/arm/boot/dts/st/stm32mp157c-lxa-mc1.dts | 2 +-
arch/arm/boot/dts/st/stm32mp15xc-lxa-tac.dtsi | 2 +-
3 files changed, 3 insertions(+), 3 deletions(-)
---
base-commit: 4549871118cf616eecdd2d939f78e3b9e1dddc48
change-id: 20260611-lxa-stdout-path-baudrate-7cf454cdae07
Best regards,
--
Ahmad Fatoum <a.fatoum@pengutronix.de>
^ permalink raw reply
* [PATCH 1/3] ARM: dts: stm32: lxa-mc1: change stdout-path baud rate from 9600 to 115200
From: Ahmad Fatoum @ 2026-06-11 18:12 UTC (permalink / raw)
To: Alexandre Torgue, Maxime Coquelin, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Leonard Göhrs,
Marc Kleine-Budde
Cc: Alexandre Torgue, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, kernel, Ahmad Fatoum
In-Reply-To: <20260611-lxa-stdout-path-baudrate-v1-0-59b60a5069ff@pengutronix.de>
The default baud rate when none is specified is up to the DT consumer.
In the case of the Linux STM32 serial driver, it defaults to 9600 baud,
which differs from the 115200 baud that this board's barebox bootloader
configured.
This went unnoticed, because barebox automatically fixes up a console=
command-line option that looks like this on the LXA boards:
console=ttySTM0,115200n8
This had precedence over the 9600 fallback baud rate.
But when EFI booting a kernel via GRUB, we run into this issue, because
the barebox-provided command-line is disregarded by GRUB.
Fix this by explicitly setting the baud rate to the correct 115200.
Fixes: 666b5ca85cd3 ("ARM: dts: stm32: add STM32MP1-based Linux Automation MC-1 board")
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/arm/boot/dts/st/stm32mp157c-lxa-mc1.dts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/st/stm32mp157c-lxa-mc1.dts b/arch/arm/boot/dts/st/stm32mp157c-lxa-mc1.dts
index eada9cf257be..b3d8eb57aa24 100644
--- a/arch/arm/boot/dts/st/stm32mp157c-lxa-mc1.dts
+++ b/arch/arm/boot/dts/st/stm32mp157c-lxa-mc1.dts
@@ -33,7 +33,7 @@ backlight: backlight {
};
chosen {
- stdout-path = &uart4;
+ stdout-path = "serial0:115200n8";
};
led-controller-0 {
--
2.47.3
^ permalink raw reply related
* [PATCH 2/3] ARM: dts: stm32: lxa-tac: change stdout-path baud rate from 9600 to 115200
From: Ahmad Fatoum @ 2026-06-11 18:12 UTC (permalink / raw)
To: Alexandre Torgue, Maxime Coquelin, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Leonard Göhrs,
Marc Kleine-Budde
Cc: Alexandre Torgue, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, kernel, Ahmad Fatoum
In-Reply-To: <20260611-lxa-stdout-path-baudrate-v1-0-59b60a5069ff@pengutronix.de>
The default baud rate when none is specified is up to the DT consumer.
In the case of the Linux STM32 serial driver, it defaults to 9600 baud,
which differs from the 115200 baud that this board's barebox bootloader
configured.
This went unnoticed, because barebox automatically fixes up a console=
command-line option that looks like this on the LXA boards:
console=ttySTM0,115200n8
This had precedence over the 9600 fallback baud rate.
But when EFI booting a kernel via GRUB, we run into this issue, because
the barebox-provided command-line is disregarded by GRUB.
Fix this by explicitly setting the baud rate to the correct 115200.
Fixes: 518272af37b2 ("ARM: dts: stm32: lxa-tac: add Linux Automation GmbH TAC")
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/arm/boot/dts/st/stm32mp15xc-lxa-tac.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/st/stm32mp15xc-lxa-tac.dtsi b/arch/arm/boot/dts/st/stm32mp15xc-lxa-tac.dtsi
index ab13f0c39892..ddb1657cd785 100644
--- a/arch/arm/boot/dts/st/stm32mp15xc-lxa-tac.dtsi
+++ b/arch/arm/boot/dts/st/stm32mp15xc-lxa-tac.dtsi
@@ -33,7 +33,7 @@ aliases {
};
chosen {
- stdout-path = &uart4;
+ stdout-path = "serial0:115200n8";
};
led-controller-0 {
--
2.47.3
^ permalink raw reply related
* [PATCH 3/3] ARM: dts: stm32: fairytux2: change stdout-path baud rate from 9600 to 115200
From: Ahmad Fatoum @ 2026-06-11 18:12 UTC (permalink / raw)
To: Alexandre Torgue, Maxime Coquelin, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Leonard Göhrs,
Marc Kleine-Budde
Cc: Alexandre Torgue, devicetree, linux-stm32, linux-arm-kernel,
linux-kernel, kernel, Ahmad Fatoum
In-Reply-To: <20260611-lxa-stdout-path-baudrate-v1-0-59b60a5069ff@pengutronix.de>
The default baud rate when none is specified is up to the DT consumer.
In the case of the Linux STM32 serial driver, it defaults to 9600 baud,
which differs from the 115200 baud that this board's barebox bootloader
configured.
This went unnoticed, because barebox automatically fixes up a console=
command-line option that looks like this on the LXA boards:
console=ttySTM0,115200n8
This had precedence over the 9600 fallback baud rate.
But when EFI booting a kernel via GRUB, we run into this issue, because
the barebox-provided command-line is disregarded by GRUB.
Fix this by explicitly setting the baud rate to the correct 115200.
Fixes: 8c6d469f5249 ("ARM: dts: stm32: lxa-fairytux2: add Linux Automation GmbH FairyTux 2")
Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
arch/arm/boot/dts/st/stm32mp153c-lxa-fairytux2.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/st/stm32mp153c-lxa-fairytux2.dtsi b/arch/arm/boot/dts/st/stm32mp153c-lxa-fairytux2.dtsi
index 7d3a6a3b5d09..d30b626a18c2 100644
--- a/arch/arm/boot/dts/st/stm32mp153c-lxa-fairytux2.dtsi
+++ b/arch/arm/boot/dts/st/stm32mp153c-lxa-fairytux2.dtsi
@@ -28,7 +28,7 @@ aliases {
};
chosen {
- stdout-path = &uart4;
+ stdout-path = "serial0:115200n8";
};
backlight: backlight {
--
2.47.3
^ permalink raw reply related
* [PATCH] Documentation: arch: fix brackets
From: Manuel Ebner @ 2026-06-11 18:35 UTC (permalink / raw)
To: Vineet Gupta, Jonathan Corbet, Shuah Khan, Krzysztof Kozlowski,
Peter Griffin, Alim Akhtar, Catalin Marinas, Will Deacon,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Christophe Leroy, open list:SYNOPSYS ARC ARCHITECTURE,
open list:DOCUMENTATION, open list,
moderated list:ARM/SAMSUNG S3C, S5P AND EXYNOS ARM ARCHITECTURES,
open list:ARM/SAMSUNG S3C, S5P AND EXYNOS ARM ARCHITECTURES,
open list:LINUX FOR POWERPC (32-BIT AND 64-BIT)
Cc: Manuel Ebner
Add missing and remove needless parentheses, brackets and curly braces.
Signed-off-by: Manuel Ebner <manuelebner@mailbox.org>
---
Documentation/arch/arc/arc.rst | 2 +-
.../arm/samsung/clksrc-change-registers.awk | 2 +-
Documentation/arch/arm/vlocks.rst | 4 ++--
.../arch/arm64/memory-tagging-extension.rst | 2 +-
Documentation/arch/powerpc/vas-api.rst | 2 +-
Documentation/arch/sparc/oradax/dax-hv-api.txt | 18 +++++++++---------
Documentation/arch/sparc/oradax/oracle-dax.rst | 3 ++-
7 files changed, 17 insertions(+), 16 deletions(-)
diff --git a/Documentation/arch/arc/arc.rst b/Documentation/arch/arc/arc.rst
index 6c4d978f3f4e..553851f43be7 100644
--- a/Documentation/arch/arc/arc.rst
+++ b/Documentation/arch/arc/arc.rst
@@ -36,7 +36,7 @@ Important note on ARC processors configurability
ARC processors are highly configurable and several configurable options
are supported in Linux. Some options are transparent to software
-(i.e cache geometries, some can be detected at runtime and configured
+(i.e cache geometries), some can be detected at runtime and configured
and used accordingly, while some need to be explicitly selected or configured
in the kernel's configuration utility (AKA "make menuconfig").
diff --git a/Documentation/arch/arm/samsung/clksrc-change-registers.awk b/Documentation/arch/arm/samsung/clksrc-change-registers.awk
index 7be1b8aa7cd9..48464397088c 100755
--- a/Documentation/arch/arm/samsung/clksrc-change-registers.awk
+++ b/Documentation/arch/arm/samsung/clksrc-change-registers.awk
@@ -163,4 +163,4 @@ BEGIN {
}
}
-// && ! /clksrc_clk.*=.*{/ { print $0 }
+// && ! /clksrc_clk.*=.*{/ { print $0 }}
diff --git a/Documentation/arch/arm/vlocks.rst b/Documentation/arch/arm/vlocks.rst
index 737aa8661a21..b0ac33263086 100644
--- a/Documentation/arch/arm/vlocks.rst
+++ b/Documentation/arch/arm/vlocks.rst
@@ -102,10 +102,10 @@ Features and limitations
if (I_won) {
/* we won the town election, let's go for the state */
my_state = states[(this_cpu >> 8) & 0xf];
- I_won = vlock_lock(my_state, this_cpu & 0xf));
+ I_won = vlock_lock(my_state, this_cpu & 0xf);
if (I_won) {
/* and so on */
- I_won = vlock_lock(the_whole_country, this_cpu & 0xf];
+ I_won = vlock_lock(the_whole_country, this_cpu & 0xf);
if (I_won) {
/* ... */
}
diff --git a/Documentation/arch/arm64/memory-tagging-extension.rst b/Documentation/arch/arm64/memory-tagging-extension.rst
index 679725030731..e6fe428f0e2a 100644
--- a/Documentation/arch/arm64/memory-tagging-extension.rst
+++ b/Documentation/arch/arm64/memory-tagging-extension.rst
@@ -222,7 +222,7 @@ programs should not retry in case of a non-zero system call return.
address ABI control and MTE configuration of a process as per the
``prctl()`` options described in
Documentation/arch/arm64/tagged-address-abi.rst and above. The corresponding
-``regset`` is 1 element of 8 bytes (``sizeof(long))``).
+``regset`` is 1 element of 8 bytes (``sizeof(long)``).
Core dump support
-----------------
diff --git a/Documentation/arch/powerpc/vas-api.rst b/Documentation/arch/powerpc/vas-api.rst
index a9625a2fa0c6..1d0d055356e3 100644
--- a/Documentation/arch/powerpc/vas-api.rst
+++ b/Documentation/arch/powerpc/vas-api.rst
@@ -293,7 +293,7 @@ Simple example
//Format CRB request with compression or
//uncompression
// Refer tests for vas_copy/vas_paste
- vas_copy((&crb, 0, 1);
+ vas_copy(&crb, 0, 1);
vas_paste(addr, 0, 1);
// Poll on csb.flags with timeout
// csb address is listed in CRB
diff --git a/Documentation/arch/sparc/oradax/dax-hv-api.txt b/Documentation/arch/sparc/oradax/dax-hv-api.txt
index ef1a4c2bf08b..ef6088aeaa66 100644
--- a/Documentation/arch/sparc/oradax/dax-hv-api.txt
+++ b/Documentation/arch/sparc/oradax/dax-hv-api.txt
@@ -457,7 +457,7 @@ bits set, and terminate at a CCB that has the Conditional bit set, but not the P
Offset Size Field Description
Bits Field Description
[15:14] Secondary Input Element Size (see Section 36.2.1.1.4,
- “Secondary Input Element Size”
+ “Secondary Input Element Size”)
[13:10] Output Format (see Section 36.2.1.1.6, “Output Format”)
[9] Padding Direction selector: A value of 1 causes padding bytes
to be added to the left side of output elements. A value of 0
@@ -656,7 +656,7 @@ Offset Size Field Description
[18:16] Secondary Input Starting Offset (see Section 36.2.1.1.5, “Input
Element Offsets”)
[15:14] Secondary Input Element Size (see Section 36.2.1.1.4,
- “Secondary Input Element Size”
+ “Secondary Input Element Size”)
[13:10] Output Format (see Section 36.2.1.1.6, “Output Format”)
[9:5] Operand size for first scan criteria value. In a scan value
operation, this is one of two potential exact match values.
@@ -793,13 +793,13 @@ Offset Size Field Description
[18:16] Secondary Input Starting Offset (see Section 36.2.1.1.5, “Input
Element Offsets”)
[15:14] Secondary Input Element Size (see Section 36.2.1.1.4,
- “Secondary Input Element Size”
+ “Secondary Input Element Size”)
[13:10] Output Format (see Section 36.2.1.1.6, “Output Format”)
[9] Reserved
[8:0] Test value used for comparison against the most significant bits
in the input values, when using 2 or 3 byte input elements.
-8 8 Completion (same fields as Section 36.2.1.2, “Extract command”
-16 8 Primary Input (same fields as Section 36.2.1.2, “Extract command”
+8 8 Completion (same fields as Section 36.2.1.2, “Extract command)”
+16 8 Primary Input (same fields as Section 36.2.1.2, “Extract command”)
24 8 Data Access Control (same fields as Section 36.2.1.2, “Extract command”,
except Primary Input Length Format may not use the 0x0 value)
32 8 Secondary Input, if used by Primary Input Format. Same fields as Primary
@@ -880,7 +880,7 @@ Offset Size Field Description
[18:16] Secondary Input Starting Offset (see Section 36.2.1.1.5, “Input
Element Offsets”)
[15:14] Secondary Input Element Size (see Section 36.2.1.1.4,
- “Secondary Input Element Size”
+ “Secondary Input Element Size”)
524
@@ -895,8 +895,8 @@ Offset Size Field Description
causes padding bytes to be added to the right side of output
elements.
[8:0] Reserved
- 8 8 Completion (same fields as Section 36.2.1.2, “Extract command”
- 16 8 Primary Input (same fields as Section 36.2.1.2, “Extract command”
+ 8 8 Completion (same fields as Section 36.2.1.2, “Extract command”)
+ 16 8 Primary Input (same fields as Section 36.2.1.2, “Extract command”)
24 8 Data Access Control (same fields as Section 36.2.1.2, “Extract command”)
32 8 Secondary Bit Vector Input. Same fields as Primary Input.
40 8 Reserved
@@ -949,7 +949,7 @@ Offset Size Field Description
[31] If set, this CCB functions as a Sync command. If clear, this
CCB functions as a No-op command.
[30:0] Reserved
- 8 8 Completion (same fields as Section 36.2.1.2, “Extract command”
+ 8 8 Completion (same fields as Section 36.2.1.2, “Extract command”)
16 46 Reserved
36.2.2. CCB Completion Area
diff --git a/Documentation/arch/sparc/oradax/oracle-dax.rst b/Documentation/arch/sparc/oradax/oracle-dax.rst
index d1e14d572918..67867ea7be40 100644
--- a/Documentation/arch/sparc/oradax/oracle-dax.rst
+++ b/Documentation/arch/sparc/oradax/oracle-dax.rst
@@ -438,7 +438,8 @@ that in user land::
The output bitmap is ready for consumption immediately after the
completion status indicates success.
-Excer[t from UltraSPARC Virtual Machine Specification
+Excer?t from UltraSPARC Virtual Machine Specification
+i guess this is wrong, but i don't know what's correct
=====================================================
.. include:: dax-hv-api.txt
--
2.54.0
^ permalink raw reply related
* [PATCH RFC 0/9] net: stmmac: qcom-ethqos: add Shikra EMAC support
From: Mohd Ayaan Anwar @ 2026-06-11 18:36 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Richard Cochran, Bjorn Andersson, Konrad Dybcio, Maxime Coquelin,
Alexandre Torgue, Russell King
Cc: linux-arm-msm, netdev, devicetree, linux-kernel, linux-stm32,
linux-arm-kernel, Mohd Ayaan Anwar
(Sending this out as an RFC because we have a couple of fixes for RGMII
handling in dwmac-qcom-ethqos, as well as a new framework to manage
clocks required by the System NOC. If this gets into a decent shape,
then this series will be split between patches for DT and net-next.)
Hi,
This series adds Gigabit Ethernet support for the Qualcomm Shikra SoC,
which integrates two EMAC controllers based on the Synopsys GMAC IP,
similar to previous Qualcomm platforms.
Before introducing the new compatible, two generic fixes are applied to
the driver that benefit all platforms:
- ethqos_rgmii_macro_init() is converted to void; the return value was
never checked and the speed validation was redundant.
- RGMII_ID mode ("rgmii-id") was incorrectly falling through to the
standard DLL bring-up path, which may add MAC-internal delays. The
fix powers down the DLL, sets DDR bypass mode, and programs the
IO_MACRO via a new ethqos_rgmii_id_macro_init() helper. The clock
rate doubling in ethqos_set_clk_tx_rate() is also corrected for
bypass mode.
Shikra-specific additions:
- On Shikra, access to the System NOC from the EMAC is gated by three
dedicated clocks. The axi and axi-noc clocks are sourced from a
dedicated RCG (emac0_axi_clk_srcg) per EMAC instance. The
pcie-tile-axi-noc clock (gcc_pcie_tile_axi_sys_noc_clk) gates an
arbiter internal to the NOC shared by both EMAC instances and must
be enabled for any EMAC-to-DDR traffic to complete. Generic
infrastructure is introduced (ethqos_noc_clk_cfg) so future
platforms can declare their own sets.
- The qcom,shikra-ethqos compatible is registered.
- DTS for the SoC and three EVK boards (CQM, CQS, IQS) is included.
The CQM and CQS boards expose one EMAC; the IQS board exposes both.
Dependencies:
This series is based on linux-next and depends on the core Shikra SoC
framework (DT, clock, regulator) being merged.
Tested on the Shikra CQM-EVK, CQS-EVK, and IQS-EVK with rgmii-id mode
at 1G/100M/10M speeds.
---
Mohd Ayaan Anwar (9):
dt-bindings: net: qcom,ethqos: add qcom,shikra-ethqos compatible
net: stmmac: qcom-ethqos: convert ethqos_rgmii_macro_init() to void
net: stmmac: qcom-ethqos: fix RGMII_ID mode to use DLL bypass
net: stmmac: qcom-ethqos: add per-platform NOC clock voting
net: stmmac: qcom-ethqos: add Shikra EMAC support
arm64: dts: qcom: shikra: Add ethernet nodes
arm64: dts: qcom: shikra-cqm-evk: Enable ethernet0
arm64: dts: qcom: shikra-cqs-evk: Enable ethernet0
arm64: dts: qcom: shikra-iqs-evk: Enable both ethernet ports
.../devicetree/bindings/net/qcom,ethqos.yaml | 31 ++-
arch/arm64/boot/dts/qcom/shikra-cqm-evk.dts | 119 +++++++++++
arch/arm64/boot/dts/qcom/shikra-cqs-evk.dts | 119 +++++++++++
arch/arm64/boot/dts/qcom/shikra-iqs-evk.dts | 235 +++++++++++++++++++++
arch/arm64/boot/dts/qcom/shikra.dtsi | 78 +++++++
.../ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c | 167 ++++++++++++++-
6 files changed, 730 insertions(+), 19 deletions(-)
---
base-commit: 999ed3289ceea2b2808f396b4bd2f9a43dcba033
change-id: 20260611-shikra_ethernet-11a1b318d5b6
Best regards,
--
Mohd Ayaan Anwar <mohd.anwar@oss.qualcomm.com>
^ permalink raw reply
* [PATCH RFC 1/9] dt-bindings: net: qcom,ethqos: add qcom,shikra-ethqos compatible
From: Mohd Ayaan Anwar @ 2026-06-11 18:36 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Richard Cochran, Bjorn Andersson, Konrad Dybcio, Maxime Coquelin,
Alexandre Torgue, Russell King
Cc: linux-arm-msm, netdev, devicetree, linux-kernel, linux-stm32,
linux-arm-kernel, Mohd Ayaan Anwar
In-Reply-To: <20260612-shikra_ethernet-v1-0-f0f4a1d19929@oss.qualcomm.com>
Shikra's EMAC requires three additional clocks beyond the standard four
(axi, axi-noc, pcie-tile-axi-noc) for NOC interconnect voting. Add the
compatible string and extend clock-names with a oneOf variant for this
seven-clock configuration.
The AXI clock appears twice (as "stmmaceth" and "axi") because the
stmmac core and the driver's NOC bulk-clock array each consume one
reference; CCF refcounting makes this safe.
Signed-off-by: Mohd Ayaan Anwar <mohd.anwar@oss.qualcomm.com>
---
.../devicetree/bindings/net/qcom,ethqos.yaml | 31 ++++++++++++++++------
1 file changed, 23 insertions(+), 8 deletions(-)
diff --git a/Documentation/devicetree/bindings/net/qcom,ethqos.yaml b/Documentation/devicetree/bindings/net/qcom,ethqos.yaml
index 423959cb928d945aa3e758a3c803d12bd61ec42b..4528946856c8c6e33bc6ad4159ed111f35ec37d9 100644
--- a/Documentation/devicetree/bindings/net/qcom,ethqos.yaml
+++ b/Documentation/devicetree/bindings/net/qcom,ethqos.yaml
@@ -32,6 +32,7 @@ properties:
- qcom,qcs404-ethqos
- qcom,sa8775p-ethqos
- qcom,sc8280xp-ethqos
+ - qcom,shikra-ethqos
- qcom,sm8150-ethqos
reg:
@@ -57,16 +58,30 @@ properties:
- const: sfty
clocks:
- maxItems: 4
+ minItems: 4
+ maxItems: 7
clock-names:
- items:
- - const: stmmaceth
- - const: pclk
- - const: ptp_ref
- - enum:
- - rgmii
- - phyaux
+ oneOf:
+ - items:
+ - const: stmmaceth
+ - const: pclk
+ - const: ptp_ref
+ - enum:
+ - rgmii
+ - phyaux
+ - description: |
+ Extended clock list for platforms with AXI NOC clocks that require
+ explicit driver management (e.g. Shikra). GCC_EMAC0_AXI_CLK appears
+ as both "stmmaceth" and "axi"; CCF refcounting makes this safe.
+ items:
+ - const: stmmaceth
+ - const: pclk
+ - const: ptp_ref
+ - const: rgmii
+ - const: axi
+ - const: axi-noc
+ - const: pcie-tile-axi-noc
iommus:
maxItems: 1
--
2.34.1
^ permalink raw reply related
* [PATCH RFC 2/9] net: stmmac: qcom-ethqos: convert ethqos_rgmii_macro_init() to void
From: Mohd Ayaan Anwar @ 2026-06-11 18:36 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Richard Cochran, Bjorn Andersson, Konrad Dybcio, Maxime Coquelin,
Alexandre Torgue, Russell King
Cc: linux-arm-msm, netdev, devicetree, linux-kernel, linux-stm32,
linux-arm-kernel, Mohd Ayaan Anwar
In-Reply-To: <20260612-shikra_ethernet-v1-0-f0f4a1d19929@oss.qualcomm.com>
The return value is never checked by its sole caller and the speed
validation duplicates a check higher up the call stack. Convert to
void and remove the dead code.
Signed-off-by: Mohd Ayaan Anwar <mohd.anwar@oss.qualcomm.com>
---
drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c | 10 +---------
1 file changed, 1 insertion(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
index ac7d6d3e205a1ab5b391def879d6f1033a0961b6..44270c25d874f72e7f971757fec659d36468c315 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
@@ -371,9 +371,8 @@ static int ethqos_dll_configure(struct qcom_ethqos *ethqos)
return 0;
}
-static int ethqos_rgmii_macro_init(struct qcom_ethqos *ethqos, int speed)
+static void ethqos_rgmii_macro_init(struct qcom_ethqos *ethqos, int speed)
{
- struct device *dev = ðqos->pdev->dev;
unsigned int prg_rclk_dly, loopback;
unsigned int phase_shift;
@@ -384,11 +383,6 @@ static int ethqos_rgmii_macro_init(struct qcom_ethqos *ethqos, int speed)
/* Select RGMII, write 0 to interface select */
rgmii_clrmask(ethqos, RGMII_CONFIG_INTF_SEL, RGMII_IO_MACRO_CONFIG);
- if (speed != SPEED_1000 && speed != SPEED_100 && speed != SPEED_10) {
- dev_err(dev, "Invalid speed %d\n", speed);
- return -EINVAL;
- }
-
rgmii_setmask(ethqos, RGMII_CONFIG_DDR_MODE, RGMII_IO_MACRO_CONFIG);
if (speed == SPEED_1000) {
@@ -479,8 +473,6 @@ static int ethqos_rgmii_macro_init(struct qcom_ethqos *ethqos, int speed)
rgmii_updatel(ethqos, RGMII_CONFIG_LOOPBACK_EN, loopback,
RGMII_IO_MACRO_CONFIG);
-
- return 0;
}
static void ethqos_fix_mac_speed_rgmii(void *bsp_priv,
--
2.34.1
^ permalink raw reply related
* [PATCH RFC 3/9] net: stmmac: qcom-ethqos: fix RGMII_ID mode to use DLL bypass
From: Mohd Ayaan Anwar @ 2026-06-11 18:36 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Richard Cochran, Bjorn Andersson, Konrad Dybcio, Maxime Coquelin,
Alexandre Torgue, Russell King
Cc: linux-arm-msm, netdev, devicetree, linux-kernel, linux-stm32,
linux-arm-kernel, Mohd Ayaan Anwar
In-Reply-To: <20260612-shikra_ethernet-v1-0-f0f4a1d19929@oss.qualcomm.com>
When "rgmii-id" is selected the PHY supplies both TX and RX delays, so
the MAC must not add its own. The driver currently falls through to the
generic DLL initialisation path which programs it to add a delay.
Power down the DLL and set DDR bypass mode for RGMII_ID, then program
the IO_MACRO via a new ethqos_rgmii_id_macro_init() helper. Also fix
ethqos_set_clk_tx_rate() to not double the clock rate in bypass mode at
100M/10M, and remove RGMII_ID from the phase-shift suppression in
ethqos_rgmii_macro_init() since RGMII_ID no longer reaches that path.
Signed-off-by: Mohd Ayaan Anwar <mohd.anwar@oss.qualcomm.com>
---
.../ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c | 65 +++++++++++++++++++++-
1 file changed, 62 insertions(+), 3 deletions(-)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
index 44270c25d874f72e7f971757fec659d36468c315..bec08f1eb8cb41484ba3c91c77393e163e7fd071 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
@@ -67,6 +67,9 @@
/* SDC4_STATUS bits */
#define SDC4_STATUS_DLL_LOCK BIT(7)
+/* SDCC_USR_CTL bits */
+#define SDCC_USR_CTL_DDR_BYPASS BIT(30)
+
/* RGMII_IO_MACRO_CONFIG2 fields */
#define RGMII_CONFIG2_RSVD_CONFIG15 GENMASK(31, 17)
#define RGMII_CONFIG2_RGMII_CLK_SEL_CFG BIT(16)
@@ -183,7 +186,15 @@ static int ethqos_set_clk_tx_rate(void *bsp_priv, struct clk *clk_tx_i,
if (rate < 0)
return rate;
- return clk_set_rate(ethqos->link_clk, rate * 2);
+ /* Clock Rate Requirements:
+ * MAC added delay: 250/50/5 Mhz for 1G/100M/10M
+ * No MAC delay (DLL bypass): 250/25/2.5 Mhz for 1G/100M/10M
+ */
+ if (ethqos->phy_mode == PHY_INTERFACE_MODE_RGMII ||
+ speed == SPEED_1000)
+ rate *= 2;
+
+ return clk_set_rate(ethqos->link_clk, rate);
}
static void
@@ -405,8 +416,7 @@ static void ethqos_rgmii_macro_init(struct qcom_ethqos *ethqos, int speed)
RGMII_IO_MACRO_CONFIG2);
/* Determine if the PHY adds a 2 ns TX delay or the MAC handles it */
- if (ethqos->phy_mode == PHY_INTERFACE_MODE_RGMII_ID ||
- ethqos->phy_mode == PHY_INTERFACE_MODE_RGMII_TXID)
+ if (ethqos->phy_mode == PHY_INTERFACE_MODE_RGMII_TXID)
phase_shift = 0;
else
phase_shift = RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN;
@@ -475,6 +485,40 @@ static void ethqos_rgmii_macro_init(struct qcom_ethqos *ethqos, int speed)
RGMII_IO_MACRO_CONFIG);
}
+static void ethqos_rgmii_id_macro_init(struct qcom_ethqos *ethqos, int speed)
+{
+ rgmii_clrmask(ethqos, RGMII_CONFIG2_TX_TO_RX_LOOPBACK_EN,
+ RGMII_IO_MACRO_CONFIG2);
+
+ if (speed == SPEED_1000)
+ rgmii_setmask(ethqos, RGMII_CONFIG_DDR_MODE, RGMII_IO_MACRO_CONFIG);
+ else
+ rgmii_clrmask(ethqos, RGMII_CONFIG_DDR_MODE, RGMII_IO_MACRO_CONFIG);
+ rgmii_setmask(ethqos, RGMII_CONFIG_BYPASS_TX_ID_EN, RGMII_IO_MACRO_CONFIG);
+ rgmii_clrmask(ethqos, RGMII_CONFIG_POS_NEG_DATA_SEL, RGMII_IO_MACRO_CONFIG);
+ rgmii_clrmask(ethqos, RGMII_CONFIG_PROG_SWAP, RGMII_IO_MACRO_CONFIG);
+
+ if (ethqos->has_emac_ge_3)
+ rgmii_clrmask(ethqos, RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL,
+ RGMII_IO_MACRO_CONFIG2);
+ else
+ rgmii_setmask(ethqos, RGMII_CONFIG2_DATA_DIVIDE_CLK_SEL,
+ RGMII_IO_MACRO_CONFIG2);
+
+ rgmii_clrmask(ethqos, RGMII_CONFIG2_TX_CLK_PHASE_SHIFT_EN,
+ RGMII_IO_MACRO_CONFIG2);
+
+ if (speed == SPEED_1000)
+ rgmii_clrmask(ethqos, RGMII_CONFIG2_RSVD_CONFIG15, RGMII_IO_MACRO_CONFIG2);
+ else
+ rgmii_setmask(ethqos, RGMII_CONFIG2_RSVD_CONFIG15, RGMII_IO_MACRO_CONFIG2);
+
+ if (!ethqos->rgmii_config_loopback_en)
+ rgmii_clrmask(ethqos, RGMII_CONFIG_LOOPBACK_EN, RGMII_IO_MACRO_CONFIG);
+
+ rgmii_setmask(ethqos, RGMII_CONFIG2_RX_PROG_SWAP, RGMII_IO_MACRO_CONFIG2);
+}
+
static void ethqos_fix_mac_speed_rgmii(void *bsp_priv,
phy_interface_t interface, int speed,
unsigned int mode)
@@ -493,6 +537,21 @@ static void ethqos_fix_mac_speed_rgmii(void *bsp_priv,
ethqos_set_func_clk_en(ethqos);
+ /* For rgmii-id mode, the PHY should add the required delays.
+ * Therefore, power down the DLL and program it in bypass mode.
+ * Program the IO_MACRO as per the settings recommended by the
+ * programming guide for bypass mode. This will ensure that the
+ * MAC core doesn't add any additional delays.
+ */
+ if (ethqos->phy_mode == PHY_INTERFACE_MODE_RGMII_ID) {
+ rgmii_setmask(ethqos, SDCC_DLL_CONFIG_PDN, SDCC_HC_REG_DLL_CONFIG);
+ rgmii_setmask(ethqos, SDCC_USR_CTL_DDR_BYPASS, SDCC_USR_CTL);
+
+ ethqos_rgmii_id_macro_init(ethqos, speed);
+
+ return;
+ }
+
/* Initialize the DLL first */
/* Set DLL_RST */
--
2.34.1
^ permalink raw reply related
* [PATCH RFC 4/9] net: stmmac: qcom-ethqos: add per-platform NOC clock voting
From: Mohd Ayaan Anwar @ 2026-06-11 18:37 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Richard Cochran, Bjorn Andersson, Konrad Dybcio, Maxime Coquelin,
Alexandre Torgue, Russell King
Cc: linux-arm-msm, netdev, devicetree, linux-kernel, linux-stm32,
linux-arm-kernel, Mohd Ayaan Anwar
In-Reply-To: <20260612-shikra_ethernet-v1-0-f0f4a1d19929@oss.qualcomm.com>
Some SoCs gate the EMAC's path to the System NOC behind dedicated clocks
that must be enabled before the DMA can reach memory. Add
ethqos_noc_clk_cfg and the corresponding fields in the driver-data and
runtime structs so each compatible can declare its own set with per-clock
rates. The clocks are acquired during probe and enabled/disabled
alongside the existing link clock in ethqos_clks_config().
No functional change for existing compatibles. This will help us when
we add support for Shikra.
Signed-off-by: Mohd Ayaan Anwar <mohd.anwar@oss.qualcomm.com>
---
.../ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c | 63 ++++++++++++++++++++++
1 file changed, 63 insertions(+)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
index bec08f1eb8cb41484ba3c91c77393e163e7fd071..f4d4b419a76277cc6c56f03bb10d883cd4dff424 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
@@ -84,11 +84,18 @@
#define SGMII_10M_RX_CLK_DVDR 0x31
+#define ETHQOS_MAX_NOC_CLKS 3
+
struct ethqos_emac_por {
unsigned int offset;
unsigned int value;
};
+struct ethqos_noc_clk_cfg {
+ const char *id;
+ unsigned long rate;
+};
+
struct ethqos_emac_driver_data {
const struct ethqos_emac_por *rgmii_por;
unsigned int num_rgmii_por;
@@ -98,6 +105,8 @@ struct ethqos_emac_driver_data {
const char *link_clk_name;
struct dwmac4_addrs dwmac4_addrs;
bool needs_sgmii_loopback;
+ const struct ethqos_noc_clk_cfg *noc_clk_cfg;
+ unsigned int num_noc_clks;
};
struct qcom_ethqos {
@@ -112,6 +121,9 @@ struct qcom_ethqos {
bool rgmii_config_loopback_en;
bool has_emac_ge_3;
bool needs_sgmii_loopback;
+
+ struct clk_bulk_data noc_clks[ETHQOS_MAX_NOC_CLKS];
+ int num_noc_clks;
};
static u32 rgmii_readl(struct qcom_ethqos *ethqos, unsigned int offset)
@@ -696,6 +708,17 @@ static int ethqos_clks_config(void *priv, bool enabled)
return ret;
}
+ if (ethqos->num_noc_clks) {
+ ret = clk_bulk_prepare_enable(ethqos->num_noc_clks,
+ ethqos->noc_clks);
+ if (ret) {
+ dev_err(ðqos->pdev->dev,
+ "NOC clocks enable failed: %d\n", ret);
+ clk_disable_unprepare(ethqos->link_clk);
+ return ret;
+ }
+ }
+
/* Enable functional clock to prevent DMA reset to timeout due
* to lacking PHY clock after the hardware block has been power
* cycled. The actual configuration will be adjusted once
@@ -704,6 +727,9 @@ static int ethqos_clks_config(void *priv, bool enabled)
qcom_ethqos_set_sgmii_loopback(ethqos, true);
ethqos_set_func_clk_en(ethqos);
} else {
+ if (ethqos->num_noc_clks)
+ clk_bulk_disable_unprepare(ethqos->num_noc_clks,
+ ethqos->noc_clks);
clk_disable_unprepare(ethqos->link_clk);
}
@@ -732,6 +758,37 @@ static void ethqos_ptp_clk_freq_config(struct stmmac_priv *priv)
netdev_dbg(priv->dev, "PTP rate %lu\n", plat_dat->clk_ptp_rate);
}
+/*
+ * Some SoCs gate interconnect access to the System NOC behind dedicated
+ * clocks. Acquire them, set their required rates, and store the result in
+ * ethqos so ethqos_clks_config() can enable/disable them at runtime.
+ */
+static int qcom_ethqos_init_noc_clks(struct qcom_ethqos *ethqos,
+ const struct ethqos_emac_driver_data *data)
+{
+ struct device *dev = ðqos->pdev->dev;
+ unsigned int i;
+ int ret;
+
+ for (i = 0; i < data->num_noc_clks; i++)
+ ethqos->noc_clks[i].id = data->noc_clk_cfg[i].id;
+ ethqos->num_noc_clks = data->num_noc_clks;
+
+ ret = devm_clk_bulk_get(dev, ethqos->num_noc_clks, ethqos->noc_clks);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get NOC clocks\n");
+
+ for (i = 0; i < data->num_noc_clks; i++) {
+ ret = clk_set_rate(ethqos->noc_clks[i].clk,
+ data->noc_clk_cfg[i].rate);
+ if (ret)
+ dev_warn(dev, "Failed to set %s rate: %d\n",
+ data->noc_clk_cfg[i].id, ret);
+ }
+
+ return 0;
+}
+
static int qcom_ethqos_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
@@ -791,6 +848,12 @@ static int qcom_ethqos_probe(struct platform_device *pdev)
ethqos->has_emac_ge_3 = data->has_emac_ge_3;
ethqos->needs_sgmii_loopback = data->needs_sgmii_loopback;
+ if (data->num_noc_clks) {
+ ret = qcom_ethqos_init_noc_clks(ethqos, data);
+ if (ret)
+ return ret;
+ }
+
ethqos->link_clk = devm_clk_get(dev, data->link_clk_name ?: "rgmii");
if (IS_ERR(ethqos->link_clk))
return dev_err_probe(dev, PTR_ERR(ethqos->link_clk),
--
2.34.1
^ permalink raw reply related
* [PATCH RFC 5/9] net: stmmac: qcom-ethqos: add Shikra EMAC support
From: Mohd Ayaan Anwar @ 2026-06-11 18:37 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Richard Cochran, Bjorn Andersson, Konrad Dybcio, Maxime Coquelin,
Alexandre Torgue, Russell King
Cc: linux-arm-msm, netdev, devicetree, linux-kernel, linux-stm32,
linux-arm-kernel, Mohd Ayaan Anwar
In-Reply-To: <20260612-shikra_ethernet-v1-0-f0f4a1d19929@oss.qualcomm.com>
Shikra integrates two Qualcomm ETHQOS controllers based on the Synopsys
GMAC IP, similar to previous platforms. Register qcom,shikra-ethqos
backed by a new shikra_data descriptor that enables the three NOC clocks
required for DMA memory access and the 36-bit DMA address width.
Signed-off-by: Mohd Ayaan Anwar <mohd.anwar@oss.qualcomm.com>
---
.../ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c | 31 ++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
index f4d4b419a76277cc6c56f03bb10d883cd4dff424..59fa8779e4e4628ae585dd5625168fca824662b0 100644
--- a/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
+++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-qcom-ethqos.c
@@ -326,6 +326,36 @@ static const struct ethqos_emac_driver_data emac_v4_0_0_data = {
},
};
+static const struct ethqos_noc_clk_cfg shikra_noc_clks[] = {
+ { "axi", 120000000 },
+ { "axi-noc", 120000000 },
+ { "pcie-tile-axi-noc", 120000000 },
+};
+
+static const struct ethqos_emac_driver_data shikra_data = {
+ .dma_addr_width = 36,
+ .has_emac_ge_3 = true,
+ .noc_clk_cfg = shikra_noc_clks,
+ .num_noc_clks = ARRAY_SIZE(shikra_noc_clks),
+ .rgmii_config_loopback_en = false,
+ .dwmac4_addrs = {
+ .dma_chan = 0x00008100,
+ .dma_chan_offset = 0x1000,
+ .mtl_chan = 0x00008000,
+ .mtl_chan_offset = 0x1000,
+ .mtl_ets_ctrl = 0x00008010,
+ .mtl_ets_ctrl_offset = 0x1000,
+ .mtl_txq_weight = 0x00008018,
+ .mtl_txq_weight_offset = 0x1000,
+ .mtl_send_slp_cred = 0x0000801c,
+ .mtl_send_slp_cred_offset = 0x1000,
+ .mtl_high_cred = 0x00008020,
+ .mtl_high_cred_offset = 0x1000,
+ .mtl_low_cred = 0x00008024,
+ .mtl_low_cred_offset = 0x1000,
+ },
+};
+
static int ethqos_dll_configure(struct qcom_ethqos *ethqos)
{
struct device *dev = ðqos->pdev->dev;
@@ -915,6 +945,7 @@ static const struct of_device_id qcom_ethqos_match[] = {
{ .compatible = "qcom,qcs404-ethqos", .data = &emac_v2_3_0_data},
{ .compatible = "qcom,sa8775p-ethqos", .data = &emac_v4_0_0_data},
{ .compatible = "qcom,sc8280xp-ethqos", .data = &emac_v3_0_0_data},
+ { .compatible = "qcom,shikra-ethqos", .data = &shikra_data},
{ .compatible = "qcom,sm8150-ethqos", .data = &emac_v2_1_0_data},
{ }
};
--
2.34.1
^ permalink raw reply related
* [PATCH RFC 6/9] arm64: dts: qcom: shikra: Add ethernet nodes
From: Mohd Ayaan Anwar @ 2026-06-11 18:37 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Richard Cochran, Bjorn Andersson, Konrad Dybcio, Maxime Coquelin,
Alexandre Torgue, Russell King
Cc: linux-arm-msm, netdev, devicetree, linux-kernel, linux-stm32,
linux-arm-kernel, Mohd Ayaan Anwar
In-Reply-To: <20260612-shikra_ethernet-v1-0-f0f4a1d19929@oss.qualcomm.com>
Add the two Gigabit Ethernet controllers present on Shikra (ethernet0
at 0x5d00000, ethernet1 at 0x5d20000). Both nodes are left disabled;
board files supply the PHY, pin-control, and queue configuration.
Signed-off-by: Mohd Ayaan Anwar <mohd.anwar@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/shikra.dtsi | 78 ++++++++++++++++++++++++++++++++++++
1 file changed, 78 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/shikra.dtsi b/arch/arm64/boot/dts/qcom/shikra.dtsi
index e67fe047a683aa566b444a847b57b4b47a25aa8a..cac1573e3eec9e52b62f4b4cd7c564c70d0d8f78 100644
--- a/arch/arm64/boot/dts/qcom/shikra.dtsi
+++ b/arch/arm64/boot/dts/qcom/shikra.dtsi
@@ -1990,6 +1990,84 @@ gpucc: clock-controller@5990000 {
#power-domain-cells = <1>;
};
+ ethernet0: ethernet@5d00000 {
+ compatible = "qcom,shikra-ethqos";
+ reg = <0x0 0x05d00000 0x0 0x10000>,
+ <0x0 0x05d16000 0x0 0x100>;
+ reg-names = "stmmaceth", "rgmii";
+
+ interrupts = <GIC_SPI 478 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "macirq";
+
+ clocks = <&gcc GCC_EMAC0_AXI_CLK>,
+ <&gcc GCC_EMAC0_AHB_CLK>,
+ <&gcc GCC_EMAC0_PTP_CLK>,
+ <&gcc GCC_EMAC0_RGMII_CLK>,
+ <&gcc GCC_EMAC0_AXI_CLK>,
+ <&gcc GCC_EMAC0_AXI_SYS_NOC_CLK>,
+ <&gcc GCC_PCIE_TILE_AXI_SYS_NOC_CLK>;
+ clock-names = "stmmaceth", "pclk", "ptp_ref", "rgmii",
+ "axi", "axi-noc", "pcie-tile-axi-noc";
+
+ power-domains = <&gcc GCC_EMAC0_GDSC>;
+ resets = <&gcc GCC_EMAC0_BCR>;
+ iommus = <&apps_smmu 0x0380 0x0007>;
+
+ interconnects = <&mem_noc MASTER_AMPSS_M0 QCOM_ICC_TAG_ALWAYS
+ &config_noc SLAVE_EMAC0_CFG QCOM_ICC_TAG_ALWAYS>,
+ <&system_noc MASTER_EMAC_0 QCOM_ICC_TAG_ALWAYS
+ &mc_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>;
+ interconnect-names = "cpu-mac", "mac-mem";
+
+ snps,tso;
+ snps,pbl = <32>;
+ rx-fifo-depth = <8192>;
+ tx-fifo-depth = <8192>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
+ ethernet1: ethernet@5d20000 {
+ compatible = "qcom,shikra-ethqos";
+ reg = <0x0 0x05d20000 0x0 0x10000>,
+ <0x0 0x05d36000 0x0 0x100>;
+ reg-names = "stmmaceth", "rgmii";
+
+ interrupts = <GIC_SPI 458 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "macirq";
+
+ clocks = <&gcc GCC_EMAC1_AXI_CLK>,
+ <&gcc GCC_EMAC1_AHB_CLK>,
+ <&gcc GCC_EMAC1_PTP_CLK>,
+ <&gcc GCC_EMAC1_RGMII_CLK>,
+ <&gcc GCC_EMAC1_AXI_CLK>,
+ <&gcc GCC_EMAC1_AXI_SYS_NOC_CLK>,
+ <&gcc GCC_PCIE_TILE_AXI_SYS_NOC_CLK>;
+ clock-names = "stmmaceth", "pclk", "ptp_ref", "rgmii",
+ "axi", "axi-noc", "pcie-tile-axi-noc";
+
+ power-domains = <&gcc GCC_EMAC1_GDSC>;
+ resets = <&gcc GCC_EMAC1_BCR>;
+ iommus = <&apps_smmu 0x03a0 0x0007>;
+
+ interconnects = <&mem_noc MASTER_AMPSS_M0 QCOM_ICC_TAG_ALWAYS
+ &config_noc SLAVE_EMAC1_CFG QCOM_ICC_TAG_ALWAYS>,
+ <&system_noc MASTER_EMAC_1 QCOM_ICC_TAG_ALWAYS
+ &mc_virt SLAVE_EBI_CH0 QCOM_ICC_TAG_ALWAYS>;
+ interconnect-names = "cpu-mac", "mac-mem";
+
+ snps,tso;
+ snps,pbl = <32>;
+ rx-fifo-depth = <8192>;
+ tx-fifo-depth = <8192>;
+
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "disabled";
+ };
+
dispcc: clock-controller@5f00000 {
compatible = "qcom,shikra-dispcc", "qcom,qcm2290-dispcc";
reg = <0x0 0x05f00000 0x0 0x20000>;
--
2.34.1
^ permalink raw reply related
* [PATCH RFC 7/9] arm64: dts: qcom: shikra-cqm-evk: Enable ethernet0
From: Mohd Ayaan Anwar @ 2026-06-11 18:37 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Richard Cochran, Bjorn Andersson, Konrad Dybcio, Maxime Coquelin,
Alexandre Torgue, Russell King
Cc: linux-arm-msm, netdev, devicetree, linux-kernel, linux-stm32,
linux-arm-kernel, Mohd Ayaan Anwar
In-Reply-To: <20260612-shikra_ethernet-v1-0-f0f4a1d19929@oss.qualcomm.com>
Enable the first Gigabit Ethernet controller. Add pin-control for the
RGMII and MDIO bus, a gpio-hog to assert the PHY power-enable GPIO at
boot, and the board-level ethernet0 overlay with PHY and MTL queue
configuration.
Signed-off-by: Mohd Ayaan Anwar <mohd.anwar@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/shikra-cqm-evk.dts | 119 ++++++++++++++++++++++++++++
1 file changed, 119 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/shikra-cqm-evk.dts b/arch/arm64/boot/dts/qcom/shikra-cqm-evk.dts
index 683b5245923bbc1fb5df28ab7899d47a0cf8dbe7..6a6bcd2e712a1d4db5ceea4733751397779963c9 100644
--- a/arch/arm64/boot/dts/qcom/shikra-cqm-evk.dts
+++ b/arch/arm64/boot/dts/qcom/shikra-cqm-evk.dts
@@ -7,6 +7,7 @@
#include "shikra-cqm-som.dtsi"
#include "shikra-evk.dtsi"
+#include <dt-bindings/net/ti-dp83867.h>
/ {
model = "Qualcomm Technologies, Inc. Shikra CQM EVK";
@@ -60,6 +61,92 @@ vreg_pmu_ch1: ldo4 {
};
};
+ðernet0 {
+ status = "okay";
+ phy-handle = <ðphy0>;
+ phy-mode = "rgmii-id";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <ðernet0_defaults>;
+
+ snps,mtl-rx-config = <&mtl_rx_setup>;
+ snps,mtl-tx-config = <&mtl_tx_setup>;
+
+ mdio {
+ compatible = "snps,dwmac-mdio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethphy0: ethernet-phy@7 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <7>;
+ reset-gpios = <&tlmm 135 GPIO_ACTIVE_LOW>;
+ reset-assert-us = <10000>;
+ reset-deassert-us = <50000>;
+ ti,tx-internal-delay = <DP83867_RGMIIDCTL_2_00_NS>;
+ ti,rx-internal-delay = <DP83867_RGMIIDCTL_2_00_NS>;
+ };
+ };
+
+ mtl_rx_setup: rx-queues-config {
+ snps,rx-queues-to-use = <4>;
+ snps,rx-sched-sp;
+
+ queue0 {
+ snps,dcb-algorithm;
+ snps,map-to-dma-channel = <0x0>;
+ snps,route-up;
+ snps,priority = <0x1>;
+ };
+
+ queue1 {
+ snps,dcb-algorithm;
+ snps,map-to-dma-channel = <0x1>;
+ snps,route-ptp;
+ };
+
+ queue2 {
+ snps,avb-algorithm;
+ snps,map-to-dma-channel = <0x2>;
+ snps,route-avcp;
+ };
+
+ queue3 {
+ snps,avb-algorithm;
+ snps,map-to-dma-channel = <0x3>;
+ snps,priority = <0xc>;
+ };
+ };
+
+ mtl_tx_setup: tx-queues-config {
+ snps,tx-queues-to-use = <4>;
+
+ queue0 {
+ snps,dcb-algorithm;
+ };
+
+ queue1 {
+ snps,dcb-algorithm;
+ };
+
+ queue2 {
+ snps,avb-algorithm;
+ snps,send_slope = <0x1000>;
+ snps,idle_slope = <0x1000>;
+ snps,high_credit = <0x3e800>;
+ snps,low_credit = <0xffc18000>;
+ };
+
+ queue3 {
+ snps,avb-algorithm;
+ snps,send_slope = <0x1000>;
+ snps,idle_slope = <0x1000>;
+ snps,high_credit = <0x3e800>;
+ snps,low_credit = <0xffc18000>;
+ };
+ };
+};
+
&remoteproc_cdsp {
firmware-name = "qcom/shikra/cdsp.mbn";
@@ -95,6 +182,38 @@ &sdhc_1 {
status = "okay";
};
+&tlmm {
+ ethernet0_defaults: ethernet0-defaults-state {
+ rgmii-rx-pins {
+ pins = "gpio121", "gpio122", "gpio123",
+ "gpio124", "gpio125", "gpio126";
+ function = "rgmii";
+ bias-disable;
+ drive-strength = <16>;
+ };
+ rgmii-tx-pins {
+ pins = "gpio127", "gpio128", "gpio129",
+ "gpio130", "gpio131", "gpio132";
+ function = "rgmii";
+ bias-pull-up;
+ drive-strength = <16>;
+ };
+ rgmii-mdio-pins {
+ pins = "gpio133", "gpio134";
+ function = "rgmii";
+ bias-pull-up;
+ drive-strength = <16>;
+ };
+ };
+
+ emac0_phy_en_hog: emac0-phy-en-hog {
+ gpio-hog;
+ gpios = <149 GPIO_ACTIVE_HIGH>;
+ output-high;
+ line-name = "emac0-phy-en";
+ };
+};
+
&uart8 {
status = "okay";
--
2.34.1
^ permalink raw reply related
* [PATCH RFC 8/9] arm64: dts: qcom: shikra-cqs-evk: Enable ethernet0
From: Mohd Ayaan Anwar @ 2026-06-11 18:37 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Richard Cochran, Bjorn Andersson, Konrad Dybcio, Maxime Coquelin,
Alexandre Torgue, Russell King
Cc: linux-arm-msm, netdev, devicetree, linux-kernel, linux-stm32,
linux-arm-kernel, Mohd Ayaan Anwar
In-Reply-To: <20260612-shikra_ethernet-v1-0-f0f4a1d19929@oss.qualcomm.com>
Enable the first Gigabit Ethernet controller. The board layout is
identical to the CQM EVK.
Signed-off-by: Mohd Ayaan Anwar <mohd.anwar@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/shikra-cqs-evk.dts | 119 ++++++++++++++++++++++++++++
1 file changed, 119 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/shikra-cqs-evk.dts b/arch/arm64/boot/dts/qcom/shikra-cqs-evk.dts
index 26ff8007a819e46bbc9ffa3dddc6fee6530a4a7a..1f2e4f6dd7cca436f62ba9f09cd328e5a2079095 100644
--- a/arch/arm64/boot/dts/qcom/shikra-cqs-evk.dts
+++ b/arch/arm64/boot/dts/qcom/shikra-cqs-evk.dts
@@ -7,6 +7,7 @@
#include "shikra-cqm-som.dtsi"
#include "shikra-evk.dtsi"
+#include <dt-bindings/net/ti-dp83867.h>
/ {
model = "Qualcomm Technologies, Inc. Shikra CQS EVK";
@@ -60,6 +61,92 @@ vreg_pmu_ch1: ldo4 {
};
};
+ðernet0 {
+ status = "okay";
+ phy-handle = <ðphy0>;
+ phy-mode = "rgmii-id";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <ðernet0_defaults>;
+
+ snps,mtl-rx-config = <&mtl_rx_setup>;
+ snps,mtl-tx-config = <&mtl_tx_setup>;
+
+ mdio {
+ compatible = "snps,dwmac-mdio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethphy0: ethernet-phy@7 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <7>;
+ reset-gpios = <&tlmm 135 GPIO_ACTIVE_LOW>;
+ reset-assert-us = <10000>;
+ reset-deassert-us = <50000>;
+ ti,tx-internal-delay = <DP83867_RGMIIDCTL_2_00_NS>;
+ ti,rx-internal-delay = <DP83867_RGMIIDCTL_2_00_NS>;
+ };
+ };
+
+ mtl_rx_setup: rx-queues-config {
+ snps,rx-queues-to-use = <4>;
+ snps,rx-sched-sp;
+
+ queue0 {
+ snps,dcb-algorithm;
+ snps,map-to-dma-channel = <0x0>;
+ snps,route-up;
+ snps,priority = <0x1>;
+ };
+
+ queue1 {
+ snps,dcb-algorithm;
+ snps,map-to-dma-channel = <0x1>;
+ snps,route-ptp;
+ };
+
+ queue2 {
+ snps,avb-algorithm;
+ snps,map-to-dma-channel = <0x2>;
+ snps,route-avcp;
+ };
+
+ queue3 {
+ snps,avb-algorithm;
+ snps,map-to-dma-channel = <0x3>;
+ snps,priority = <0xc>;
+ };
+ };
+
+ mtl_tx_setup: tx-queues-config {
+ snps,tx-queues-to-use = <4>;
+
+ queue0 {
+ snps,dcb-algorithm;
+ };
+
+ queue1 {
+ snps,dcb-algorithm;
+ };
+
+ queue2 {
+ snps,avb-algorithm;
+ snps,send_slope = <0x1000>;
+ snps,idle_slope = <0x1000>;
+ snps,high_credit = <0x3e800>;
+ snps,low_credit = <0xffc18000>;
+ };
+
+ queue3 {
+ snps,avb-algorithm;
+ snps,send_slope = <0x1000>;
+ snps,idle_slope = <0x1000>;
+ snps,high_credit = <0x3e800>;
+ snps,low_credit = <0xffc18000>;
+ };
+ };
+};
+
&remoteproc_cdsp {
firmware-name = "qcom/shikra/cdsp.mbn";
@@ -95,6 +182,38 @@ &sdhc_1 {
status = "okay";
};
+&tlmm {
+ ethernet0_defaults: ethernet0-defaults-state {
+ rgmii-rx-pins {
+ pins = "gpio121", "gpio122", "gpio123",
+ "gpio124", "gpio125", "gpio126";
+ function = "rgmii";
+ bias-disable;
+ drive-strength = <16>;
+ };
+ rgmii-tx-pins {
+ pins = "gpio127", "gpio128", "gpio129",
+ "gpio130", "gpio131", "gpio132";
+ function = "rgmii";
+ bias-pull-up;
+ drive-strength = <16>;
+ };
+ rgmii-mdio-pins {
+ pins = "gpio133", "gpio134";
+ function = "rgmii";
+ bias-pull-up;
+ drive-strength = <16>;
+ };
+ };
+
+ emac0_phy_en_hog: emac0-phy-en-hog {
+ gpio-hog;
+ gpios = <149 GPIO_ACTIVE_HIGH>;
+ output-high;
+ line-name = "emac0-phy-en";
+ };
+};
+
&uart8 {
status = "okay";
--
2.34.1
^ permalink raw reply related
* [PATCH RFC 9/9] arm64: dts: qcom: shikra-iqs-evk: Enable both ethernet ports
From: Mohd Ayaan Anwar @ 2026-06-11 18:37 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Richard Cochran, Bjorn Andersson, Konrad Dybcio, Maxime Coquelin,
Alexandre Torgue, Russell King
Cc: linux-arm-msm, netdev, devicetree, linux-kernel, linux-stm32,
linux-arm-kernel, Mohd Ayaan Anwar
In-Reply-To: <20260612-shikra_ethernet-v1-0-f0f4a1d19929@oss.qualcomm.com>
Enable both Gigabit Ethernet controllers. Each port has a dedicated
PHY with a gpio-hog to assert the power-enable GPIO at boot,
pin-control for the RGMII and MDIO bus, and MTL queue configuration.
Signed-off-by: Mohd Ayaan Anwar <mohd.anwar@oss.qualcomm.com>
---
arch/arm64/boot/dts/qcom/shikra-iqs-evk.dts | 235 ++++++++++++++++++++++++++++
1 file changed, 235 insertions(+)
diff --git a/arch/arm64/boot/dts/qcom/shikra-iqs-evk.dts b/arch/arm64/boot/dts/qcom/shikra-iqs-evk.dts
index fd691d53a0fa8179111b921bf3bacc08884b84fb..d69b63dbc8e44f1bcec064564236ea23673bfa1f 100644
--- a/arch/arm64/boot/dts/qcom/shikra-iqs-evk.dts
+++ b/arch/arm64/boot/dts/qcom/shikra-iqs-evk.dts
@@ -7,6 +7,7 @@
#include "shikra-iqs-som.dtsi"
#include "shikra-evk.dtsi"
+#include <dt-bindings/net/ti-dp83867.h>
/ {
model = "Qualcomm Technologies, Inc. Shikra IQS EVK";
@@ -68,6 +69,178 @@ vreg_pmu_ch1: ldo4 {
};
};
+ðernet0 {
+ status = "okay";
+ phy-handle = <ðphy0>;
+ phy-mode = "rgmii-id";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <ðernet0_defaults>;
+
+ snps,mtl-rx-config = <&emac0_mtl_rx_setup>;
+ snps,mtl-tx-config = <&emac0_mtl_tx_setup>;
+
+ mdio {
+ compatible = "snps,dwmac-mdio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethphy0: ethernet-phy@7 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <7>;
+ reset-gpios = <&tlmm 135 GPIO_ACTIVE_LOW>;
+ reset-assert-us = <10000>;
+ reset-deassert-us = <50000>;
+ ti,tx-internal-delay = <DP83867_RGMIIDCTL_2_00_NS>;
+ ti,rx-internal-delay = <DP83867_RGMIIDCTL_2_00_NS>;
+ };
+ };
+
+ emac0_mtl_rx_setup: rx-queues-config {
+ snps,rx-queues-to-use = <4>;
+ snps,rx-sched-sp;
+
+ queue0 {
+ snps,dcb-algorithm;
+ snps,map-to-dma-channel = <0x0>;
+ snps,route-up;
+ snps,priority = <0x1>;
+ };
+
+ queue1 {
+ snps,dcb-algorithm;
+ snps,map-to-dma-channel = <0x1>;
+ snps,route-ptp;
+ };
+
+ queue2 {
+ snps,avb-algorithm;
+ snps,map-to-dma-channel = <0x2>;
+ snps,route-avcp;
+ };
+
+ queue3 {
+ snps,avb-algorithm;
+ snps,map-to-dma-channel = <0x3>;
+ snps,priority = <0xc>;
+ };
+ };
+
+ emac0_mtl_tx_setup: tx-queues-config {
+ snps,tx-queues-to-use = <4>;
+
+ queue0 {
+ snps,dcb-algorithm;
+ };
+
+ queue1 {
+ snps,dcb-algorithm;
+ };
+
+ queue2 {
+ snps,avb-algorithm;
+ snps,send_slope = <0x1000>;
+ snps,idle_slope = <0x1000>;
+ snps,high_credit = <0x3e800>;
+ snps,low_credit = <0xffc18000>;
+ };
+
+ queue3 {
+ snps,avb-algorithm;
+ snps,send_slope = <0x1000>;
+ snps,idle_slope = <0x1000>;
+ snps,high_credit = <0x3e800>;
+ snps,low_credit = <0xffc18000>;
+ };
+ };
+};
+
+ðernet1 {
+ status = "okay";
+ phy-handle = <ðphy1>;
+ phy-mode = "rgmii-id";
+
+ pinctrl-names = "default";
+ pinctrl-0 = <ðernet1_defaults>;
+
+ snps,mtl-rx-config = <&emac1_mtl_rx_setup>;
+ snps,mtl-tx-config = <&emac1_mtl_tx_setup>;
+
+ mdio {
+ compatible = "snps,dwmac-mdio";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ ethphy1: ethernet-phy@7 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <7>;
+ reset-gpios = <&tlmm 151 GPIO_ACTIVE_LOW>;
+ reset-assert-us = <10000>;
+ reset-deassert-us = <50000>;
+ ti,tx-internal-delay = <DP83867_RGMIIDCTL_2_00_NS>;
+ ti,rx-internal-delay = <DP83867_RGMIIDCTL_2_00_NS>;
+ };
+ };
+
+ emac1_mtl_rx_setup: rx-queues-config {
+ snps,rx-queues-to-use = <4>;
+ snps,rx-sched-sp;
+
+ queue0 {
+ snps,dcb-algorithm;
+ snps,map-to-dma-channel = <0x0>;
+ snps,route-up;
+ snps,priority = <0x1>;
+ };
+
+ queue1 {
+ snps,dcb-algorithm;
+ snps,map-to-dma-channel = <0x1>;
+ snps,route-ptp;
+ };
+
+ queue2 {
+ snps,avb-algorithm;
+ snps,map-to-dma-channel = <0x2>;
+ snps,route-avcp;
+ };
+
+ queue3 {
+ snps,avb-algorithm;
+ snps,map-to-dma-channel = <0x3>;
+ snps,priority = <0xc>;
+ };
+ };
+
+ emac1_mtl_tx_setup: tx-queues-config {
+ snps,tx-queues-to-use = <4>;
+
+ queue0 {
+ snps,dcb-algorithm;
+ };
+
+ queue1 {
+ snps,dcb-algorithm;
+ };
+
+ queue2 {
+ snps,avb-algorithm;
+ snps,send_slope = <0x1000>;
+ snps,idle_slope = <0x1000>;
+ snps,high_credit = <0x3e800>;
+ snps,low_credit = <0xffc18000>;
+ };
+
+ queue3 {
+ snps,avb-algorithm;
+ snps,send_slope = <0x1000>;
+ snps,idle_slope = <0x1000>;
+ snps,high_credit = <0x3e800>;
+ snps,low_credit = <0xffc18000>;
+ };
+ };
+};
+
&remoteproc_cdsp {
firmware-name = "qcom/shikra/cdsp.mbn";
@@ -103,6 +276,68 @@ &sdhc_1 {
status = "okay";
};
+&tlmm {
+ ethernet0_defaults: ethernet0-defaults-state {
+ rgmii-rx-pins {
+ pins = "gpio121", "gpio122", "gpio123",
+ "gpio124", "gpio125", "gpio126";
+ function = "rgmii";
+ bias-disable;
+ drive-strength = <16>;
+ };
+ rgmii-tx-pins {
+ pins = "gpio127", "gpio128", "gpio129",
+ "gpio130", "gpio131", "gpio132";
+ function = "rgmii";
+ bias-pull-up;
+ drive-strength = <16>;
+ };
+ rgmii-mdio-pins {
+ pins = "gpio133", "gpio134";
+ function = "rgmii";
+ bias-pull-up;
+ drive-strength = <16>;
+ };
+ };
+
+ ethernet1_defaults: ethernet1-defaults-state {
+ rgmii-rx-pins {
+ pins = "gpio137", "gpio138", "gpio139",
+ "gpio140", "gpio141", "gpio142";
+ function = "rgmii";
+ bias-disable;
+ drive-strength = <16>;
+ };
+ rgmii-tx-pins {
+ pins = "gpio143", "gpio144", "gpio145",
+ "gpio146", "gpio147", "gpio148";
+ function = "rgmii";
+ bias-pull-up;
+ drive-strength = <16>;
+ };
+ rgmii-mdio-pins {
+ pins = "gpio149", "gpio150";
+ function = "rgmii";
+ bias-pull-up;
+ drive-strength = <16>;
+ };
+ };
+
+ emac0_phy_en_hog: emac0-phy-en-hog {
+ gpio-hog;
+ gpios = <66 GPIO_ACTIVE_HIGH>;
+ output-high;
+ line-name = "emac0-phy-en";
+ };
+
+ emac1_phy_en_hog: emac1-phy-en-hog {
+ gpio-hog;
+ gpios = <53 GPIO_ACTIVE_HIGH>;
+ output-high;
+ line-name = "emac1-phy-en";
+ };
+};
+
&uart8 {
status = "okay";
--
2.34.1
^ permalink raw reply related
* [PATCH net 0/4] ICSSG XDP zero copy bug fixes
From: Meghana Malladi @ 2026-06-11 18:57 UTC (permalink / raw)
To: diogo.ivo, haokexin, vadim.fedorenko, devnexen, horms,
jacob.e.keller, m-malladi, sdf, john.fastabend, hawk, daniel, ast,
pabeni, kuba, edumazet, davem, andrew+netdev
Cc: bpf, linux-kernel, netdev, linux-arm-kernel, srk,
Vignesh Raghavendra, Roger Quadros, danishanwar
This patch series fixes bugs introduced while adding xdp
zero copy support in the icssg driver.
Patch 1/4: Fix wakeup handling for Rx when available CPPI
descriptor is zero
Patch 2,3/4: Fix destination tag in CPPI descriptor to enable
proper Tx xmit for HSR offload mode with XDP and zero copy
Patch 4/4: Fix Tx copy wakeup handling for XDP zero copy
Meghana Malladi (4):
net: ti: icssg-prueth: Fix AF_XDP fill ring alloc and wakeup condition
net: ti: icssg: Use undirected TX tag for native XDP in HSR offload
mode
net: ti: icssg: Use undirected TX tag for XDP zero copy in HSR offload
mode
net: ti: icssg: Fix XSK zero copy TX during application wakeup
drivers/net/ethernet/ti/icssg/icssg_common.c | 62 +++++++++++++-------
1 file changed, 41 insertions(+), 21 deletions(-)
base-commit: 29899ec61ac6fcc9d46f5f8d0b72117d9a676c2e
prerequisite-patch-id: ad8d47ed85a33f742a58e9f48e060303908493ba
--
2.43.0
^ permalink raw reply
* [PATCH net 1/4] net: ti: icssg-prueth: Fix AF_XDP fill ring alloc and wakeup condition
From: Meghana Malladi @ 2026-06-11 18:57 UTC (permalink / raw)
To: diogo.ivo, haokexin, vadim.fedorenko, devnexen, horms,
jacob.e.keller, m-malladi, sdf, john.fastabend, hawk, daniel, ast,
pabeni, kuba, edumazet, davem, andrew+netdev
Cc: bpf, linux-kernel, netdev, linux-arm-kernel, srk,
Vignesh Raghavendra, Roger Quadros, danishanwar
In-Reply-To: <20260611185744.2498070-1-m-malladi@ti.com>
emac_rx_packet_zc() calls prueth_rx_alloc_zc() with count (frames
received in the current NAPI poll) as the allocation budget. Two
problems arise from this:
1. When the CPPI5 descriptor pool is exhausted (avail_desc == 0,
FDQ already holds the maximum number of descriptors), count > 0
still triggers allocation attempts that all fail, spamming the
kernel log with "rx push: failed to allocate descriptor" at
high packet rates.
2. The XSK wakeup condition "ret < count" is wrong when avail_desc
is zero: ret == 0 and count can be up to 64, so the condition is
always true. This causes ~200 spurious ndo_xsk_wakeup() calls
per second even when the FDQ is already full, wasting CPU cycles
in repeated NAPI invocations that process zero frames.
Fix both by introducing alloc_budget = min(budget, avail_desc):
- When avail_desc == 0 no allocation is attempted, avoiding pool
exhaustion errors. The wakeup condition "ret < alloc_budget"
evaluates to 0 < 0 == false, correctly clearing the wakeup flag
so the hardware IRQ re-arms NAPI without spurious kicks.
- In steady state avail_desc == count <= budget, so alloc_budget
== count and behaviour is unchanged.
- After a dry-ring stall (count == 0, avail_desc > 0), alloc_budget
> 0 causes new descriptors to be posted to the FDQ so the hardware
can resume receiving immediately.
Fixes: 7a64bb388df3 ("net: ti: icssg-prueth: Add AF_XDP zero copy for RX")
Signed-off-by: Meghana Malladi <m-malladi@ti.com>
---
drivers/net/ethernet/ti/icssg/icssg_common.c | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)
diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c
index a28a608f9bf4..55a696912811 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_common.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_common.c
@@ -927,6 +927,7 @@ static int emac_rx_packet_zc(struct prueth_emac *emac, u32 flow_id,
struct cppi5_host_desc_t *desc_rx;
struct prueth_swdata *swdata;
dma_addr_t desc_dma, buf_dma;
+ int avail_desc, alloc_budget;
struct xdp_buff *xdp;
int xdp_status = 0;
int count = 0;
@@ -993,16 +994,13 @@ static int emac_rx_packet_zc(struct prueth_emac *emac, u32 flow_id,
if (xdp_status & ICSSG_XDP_REDIR)
xdp_do_flush();
- /* Allocate xsk buffers from the pool for the "count" number of
- * packets processed in order to be able to receive more packets.
- */
- ret = prueth_rx_alloc_zc(emac, count);
+ avail_desc = k3_cppi_desc_pool_avail(rx_chn->desc_pool);
+ alloc_budget = min_t(int, budget, avail_desc);
+
+ ret = prueth_rx_alloc_zc(emac, alloc_budget);
if (xsk_uses_need_wakeup(rx_chn->xsk_pool)) {
- /* If the user space doesn't provide enough buffers then it must
- * explicitly wake up the kernel when new buffers are available
- */
- if (ret < count)
+ if (ret < alloc_budget)
xsk_set_rx_need_wakeup(rx_chn->xsk_pool);
else
xsk_clear_rx_need_wakeup(rx_chn->xsk_pool);
--
2.43.0
^ permalink raw reply related
* [PATCH net 2/4] net: ti: icssg: Use undirected TX tag for native XDP in HSR offload mode
From: Meghana Malladi @ 2026-06-11 18:57 UTC (permalink / raw)
To: diogo.ivo, haokexin, vadim.fedorenko, devnexen, horms,
jacob.e.keller, m-malladi, sdf, john.fastabend, hawk, daniel, ast,
pabeni, kuba, edumazet, davem, andrew+netdev
Cc: bpf, linux-kernel, netdev, linux-arm-kernel, srk,
Vignesh Raghavendra, Roger Quadros, danishanwar
In-Reply-To: <20260611185744.2498070-1-m-malladi@ti.com>
emac_xmit_xdp_frame() always sets the CPPI5 descriptor destination
tag to emac->port_id, which directs the PRU firmware to transmit
the frame on that specific slave port only. In HSR offload mode
this bypasses the firmware's HSR duplication logic: the frame goes
out on one ring leg and never appears on the other, breaking HSR
redundancy for XDP_TX paths.
icssg_ndo_start_xmit() already handles this correctly: when HSR
offload mode is active and NETIF_F_HW_HSR_DUP is set it substitutes
PRUETH_UNDIRECTED_PKT_DST_TAG (port 0) so the PRU duplicates the
frame to both slave ports. It also sets PRUETH_UNDIRECTED_PKT_TAG_INS
in epib[1] when NETIF_F_HW_HSR_TAG_INS is set so the PRU inserts the
HSR sequence tag, which XDP_TX frames lack (the tag is stripped by
the PRU on RX before the frame reaches the XDP program).
Apply the same logic in emac_xmit_xdp_frame() so XDP_TX frames in
HSR mode are treated identically to skb TX via hsr0.
Fixes: 62aa3246f462 ("net: ti: icssg-prueth: Add XDP support")
Signed-off-by: Meghana Malladi <m-malladi@ti.com>
---
drivers/net/ethernet/ti/icssg/icssg_common.c | 21 ++++++++++++++++++--
1 file changed, 19 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c
index 55a696912811..ede32f266729 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_common.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_common.c
@@ -696,6 +696,7 @@ u32 emac_xmit_xdp_frame(struct prueth_emac *emac,
dma_addr_t desc_dma, buf_dma;
struct prueth_swdata *swdata;
struct page *page;
+ u32 dst_tag_id;
u32 *epib;
int ret;
@@ -737,9 +738,25 @@ u32 emac_xmit_xdp_frame(struct prueth_emac *emac,
/* set dst tag to indicate internal qid at the firmware which is at
* bit8..bit15. bit0..bit7 indicates port num for directed
- * packets in case of switch mode operation
+ * packets in case of switch mode operation and port num 0
+ * for undirected packets in case of HSR offload mode.
+ *
+ * XDP_TX frames arrive on a slave port with the HSR tag already
+ * stripped by the PRU firmware. Like skb TX via hsr0, they must
+ * be sent as undirected so the PRU duplicates them to both ports
+ * and re-inserts the HSR sequence tag.
*/
- cppi5_desc_set_tags_ids(&first_desc->hdr, 0, (emac->port_id | (q_idx << 8)));
+ dst_tag_id = emac->port_id | (q_idx << 8);
+
+ if (emac->prueth->is_hsr_offload_mode &&
+ (ndev->features & NETIF_F_HW_HSR_DUP))
+ dst_tag_id = PRUETH_UNDIRECTED_PKT_DST_TAG;
+
+ if (emac->prueth->is_hsr_offload_mode &&
+ (ndev->features & NETIF_F_HW_HSR_TAG_INS))
+ epib[1] |= PRUETH_UNDIRECTED_PKT_TAG_INS;
+
+ cppi5_desc_set_tags_ids(&first_desc->hdr, 0, dst_tag_id);
k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &buf_dma);
cppi5_hdesc_attach_buf(first_desc, buf_dma, xdpf->len, buf_dma, xdpf->len);
swdata = cppi5_hdesc_get_swdata(first_desc);
--
2.43.0
^ permalink raw reply related
* [PATCH net 4/4] net: ti: icssg: Fix XSK zero copy TX during application wakeup
From: Meghana Malladi @ 2026-06-11 18:57 UTC (permalink / raw)
To: diogo.ivo, haokexin, vadim.fedorenko, devnexen, horms,
jacob.e.keller, m-malladi, sdf, john.fastabend, hawk, daniel, ast,
pabeni, kuba, edumazet, davem, andrew+netdev
Cc: bpf, linux-kernel, netdev, linux-arm-kernel, srk,
Vignesh Raghavendra, Roger Quadros, danishanwar
In-Reply-To: <20260611185744.2498070-1-m-malladi@ti.com>
emac_xsk_xmit_zc() handles tx xmit for zero copy and gets called
inside napi context. User application wakes up the kernel while
initiating the transmit which triggers napi to start processing
the tx packets. The num_tx check inside emac_tx_complete_packets()
returns early if no packet transfer happen hindering the call
to emac_xsk_xmit_zc(). Remove this check to let application
wakeup initiate zero copy xmit traffic.
Add __netif_tx_lock() to ensure that the TX queue is protected
from concurrent access during the transmission of XDP frames.
This fixes netdev watchdog timeout for long runs.
Fixes: e2dc7bfd677f ("net: ti: icssg-prueth: Move common functions into a separate file")
Signed-off-by: Meghana Malladi <m-malladi@ti.com>
---
drivers/net/ethernet/ti/icssg/icssg_common.c | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c
index 82ddef9c17d5..c3f3cacdb28e 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_common.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_common.c
@@ -93,8 +93,8 @@ void prueth_ndev_del_tx_napi(struct prueth_emac *emac, int num)
}
EXPORT_SYMBOL_GPL(prueth_ndev_del_tx_napi);
-static int emac_xsk_xmit_zc(struct prueth_emac *emac,
- unsigned int q_idx)
+static void emac_xsk_xmit_zc(struct prueth_emac *emac,
+ unsigned int q_idx)
{
struct prueth_tx_chn *tx_chn = &emac->tx_chns[q_idx];
struct xsk_buff_pool *pool = tx_chn->xsk_pool;
@@ -115,7 +115,7 @@ static int emac_xsk_xmit_zc(struct prueth_emac *emac,
* necessary
*/
if (descs_avail <= MAX_SKB_FRAGS)
- return 0;
+ return;
descs_avail -= MAX_SKB_FRAGS;
@@ -169,9 +169,6 @@ static int emac_xsk_xmit_zc(struct prueth_emac *emac,
num_tx++;
}
-
- xsk_tx_release(tx_chn->xsk_pool);
- return num_tx;
}
void prueth_xmit_free(struct prueth_tx_chn *tx_chn,
@@ -279,9 +276,6 @@ int emac_tx_complete_packets(struct prueth_emac *emac, int chn,
num_tx++;
}
- if (!num_tx)
- return 0;
-
netif_txq = netdev_get_tx_queue(ndev, chn);
netdev_tx_completed_queue(netif_txq, num_tx, total_bytes);
@@ -306,7 +300,9 @@ int emac_tx_complete_packets(struct prueth_emac *emac, int chn,
netif_txq = netdev_get_tx_queue(ndev, chn);
txq_trans_cond_update(netif_txq);
+ __netif_tx_lock(netif_txq, smp_processor_id());
emac_xsk_xmit_zc(emac, chn);
+ __netif_tx_unlock(netif_txq);
}
return num_tx;
--
2.43.0
^ permalink raw reply related
* [PATCH net 3/4] net: ti: icssg: Use undirected TX tag for XDP zero copy in HSR offload mode
From: Meghana Malladi @ 2026-06-11 18:57 UTC (permalink / raw)
To: diogo.ivo, haokexin, vadim.fedorenko, devnexen, horms,
jacob.e.keller, m-malladi, sdf, john.fastabend, hawk, daniel, ast,
pabeni, kuba, edumazet, davem, andrew+netdev
Cc: bpf, linux-kernel, netdev, linux-arm-kernel, srk,
Vignesh Raghavendra, Roger Quadros, danishanwar
In-Reply-To: <20260611185744.2498070-1-m-malladi@ti.com>
emac_xsk_xmit_zc() has the same issue as the fixed emac_xmit_xdp_frame():
it always sets the CPPI5 descriptor destination tag to emac->port_id,
which directs the PRU firmware to transmit on only one slave port in HSR
mode, breaking redundancy.
Apply the same fix: in HSR offload mode when NETIF_F_HW_HSR_DUP is set,
use PRUETH_UNDIRECTED_PKT_DST_TAG (port 0) so the PRU duplicates frames
to both ports. Also set PRUETH_UNDIRECTED_PKT_TAG_INS when
NETIF_F_HW_HSR_TAG_INS is set so the PRU re-inserts the HSR sequence tag
that was stripped by the PRU on RX before the XDP program saw the frame.
This ensures XSK XDP_TX frames in HSR mode are treated identically to
skb TX via hsr0.
Fixes: 8756ef2eb078 ("net: ti: icssg-prueth: Add AF_XDP zero copy for TX")
Signed-off-by: Meghana Malladi <m-malladi@ti.com>
---
drivers/net/ethernet/ti/icssg/icssg_common.c | 13 +++++++++++--
1 file changed, 11 insertions(+), 2 deletions(-)
diff --git a/drivers/net/ethernet/ti/icssg/icssg_common.c b/drivers/net/ethernet/ti/icssg/icssg_common.c
index ede32f266729..82ddef9c17d5 100644
--- a/drivers/net/ethernet/ti/icssg/icssg_common.c
+++ b/drivers/net/ethernet/ti/icssg/icssg_common.c
@@ -105,6 +105,7 @@ static int emac_xsk_xmit_zc(struct prueth_emac *emac,
struct xdp_desc xdp_desc;
int num_tx = 0, pkt_len;
int descs_avail, ret;
+ u32 dst_tag_id;
u32 *epib;
int i;
@@ -137,9 +138,17 @@ static int emac_xsk_xmit_zc(struct prueth_emac *emac,
epib[0] = 0;
epib[1] = 0;
cppi5_hdesc_set_pktlen(host_desc, pkt_len);
- cppi5_desc_set_tags_ids(&host_desc->hdr, 0,
- (emac->port_id | (q_idx << 8)));
+ dst_tag_id = emac->port_id | (q_idx << 8);
+
+ if (emac->prueth->is_hsr_offload_mode &&
+ (ndev->features & NETIF_F_HW_HSR_DUP))
+ dst_tag_id = PRUETH_UNDIRECTED_PKT_DST_TAG;
+
+ if (emac->prueth->is_hsr_offload_mode &&
+ (ndev->features & NETIF_F_HW_HSR_TAG_INS))
+ epib[1] |= PRUETH_UNDIRECTED_PKT_TAG_INS;
+ cppi5_desc_set_tags_ids(&host_desc->hdr, 0, dst_tag_id);
k3_udma_glue_tx_dma_to_cppi5_addr(tx_chn->tx_chn, &dma_buf);
cppi5_hdesc_attach_buf(host_desc, dma_buf, pkt_len, dma_buf,
pkt_len);
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox