* Re: [PATCH 2/2] iio: adc: Add Nuvoton MA35D1 EADC driver
From: David Lechner @ 2026-06-29 15:09 UTC (permalink / raw)
To: Chi-Wen Weng, jic23, robh, krzk+dt, conor+dt
Cc: nuno.sa, andy, linux-arm-kernel, linux-iio, devicetree,
linux-kernel, cwweng
In-Reply-To: <5e65eabd-699b-4587-bb38-d8ef5c6d2aaa@gmail.com>
On 6/29/26 2:32 AM, Chi-Wen Weng wrote:
> Hi David,
>
> Thanks for the detailed review.
>
> After looking at your comments and the other review feedback, I plan to
> simplify v2 and limit the initial upstream driver to direct raw reads for
> the external single-ended ADC channels.
>
> In v2, I will drop the triggered buffer support, the device trigger and
> the differential channel support for now. Buffered capture and
> differential inputs can be added later as follow-up patches once the
> scan sequencing, trigger model and differential pair constraints are
> handled properly.
This is a simple/small enough driver that it would be fine to still
keep all of those features in v2. It would still be fine to split
them into separate patches, but you could still send them all as
a single patch series.
>
> This also means that the scan buffer layout comments will no longer
> apply to v2, since the triggered-buffer path will be removed from the
> initial submission.
>
> I will address the other driver comments in v2:
> - drop the unused struct device pointer,
> - remove the triggered-buffer and trigger-related Kconfig selects,
> - switch the register access helpers to regmap,
> - avoid forcing the external reference path unconditionally,
> - add optional vref-supply handling,
> - add IIO_CHAN_INFO_SCALE based on the selected reference source,
> - use the internal reference when no vref-supply is provided,
> - add a named macro for the sample-time field,
> - drop the unused channel address field,
> - drop datasheet_name from the initial driver,
> - use device_for_each_child_node_scoped(),
> - use devm_mutex_init(),
> - keep only INDIO_DIRECT_MODE for the initial driver.
>
> For the firmware-described channels, v2 will only accept the external
> ADC input channels 0 to 7. The internal VBAT channel and differential
> inputs will not be described or exposed by the initial driver.
>
> Thanks,
> Chi-Wen
>
> David Lechner 於 2026/6/28 上午 04:52 寫道:
>> On 6/25/26 6:06 AM, Chi-Wen Weng wrote:
>>> From: Chi-Wen Weng <cwweng@nuvoton.com>
>>>
>>> Add an IIO driver for the Nuvoton MA35D1 Enhanced ADC controller.
>>>
For future reference, please don't top-post, but rather put your
reply inline like this and trim any irrelevant context.
^ permalink raw reply
* Re: [PATCH 4/5] media: nxp: imx8-isi: Correct color map between V4L2 and ISI
From: Frank Li @ 2026-06-29 15:08 UTC (permalink / raw)
To: Guoniu Zhou
Cc: Laurent Pinchart, Mauro Carvalho Chehab, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Christian Hemp,
Stefan Riedmueller, Jacopo Mondi, Dong Aisheng, Guoniu Zhou,
linux-media, imx, linux-arm-kernel, linux-kernel, stable
In-Reply-To: <20260629-isi-v1-4-deebfdb1b07b@oss.nxp.com>
On Mon, Jun 29, 2026 at 03:44:58PM +0800, Guoniu Zhou wrote:
Subject: Correct V4L2_PIX_FMT_XBGR32 mapping in m2m mode
> Correct color map between V4L2_PIX_FMT_XBGR32 and ISI input
> format XRGB8 when ISI works at memory to memory mode.
Fix the ISI input format for the color map V4L2_PIX_FMT_XBGR32 in
memory-to-memory mode.
Frank
>
> Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver")
> Cc: stable@vger.kernel.org
> Signed-off-by: Guoniu Zhou <guoniu.zhou@nxp.com>
> ---
> drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
> index 5eb448f4c26f..05b51b98344b 100644
> --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
> +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
> @@ -151,7 +151,7 @@ static const struct mxc_isi_format_info mxc_isi_formats[] = {
> .fourcc = V4L2_PIX_FMT_XBGR32,
> .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT
> | MXC_ISI_VIDEO_M2M_CAP,
> - .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_XBGR8,
> + .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_XRGB8,
> .isi_out_format = CHNL_IMG_CTRL_FORMAT_XRGB888,
> .mem_planes = 1,
> .color_planes = 1,
>
> --
> 2.34.1
>
>
^ permalink raw reply
* [PATCH v5 4/4] arm64: escalate smp_send_stop() to an SDEI NMI as a last resort
From: Kiryl Shutsemau @ 2026-06-29 15:07 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, James Morse
Cc: Mark Rutland, Marc Zyngier, Doug Anderson, 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,
Kiryl Shutsemau (Meta)
In-Reply-To: <cover.1782744912.git.kas@kernel.org>
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
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: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
---
arch/arm64/include/asm/nmi.h | 24 +++++++
arch/arm64/kernel/smp.c | 113 +++++++++++++++++++++-----------
drivers/firmware/Kconfig | 2 +
drivers/firmware/arm_sdei_nmi.c | 87 ++++++++++++++++++++++++
4 files changed, 188 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..05c7007a1f4a 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,62 @@ void arch_irq_work_raise(void)
}
#endif
-static void __noreturn local_cpu_stop(unsigned int cpu)
+/**
+ * arm64_nmi_cpu_stop() - stop the local CPU after it is told to stop.
+ * @regs: register state to record in the vmcore on a crash stop, or NULL for
+ * panic_smp_self_stop(), which has no interrupted context to save.
+ * @die_on_crash: on the kdump crash path, power the CPU off via PSCI CPU_OFF
+ * (so a capture kernel can reclaim it) rather than parking it.
+ *
+ * The single point every arm64 stop path funnels through, keeping the
+ * bookkeeping (mask interrupts, save the crash context, mark offline, mask
+ * SDEI, optionally power off) 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.
+ *
+ * The IPI stop handlers pass @die_on_crash true. 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();
+
+#ifdef CONFIG_KEXEC_CORE
+ if (crash && regs)
+ crash_save_cpu(regs, cpu);
+#endif
+
+ /* 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 +927,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 +1004,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 +1278,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 dd47584d3b57..b5f502bd2da3 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 4047fcda0bc3..038eb79e28ac 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,57 @@ 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)
{
+ /*
+ * No smp_rmb() pairing sdei_nmi_stop_cpus()'s dsb(ishst): the flag is
+ * the only shared value, and this handler runs only because firmware
+ * delivered the event -- a round-trip past that store -- so the read
+ * cannot be stale and there is no second load for a barrier to order.
+ */
+ 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()),
@@ -122,6 +176,39 @@ 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 signalling. The signal goes out via an SMC
+ * to firmware, not a memory store, so smp_wmb() ordering is not
+ * enough: use dsb(ishst) to make the store globally visible before the
+ * SMC executes, as gic_ipi_send_mask() does for its SGI. The SDEI spec
+ * does not require the dispatch to order the caller's prior stores.
+ */
+ dsb(ishst);
+
+ 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
--
2.54.0
^ permalink raw reply related
* [PATCH v5 2/4] firmware: arm_sdei: add SDEI_EVENT_SIGNAL support
From: Kiryl Shutsemau @ 2026-06-29 15:07 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, James Morse
Cc: Mark Rutland, Marc Zyngier, Doug Anderson, 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,
Kiryl Shutsemau (Meta)
In-Reply-To: <cover.1782744912.git.kas@kernel.org>
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
Add sdei_event_signal(), a thin wrapper over the SDEI_EVENT_SIGNAL call
(DEN0054) that makes the software-signalled event (event 0) pending on a
target PE -- delivered NMI-like even when that PE has interrupts masked.
It takes no locks, so it is safe to call from NMI / crash context.
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
---
drivers/firmware/arm_sdei.c | 12 ++++++++++++
include/linux/arm_sdei.h | 6 ++++++
include/uapi/linux/arm_sdei.h | 1 +
3 files changed, 19 insertions(+)
diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
index c161cf263547..e8dd2f0f3919 100644
--- a/drivers/firmware/arm_sdei.c
+++ b/drivers/firmware/arm_sdei.c
@@ -339,6 +339,18 @@ static void _ipi_unmask_cpu(void *ignored)
sdei_unmask_local_cpu();
}
+/*
+ * Signal the software-signalled event (event 0) to @mpidr. Does nothing
+ * but the SMC -- no locks, no event lookup -- so it is safe from NMI /
+ * crash context (e.g. the cross-CPU NMI service).
+ */
+int sdei_event_signal(u32 event_num, u64 mpidr)
+{
+ return invoke_sdei_fn(SDEI_1_0_FN_SDEI_EVENT_SIGNAL, event_num,
+ mpidr, 0, 0, 0, NULL);
+}
+NOKPROBE_SYMBOL(sdei_event_signal);
+
/*
* Was SDEI firmware probed and is it usable? Lets optional consumers skip
* registering an event -- and the warning a failed registration emits -- on
diff --git a/include/linux/arm_sdei.h b/include/linux/arm_sdei.h
index b07113eeeff7..b9dc21c241be 100644
--- a/include/linux/arm_sdei.h
+++ b/include/linux/arm_sdei.h
@@ -37,6 +37,12 @@ int sdei_event_unregister(u32 event_num);
int sdei_event_enable(u32 event_num);
int sdei_event_disable(u32 event_num);
+/*
+ * Signal the software-signalled event (event 0) to another PE, NMI-like.
+ * @mpidr is the target's MPIDR affinity.
+ */
+int sdei_event_signal(u32 event_num, u64 mpidr);
+
/* Was SDEI firmware probed and usable? */
bool sdei_is_present(void);
diff --git a/include/uapi/linux/arm_sdei.h b/include/uapi/linux/arm_sdei.h
index af0630ba5437..22eb61612673 100644
--- a/include/uapi/linux/arm_sdei.h
+++ b/include/uapi/linux/arm_sdei.h
@@ -22,6 +22,7 @@
#define SDEI_1_0_FN_SDEI_PE_UNMASK SDEI_1_0_FN(0x0C)
#define SDEI_1_0_FN_SDEI_INTERRUPT_BIND SDEI_1_0_FN(0x0D)
#define SDEI_1_0_FN_SDEI_INTERRUPT_RELEASE SDEI_1_0_FN(0x0E)
+#define SDEI_1_0_FN_SDEI_EVENT_SIGNAL SDEI_1_0_FN(0x0F)
#define SDEI_1_0_FN_SDEI_PRIVATE_RESET SDEI_1_0_FN(0x11)
#define SDEI_1_0_FN_SDEI_SHARED_RESET SDEI_1_0_FN(0x12)
--
2.54.0
^ permalink raw reply related
* [PATCH v5 3/4] drivers/firmware: add SDEI cross-CPU NMI service for arm64
From: Kiryl Shutsemau @ 2026-06-29 15:07 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, James Morse
Cc: Mark Rutland, Marc Zyngier, Doug Anderson, 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,
Kiryl Shutsemau (Meta)
In-Reply-To: <cover.1782744912.git.kas@kernel.org>
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
Deliver an NMI-like event to an interrupt-masked arm64 CPU via the
standard SDEI software-signalled event (event 0), without the pseudo-NMI
hot-path cost: register a handler for event 0 and poke a target with
sdei_event_signal(0, mpidr).
First user is arch_trigger_cpumask_backtrace() (sysrq-l, RCU stalls,
hung-task/soft-lockup dumps), which otherwise rides an IPI that can't
reach a masked CPU. Falls back to the IPI path when SDEI is absent; no
watchdog backend yet, so the stock detector is untouched.
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
---
MAINTAINERS | 2 +-
arch/arm64/include/asm/nmi.h | 24 +++++
arch/arm64/kernel/smp.c | 11 +++
drivers/firmware/Kconfig | 19 ++++
drivers/firmware/Makefile | 1 +
drivers/firmware/arm_sdei_nmi.c | 159 ++++++++++++++++++++++++++++++++
6 files changed, 215 insertions(+), 1 deletion(-)
create mode 100644 arch/arm64/include/asm/nmi.h
create mode 100644 drivers/firmware/arm_sdei_nmi.c
diff --git a/MAINTAINERS b/MAINTAINERS
index c8d4b913f26c..b5ddfb85dce9 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24797,7 +24797,7 @@ M: James Morse <james.morse@arm.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: Documentation/devicetree/bindings/arm/firmware/sdei.txt
-F: drivers/firmware/arm_sdei.c
+F: drivers/firmware/arm_sdei*
F: include/linux/arm_sdei.h
F: include/uapi/linux/arm_sdei.h
diff --git a/arch/arm64/include/asm/nmi.h b/arch/arm64/include/asm/nmi.h
new file mode 100644
index 000000000000..9366be419d18
--- /dev/null
+++ b/arch/arm64/include/asm/nmi.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_NMI_H
+#define __ASM_NMI_H
+
+#include <linux/cpumask.h>
+
+/*
+ * 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.
+ */
+#ifdef CONFIG_ARM_SDEI_NMI
+bool sdei_nmi_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu);
+#else
+static inline bool sdei_nmi_trigger_cpumask_backtrace(const cpumask_t *mask,
+ int exclude_cpu)
+{
+ return false;
+}
+#endif
+
+#endif /* __ASM_NMI_H */
diff --git a/arch/arm64/kernel/smp.c b/arch/arm64/kernel/smp.c
index 1aa324104afb..a670434a8cae 100644
--- a/arch/arm64/kernel/smp.c
+++ b/arch/arm64/kernel/smp.c
@@ -45,6 +45,7 @@
#include <asm/daifflags.h>
#include <asm/kvm_mmu.h>
#include <asm/mmu_context.h>
+#include <asm/nmi.h>
#include <asm/numa.h>
#include <asm/processor.h>
#include <asm/smp_plat.h>
@@ -927,6 +928,16 @@ static void arm64_backtrace_ipi(cpumask_t *mask)
void arch_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu)
{
+ /*
+ * Prefer the SDEI cross-CPU NMI provider when active: firmware
+ * dispatches the event out of EL3 and reaches CPUs that have
+ * interrupts locally masked, without the per-IRQ-mask cost that
+ * pseudo-NMI pays for the same reach. The plain IPI path below
+ * can't reach such a CPU unless pseudo-NMI is enabled.
+ */
+ if (sdei_nmi_trigger_cpumask_backtrace(mask, exclude_cpu))
+ return;
+
/*
* NOTE: though nmi_trigger_cpumask_backtrace() has "nmi_" in the name,
* nothing about it truly needs to be implemented using an NMI, it's
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index bbd2155d8483..dd47584d3b57 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -36,6 +36,25 @@ config ARM_SDE_INTERFACE
standard for registering callbacks from the platform firmware
into the OS. This is typically used to implement RAS notifications.
+config ARM_SDEI_NMI
+ bool "SDEI-based cross-CPU NMI service (arm64)"
+ depends on ARM_SDE_INTERFACE
+ help
+ Provides SDEI-based cross-CPU NMI delivery for hooks that need
+ to reach interrupt-masked CPUs on silicon that lacks FEAT_NMI:
+
+ - arch_trigger_cpumask_backtrace() (sysrq-l, RCU stalls,
+ hardlockup_all_cpu_backtrace, soft-lockup secondary dumps,
+ hung-task auxiliary dumps)
+
+ The driver registers a handler for the SDEI software-signalled
+ event (event 0) and reaches a target CPU by signalling it with
+ SDEI_EVENT_SIGNAL. Firmware delivers the event out of EL3
+ regardless of the target's PSTATE.DAIF -- forced delivery into a
+ CPU wedged with interrupts locally masked.
+
+ If unsure, say N.
+
config EDD
tristate "BIOS Enhanced Disk Drive calls determine boot disk"
depends on X86
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 4ddec2820c96..be46f1e1dc77 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -4,6 +4,7 @@
#
obj-$(CONFIG_ARM_SCPI_PROTOCOL) += arm_scpi.o
obj-$(CONFIG_ARM_SDE_INTERFACE) += arm_sdei.o
+obj-$(CONFIG_ARM_SDEI_NMI) += arm_sdei_nmi.o
obj-$(CONFIG_DMI) += dmi_scan.o
obj-$(CONFIG_DMI_SYSFS) += dmi-sysfs.o
obj-$(CONFIG_EDD) += edd.o
diff --git a/drivers/firmware/arm_sdei_nmi.c b/drivers/firmware/arm_sdei_nmi.c
new file mode 100644
index 000000000000..4047fcda0bc3
--- /dev/null
+++ b/drivers/firmware/arm_sdei_nmi.c
@@ -0,0 +1,159 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * arm64 SDEI-based cross-CPU NMI service.
+ *
+ * Delivering an "NMI-shaped" event to an EL1 context that has locally
+ * masked interrupts, on silicon without FEAT_NMI, can be done two ways:
+ *
+ * - pseudo-NMI: mask "interrupts" via the GIC priority register
+ * (ICC_PMR_EL1) instead of PSTATE.DAIF, leaving a high-priority band
+ * deliverable. Functionally this works -- but it reimplements every
+ * local_irq_disable()/enable() and exception entry/exit as a PMR
+ * write plus synchronisation, a cost paid on that hot path forever,
+ * whether or not an NMI is ever delivered.
+ *
+ * - SDEI: leave interrupt masking as the cheap PSTATE.DAIF operation
+ * and have the firmware bounce an EL3-routed Group-0 SGI back to
+ * NS-EL1 as an event callback. The cost is a firmware round-trip,
+ * but only at the rare moment delivery is actually needed.
+ *
+ * This driver takes the second path: it keeps the IRQ-mask hot path
+ * free and pays only when it fires, which is what makes cross-CPU NMI
+ * affordable on hardware where the pseudo-NMI tax isn't, until FEAT_NMI
+ * makes NMI masking cheap in the architecture itself.
+ *
+ * Capabilities provided:
+ *
+ * - sdei_nmi_trigger_cpumask_backtrace() — override for arm64's
+ * arch_trigger_cpumask_backtrace(), so sysrq-l, RCU stall dumps,
+ * hardlockup_all_cpu_backtrace, soft-lockup/hung-task secondary
+ * dumps all reach interrupt-masked CPUs.
+ *
+ * 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
+ * event 0 pending on that PE and dispatches the handler NMI-like,
+ * regardless of the target's DAIF.
+ * Availability is simply whether event 0 registers and enables -- if SDEI
+ * and its software-signalled event are present we use it, otherwise the
+ * driver stays inert.
+ */
+
+#define pr_fmt(fmt) "sdei_nmi: " fmt
+
+#include <linux/arm_sdei.h>
+#include <linux/cpumask.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kprobes.h>
+#include <linux/nmi.h>
+#include <linux/printk.h>
+#include <linux/ptrace.h>
+#include <linux/smp.h>
+#include <linux/types.h>
+
+#include <asm/nmi.h>
+#include <asm/smp_plat.h>
+
+static bool sdei_nmi_available;
+
+#define SDEI_NMI_EVENT 0
+
+static int sdei_nmi_handler(u32 event, struct pt_regs *regs, void *arg)
+{
+ /*
+ * nmi_cpu_backtrace() no-ops unless this CPU's bit is set in the
+ * global backtrace mask (driven by nmi_trigger_cpumask_backtrace()),
+ * so a fire that reaches a CPU not being backtraced is harmless.
+ */
+ nmi_cpu_backtrace(regs);
+ return SDEI_EV_HANDLED;
+}
+NOKPROBE_SYMBOL(sdei_nmi_handler);
+
+static void sdei_nmi_fire(unsigned int target_cpu)
+{
+ int err = sdei_event_signal(SDEI_NMI_EVENT, cpu_logical_map(target_cpu));
+
+ if (err)
+ pr_warn("SDEI_EVENT_SIGNAL to CPU %u failed: %d\n",
+ target_cpu, err);
+}
+
+/*
+ * Raise callback for nmi_trigger_cpumask_backtrace(): signal event 0
+ * at every CPU still pending in @mask. The framework excludes the local
+ * CPU from @mask before calling us.
+ */
+static void sdei_nmi_raise_backtrace(cpumask_t *mask)
+{
+ unsigned int cpu;
+
+ /*
+ * Publish backtrace_mask (set by nmi_trigger_cpumask_backtrace())
+ * before signalling. As in the stop path, the SMC is not a memory
+ * store, so dsb(ishst) is needed for the target to observe the mask.
+ */
+ dsb(ishst);
+
+ for_each_cpu(cpu, mask)
+ sdei_nmi_fire(cpu);
+}
+
+/*
+ * Override hook for arch_trigger_cpumask_backtrace() (see
+ * arch/arm64/kernel/smp.c). Returns true when SDEI handled the request,
+ * which is the case whenever SDEI is active; on a false return the arch
+ * falls back to its regular-IRQ (or pseudo-NMI, if enabled) IPI.
+ *
+ * On a kernel built without paying the pseudo-NMI hot-path cost (the
+ * usual case for this driver's target), the IPI can't reach a CPU that
+ * has interrupts masked -- so the backtrace of the one CPU you care
+ * about comes back empty. SDEI is dispatched out of EL3 and lands
+ * regardless of the target's DAIF, without taxing the IRQ-mask path.
+ */
+bool sdei_nmi_trigger_cpumask_backtrace(const cpumask_t *mask, int exclude_cpu)
+{
+ if (!sdei_nmi_available)
+ return false;
+
+ nmi_trigger_cpumask_backtrace(mask, exclude_cpu,
+ sdei_nmi_raise_backtrace);
+ return true;
+}
+
+/*
+ * device_initcall (after arch_initcall(sdei_init), so the SDEI subsystem
+ * is up): probe the firmware, register the event, and turn on the
+ * cross-CPU service. If the probe fails the driver stays inert and the
+ * override hooks decline, leaving the arch's own paths in place.
+ */
+static int __init sdei_nmi_init(void)
+{
+ int err;
+
+ if (!sdei_is_present())
+ return 0;
+
+ err = sdei_event_register(SDEI_NMI_EVENT, sdei_nmi_handler, NULL);
+ if (err) {
+ pr_err("sdei_event_register(%u) failed: %d\n",
+ SDEI_NMI_EVENT, err);
+ return 0;
+ }
+
+ err = sdei_event_enable(SDEI_NMI_EVENT);
+ if (err) {
+ pr_err("sdei_event_enable(%u) failed: %d\n",
+ SDEI_NMI_EVENT, err);
+ sdei_event_unregister(SDEI_NMI_EVENT);
+ return 0;
+ }
+
+ sdei_nmi_available = true;
+ pr_info("using SDEI cross-CPU NMI (SDEI_EVENT_SIGNAL, event %u)\n",
+ SDEI_NMI_EVENT);
+
+ return 0;
+}
+device_initcall(sdei_nmi_init);
--
2.54.0
^ permalink raw reply related
* [PATCH v5 1/4] firmware: arm_sdei: add sdei_is_present()
From: Kiryl Shutsemau @ 2026-06-29 15:07 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, James Morse
Cc: Mark Rutland, Marc Zyngier, Doug Anderson, 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,
Kiryl Shutsemau (Meta)
In-Reply-To: <cover.1782744912.git.kas@kernel.org>
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
invoke_sdei_fn() returns -EIO when no SDEI conduit was probed, and the
core warns ("Failed to create event ...") on any registration that hits
that. An optional consumer that registers an event from an unconditional
initcall would therefore make every boot on a non-SDEI system emit that
warning for what is simply absent firmware.
Expose whether SDEI firmware is present so such a consumer can skip
registration -- and the warning -- when there is nothing to talk to.
Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
---
drivers/firmware/arm_sdei.c | 10 ++++++++++
include/linux/arm_sdei.h | 3 +++
2 files changed, 13 insertions(+)
diff --git a/drivers/firmware/arm_sdei.c b/drivers/firmware/arm_sdei.c
index f39ed7ba3a38..c161cf263547 100644
--- a/drivers/firmware/arm_sdei.c
+++ b/drivers/firmware/arm_sdei.c
@@ -339,6 +339,16 @@ static void _ipi_unmask_cpu(void *ignored)
sdei_unmask_local_cpu();
}
+/*
+ * Was SDEI firmware probed and is it usable? Lets optional consumers skip
+ * registering an event -- and the warning a failed registration emits -- on
+ * systems with no SDEI.
+ */
+bool sdei_is_present(void)
+{
+ return sdei_firmware_call;
+}
+
static void _ipi_private_reset(void *ignored)
{
int err;
diff --git a/include/linux/arm_sdei.h b/include/linux/arm_sdei.h
index f652a5028b59..b07113eeeff7 100644
--- a/include/linux/arm_sdei.h
+++ b/include/linux/arm_sdei.h
@@ -37,6 +37,9 @@ int sdei_event_unregister(u32 event_num);
int sdei_event_enable(u32 event_num);
int sdei_event_disable(u32 event_num);
+/* Was SDEI firmware probed and usable? */
+bool sdei_is_present(void);
+
/* GHES register/unregister helpers */
int sdei_register_ghes(struct ghes *ghes, sdei_event_callback *normal_cb,
sdei_event_callback *critical_cb);
--
2.54.0
^ permalink raw reply related
* [PATCH v5 0/4] arm64: cross-CPU NMI via SDEI
From: Kiryl Shutsemau @ 2026-06-29 15:07 UTC (permalink / raw)
To: Catalin Marinas, Will Deacon, James Morse
Cc: Mark Rutland, Marc Zyngier, Doug Anderson, 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,
Kiryl Shutsemau (Meta)
From: "Kiryl Shutsemau (Meta)" <kas@kernel.org>
A class of debug/observability features needs to interrupt a CPU that has
its interrupts locally masked: the all-CPU backtrace behind sysrq-l /
RCU-stall / hung-task / hard-lockup dumps, and crash_smp_send_stop()
capturing a stuck CPU's state into the vmcore. On arm64 these need a
mechanism that reaches a CPU spinning with DAIF masked, which a normal IPI
cannot.
arm64 has two such mechanisms today:
- GICv3 pseudo-NMI (interrupt priority masking). The cost lands on the
interrupt mask/unmask hot path: local_irq_enable() becomes an
ICC_PMR_EL1 write, and exception entry/exit save and restore the PMR,
paid on every CPU whether or not an NMI is ever delivered.
Measured on Grace (Neoverse V2; ICC_CTLR_EL1.PMHE=0, so the PMR-sync
DSB is already patched to a NOP), pseudo_nmi=0 vs pseudo_nmi=1:
gettid() loop: 178 -> 253 ns/call (+42%, ~74 ns)
will-it-scale sched_yield: 0.705x throughput, flat from 1 to 72 cores
will-it-scale page_fault1: within ~5%
The ~74 ns is a fixed per-syscall entry/exit tax -- it reproduces at
+73.5 ns on Neoverse N2 -- so the hit tracks syscall/exception density
and is unacceptable on syscall-bound fleet workloads, which therefore
run with pseudo-NMI disabled.
- FEAT_NMI (Armv8.8) -- the architectural fix, but absent from deployed
silicon and from most of the fleet for years to come.
For deployments that do not run pseudo-NMI, the backtrace and crash paths
are degraded: a plain IPI can't reach the masked CPU, so the backtrace of
the CPU you care about comes back empty and the kdump is missing the
culprit's registers. The hard-lockup detector on these systems is the
software buddy detector (HARDLOCKUP_DETECTOR_BUDDY): it detects a stall
from a neighbour CPU, but it cannot itself interrupt the wedged CPU, so
its report has no stack for the culprit and (with hardlockup_panic) the
panic runs on the bystander.
This series adds a third delivery backend that costs nothing on the hot
path: SDEI. Firmware delivers an SDEI event into a CPU regardless of its
DAIF state, so interrupt masking stays the cheap PSTATE.DAIF operation and
the firmware round-trip is paid only at the rare moment a CPU must be
interrupted.
It does not add a hard-lockup detector. Detection stays with the buddy
detector (CONFIG_HARDLOCKUP_DETECTOR_PREFER_BUDDY); this series gives the
backtrace and crash-stop paths -- including the buddy detector's
backtrace of the stalled CPU -- a way to actually reach a masked CPU.
Mechanism
=========
It uses the standard SDEI software-signalled event (event 0) and the
SDEI_EVENT_SIGNAL call (DEN0054) -- a spec-defined cross-PE signal, not a
vendor extension. The driver registers a handler for event 0 and pokes a
target CPU with sdei_event_signal(0, target_mpidr); firmware makes event 0
pending on that PE and dispatches the handler NMI-like.
No firmware change is required beyond SDEI being enabled, which
firmware-first RAS (APEI/GHES) deployments already have; the only
SDEI-core addition is a thin sdei_event_signal() wrapper over the standard
call.
Prior SDEI watchdog work
========================
Out-of-tree SDEI hard-lockup watchdogs exist (e.g. in the openEuler and
Anolis kernels). They bind the secure physical timer as an SDEI event, so
firmware delivers a periodic self-CPU tick that drives a detector. That
requires a new SDEI interrupt-binding API, pushes the watchdog period into
firmware, and adds secure-timer EOI handling on the kexec path. This
series instead uses only the standard software-signalled event 0, keeps
all timing in the kernel (the buddy detector), and the same delivery
primitive serves the backtrace and crash-stop users, not just lockup
reporting.
Not included / follow-ups
=========================
- No SDEI hard-lockup-detector backend. v1 had one; it is dropped here.
The buddy detector plus this series' backtrace already cover the
no-pseudo-NMI case, and a dedicated SDEI backend duplicated the
perf-NMI detector it had to compile-exclude. Run PREFER_BUDDY.
- A CPU stopped by the SDEI rung is parked, not powered off via PSCI
CPU_OFF. Reaching and dumping the wedged CPU -- the point of the
series -- works, and it matches the shared stop path's own park
fallback when CPU_OFF is unavailable. The consequence is that an SMP
crash-capture kernel cannot re-online such a CPU (it stays "already
on"); the capture kernel boots and runs on the remaining CPUs.
Powering the stopped CPU off so a capture kernel can reclaim it would
need CPU_OFF from the SDEI stop context, which does not work in
practice (see the v4 discussion); left as a follow-up and does not
affect the dump's contents.
Testing
=======
Developed on QEMU 'virt' (Trusted Firmware-A with SDEI enabled) and
validated on NVIDIA Grace (Neoverse V2) hardware, under
irqchip.gicv3_pseudo_nmi=0 with HARDLOCKUP_DETECTOR_PREFER_BUDDY=y:
- sysrq-l backtrace of an interrupt-masked CPU returns its real stack,
pstate showing DAIF set -- proof SDEI delivered into the masked CPU;
- buddy detector catches a hard lockup (LKDTM) and the wedged CPU's
stack is fetched via the SDEI backtrace;
- reboot/halt and the panic/kdump crash stop reach a wedged CPU via the
SDEI rung ("SMP: retry stop with SDEI NMI for CPUs N"), and the kdump
captures the wedged CPU's registers in the vmcore;
- with SDEI absent (plain QEMU 'virt', no firmware support) the driver
stays inert: no event registration and no boot-time warning.
Changes since v4
================
- Strengthen the publish barrier from smp_wmb() to dsb(ishst) on the
stop path, and add the missing one on the backtrace path; the SMC is
not a memory store, so ordering alone is not enough (Catalin Marinas).
- Drop the redundant ARM64 Kconfig dependency, already implied by
ARM_SDE_INTERFACE (Julian Braha).
- Reviewed-by from Douglas Anderson now on all four patches.
- Replaced the stale perf numbers in this cover with fresh, reproducible
measurements (Catalin Marinas, Marc Zyngier).
Changes since v3
================
- New sdei_is_present() patch; the NMI initcall now skips registration
(and its boot warning) on non-SDEI systems (Puranjay Mohan).
- Fixed a NULL deref on a parallel-panic crash stop and the
CONFIG_KEXEC_CORE=n build (Puranjay Mohan).
- kernel-doc + barrier comments on the stop path; reordered the two
arm_sdei core patches (Doug Anderson).
Changes since v2
================
- Unified the CPU-stop paths into one arm64_nmi_cpu_stop(regs,
die_on_crash), dropping local_cpu_stop()/ipi_cpu_crash_stop().
- SDEI rung tests sdei_nmi_active() first; sdei_nmi_stop_cpus() is void.
- Replaced the per-CPU stop cpumask with a write-once flag.
- Commented the SDEI-park / no-CPU_OFF rationale.
Changes since v1
================
- Dropped the SDEI hard-lockup-detector patch; use the buddy detector.
- Reworked crash-stop into a third rung of smp_send_stop().
- Renamed the driver to arm_sdei_nmi.c; widened the MAINTAINERS glob.
v4: https://lore.kernel.org/all/cover.1781709543.git.kas@kernel.org
v3: https://lore.kernel.org/all/cover.1781490440.git.kas@kernel.org
v2: https://lore.kernel.org/all/cover.1781082212.git.kas@kernel.org
v1: https://lore.kernel.org/all/cover.1780496779.git.kas@kernel.org
Also available at:
git://git.kernel.org/pub/scm/linux/kernel/git/kas/linux.git sdei-nmi/v5
Kiryl Shutsemau (Meta) (4):
firmware: arm_sdei: add sdei_is_present()
firmware: arm_sdei: add SDEI_EVENT_SIGNAL support
drivers/firmware: add SDEI cross-CPU NMI service for arm64
arm64: escalate smp_send_stop() to an SDEI NMI as a last resort
MAINTAINERS | 2 +-
arch/arm64/include/asm/nmi.h | 48 +++++++
arch/arm64/kernel/smp.c | 124 +++++++++++-----
drivers/firmware/Kconfig | 21 +++
drivers/firmware/Makefile | 1 +
drivers/firmware/arm_sdei.c | 22 +++
drivers/firmware/arm_sdei_nmi.c | 246 ++++++++++++++++++++++++++++++++
include/linux/arm_sdei.h | 9 ++
include/uapi/linux/arm_sdei.h | 1 +
9 files changed, 435 insertions(+), 39 deletions(-)
create mode 100644 arch/arm64/include/asm/nmi.h
create mode 100644 drivers/firmware/arm_sdei_nmi.c
base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
--
2.54.0
^ permalink raw reply
* Re: [PATCH v15 08/11] arm64/ptrace: Define and use _TIF_SYSCALL_EXIT_WORK
From: Ada Couprie Diaz @ 2026-06-29 15:05 UTC (permalink / raw)
To: Jinjie Ruan
Cc: mark.rutland, peterz, catalin.marinas, ldv, song, will, kees,
thuth, ryan.roberts, anshuman.khandual, kevin.brodsky, pengcan,
broonie, luto, linux-arm-kernel, wad, yeoreum.yun, oleg,
linux-kernel, james.morse, tglx, liqiang01, linusw
In-Reply-To: <a1f5cfdc-ea1c-421c-a5bb-675406ca3311@huawei.com>
On 25/06/2026 12:41, Jinjie Ruan wrote:
> On 6/24/2026 10:53 PM, Ada Couprie Diaz wrote:
>> On 11/05/2026 10:21, Jinjie Ruan wrote:
>>> Introduce _TIF_SYSCALL_EXIT_WORK to filter out entry-only flags
>>> during the syscall exit path. This aligns arm64 with the generic
>>> entry framework's SYSCALL_WORK_EXIT semantics.
>>>
>>> [Rationale]
>>> The current syscall exit path uses _TIF_SYSCALL_WORK to decide whether
>>> to invoke syscall_exit_work(). However, _TIF_SYSCALL_WORK includes
>>> flags that are only relevant during syscall entry:
>>>
>>> 1. _TIF_SECCOMP: Seccomp filtering (__secure_computing) only runs
>>> on entry. There is no seccomp callback for syscall exit.
>>>
>>> 2. _TIF_SYSCALL_EMU: In PTRACE_SYSEMU mode, the syscall is
>>> intercepted and skipped on entry. Since the syscall is never
>>> executed, reporting a syscall exit stop is unnecessary.
>>>
>>> [Changes]
>>> - Define _TIF_SYSCALL_EXIT_WORK: A new mask containing only flags
>>> requiring exit processing: _TIF_SYSCALL_TRACE, _TIF_SYSCALL_AUDIT,
>>> and _TIF_SYSCALL_TRACEPOINT.
>>>
>>> - Update exit path: Use _TIF_SYSCALL_EXIT_WORK in
>>> syscall_exit_to_user_mode_work() to avoid redundant calls to
>>> audit and ptrace reporting when only entry-flags are set.
>>>
>>> - Cleanup: Remove the has_syscall_work() helper as it is no longer
>>> needed. Direct flag comparison is now used to distinguish between
>>> entry and exit work requirements.
>>>
>>> [Impact]
>>> audit_syscall_exit() and report_syscall_exit() will no longer be
>>> triggered for seccomp-only or emu-only syscalls. This matches the
>>> generic entry behavior and improves efficiency by skipping unnecessary
>>> exit processing.
>>>
>>> Cc: Mark Rutland <mark.rutland@arm.com>
>>> Cc: Will Deacon <will@kernel.org>
>>> Cc: Catalin Marinas <catalin.marinas@arm.com>
>>> Reviewed-by: Linus Walleij <linusw@kernel.org>
>>> Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
>>> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
>>> ---
>> Reviewed-by: Ada Couprie Diaz <ada.coupriediaz@arm.com>
>>
>> Definitely not related to this series, but it feels like this brings us
>> quite close to being able to switch to generic TIF flags as well :
>> only TIF_FOREIGN_PSTATE and TIF_MTE_ASYNC_FAULT
>> would need to be moved to the upper 16 bits,
>> with TIF_RESTORE_SIGMASK and TIF_MEMDIE freeing two slots there.
>>
>> Not sure how important the bit number changes would be and if any
>> of the extra generic bits require any arch support (TIF_POLLING_NRFLAG,
>> TIF_USER_RETURN_NOTIFY, TIF_RSEQ, TIF_HRTIMER_REARM)...
>>
>> But again, just thinking out loud !
> Hi Ada,
>
> You have incredible foresight! You are absolutely right that this series
> lays the perfect groundwork for switching arm64 to generic TIF flags
> (HAVE_GENERIC_TIF_BITS).
>
> I am happy to share that I have already implemented exactly what you
> described—including migrating the architecture-specific flags to the
> upper 16 bits and enabling the generic TIF infrastructure—in a separate,
> dedicated patch series.
>
> You can find the implementation and discussion here:
>
> https://lore.kernel.org/all/20260320104222.1381274-1-ruanjinjie@huawei.com/
>
> Since that series directly builds on top of the cleanups and
> infrastructure introduced here, I plan to actively push it forward right
> after we get this core generic entry conversion landed.
>
> Thanks again for looking so far ahead and validating the direction!
>
> Best regards,
> Jinjie
Hi Jinjie,
Oh, neat, I evidently missed this series ;
glad you confirmed I was on the right track !
I am looking forward to seeing both those works merged then :)
Kind regards,
Ada
^ permalink raw reply
* Re: [PATCH v20 01/14] dmaengine: constify struct dma_descriptor_metadata_ops
From: Pandey, Radhey Shyam @ 2026-06-29 15:04 UTC (permalink / raw)
To: Bartosz Golaszewski, Vinod Koul, Jonathan Corbet, Thara Gopinath,
Herbert Xu, David S. Miller, Udit Tiwari, Md Sadre Alam,
Dmitry Baryshkov, Manivannan Sadhasivam, Stephan Gerhold,
Bjorn Andersson, Peter Ujfalusi, Michal Simek, Frank Li,
Andy Gross, Neil Armstrong
Cc: dmaengine, linux-doc, linux-kernel, linux-arm-msm, linux-crypto,
linux-arm-kernel, brgl, Bartosz Golaszewski
In-Reply-To: <20260629-qcom-qce-cmd-descr-v20-1-56f67da84c05@oss.qualcomm.com>
> There's no reason for the instances of this struct to be modifiable.
> Constify the pointer in struct dma_async_tx_descriptor and all drivers
> currently using it.
>
> Reviewed-by: Manivannan Sadhasivam <mani@kernel.org>
> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Reviewed-by: Radhey Shyam Pandey <radhey.shyam.pandey@amd.com>
Thanks!
> ---
> drivers/dma/ti/k3-udma.c | 2 +-
> drivers/dma/xilinx/xilinx_dma.c | 2 +-
> include/linux/dmaengine.h | 2 +-
> 3 files changed, 3 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
> index 1cf158eb7bdb541c4e7f4f79f65ab70be4311fad..fb21e0df5ab7b20e4e16777b5ff7f61d2ae67b2b 100644
> --- a/drivers/dma/ti/k3-udma.c
> +++ b/drivers/dma/ti/k3-udma.c
> @@ -3408,7 +3408,7 @@ static int udma_set_metadata_len(struct dma_async_tx_descriptor *desc,
> return 0;
> }
>
> -static struct dma_descriptor_metadata_ops metadata_ops = {
> +static const struct dma_descriptor_metadata_ops metadata_ops = {
> .attach = udma_attach_metadata,
> .get_ptr = udma_get_metadata_ptr,
> .set_len = udma_set_metadata_len,
> diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c
> index 404235c1735384635597e88edc25c67c7d250647..165b11a7c776abc6a8d66d631e19da669644577d 100644
> --- a/drivers/dma/xilinx/xilinx_dma.c
> +++ b/drivers/dma/xilinx/xilinx_dma.c
> @@ -653,7 +653,7 @@ static void *xilinx_dma_get_metadata_ptr(struct dma_async_tx_descriptor *tx,
> return seg->hw.app;
> }
>
> -static struct dma_descriptor_metadata_ops xilinx_dma_metadata_ops = {
> +static const struct dma_descriptor_metadata_ops xilinx_dma_metadata_ops = {
> .get_ptr = xilinx_dma_get_metadata_ptr,
> };
>
> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index b3d251c9734e95e1b75cf6763d4d2c3a1c6a9910..5244edb90e7e7510bf4460b6a74ee2a7f91c1ccc 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h
> @@ -623,7 +623,7 @@ struct dma_async_tx_descriptor {
> void *callback_param;
> struct dmaengine_unmap_data *unmap;
> enum dma_desc_metadata_mode desc_metadata_mode;
> - struct dma_descriptor_metadata_ops *metadata_ops;
> + const struct dma_descriptor_metadata_ops *metadata_ops;
> #ifdef CONFIG_ASYNC_TX_ENABLE_CHANNEL_SWITCH
> struct dma_async_tx_descriptor *next;
> struct dma_async_tx_descriptor *parent;
>
^ permalink raw reply
* Re: [PATCH 1/2] dt-bindings: iio: adc: Add Nuvoton MA35D1 EADC
From: David Lechner @ 2026-06-29 15:04 UTC (permalink / raw)
To: Chi-Wen Weng, jic23, robh, krzk+dt, conor+dt
Cc: nuno.sa, andy, linux-arm-kernel, linux-iio, devicetree,
linux-kernel, cwweng
In-Reply-To: <7e96cc1a-eb60-4eeb-937d-64e83bc35279@gmail.com>
On 6/29/26 2:11 AM, Chi-Wen Weng wrote:
>> Should there be a dmas property? Datasheet says it supports PDMA transfer.
>
> The hardware does support PDMA, but DMA support is intentionally not
> included in this initial upstream version. The initial driver will only
> support interrupt-driven direct raw reads, and the MA35D1 PDMA provider
> is not upstream yet.
>
> I would prefer to leave dmas/dma-names out of the initial binding and
> add them later together with DMA support. Please let me know if you
> would prefer optional DMA properties to be described now.
We always want the devicetree to be as complete as possible even
if the drier doesn't use all of the information.
So for trivial/well-known bindings like dmas, we should be able to
add it now.
>
>> I assume 8 is for the internal batter voltage channel? Often, we don't
>> include fixed internal channels like this in the devicetree since they
>> are always the same and don't depend on external wiring.
>
> Correct. Channels 0 to 7 are the external ADC input pins, while channel
> 8 is the internal VBAT input. I will limit the DT child channel nodes to
> external channels 0 to 7.
>
> If VBAT support is added later, it can be exposed by the driver as a
> fixed internal channel rather than being described by devicetree.
>
>> adc.yaml already specifies minItems and maxItems, so we don't need to
>> repeat it.
>
> Since I plan to simplify v2 and drop differential channel support from
> the initial submission, I will remove diff-channels from the initial
> binding.
Same reasoning as above, we want the binding to be as complete as
possible, so we should not omit diff-channels since we know what
the bindings should look like already.
>
> Differential input support can be added later once the fixed hardware
> pair constraints and signed output handling are implemented in the
> driver.
>
^ permalink raw reply
* Re: [PATCH v5 1/7] dt-bindings: display: verisilicon,dc: generalize for single-output variants
From: Conor Dooley @ 2026-06-29 15:02 UTC (permalink / raw)
To: Icenowy Zheng
Cc: Joey Lu, Conor Dooley, maarten.lankhorst, mripard, tzimmermann,
airlied, simona, robh, krzk+dt, conor+dt, ychuang3, schung, yclu4,
dri-devel, devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <80ae28925a67b7bee3b8873db3c113111437e717.camel@iscas.ac.cn>
[-- Attachment #1: Type: text/plain, Size: 3496 bytes --]
On Mon, Jun 29, 2026 at 01:31:46PM +0800, Icenowy Zheng wrote:
> > > > > > > > > +
> > > > > > > > > + resets:
> > > > > > > > > + minItems: 1
> > > > > > > > > + maxItems: 1
> > > > > > > > > +
> > > > > > > > > + reset-names:
> > > > > > > > > + items:
> > > > > > > > > + - const: core
> > > > > > > > This is just maxItems: 1.
> > > > > > > Well the implicit rules of DT binding schemas are quite
> > > > > > > weird...
> > > > > > I don't think it is that strange, as the binding has
> > > > > > reset-names:
> > > > > > items:
> > > > > > - const: core
> > > > > > - const: axi
> > > > > > - const: ahb
> > > > > Ah does the list constraint the order of items? If it
> > > > > constrains
> > > > > the
> > > > It does, yes.
> > > > Alternatively, using an enum permits free ordering.
> > > Ah in this case this should be converted to an enum, I think.
> > >
> > > Should I send a patch for converting it?
> > >
> > > Thanks,
> > > Icenowy
> > Thank you all for the detailed review and discussion, it really
> > helped
> > clarify the right approach.
> >
> > Since I will supply all four clocks with the same phandle for
> > core/axi/ahb,
> > and only one reset "core" for MA35D1, the ordering constraint in the
> > `items` list is not a problem, "core" is already the first entry.
> > There
> > is no need to convert to an enum.
> >
> > Regarding the clock situation for the MA35D1: I agree with supplying
> > all
> > four clocks (core, axi, ahb, pix0) in the devicetree, even though the
> > MA35D1 clock controller gates core/axi/ahb with a single bit. The DT
> > will
> > use the same clock phandle for core, axi, and ahb:
> >
> > clocks = <&clk X>, <&clk X>, <&clk X>, <&pix_clk Y>;
> > clock-names = "core", "axi", "ahb", "pix0";
> >
> > This correctly models the hardware topology. Since all three names
>
> No, this doesn't correctly model the hardware topology -- this will
> lead to clk_get_rate() return the rate of DC core clock when checking
> the AXI clock rate, which is problematic because both clocks are
> limiting the performance of the DC.
>
> > resolve
> > to the same underlying clock node, the CCF's standard enable
> > refcounting
> > handles the shared gate correctly without any custom implementation
> > needed.
> > I will also revert the change in patch 4/7 that made axi and ahb
> > clocks
> > optional, since they will now always be provided in the devicetree.
> >
> > Regarding moving `resets` and `reset-names` to the top-level
> > `required:`,
> > I will wait for Icenowy's patch to land before sending v6 to avoid
> > duplicating the work.
>
> The patch is sent.
>
> >
> > In v6 I will update patch 1/7 with:
> > - Update the subject to "dt-bindings: display: verisilicon,dc: add
> > support for nuvoton,ma35d1-dcu"
> > - Lower `clocks`/`clock-names` `minItems` to 4 at the top level
> > - Remove the `thead,th1520-dc8200` conditional block entirely
>
> I think this conditional block will still be needed, because it will
> need to constrain the minItems to ensure all clocks / resets are
> populated.
Correct. When the outer constraints are relaxed to deal with the new
device the conditional block for the th1520 becomes required. Or having
an else, but if all devices are likely to be different in terms of
configuration specific conditional blocks is better.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* [PATCH] iommu/mediatek-v1: Fix off-by-one in MT2701_LARB_NR_MAX
From: Akari Tsuyukusa @ 2026-06-29 14:59 UTC (permalink / raw)
To: Yong Wu, Joerg Roedel (AMD), Will Deacon, Robin Murphy,
Matthias Brugger, AngeloGioacchino Del Regno, Miles Chen,
Joerg Roedel
Cc: Akari Tsuyukusa, open list:MEDIATEK IOMMU DRIVER,
moderated list:MEDIATEK IOMMU DRIVER,
open list:ARM/Mediatek SoC support,
moderated list:ARM/Mediatek SoC support
The mt2701_m4u_in_larb[] array contains 4 (for LARB0 to LARB3)
elements, meaning mt2701_m4u_to_larb() can legitimately return 3.
The current check `if (larbid >= MT2701_LARB_NR_MAX)` incorrectly
rejects valid LARB3 with -EINVAL.
Fix this off-by-one error by updating MT2701_LARB_NR_MAX to 4.
Note that this does not cause immediate issues with the current
mt2701.dtsi and mt7623n.dtsi because it only defines 3 LARBs:
mediatek,larbs = <&larb0 &larb1 &larb2>;
Thus, larbid never reaches 3 in the existing upstream device tree.
Fixes: de78657e16f4 ("iommu/mediatek: Fix NULL pointer dereference when printing dev_name")
Signed-off-by: Akari Tsuyukusa <akkun11.open@gmail.com>
---
drivers/iommu/mtk_iommu_v1.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/iommu/mtk_iommu_v1.c b/drivers/iommu/mtk_iommu_v1.c
index ac97dd2868d4..e907c9953142 100644
--- a/drivers/iommu/mtk_iommu_v1.c
+++ b/drivers/iommu/mtk_iommu_v1.c
@@ -88,7 +88,7 @@ struct dma_iommu_mapping {
/* MTK generation one iommu HW only support 4K size mapping */
#define MT2701_IOMMU_PAGE_SHIFT 12
#define MT2701_IOMMU_PAGE_SIZE (1UL << MT2701_IOMMU_PAGE_SHIFT)
-#define MT2701_LARB_NR_MAX 3
+#define MT2701_LARB_NR_MAX 4
/*
* MTK m4u support 4GB iova address space, and only support 4K page
--
2.54.0
^ permalink raw reply related
* Re: [PATCH] mtd: nand: mtk-ecc: stop on ECC idle timeouts
From: Miquel Raynal @ 2026-06-29 14:58 UTC (permalink / raw)
To: Richard Weinberger, Vignesh Raghavendra, Matthias Brugger,
AngeloGioacchino Del Regno, Boris Brezillon, Jorge Ramirez-Ortiz,
Pengpeng Hou
Cc: linux-mtd, linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <20260623135729.52304-1-pengpeng@iscas.ac.cn>
On Tue, 23 Jun 2026 21:57:29 +0800, Pengpeng Hou wrote:
> mtk_ecc_wait_idle() logs when the encoder or decoder does not become
> idle, but returns void. Callers can therefore configure a non-idle ECC
> engine or read parity bytes after an unconfirmed encoder idle state.
>
> Return the idle poll result and propagate it from the enable and encode
> paths that require the engine to be idle before continuing.
>
> [...]
Applied to mtd/fixes, thanks!
[1/1] mtd: nand: mtk-ecc: stop on ECC idle timeouts
commit: 16f7ec8d5dc100eafd2c8e06cd30340a30b104a1
Patche(s) should be available on mtd/linux.git and will be
part of the next PR (provided that no robot complains by then).
Kind regards,
Miquèl
^ permalink raw reply
* Re: [PATCH 2/5] media: nxp: imx8-isi: Fix per-stream reference counting for multiplexed streams
From: Frank Li @ 2026-06-29 14:55 UTC (permalink / raw)
To: Guoniu Zhou
Cc: Laurent Pinchart, Mauro Carvalho Chehab, Frank Li, Sascha Hauer,
Pengutronix Kernel Team, Fabio Estevam, Christian Hemp,
Stefan Riedmueller, Jacopo Mondi, Dong Aisheng, Guoniu Zhou,
linux-media, imx, linux-arm-kernel, linux-kernel, stable
In-Reply-To: <20260629-isi-v1-2-deebfdb1b07b@oss.nxp.com>
On Mon, Jun 29, 2026 at 03:44:56PM +0800, Guoniu Zhou wrote:
subject: use per-stream state to fix ...
> The ISI crossbar fails to properly enable multiple streams from different
> virtual channels on the same input pad. Only the first stream gets enabled
> in hardware, subsequent streams are silently ignored.
>
> The driver uses a single enable_count per input to track the input state.
> When enable_count is non-zero, the code assumes the input is already active
> and skips calling v4l2_subdev_enable_streams() for additional streams:
>
> Call 1: enable_streams(stream 0)
> -> enable_count == 0, enable gasket and stream 0 in hardware
> -> enable_count = 1
>
> Call 2: enable_streams(stream 1)
> -> enable_count == 1, skip hardware enable (BUG!)
> -> enable_count = 2
> -> stream 1 never gets enabled
>
> Similarly on disable, when enable_count reaches zero, ALL streams are
> disabled regardless of which streams are actually still active.
>
> Fix this by tracking per-stream state using:
> - enabled_streams (u64 bitmask): tracks which streams are currently enabled
> - enabled_count[] (array): per-stream reference counter to support the same
> stream being enabled/disabled multiple times
>
> Now each stream is independently enabled/disabled in hardware based on the
> enabled_streams bitmask, while enabled_count[] provides reference counting
> for scenarios where the same stream is enabled multiple times, such as
> duplicate cases in the ISI stream.
>
> Fixes: cf21f328fcaf ("media: nxp: Add i.MX8 ISI driver")
> Cc: stable@vger.kernel.org
> Signed-off-by: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
> ---
> .../media/platform/nxp/imx8-isi/imx8-isi-core.h | 4 +-
> .../platform/nxp/imx8-isi/imx8-isi-crossbar.c | 121 +++++++++++++++++----
> 2 files changed, 104 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
> index 7547a6559d4c..bb2cfba27e20 100644
> --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
> +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-core.h
> @@ -185,7 +185,9 @@ struct mxc_isi_dma_buffer {
> };
>
> struct mxc_isi_input {
> - unsigned int enable_count;
> + u64 enabled_streams;
> + /* Counter per stream */
> + unsigned int *enabled_count;
> };
>
> struct mxc_isi_crossbar {
> diff --git a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
> index 29f14d30dbbb..a4a063c60c76 100644
> --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
> +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-crossbar.c
> @@ -345,6 +345,8 @@ static int mxc_isi_crossbar_enable_streams(struct v4l2_subdev *sd,
> struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
> struct v4l2_subdev *remote_sd;
> struct mxc_isi_input *input;
> + u64 streams_to_enable;
> + unsigned long stream;
> u64 sink_streams;
> u32 sink_pad;
> u32 remote_pad;
> @@ -358,30 +360,47 @@ static int mxc_isi_crossbar_enable_streams(struct v4l2_subdev *sd,
>
> input = &xbar->inputs[sink_pad];
>
> - /*
> - * TODO: Track per-stream enable counts to support multiplexed
> - * streams.
> - */
> - if (!input->enable_count) {
> + if (!input->enabled_streams) {
> ret = mxc_isi_crossbar_gasket_enable(xbar, state, remote_sd,
> remote_pad, sink_pad);
> if (ret)
> return ret;
> + }
> +
> + /*
> + * Track per-stream enable counts to support multiplexed streams.
> + * Only enable streams that are not already enabled.
> + */
> + streams_to_enable = sink_streams & ~input->enabled_streams;
>
> + if (streams_to_enable) {
> ret = v4l2_subdev_enable_streams(remote_sd, remote_pad,
> - sink_streams);
> + streams_to_enable);
> if (ret) {
> dev_err(xbar->isi->dev,
> "failed to enable streams 0x%llx on '%s':%u: %d\n",
> - sink_streams, remote_sd->name, remote_pad, ret);
> - mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
> - return ret;
> + streams_to_enable, remote_sd->name, remote_pad, ret);
> + goto err_gasket_disable;
> }
> +
> + input->enabled_streams |= streams_to_enable;
> }
>
> - input->enable_count++;
> + /* Increment reference count for all requested streams */
> + for (stream = 0; stream < xbar->num_sources; stream++) {
> + if (!(sink_streams & BIT(stream)))
> + continue;
> +
> + input->enabled_count[stream]++;
> + }
>
> return 0;
> +
> +err_gasket_disable:
> + if (!input->enabled_streams)
> + mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
> +
> + return ret;
> }
>
> static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd,
> @@ -391,6 +410,8 @@ static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd,
> struct mxc_isi_crossbar *xbar = to_isi_crossbar(sd);
> struct v4l2_subdev *remote_sd;
> struct mxc_isi_input *input;
> + u64 streams_to_disable = 0;
> + unsigned long stream;
> u64 sink_streams;
> u32 sink_pad;
> u32 remote_pad;
> @@ -404,19 +425,36 @@ static int mxc_isi_crossbar_disable_streams(struct v4l2_subdev *sd,
>
> input = &xbar->inputs[sink_pad];
>
> - input->enable_count--;
> + /*
> + * Decrease the enable count for each stream. Only disable streams
> + * whose count reaches zero.
> + */
> + for (stream = 0; stream < xbar->num_sources; stream++) {
> + if (!(sink_streams & BIT(stream)))
> + continue;
>
> - if (!input->enable_count) {
> - ret = v4l2_subdev_disable_streams(remote_sd, remote_pad,
> - sink_streams);
> - if (ret)
> - dev_err(xbar->isi->dev,
> - "failed to disable streams 0x%llx on '%s':%u: %d\n",
> - sink_streams, remote_sd->name, remote_pad, ret);
> + if (!(input->enabled_streams & BIT(stream)))
> + continue;
>
> - mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
> + if (--input->enabled_count[stream] == 0)
> + streams_to_disable |= BIT(stream);
> }
>
> + if (!streams_to_disable)
> + return 0;
> +
> + ret = v4l2_subdev_disable_streams(remote_sd, remote_pad,
> + streams_to_disable);
> + if (ret)
> + dev_err(xbar->isi->dev,
> + "failed to disable streams 0x%llx on '%s':%u: %d\n",
> + streams_to_disable, remote_sd->name, remote_pad, ret);
> +
> + input->enabled_streams &= ~streams_to_disable;
> +
> + if (!input->enabled_streams)
> + mxc_isi_crossbar_gasket_disable(xbar, sink_pad);
> +
> return ret;
> }
>
> @@ -447,6 +485,42 @@ static const struct media_entity_operations mxc_isi_cross_entity_ops = {
> * Init & cleanup
> */
>
> +static int mxc_isi_stream_counters_alloc(struct mxc_isi_crossbar *xbar)
> +{
> + unsigned int i;
> + int ret;
> +
> + for (i = 0; i < xbar->num_sinks; ++i) {
> + struct mxc_isi_input *input = &xbar->inputs[i];
> +
> + input->enabled_count = kcalloc(xbar->num_sources,
> + sizeof(*input->enabled_count),
> + GFP_KERNEL);
kzalloc_objs();
Frank
> + if (!input->enabled_count) {
> + dev_err(xbar->isi->dev,
> + "failed to alloc memory for ISI input(%d)\n", i);
> + ret = -ENOMEM;
> + goto err_free;
> + }
> + }
> +
> + return 0;
> +
> +err_free:
> + while (i--)
> + kfree(xbar->inputs[i].enabled_count);
> +
> + return ret;
> +}
> +
> +static void mxc_isi_stream_counters_free(struct mxc_isi_crossbar *xbar)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < xbar->num_sinks; ++i)
> + kfree(xbar->inputs[i].enabled_count);
> +}
> +
> int mxc_isi_crossbar_init(struct mxc_isi_dev *isi)
> {
> struct mxc_isi_crossbar *xbar = &isi->crossbar;
> @@ -484,6 +558,10 @@ int mxc_isi_crossbar_init(struct mxc_isi_dev *isi)
> goto err_free;
> }
>
> + ret = mxc_isi_stream_counters_alloc(xbar);
> + if (ret)
> + goto err_free;
> +
> for (i = 0; i < xbar->num_sinks; ++i)
> xbar->pads[i].flags = MEDIA_PAD_FL_SINK
> | MEDIA_PAD_FL_MUST_CONNECT;
> @@ -492,7 +570,7 @@ int mxc_isi_crossbar_init(struct mxc_isi_dev *isi)
>
> ret = media_entity_pads_init(&sd->entity, num_pads, xbar->pads);
> if (ret)
> - goto err_free;
> + goto err_free_cnt;
>
> ret = v4l2_subdev_init_finalize(sd);
> if (ret < 0)
> @@ -502,6 +580,8 @@ int mxc_isi_crossbar_init(struct mxc_isi_dev *isi)
>
> err_entity:
> media_entity_cleanup(&sd->entity);
> +err_free_cnt:
> + mxc_isi_stream_counters_free(xbar);
> err_free:
> kfree(xbar->pads);
> kfree(xbar->inputs);
> @@ -512,6 +592,7 @@ int mxc_isi_crossbar_init(struct mxc_isi_dev *isi)
> void mxc_isi_crossbar_cleanup(struct mxc_isi_crossbar *xbar)
> {
> v4l2_subdev_cleanup(&xbar->sd);
> + mxc_isi_stream_counters_free(xbar);
> media_entity_cleanup(&xbar->sd.entity);
> kfree(xbar->pads);
> kfree(xbar->inputs);
>
> --
> 2.34.1
>
>
^ permalink raw reply
* Re: [PATCH v3 2/2] iio: adc: add Axiado SARADC driver
From: David Lechner @ 2026-06-29 14:54 UTC (permalink / raw)
To: Petar Stepanovic, Akhila Kavi, Prasad Bolisetty, Jonathan Cameron,
Nuno Sá, Andy Shevchenko, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Harshit Shah
Cc: linux-iio, devicetree, linux-arm-kernel, linux-kernel
In-Reply-To: <b06005e0-b7bc-4967-ac7b-cb170219f131@axiado.com>
On 6/28/26 9:33 PM, Petar Stepanovic wrote:
>
> On 6/28/2026 1:07 AM, David Lechner wrote:
...
>>> +};
>>> +
>>> +static void axiado_saradc_disable(void *data)
>>> +{
>>> + struct axiado_saradc *info = data;
>>> +
>>> + writel(AX_SARADC_GLOBAL_CTRL_PD, info->regs + AX_SARADC_GLOBAL_CTRL_REG);
>> People usual make read and write wrappers or use regmap to avoid having
>> to write `info->regs + AX_SARADC_GLOBAL_CTRL_REG` so many times.
>
> My understanding is that simple read/write wrappers are not always
> preferred unless they provide additional value. Would switching the
> driver to regmap be acceptable here to avoid repeating the base address
> calculation?
>
Yes, regmap is always nice because it brings a lot of extra features
for free.
^ permalink raw reply
* Re: [PATCH net-next v11 1/7] dt-bindings: phy: document the serdes PHY on sa8255p
From: Geert Uytterhoeven @ 2026-06-29 14:51 UTC (permalink / raw)
To: Bartosz Golaszewski
Cc: Bjorn Andersson, Konrad Dybcio, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Maxime Coquelin, Alexandre Torgue,
Vinod Koul, Giuseppe Cavallaro, Chen-Yu Tsai, Jernej Skrabec,
Neil Armstrong, Kevin Hilman, Jerome Brunet, Shawn Guo,
Fabio Estevam, Jan Petrous, s32, Mohd Ayaan Anwar, Romain Gantois,
Magnus Damm, Maxime Ripard, Christophe Roullier, Radu Rendec,
linux-arm-msm, devicetree, linux-kernel, netdev, linux-stm32,
linux-arm-kernel, Drew Fustini, linux-sunxi, linux-amlogic,
linux-mips, imx, linux-renesas-soc, linux-rockchip, sophgo,
linux-riscv, Bartosz Golaszewski, Bartosz Golaszewski
In-Reply-To: <CAMRc=Me3jaZXiXa1sFXr=8Do4sCd+XN1pKTcWC8-0j78SjCkKA@mail.gmail.com>
Hi Bartosz,
On Mon, 29 Jun 2026 at 16:07, Bartosz Golaszewski <brgl@kernel.org> wrote:
> On Mon, 29 Jun 2026 15:51:31 +0200, Geert Uytterhoeven
> <geert@linux-m68k.org> said:
> > On Mon, 29 Jun 2026 at 13:29, Bartosz Golaszewski
> > <bartosz.golaszewski@oss.qualcomm.com> wrote:
> >> Describe the SGMII/SerDes PHY present on the Qualcomm sa8255p platforms.
> >> This is essentially the same hardware as sa8775p rev3 but the PHY is
> >> managed by firmware over SCMI.
> >
> > So why can't it be reuse the DT bindings, and be compatible with
> > qcom,sa8775p-dwmac-sgmii-phy?
> >
> >> Signed-off-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
> >
> >> --- /dev/null
> >> +++ b/Documentation/devicetree/bindings/phy/qcom,sa8255p-dwmac-sgmii-phy.yaml
> >
> >> + power-domains:
> >> + maxItems: 1
> >> +
> >> + power-domain-names:
> >> + items:
> >> + - const: serdes
> >
> >> +examples:
> >> + - |
> >> + phy@8901000 {
> >> + compatible = "qcom,sa8255p-dwmac-sgmii-phy";
> >> + reg = <0x08901000 0xe10>;
> >> + #phy-cells = <0>;
> >> + power-domains = <&scmi7_dvfs 0>;
> >> + power-domain-names = "serdes";
> >
> > Ah, this uses power-domains, while the existing bindings for
> > qcom,sa8775p-dwmac-sgmii-phy use a clock.
> > I guess the clock is the correct hardware description?
> >
> > Adding to my list of examples for backing a hardware-to-SCMI remapping
> > driver...
> >
>
> Russell King asked me to put the PHY logic for SCMI pm domains into the PHY
> driver instead of the MAC driver where it was previously. Instead of cramming
> both HLOS and firmware handling into the same driver, I figured it makes more
> sense to have a dedicated, cleaner driver as the two share very little code (if
> any).
I think you are mixing up DT bindings and driver implementation?
Gr{oetje,eeting}s,
Geert
--
Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@linux-m68k.org
In personal conversations with technical people, I call myself a hacker. But
when I'm talking to journalists I just say "programmer" or something like that.
-- Linus Torvalds
^ permalink raw reply
* [PATCH 5/5] drm/msm/dp: mark the SST connector disconnected when MST is enabled
From: Yongxing Mou @ 2026-06-29 14:48 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Rob Clark,
Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Sean Paul,
Marijn Suijten, Tomi Valkeinen, Bjorn Andersson, Konrad Dybcio
Cc: dri-devel, linux-kernel, linux-amlogic, linux-arm-kernel,
linux-arm-msm, freedreno, Yongxing Mou
In-Reply-To: <20260629-msm-dp-msttypec-v1-0-646a10256233@oss.qualcomm.com>
When MST becomes active, the initial HPD plug notification updates the
SST connector state to connected.
However, the subsequent SST connector detect path reports disconnected
while MST is enabled. This connected -> disconnected transition is then
observed by the polling logic and may result in an unnecessary hotplug
event.
Set the SST connector state to disconnected immediately after MST is
initialized so that the detect path does not introduce a transient
state change.
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 4ee391cc7165..f8ca60d6d714 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -352,8 +352,10 @@ static int msm_dp_display_process_hpd_high(struct msm_dp_display_private *dp)
if (dp->max_stream > 1 && drm_dp_read_mst_cap(dp->aux, dp->panel->dpcd))
msm_dp_display_mst_init(dp);
- if (dp->msm_dp_display.mst_active)
+ if (dp->msm_dp_display.mst_active) {
+ connector->status = connector_status_disconnected;
msm_dp_mst_display_set_mgr_state(&dp->msm_dp_display, true);
+ }
msm_dp_link_reset_phy_params_vx_px(dp->link);
--
2.43.0
^ permalink raw reply related
* [PATCH 4/5] drm/msm/dp: report IRQ_HPD as an IRQ-only notification
From: Yongxing Mou @ 2026-06-29 14:48 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Rob Clark,
Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Sean Paul,
Marijn Suijten, Tomi Valkeinen, Bjorn Andersson, Konrad Dybcio
Cc: dri-devel, linux-kernel, linux-amlogic, linux-arm-kernel,
linux-arm-msm, freedreno, Yongxing Mou
In-Reply-To: <20260629-msm-dp-msttypec-v1-0-646a10256233@oss.qualcomm.com>
MST reuses the SST connector bridge to propagate HPD IRQ events through
the bridge chain.
For IRQ_HPD notifications there is no connector state transition to
report. Use connector_status_unknown together with
DRM_CONNECTOR_DP_IRQ_HPD so that the bridge connector framework treats
them as IRQ-only notifications and forwards them without modifying
connector state.
The DP driver handles IRQ_HPD events based on
DRM_CONNECTOR_DP_IRQ_HPD rather than connector status transitions.
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/msm/dp/dp_display.c | 22 +++++++++-------------
drivers/soc/qcom/pmic_glink_altmode.c | 14 +++++++++-----
2 files changed, 18 insertions(+), 18 deletions(-)
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index bc93b566fbca..4ee391cc7165 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1119,14 +1119,10 @@ static irqreturn_t msm_dp_display_irq_thread(int irq, void *dev_id)
drm_bridge_hpd_notify(dp->msm_dp_display.bridge,
connector_status_connected);
- /* Send HPD as connected and distinguish it in the notifier */
- if (hpd_isr_status & DP_DP_IRQ_HPD_INT_MASK) {
- if (dp->msm_dp_display.mst_active)
- msm_dp_irq_hpd_handle(dp);
- else
- drm_bridge_hpd_notify(dp->msm_dp_display.bridge,
- connector_status_connected);
- }
+ if (hpd_isr_status & DP_DP_IRQ_HPD_INT_MASK)
+ drm_bridge_hpd_notify_extra(dp->msm_dp_display.bridge,
+ connector_status_unknown,
+ DRM_CONNECTOR_DP_IRQ_HPD);
ret = IRQ_HANDLED;
@@ -1781,11 +1777,11 @@ void msm_dp_bridge_hpd_notify(struct drm_bridge *bridge,
drm_dbg_dp(dp->drm_dev, "type=%d link hpd_link_status=0x%x, status=%d\n",
msm_dp_display->connector_type, hpd_link_status, status);
- if (status == connector_status_connected) {
- if (hpd_link_status == ISR_IRQ_HPD_PULSE_COUNT ||
- extra_status == DRM_CONNECTOR_DP_IRQ_HPD) {
- msm_dp_irq_hpd_handle(dp);
- } else if (hpd_link_status == ISR_HPD_REPLUG_COUNT) {
+ if (extra_status == DRM_CONNECTOR_DP_IRQ_HPD ||
+ hpd_link_status == ISR_IRQ_HPD_PULSE_COUNT) {
+ msm_dp_irq_hpd_handle(dp);
+ } else if (status == connector_status_connected) {
+ if (hpd_link_status == ISR_HPD_REPLUG_COUNT) {
msm_dp_hpd_unplug_handle(dp);
msm_dp_hpd_plug_handle(dp);
} else {
diff --git a/drivers/soc/qcom/pmic_glink_altmode.c b/drivers/soc/qcom/pmic_glink_altmode.c
index 946eb20b8f83..28ab8cbb5ef9 100644
--- a/drivers/soc/qcom/pmic_glink_altmode.c
+++ b/drivers/soc/qcom/pmic_glink_altmode.c
@@ -373,11 +373,15 @@ static void pmic_glink_altmode_worker(struct work_struct *work)
else
conn_status = connector_status_disconnected;
- drm_aux_hpd_bridge_notify_extra(&alt_port->bridge->dev,
- conn_status,
- alt_port->hpd_irq ?
- DRM_CONNECTOR_DP_IRQ_HPD :
- DRM_CONNECTOR_NO_EXTRA_STATUS);
+ if (alt_port->hpd_irq) {
+ drm_aux_hpd_bridge_notify_extra(&alt_port->bridge->dev,
+ connector_status_unknown,
+ DRM_CONNECTOR_DP_IRQ_HPD);
+ } else {
+ drm_aux_hpd_bridge_notify_extra(&alt_port->bridge->dev,
+ conn_status,
+ DRM_CONNECTOR_NO_EXTRA_STATUS);
+ }
} else if (alt_port->mux_ctrl == MUX_CTRL_STATE_TUNNELING) {
if (alt_port->svid == USB_TYPEC_TBT_SID)
pmic_glink_altmode_enable_tbt(altmode, alt_port);
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v15 07/11] arm64: syscall: Introduce syscall_exit_to_user_mode_work()
From: Ada Couprie Diaz @ 2026-06-29 14:48 UTC (permalink / raw)
To: Jinjie Ruan, mark.rutland
Cc: peterz, catalin.marinas, oleg, song, will, kees, thuth,
ryan.roberts, anshuman.khandual, kevin.brodsky, pengcan, broonie,
luto, ldv, linux-arm-kernel, wad, yeoreum.yun, linux-kernel,
james.morse, tglx, liqiang01, linusw
In-Reply-To: <83aed271-5f28-4f30-a800-b94192cb06a6@huawei.com>
On 25/06/2026 10:18, Jinjie Ruan wrote:
> On 6/24/2026 10:37 PM, Ada Couprie Diaz wrote:
>> On 11/05/2026 10:20, Jinjie Ruan wrote:
>>> Refactor the system call exit path to align with the generic entry
>>> framework. This consolidates thread flag checking, rseq handling, and
>>> syscall tracing into a structure that mirrors the generic
>>> syscall_exit_to_user_mode_work() implementation.
>>>
>>> [Rationale]
>>> The generic entry code employs a hierarchical approach for
>>> syscall exit work:
>>>
>>> 1. syscall_exit_to_user_mode_work(): The entry point that handles
>>> rseq and checks if further exit work (tracing/audit) is required.
>>>
>>> 2. syscall_exit_work(): Performs the actual tracing, auditing, and
>>> ptrace reporting.
>>>
>>> [Changes]
>>> - Rename and Encapsulate: Rename syscall_trace_exit() to
>>> syscall_exit_work() and make it static, as it is now an internal
>>> helper for the exit path.
>>>
>>> - New Entry Point: Implement syscall_exit_to_user_mode_work() to
>>> replace the manual flag-reading logic in el0_svc_common(). This
>>> function now encapsulates the rseq_syscall() call and the
>>> conditional execution of syscall_exit_work().
>>>
>>> - Simplify el0_svc_common(): Remove the complex conditional checks
>>> for tracing and CONFIG_DEBUG_RSEQ at the end of the syscall path,
>>> delegating this responsibility to the new helper.
>> It is indeed simpler, however to me there are two changes to the behaviour,
>> which are not called out (apologies if I missed some prior discussion
>> when I looked for some) :
>> 1. As pointed by the removed comment, in mainline we *always* trace on exit
>> if we traced on entry. This is why there are two `has_syscall_work()`
>> checks
>> on exit, with a re-read of the flags after syscall execution in between.
>> This change only checks once on exit after updating the flags, so if
>> there was work on entry but the flags got cleared, it *won't* trace
>> on exit.
>> Is this desired ? Can this change of behaviour have an impact ?
> Hi, Ada,
>
> After rework, `syscall_exit_to_user_mode_work()` will be executed
> unconditionally, regardless of whether the conditions below evaluate to
> true or false. [...]
Hi Jinjie,
Indeed, my worry was about the actual work in
`syscall_exit_to_user_mode_work()`
being gated by a different truth table, but it makes sense now, I will
detail below.
> [...] You can see how this is handled in the finer-grained
> refactoring split which will be shown in v16.
I have seen that you just posted v16, thanks for splitting things up a
bit more !
>
> if (!has_syscall_work(flags) && !IS_ENABLED(CONFIG_DEBUG_RSEQ))
>
>>> - Helper Migration: Move has_syscall_work() to asm/syscall.h
>>> to allow its reuse across ptrace.c and syscall.c.
>>>
>>> - Clean up RSEQ: Remove the explicit IS_ENABLED(CONFIG_DEBUG_RSEQ)
>>> check in the caller, as rseq_syscall() is already a no-op when the
>>> config is disabled.
>> 2. `rseq_syscall()` is indeed a no-op, but removing the explicit check here
>> does change the behaviour : in mainline we *always* trace on exit if
>> `CONFIG_DEBUG_RSEQ` is enabled, bypassing the `has_syscall_work()`
>> check.
>> This change does not bypass the `has_syscall_work()` check if
>> `CONFIG_DEBUG_RSEQ` is enabled, so there might be a change of behaviour.
>> Same questions as above : is this change desired ? Can it have an
>> impact ?
> This should not introduce any functional changes.
>
> Except for "audit", the internal code execution of
> `syscall_trace_exit()` is gated by the "_TIF_SYSCALL_TRACEPOINT,
> _TIF_SYSCALL_TRACE, or _TIF_SINGLESTEP" TIF flags.
>
> And gating audit_syscall_exit() behind `_TIF_SYSCALL_AUDIT` introduces
> no functional changes.
>
> The `SYSCALL_AUDIT` flag and its context are
> statically allocated via audit_alloc() at fork and only freed via
> audit_free() at do_exit(). Since the flag remains persistent and static
> throughout syscall execution, checking the `_TIF_SYSCALL_AUDIT` flag is
> completely equivalent to evaluating audit_context() in
> audit_syscall_exit().
Thank you for the additional details : I did miss that
`audit_syscall_exit()`
did its own check internally for `SYSCALL_WORK_SYSCALL_AUDIT`, which
is part of `SYSCALL_WORK_{ENTRY,EXIT}`, so this indeed does not change
the behaviour.
As you moved `rseq_debug_syscall_return()` outside of `syscall_exit_work()`,
it will indeed always be executed (potentially being no-op),
which removes the need for the removed check and does not change
the actual behaviour, because the rest of `syscall_trace_exit()` used
the exit TIFs as well anyway, as far as I understand.
> I probably moved too fast with this refactoring. I'll split this into
> smaller, more granular steps in v16 to make the logic clearer and easier
> to follow."
There were a bit more subtleties than I expected, so I think it is good
to have split it in more self-contained patches !
>> I understand that the change is to align with the generic entry, but it
>> seems
>> like this could have an impact that I do not really understand, so I prefer
>> asking !
>>
>> Apart from the above everything looks OK to me, but I'd like
>> some confirmation that the change of behaviours either do not exist or
>> are OK !
> Thank you for the review.
>
>> Thanks,
>> Ada
>>
>>> Cc: Mark Rutland <mark.rutland@arm.com>
>>> Cc: Will Deacon <will@kernel.org>
>>> Cc: Catalin Marinas <catalin.marinas@arm.com>
>>> Reviewed-by: Linus Walleij <linusw@kernel.org>
>>> Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
>>> Reviewed-by: Kevin Brodsky <kevin.brodsky@arm.com>
>>> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
>>> ---
>>> v15
>>> - Make syscall_exit_to_user_mode_work() __always_inline to keep
>>> the fast-path performance as Sashiko pointed out.
>>> ---
>>> arch/arm64/include/asm/syscall.h | 18 +++++++++++++++++-
>>> arch/arm64/kernel/ptrace.c | 5 +----
>>> arch/arm64/kernel/syscall.c | 20 +-------------------
>>> 3 files changed, 19 insertions(+), 24 deletions(-)
>>>
>>> diff --git a/arch/arm64/include/asm/syscall.h b/arch/arm64/include/
>>> asm/syscall.h
>>> index 30b203ef156b..b331e09b937f 100644
>>> --- a/arch/arm64/include/asm/syscall.h
>>> +++ b/arch/arm64/include/asm/syscall.h
>>> @@ -8,6 +8,7 @@
>>> #include <uapi/linux/audit.h>
>>> #include <linux/compat.h>
>>> #include <linux/err.h>
>>> +#include <linux/rseq.h>
>>> typedef long (*syscall_fn_t)(const struct pt_regs *regs);
>>> @@ -121,6 +122,21 @@ static inline int syscall_get_arch(struct
>>> task_struct *task)
>>> }
>>> int syscall_trace_enter(struct pt_regs *regs, unsigned long flags);
>>> -void syscall_trace_exit(struct pt_regs *regs, unsigned long flags);
>>> +void syscall_exit_work(struct pt_regs *regs, unsigned long flags);
>>> +
>>> +static inline bool has_syscall_work(unsigned long flags)
>>> +{
>>> + return unlikely(flags & _TIF_SYSCALL_WORK);
>>> +}
>>> +
>>> +static __always_inline void syscall_exit_to_user_mode_work(struct
>>> pt_regs *regs)
>>> +{
>>> + unsigned long flags = read_thread_flags();
>> ^-- This only reflects the post-syscall flags
>>
>>> +
>>> + rseq_syscall(regs);
>>> +
>>> + if (has_syscall_work(flags) || flags & _TIF_SINGLESTEP)
>>> + syscall_exit_work(regs, flags);
>>> +}
>>> #endif /* __ASM_SYSCALL_H */
>>> diff --git a/arch/arm64/kernel/ptrace.c b/arch/arm64/kernel/ptrace.c
>>> index 15a45eeb56da..256aa20377e1 100644
>>> --- a/arch/arm64/kernel/ptrace.c
>>> +++ b/arch/arm64/kernel/ptrace.c
>>> @@ -28,7 +28,6 @@
>>> #include <linux/hw_breakpoint.h>
>>> #include <linux/regset.h>
>>> #include <linux/elf.h>
>>> -#include <linux/rseq.h>
>>> #include <asm/compat.h>
>>> #include <asm/cpufeature.h>
>>> @@ -2454,10 +2453,8 @@ int syscall_trace_enter(struct pt_regs *regs,
>>> unsigned long flags)
>>> return syscall;
>>> }
>>> -void syscall_trace_exit(struct pt_regs *regs, unsigned long flags)
>>> +void syscall_exit_work(struct pt_regs *regs, unsigned long flags)
>>> {
>>> - rseq_syscall(regs);
>>> -
>>> audit_syscall_exit(regs);
>> ^-- This was always called if entry had work or CONFIG_DEBUG_RSEQ
>> was enabled,
>> which is not the case anymore (same for the rest of the function)
> As explained above, thank you!
>
>>> if (flags & _TIF_SYSCALL_TRACEPOINT)
>>> diff --git a/arch/arm64/kernel/syscall.c b/arch/arm64/kernel/syscall.c
>>> index f6f87b042995..dac7bcc4bbdf 100644
>>> --- a/arch/arm64/kernel/syscall.c
>>> +++ b/arch/arm64/kernel/syscall.c
>>> @@ -54,11 +54,6 @@ static void invoke_syscall(struct pt_regs *regs,
>>> unsigned int scno,
>>> syscall_set_return_value(current, regs, 0, ret);
>>> }
>>> -static inline bool has_syscall_work(unsigned long flags)
>>> -{
>>> - return unlikely(flags & _TIF_SYSCALL_WORK);
>>> -}
>>> -
>>> static void el0_svc_common(struct pt_regs *regs, int scno, int sc_nr,
>>> const syscall_fn_t syscall_table[])
>>> {
>>> @@ -120,21 +115,8 @@ static void el0_svc_common(struct pt_regs *regs,
>>> int scno, int sc_nr,
>>> }
>>> invoke_syscall(regs, scno, sc_nr, syscall_table);
>>> -
>>> - /*
>>> - * The tracing status may have changed under our feet, so we have to
>>> - * check again. However, if we were tracing entry, then we always
>>> trace
>>> - * exit regardless, as the old entry assembly did.
>>> - */
>>> - if (!has_syscall_work(flags) && !IS_ENABLED(CONFIG_DEBUG_RSEQ)) {
>> ^-- We always traced exit if CONFIG_DEBUG_RSEQ is
>> enabled
>> ^-- `flags` is unchanged since entry, and exit was always
>> traced if there was work.
> As explained above, thank you!
>
> Best regards,
> Jinjie
Thanks again for the additional details !
Kind regards,
Ada
^ permalink raw reply
* [PATCH 3/5] drm/msm/dp: suppress bridge hotplug events during MST operation
From: Yongxing Mou @ 2026-06-29 14:48 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Rob Clark,
Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Sean Paul,
Marijn Suijten, Tomi Valkeinen, Bjorn Andersson, Konrad Dybcio
Cc: dri-devel, linux-kernel, linux-amlogic, linux-arm-kernel,
linux-arm-msm, freedreno, Yongxing Mou
In-Reply-To: <20260629-msm-dp-msttypec-v1-0-646a10256233@oss.qualcomm.com>
The DP MST framework already generates the required hotplug events for
MST topology changes.
Suppress connector hotplug event generation from the bridge connector
path while MST is active, and continue propagating HPD notifications to
the DP driver.
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 6 ++++--
drivers/gpu/drm/msm/dp/dp_display.c | 9 ++++++++-
2 files changed, 12 insertions(+), 3 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 7334d6677604..82ed0dc450ab 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -162,6 +162,7 @@ static void drm_bridge_connector_handle_hpd(struct drm_bridge_connector *drm_bri
{
struct drm_connector *connector = &drm_bridge_connector->base;
struct drm_device *dev = connector->dev;
+ bool send_hotplug = true;
/*
* IRQ-only notification: extra_status carries the event but
@@ -179,9 +180,10 @@ static void drm_bridge_connector_handle_hpd(struct drm_bridge_connector *drm_bri
connector->status = status;
mutex_unlock(&dev->mode_config.mutex);
- drm_bridge_connector_hpd_notify(connector, status, extra_status, NULL);
+ drm_bridge_connector_hpd_notify(connector, status, extra_status, &send_hotplug);
- drm_kms_helper_connector_hotplug_event(connector);
+ if (send_hotplug)
+ drm_kms_helper_connector_hotplug_event(connector);
}
static void drm_bridge_connector_hpd_cb(void *cb_data,
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index 6835c68fe510..bc93b566fbca 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1790,10 +1790,17 @@ void msm_dp_bridge_hpd_notify(struct drm_bridge *bridge,
msm_dp_hpd_plug_handle(dp);
} else {
msm_dp_hpd_plug_handle(dp);
+ /* mst_active is set in plug_handle; suppress SST hotplug */
+ if (send_hotplug && msm_dp_display->mst_active)
+ *send_hotplug = false;
}
} else {
- if (hpd_link_status == ISR_DISCONNECTED)
+ if (!msm_dp_display->mst_active) {
msm_dp_hpd_unplug_handle(dp);
+ } else if (send_hotplug) {
+ msm_dp_hpd_unplug_handle(dp);
+ *send_hotplug = false;
+ }
}
pm_runtime_put_sync(&msm_dp_display->pdev->dev);
--
2.43.0
^ permalink raw reply related
* [PATCH 2/5] drm/bridge_connector: preserve connector status for IRQ-only HPD events
From: Yongxing Mou @ 2026-06-29 14:48 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Rob Clark,
Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Sean Paul,
Marijn Suijten, Tomi Valkeinen, Bjorn Andersson, Konrad Dybcio
Cc: dri-devel, linux-kernel, linux-amlogic, linux-arm-kernel,
linux-arm-msm, freedreno, Yongxing Mou
In-Reply-To: <20260629-msm-dp-msttypec-v1-0-646a10256233@oss.qualcomm.com>
The bridge connector HPD handling path currently updates
connector->status for every hpd_notify() invocation.
This does not work well for IRQ-only notifications where the event being
reported is carried by extra_status and no connector status transition is
associated with it.
One example is DP MST. HPD IRQs are propagated through
drm_bridge_hpd_notify_*() so that bridge drivers can process the
notification. During MST operation, however, the SST connector attached
to the bridge connector is intentionally kept disconnected while the MST
topology manager handles all connector creation, removal and hotplug
processing.
Updating connector->status for an IRQ-only MST notification may cause
the SST connector state to oscillate between connected and disconnected
depending on the notification path. These artificial state transitions
can later be detected by the polling logic and result in unnecessary
hotplug events being generated. Userspace then re-probes connector
status, potentially triggering the same sequence again.
Treat notifications with status == connector_status_unknown and a valid
extra_status as IRQ-only events. Forward the notification to bridge
drivers without modifying connector->status.
This keeps IRQ delivery working while leaving connector state management
to the component that actually owns it, such as the DP MST topology
framework.
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 12 ++++++++++++
1 file changed, 12 insertions(+)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 5edca47a025f..7334d6677604 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -163,6 +163,18 @@ static void drm_bridge_connector_handle_hpd(struct drm_bridge_connector *drm_bri
struct drm_connector *connector = &drm_bridge_connector->base;
struct drm_device *dev = connector->dev;
+ /*
+ * IRQ-only notification: extra_status carries the event but
+ * status is unknown — do not overwrite connector->status.
+ */
+ if (status == connector_status_unknown &&
+ extra_status != DRM_CONNECTOR_NO_EXTRA_STATUS) {
+ drm_bridge_connector_hpd_notify(connector,
+ connector->status,
+ extra_status, NULL);
+ return;
+ }
+
mutex_lock(&dev->mode_config.mutex);
connector->status = status;
mutex_unlock(&dev->mode_config.mutex);
--
2.43.0
^ permalink raw reply related
* [PATCH 1/5] drm/bridge: allow hpd_notify() to suppress connector hotplug events
From: Yongxing Mou @ 2026-06-29 14:48 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Rob Clark,
Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Sean Paul,
Marijn Suijten, Tomi Valkeinen, Bjorn Andersson, Konrad Dybcio
Cc: dri-devel, linux-kernel, linux-amlogic, linux-arm-kernel,
linux-arm-msm, freedreno, Yongxing Mou
In-Reply-To: <20260629-msm-dp-msttypec-v1-0-646a10256233@oss.qualcomm.com>
The bridge connector framework currently invokes all bridge
hpd_notify() callbacks and unconditionally emits a connector hotplug
event afterwards.
However, not every HPD notification requires a userspace hotplug event.
In particular, DP MST bridges may use hpd_notify() to propagate HPD and
IRQ notifications through the bridge chain while the actual hotplug
handling is performed by the DRM DP MST core. Connector creation,
removal and userspace hotplug events are already managed by the MST
topology framework.
Allow hpd_notify() implementations to suppress the bridge connector
hotplug event by introducing a bool *send_hotplug parameter. Drivers
can clear this flag when HPD processing should not result in a
connector hotplug notification.
A NULL pointer indicates that hotplug suppression is not supported by
the caller, such as the connector detect polling path.
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
drivers/gpu/drm/bridge/lontium-lt9611uxc.c | 3 ++-
drivers/gpu/drm/display/drm_bridge_connector.c | 15 +++++++++------
drivers/gpu/drm/meson/meson_encoder_hdmi.c | 3 ++-
drivers/gpu/drm/msm/dp/dp_display.c | 3 ++-
drivers/gpu/drm/msm/dp/dp_drm.h | 3 ++-
drivers/gpu/drm/omapdrm/dss/hdmi4.c | 3 ++-
include/drm/drm_bridge.h | 3 ++-
7 files changed, 21 insertions(+), 12 deletions(-)
diff --git a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
index 8cb17bd0e238..42e1cadcd3fb 100644
--- a/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
+++ b/drivers/gpu/drm/bridge/lontium-lt9611uxc.c
@@ -430,7 +430,8 @@ static const struct drm_edid *lt9611uxc_bridge_edid_read(struct drm_bridge *brid
static void lt9611uxc_bridge_hpd_notify(struct drm_bridge *bridge,
struct drm_connector *connector,
enum drm_connector_status status,
- enum drm_connector_status_extra extra_status)
+ enum drm_connector_status_extra extra_status,
+ bool *send_hotplug)
{
const struct drm_edid *drm_edid;
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 8f7075fd2aa5..5edca47a025f 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -142,7 +142,8 @@ struct drm_bridge_connector {
static void drm_bridge_connector_hpd_notify(struct drm_connector *connector,
enum drm_connector_status status,
- enum drm_connector_status_extra extra_status)
+ enum drm_connector_status_extra extra_status,
+ bool *send_hotplug)
{
struct drm_bridge_connector *bridge_connector =
to_drm_bridge_connector(connector);
@@ -150,13 +151,14 @@ static void drm_bridge_connector_hpd_notify(struct drm_connector *connector,
/* Notify all bridges in the pipeline of hotplug events. */
drm_for_each_bridge_in_chain_scoped(bridge_connector->encoder, bridge) {
if (bridge->funcs->hpd_notify)
- bridge->funcs->hpd_notify(bridge, connector, status, extra_status);
+ bridge->funcs->hpd_notify(bridge, connector, status,
+ extra_status, send_hotplug);
}
}
static void drm_bridge_connector_handle_hpd(struct drm_bridge_connector *drm_bridge_connector,
- enum drm_connector_status status,
- enum drm_connector_status_extra extra_status)
+ enum drm_connector_status status,
+ enum drm_connector_status_extra extra_status)
{
struct drm_connector *connector = &drm_bridge_connector->base;
struct drm_device *dev = connector->dev;
@@ -165,7 +167,7 @@ static void drm_bridge_connector_handle_hpd(struct drm_bridge_connector *drm_bri
connector->status = status;
mutex_unlock(&dev->mode_config.mutex);
- drm_bridge_connector_hpd_notify(connector, status, extra_status);
+ drm_bridge_connector_hpd_notify(connector, status, extra_status, NULL);
drm_kms_helper_connector_hotplug_event(connector);
}
@@ -227,7 +229,8 @@ drm_bridge_connector_detect(struct drm_connector *connector, bool force)
if (hdmi)
drm_atomic_helper_connector_hdmi_hotplug(connector, status);
- drm_bridge_connector_hpd_notify(connector, status, DRM_CONNECTOR_NO_EXTRA_STATUS);
+ drm_bridge_connector_hpd_notify(connector, status,
+ DRM_CONNECTOR_NO_EXTRA_STATUS, NULL);
} else {
switch (connector->connector_type) {
case DRM_MODE_CONNECTOR_DPI:
diff --git a/drivers/gpu/drm/meson/meson_encoder_hdmi.c b/drivers/gpu/drm/meson/meson_encoder_hdmi.c
index 4aecf0ffcf75..a67e7b365c5b 100644
--- a/drivers/gpu/drm/meson/meson_encoder_hdmi.c
+++ b/drivers/gpu/drm/meson/meson_encoder_hdmi.c
@@ -324,7 +324,8 @@ static int meson_encoder_hdmi_atomic_check(struct drm_bridge *bridge,
static void meson_encoder_hdmi_hpd_notify(struct drm_bridge *bridge,
struct drm_connector *connector,
enum drm_connector_status status,
- enum drm_connector_status_extra extra_status)
+ enum drm_connector_status_extra extra_status,
+ bool *send_hotplug)
{
struct meson_encoder_hdmi *encoder_hdmi = bridge_to_meson_encoder_hdmi(bridge);
diff --git a/drivers/gpu/drm/msm/dp/dp_display.c b/drivers/gpu/drm/msm/dp/dp_display.c
index fcfee26f0078..6835c68fe510 100644
--- a/drivers/gpu/drm/msm/dp/dp_display.c
+++ b/drivers/gpu/drm/msm/dp/dp_display.c
@@ -1763,7 +1763,8 @@ void msm_dp_bridge_hpd_disable(struct drm_bridge *bridge)
void msm_dp_bridge_hpd_notify(struct drm_bridge *bridge,
struct drm_connector *connector,
enum drm_connector_status status,
- enum drm_connector_status_extra extra_status)
+ enum drm_connector_status_extra extra_status,
+ bool *send_hotplug)
{
struct msm_dp_bridge *msm_dp_bridge = to_dp_bridge(bridge);
struct msm_dp *msm_dp_display = msm_dp_bridge->msm_dp_display;
diff --git a/drivers/gpu/drm/msm/dp/dp_drm.h b/drivers/gpu/drm/msm/dp/dp_drm.h
index f6b96c27408a..07ddcd055962 100644
--- a/drivers/gpu/drm/msm/dp/dp_drm.h
+++ b/drivers/gpu/drm/msm/dp/dp_drm.h
@@ -32,6 +32,7 @@ void msm_dp_bridge_hpd_disable(struct drm_bridge *bridge);
void msm_dp_bridge_hpd_notify(struct drm_bridge *bridge,
struct drm_connector *connector,
enum drm_connector_status status,
- enum drm_connector_status_extra extra_status);
+ enum drm_connector_status_extra extra_status,
+ bool *send_hotplug);
#endif /* _DP_DRM_H_ */
diff --git a/drivers/gpu/drm/omapdrm/dss/hdmi4.c b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
index d02d432abde4..ad659cef16f5 100644
--- a/drivers/gpu/drm/omapdrm/dss/hdmi4.c
+++ b/drivers/gpu/drm/omapdrm/dss/hdmi4.c
@@ -430,7 +430,8 @@ static void hdmi4_bridge_disable(struct drm_bridge *bridge,
static void hdmi4_bridge_hpd_notify(struct drm_bridge *bridge,
struct drm_connector *connector,
enum drm_connector_status status,
- enum drm_connector_status_extra extra_status)
+ enum drm_connector_status_extra extra_status,
+ bool *send_hotplug)
{
struct omap_hdmi *hdmi = drm_bridge_to_hdmi(bridge);
diff --git a/include/drm/drm_bridge.h b/include/drm/drm_bridge.h
index 9c4c88024cc5..e6de665ce8f6 100644
--- a/include/drm/drm_bridge.h
+++ b/include/drm/drm_bridge.h
@@ -616,7 +616,8 @@ struct drm_bridge_funcs {
void (*hpd_notify)(struct drm_bridge *bridge,
struct drm_connector *connector,
enum drm_connector_status status,
- enum drm_connector_status_extra extra_status);
+ enum drm_connector_status_extra extra_status,
+ bool *send_hotplug);
/**
* @hpd_enable:
--
2.43.0
^ permalink raw reply related
* [PATCH 0/5] drm/msm/dp: Add MSM Type-C MST support
From: Yongxing Mou @ 2026-06-29 14:48 UTC (permalink / raw)
To: Andrzej Hajda, Neil Armstrong, Robert Foss, Laurent Pinchart,
Jonas Karlman, Jernej Skrabec, Luca Ceresoli, Maarten Lankhorst,
Maxime Ripard, Thomas Zimmermann, David Airlie, Simona Vetter,
Kevin Hilman, Jerome Brunet, Martin Blumenstingl, Rob Clark,
Dmitry Baryshkov, Abhinav Kumar, Jessica Zhang, Sean Paul,
Marijn Suijten, Tomi Valkeinen, Bjorn Andersson, Konrad Dybcio
Cc: dri-devel, linux-kernel, linux-amlogic, linux-arm-kernel,
linux-arm-msm, freedreno, Yongxing Mou
The bridge connector framework currently treats every HPD notification
as a connector state update and emits a userspace hotplug event.
Once MST is enabled, however, connector creation, removal and hotplug
handling are already managed by the DRM DP MST topology manager.
As a result:
- the SST connector may transiently transition between connected and
disconnected during MST initialization
- MST IRQ notifications may generate duplicate userspace hotplug
events, once from the bridge connector layer and once from the MST
topology manager
Introduce explicit support for IRQ-only HPD notifications in the bridge
connector framework and allow bridge drivers to suppress connector
hotplug events when those events are already handled elsewhere.
MSM DP uses these mechanisms to integrate Type-C MST hubs without
generating duplicate or spurious userspace hotplug notifications.
Testing
-------
Tested on Hamoa-EVK with a Type-C MST hub and dual-monitor setup.
Both fbcon and Weston were exercised. No duplicate hotplug events,
connector state oscillation or display flickering were observed after
hub attachment.
Dependencies
------------
This patch series was made on top of:
[1] drm: handle IRQ_HPD events correctly (v4)
https://lore.kernel.org/r/20260608-hpd-irq-events-v4-0-30b62b335487@oss.qualcomm.com
[2] drm/msm/dp: Add MST support for MSM chipsets (v5)
https://patchwork.freedesktop.org/series/142207/#rev5
[3] drm/msm/dp: Prerequisite cleanup for upcoming MST support (v7)
https://lore.kernel.org/r/20260609-dp_mstclean-v7-0-ea04113e8233@oss.qualcomm.com
Signed-off-by: Yongxing Mou <yongxing.mou@oss.qualcomm.com>
---
Yongxing Mou (5):
drm/bridge: allow hpd_notify() to suppress connector hotplug events
drm/bridge_connector: preserve connector status for IRQ-only HPD events
drm/msm/dp: suppress bridge hotplug events during MST operation
drm/msm/dp: report IRQ_HPD as an IRQ-only notification
drm/msm/dp: mark the SST connector disconnected when MST is enabled
drivers/gpu/drm/bridge/lontium-lt9611uxc.c | 3 +-
drivers/gpu/drm/display/drm_bridge_connector.c | 31 ++++++++++++++++-----
drivers/gpu/drm/meson/meson_encoder_hdmi.c | 3 +-
drivers/gpu/drm/msm/dp/dp_display.c | 38 +++++++++++++++-----------
drivers/gpu/drm/msm/dp/dp_drm.h | 3 +-
drivers/gpu/drm/omapdrm/dss/hdmi4.c | 3 +-
drivers/soc/qcom/pmic_glink_altmode.c | 14 ++++++----
include/drm/drm_bridge.h | 3 +-
8 files changed, 65 insertions(+), 33 deletions(-)
---
base-commit: e7d700e14934e68f86338c5610cf2ae76798b663
change-id: 20260629-msm-dp-msttypec-b420df7da587
prerequisite-message-id: <20260608-hpd-irq-events-v4-0-30b62b335487@oss.qualcomm.com>
prerequisite-patch-id: adc6d802a2f71d23c077a5cdb9a257809e9db50e
prerequisite-patch-id: bbd956a3b65a90c1fe9e884bc32c49bd83fd3129
prerequisite-patch-id: 014460a25c380d089764ab4e1a58577ebc1b9e71
prerequisite-patch-id: fc03fb9435b8c60321f9ae28e396a6bff2f347ae
prerequisite-patch-id: cd4cd57a72c798fc95afdfdeb9b81cc7a2dbd25d
prerequisite-patch-id: fe431fb614383eb6444f6bfb928e7e40c2de951e
prerequisite-patch-id: 2a058aa8309016f424b1d4ca19a9b20e92f4d8b6
prerequisite-patch-id: 64c3616bb979b663a01342d1f217588b347ae8bb
prerequisite-change-id: 20260410-msm-dp-mst-35130b6e8b84:v5
prerequisite-patch-id: 1d440cb9fed2bdd66d8de0e1e20475f0fe166973
prerequisite-patch-id: be0f4b80697df7224c80362b161b8a9f0a542184
prerequisite-patch-id: eefa6e6353df301420feae1da704a9db2c2155f2
prerequisite-patch-id: 9e9095f82dd6c131c9f3c1de4fdb8a62bd65ca24
prerequisite-patch-id: 3e635f008f9b56823101abd9253905f078fcb3b5
prerequisite-patch-id: e39e0dc124ed043c7a419610ebe03ad105da27db
prerequisite-patch-id: 945af39213cd4241e1a5929fada04a9286aeb5db
prerequisite-patch-id: 898ae7e4582a6b31492c223e7dd167fb9ce78096
prerequisite-patch-id: 3887553893357c1ffbda99eb010801bc2166cbad
prerequisite-patch-id: 7ccd961fa3c6f925659dee7d7a5bd167c8e7331b
prerequisite-patch-id: be2bf918e0e87ec2ea999927f36bd172c498748e
prerequisite-patch-id: 6aacdabb2dd0536dc04da04f8419ae39e35f8b19
prerequisite-patch-id: a9f27eff8f643ff445810b17d670891928f5b416
prerequisite-patch-id: efd300a2b52715153b8c1c7407db696eb331594b
prerequisite-patch-id: 950abefc4862050ef606404977fd27c5dd2cbb2b
prerequisite-patch-id: c6a9aaba753b5538864f7f6e065d910833baec21
prerequisite-patch-id: c73c1f6eb16b2a6ff11b20495fa0981683bdaaee
prerequisite-patch-id: 94957394b3870ec63ab766d682df592da978dd19
prerequisite-patch-id: f27cba0cf5f08d21f59f29a0c9ed7f197ddfa2c7
prerequisite-patch-id: 683855949a9ed37bc0cc4d1899373e55eac4ddc3
prerequisite-patch-id: 9b3a2b526476c32c8a859824e551e23412674766
prerequisite-patch-id: be866cf2acd4960f31f0dbd05e21f0722dcb70ab
prerequisite-patch-id: 58bd115be590c0d892a72e06794f5b244dbdb7f8
prerequisite-patch-id: 87435a0f6827516f4e2b5d8471a2b289bb73a88c
prerequisite-patch-id: 1064db7111fc77377dbf246eb0fdef90c18c46ee
prerequisite-patch-id: e112aaf6088f2bfa90bc67feaac86a4fc1ad23ca
prerequisite-patch-id: fcc0f2ee6dc0358d62593c1295d26a013fa11223
prerequisite-patch-id: 66364b8806fc6abeabe1a0b871e4e8c841ce2aa7
prerequisite-patch-id: 243046f52a14b416caead3469d580ea5b029f9bb
prerequisite-patch-id: efd8014f647a6aa5fadc9d62a6e1920d76a6c80f
prerequisite-patch-id: f4dbd5ae84a01ea89c7d00ca39fd76cd247bc353
prerequisite-patch-id: 30a16a45edefc8769b10d90a7807b6522cd31f15
prerequisite-patch-id: e12fd2908ca33de1f1265fd40190eaad8637e569
prerequisite-patch-id: 3baac076fdda664d00aa9a83481f76ec38c07e8a
prerequisite-patch-id: ee6a93ffa2d5461ee7b07929ff21626a14773b7a
prerequisite-patch-id: c8d444e2a6512f106da2675d4a42a92208d5c6f1
prerequisite-patch-id: 0491a69feb036cfa2e75401e093ebad387cf2846
prerequisite-patch-id: 707e7a3e5114c86f5bcc0b36e7cc8beb0c957780
prerequisite-patch-id: b1ae90d73bd7c3d19cfe4371b5dc9a816f1316a6
prerequisite-patch-id: 4c183b8ffd599169c8d3c5f3aad5ecc467b150f2
prerequisite-message-id: <20260609-dp_mstclean-v7-0-ea04113e8233@oss.qualcomm.com>
prerequisite-patch-id: 1d440cb9fed2bdd66d8de0e1e20475f0fe166973
prerequisite-patch-id: be0f4b80697df7224c80362b161b8a9f0a542184
prerequisite-patch-id: eefa6e6353df301420feae1da704a9db2c2155f2
prerequisite-patch-id: 9e9095f82dd6c131c9f3c1de4fdb8a62bd65ca24
prerequisite-patch-id: 3e635f008f9b56823101abd9253905f078fcb3b5
prerequisite-patch-id: e39e0dc124ed043c7a419610ebe03ad105da27db
prerequisite-patch-id: 945af39213cd4241e1a5929fada04a9286aeb5db
prerequisite-patch-id: 898ae7e4582a6b31492c223e7dd167fb9ce78096
prerequisite-patch-id: 3887553893357c1ffbda99eb010801bc2166cbad
prerequisite-patch-id: 7ccd961fa3c6f925659dee7d7a5bd167c8e7331b
prerequisite-patch-id: be2bf918e0e87ec2ea999927f36bd172c498748e
prerequisite-patch-id: 6aacdabb2dd0536dc04da04f8419ae39e35f8b19
prerequisite-patch-id: a9f27eff8f643ff445810b17d670891928f5b416
prerequisite-patch-id: efd300a2b52715153b8c1c7407db696eb331594b
prerequisite-patch-id: 950abefc4862050ef606404977fd27c5dd2cbb2b
Best regards,
--
Yongxing Mou <yongxing.mou@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH] ASoC: meson: aiu: fifo-spdif: soft reset the S/PDIF datapath on start/stop
From: Christian Hewitt @ 2026-06-29 14:47 UTC (permalink / raw)
To: Jerome Brunet
Cc: Martin Blumenstingl, Liam Girdwood, Mark Brown, Jaroslav Kysela,
Takashi Iwai, Neil Armstrong, Kevin Hilman, linux-sound,
linux-arm-kernel, linux-amlogic, linux-kernel
In-Reply-To: <1jwlvha6xv.fsf@starbuckisacylon.baylibre.com>
> On 29 Jun 2026, at 4:04 pm, Jerome Brunet <jbrunet@baylibre.com> wrote:
>
> On ven. 26 juin 2026 at 22:41, Martin Blumenstingl <martin.blumenstingl@googlemail.com> wrote:
>
>> On Fri, Jun 26, 2026 at 10:04 AM Christian Hewitt
>> <christianshewitt@gmail.com> wrote:
>>>
>>> The I2S FIFO soft-resets its fast domain on start (AIU_RST_SOFT bit 0 +
>>> AIU_I2S_SYNC read in aiu_fifo_i2s_trigger), mirroring the downstream
>>> vendor driver's audio_out_i2s_enable(). The S/PDIF FIFO has no equivalent:
>>> it only toggles the IEC958 DCU, so a stale datapath FIFO can be replayed,
>>> producing the "machine gun noise" buffer underrun - on start when switching
>>> outputs, and on stop when playback ends. The latter is audible on devices
>>> with an always-on S/PDIF-fed DAC (e.g. the ES7144 on the WeTek Play2).
>>>
>>> The vendor driver resets the IEC958 fast domain (AIU_RST_SOFT bit 2) on
>>> both enable and disable (audio_hw_958_enable), and when reconfiguring
>>> (audio_hw_958_reset clears AIU_958_DCU_FF_CTRL then resets). Do the same:
>>> reset before enabling the DCU on start, and after disabling it on stop.
>>>
>>> Signed-off-by: Christian Hewitt <christianshewitt@gmail.com>
>> Reviewed-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
>>
>> This matches the vendor driver, references:
>> - fast-reset SPDIF is triggered on enable and disable: [0]
>> - fast-reset SPDIF is triggered after all of the configuration is
>> written, then DCU_FF_CTRL is enabled: [1]
>
> Take what the vendor driver does with a grain of salt, especially when
> it comes to audio
>
>>
>> [...]
>>> case SNDRV_PCM_TRIGGER_SUSPEND:
>>> case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
>>> case SNDRV_PCM_TRIGGER_STOP:
>>> fifo_spdif_dcu_enable(component, false);
>>> + snd_soc_component_write(component, AIU_RST_SOFT,
>>> + AIU_RST_SOFT_958_FAST);
>> It doesn't seem to make any difference, so just for the record:
>> The vendor driver first triggers AIU_RST_SOFT_958_FAST then disables DCU: [2]
>>
>
> One could even wonder if there is any point in applying a reset when
> everything is stopped, if the enable path will apply this same
> reset before anything else is started again ? Does it really changes
> anything to the reported issue ?
Using disable/reset or reset/disable didn’t make any difference in my
testing, but without any reset playback stop on the WeTek Play2 board
with hardwired DAC always triggers the buffer underrun.
CH.
^ permalink raw reply
* Re: [PATCH 05/37] drm/display: bridge-connector: split code creating the connector to a subfunction
From: Laurent Pinchart @ 2026-06-29 14:44 UTC (permalink / raw)
To: Luca Ceresoli
Cc: Maxime Ripard, Maarten Lankhorst, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Jonas Karlman, Jernej Skrabec, Inki Dae, Jagan Teki,
Marek Szyprowski, Marek Vasut, Stefan Agner, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam, Hui Pu,
Ian Ray, Thomas Petazzoni, dri-devel, linux-kernel, imx,
linux-arm-kernel
In-Reply-To: <DJJ4X22CJ3FG.5EYF84LVRNOG@bootlin.com>
On Fri, Jun 26, 2026 at 06:51:14PM +0200, Luca Ceresoli wrote:
> On Fri Jun 26, 2026 at 4:38 PM CEST, Maxime Ripard wrote:
> > On Fri, Jun 26, 2026 at 04:16:39PM +0200, Luca Ceresoli wrote:
> >> On Fri Jun 26, 2026 at 12:09 PM CEST, Maxime Ripard wrote:
> >> > On Wed, Jun 24, 2026 at 05:47:10PM +0200, Luca Ceresoli wrote:
> >> >> On Wed Jun 24, 2026 at 1:41 PM CEST, Maxime Ripard wrote:
> >> >> > On Fri, Jun 12, 2026 at 02:56:24PM +0200, Luca Ceresoli wrote:
> >> >> >> On Mon Jun 8, 2026 at 1:40 PM CEST, Maxime Ripard wrote:
> >> >> >> > On Tue, May 19, 2026 at 12:37:22PM +0200, Luca Ceresoli wrote:
> >> >> >> >> In preparation to introduce bridge hotplug, split out from
> >> >> >> >> drm_bridge_connector_init() the code adding the drm_connector into a
> >> >> >> >> dedicated function. This will be needed to be able to add (and re-add) the
> >> >> >> >> connector from different code paths.
> >> >> >> >
> >> >> >> > Same story here, explaining what you need later on that calls for that
> >> >> >> > change would be nice.
> >> >> >>
> >> >> >> Here's a more verbose version:
> >> >> >>
> >> >> >> Currently drm_bridge_connector_init() does two things:
> >> >> >>
> >> >> >> * allocate and initialize the drm_bridge_connector
> >> >> >> (which embeds a drm_connector)
> >> >> >> * initialize and register the embedded drm_connector
> >> >> >>
> >> >> >> For bridge hotplug we need to separate these two actions:
> >> >> >>
> >> >> >> * the drm_connector needs to be added and removed at any time based on
> >> >> >> hotplug events
> >> >> >> * the drm_bridge_connector is designated to create and remove the
> >> >> >> drm_connector, so it must be persistent for the card lifetime
> >> >> >>
> >> >> >> As the lifetimes of drm_bridge_connector and drm_connector become
> >> >> >> different, we need to create them in different moments.
> >> >> >>
> >> >> >> In preparation to support that, split out from
> >> >> >> drm_bridge_connector_init() the code adding the drm_connector into a
> >> >> >> dedicated function. No functional changes, just moving code around for
> >> >> >> now. A future commit will make the drm_connector be created based on
> >> >> >> hotplug events.
> >> >> >>
> >> >> >> Looks good?
> >> >> >
> >> >> > The message itself, yes, thanks.
> >> >> >
> >> >> > However, I have questions now :)
> >> >> >
> >> >> > Do we really expect drm_bridge_connector to stick around when a bridge
> >> >> > gets unplugged? If so, how does it cope with having, say, an HDMI
> >> >> > connector, and then swapping out the hotplugged part for an LVDS one?
> >> >> > Does the HDMI connector sticks around indefinitely?
> >> >>
> >> >> In your example, the HDMI drm_connector would be unregistered and put on
> >> >> hotunplug. Its allocation will stick around until the last put but that's
> >> >> quite irrelevant. Then, on plugging the LVDS addon, a new LVDS
> >> >> drm_connector will be created and registered.
> >> >>
> >> >> > *Especially* if we're using overlays for this, I'd expect everything
> >> >> > after the first hotplugged bridge to be destroyed, no?
> >> >>
> >> >> As said, it would be unregistered immediately but might be freed later on
> >> >> if still refcounted.
> >> >>
> >> >> This is visible in patches 36+15, the path to follow is:
> >> >>
> >> >> drm_bridge_connector_handle_event(event = DRM_BRIDGE_DETACHED) [patch 36]
> >> >> -> drm_bridge_connector_dynconn_release() [patch 15]
> >> >>
> >> >> Does this solve your concern?
> >> >
> >> > Not really, I'm talking about drm_bridge_connector. The fact that
> >> > bridges are destroyed make sense to me. The fact that
> >> > drm_bridge_connector sticks around doesn't. It's supposed to be a
> >> > connector for bridges. If you don't have bridges because they got
> >> > destroyed, and connector, drm_bridge_connector doesn't have a reason to
> >> > exist anymore, unless it's drm_bridge_hotplug in a trench coat :)
> >>
> >> It is not a hotplug-bridge in a trench coat, no :) The code is clear about
> >> this.
> >>
> >> I'd say with this series a "drm_bridge_connector" is just becoming
> >> something more (perhaps something else too). Somewhat as "a drm_bridge is
> >> either a bridge or something else". :)
> >>
> >>
> >> But let's leave names aside for a moment. If just looking at the current
> >> code, the drm_bridge_connector is "a handler, owned by the card/encoder and
> >> having the same lifetime, which takes care of drm_connector
> >> creation/destruction at card probe/removal".
> >>
> >> What we need now is just the same plug " and on hotplug events" appended.
> >>
> >> So in both cases there needs to be "a handler persitent with the card".
> >>
> >> Do we agree so far?
> >
> > Ish. If we go for that, then we need to update the name.
>
> drm_connector_manager?
> drm_bridge_connector_manager?
I'm fine with a rename. When developing drm_bridge_connector I've always
envisioned it as code that manages the creation of a connector for a
chain of bridges. In particular, the drm_bridge_connector object is
*not* and has never been a bridge.
Ideally all this should move to the DRM core and be transparent to
drivers. Drivers could set a flag somewhere to opt-in for connectors
managed by the DRM core.
> > drm_bridge_connector for something that is neither a bridge or a
> > connector is not great.
> >
> > But even then, I'm not even sure why we need to have that "manager" in
> > the first place. You want to make bridges be aware if they are the last
> > in the chain or not.
I don't think bridges should be aware of whether or not they're the last
one in the chain.
> > Use that property in attach to either create a
> > drm_bridge_connector instance if you're last, or attach the next bridge
> > if you aren't.
>
> What? o_O
>
> Several encoder drivers have been painfully converted to create a
> drm_bridge_connector. Now if the bridges start doing it themselves we
> should go back to those encoder drivers and ditch all the
> drm_bridge_connector from there?
>
> I must be missing something. Can you elaborate on this?
--
Regards,
Laurent Pinchart
^ permalink raw reply
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