* Re: [PATCH] [RFC] gpiolib: introduce gpio_name() helper
From: Andy Shevchenko @ 2026-06-29 15:23 UTC (permalink / raw)
To: Arnd Bergmann
Cc: Linus Walleij, Bartosz Golaszewski, Arnd Bergmann,
Marcel Holtmann, MyungJoo Ham, Chanwoo Choi, Geert Uytterhoeven,
Andy Shevchenko, Dmitry Torokhov, Ulf Hansson, linux-bluetooth,
linux-kernel, linux-gpio, dri-devel, linux-i2c, linux-iio,
linux-input, linux-mmc, linux-arm-kernel, linux-pm, linux-usb
In-Reply-To: <20260629135917.1308621-1-arnd@kernel.org>
On Mon, Jun 29, 2026 at 03:56:29PM +0200, Arnd Bergmann wrote:
> Most remaining users of desc_to_gpio() only call it for printing debug
> information.
>
> Replace this with a new gpiod_name() helper that returns the
> gpio_desc->name string after checking the gpio_desc pointer.
Oh, that's nice!
...
> +/**
> + * gpiod_name() - get a name to print for a gpio descriptor
> + * @desc: gpio or NULL pointer to query
> + *
> + * Returns:
> + * The desc->name field or a dummy string for unknown GPIOs.
> + */
> +const char *gpiod_name(const struct gpio_desc *desc)
> +{
> + return desc ? desc->name : "(no gpio)";
Can we get into here with wrong (error pointer descriptor)? Shouldn't you call
one of validate_desc() / VALIDATE_DESC()?
Also not sure if "(no gpio)" is a good choice. "not requested"? "not provided"?
> +}
...
> +static inline const char *gpiod_name(const struct gpio_desc *desc)
> +{
> + WARN_ON(desc);
> + return "(no gpio)";
Hmm... This will be a second copy with a slight potential of going apart from
the other case. Perhaps a #define? (Yes, yes, I understand that there are pros
and cons, in particular readability with define is questionable.)
> +}
--
With Best Regards,
Andy Shevchenko
^ permalink raw reply
* Re: [PATCH rc v6 4/7] iommu/arm-smmu-v3: Skip EVTQ/PRIQ setup in kdump kernel
From: Pranjal Shrivastava @ 2026-06-29 15:15 UTC (permalink / raw)
To: Nicolin Chen
Cc: will, robin.murphy, jgg, joro, kees, baolu.lu, kevin.tian,
miko.lenczewski, smostafa, linux-arm-kernel, iommu, linux-kernel,
stable, jamien
In-Reply-To: <1280ac4fdb37f998fd6dcb2bf8f4437283279395.1779265413.git.nicolinc@nvidia.com>
On Wed, May 20, 2026 at 10:03:21AM -0700, Nicolin Chen wrote:
> In kdump cases, the crashed kernel's CDs and page tables can be corrupted,
> which could trigger event spamming. Also, we cannot serve page requests.
>
> Skip the EVTQ/PRIQ setup entirely rather than enabling then disabling them.
>
> Also add some inline comments explaining that.
>
> Fixes: b63b3439b856 ("iommu/arm-smmu-v3: Abort all transactions if SMMU is enabled in kdump kernel")
> Cc: stable@vger.kernel.org # v6.12+
> Suggested-by: Kevin Tian <kevin.tian@intel.com>
> Reviewed-by: Kevin Tian <kevin.tian@intel.com>
> Reviewed-by: Jason Gunthorpe <jgg@nvidia.com>
> Signed-off-by: Nicolin Chen <nicolinc@nvidia.com>
> ---
> drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c | 43 +++++++++++++--------
> 1 file changed, 27 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
> index e00b28e36f9c4..3f22949391c82 100644
> --- a/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
> +++ b/drivers/iommu/arm/arm-smmu-v3/arm-smmu-v3.c
> @@ -5161,21 +5161,35 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
> cmd.opcode = CMDQ_OP_TLBI_NSNH_ALL;
> arm_smmu_cmdq_issue_cmd_with_sync(smmu, &cmd);
>
> - /* Event queue */
> - writeq_relaxed(smmu->evtq.q.q_base, smmu->base + ARM_SMMU_EVTQ_BASE);
> - writel_relaxed(smmu->evtq.q.llq.prod, smmu->page1 + ARM_SMMU_EVTQ_PROD);
> - writel_relaxed(smmu->evtq.q.llq.cons, smmu->page1 + ARM_SMMU_EVTQ_CONS);
> -
> - enables |= CR0_EVTQEN;
> - ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
> - ARM_SMMU_CR0ACK);
> - if (ret) {
> - dev_err(smmu->dev, "failed to enable event queue\n");
> - return ret;
> + /*
> + * Event queue
> + *
> + * Do not enable in a kdump case, as the crashed kernel's CDs and page
> + * tables might be corrupted, triggering event spamming.
> + */
> + if (!is_kdump_kernel()) {
> + writeq_relaxed(smmu->evtq.q.q_base,
> + smmu->base + ARM_SMMU_EVTQ_BASE);
> + writel_relaxed(smmu->evtq.q.llq.prod,
> + smmu->page1 + ARM_SMMU_EVTQ_PROD);
> + writel_relaxed(smmu->evtq.q.llq.cons,
> + smmu->page1 + ARM_SMMU_EVTQ_CONS);
> +
> + enables |= CR0_EVTQEN;
> + ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
> + ARM_SMMU_CR0ACK);
Nit:
I believe only the write_reg_sync(CR0) should be under this if condition
do we see any weird behavior if we perform the reg writes in
kdump_kernel?
> + if (ret) {
> + dev_err(smmu->dev, "failed to enable event queue\n");
> + return ret;
> + }
> }
>
> - /* PRI queue */
> - if (smmu->features & ARM_SMMU_FEAT_PRI) {
> + /*
> + * PRI queue
> + *
> + * Do not enable in a kdump case, as we cannot serve page requests.
> + */
> + if (!is_kdump_kernel() && (smmu->features & ARM_SMMU_FEAT_PRI)) {
> writeq_relaxed(smmu->priq.q.q_base,
> smmu->base + ARM_SMMU_PRIQ_BASE);
> writel_relaxed(smmu->priq.q.llq.prod,
> @@ -5208,9 +5222,6 @@ static int arm_smmu_device_reset(struct arm_smmu_device *smmu)
> return ret;
> }
>
> - if (is_kdump_kernel())
> - enables &= ~(CR0_EVTQEN | CR0_PRIQEN);
> -
> /* Enable the SMMU interface */
> enables |= CR0_SMMUEN;
> ret = arm_smmu_write_reg_sync(smmu, enables, ARM_SMMU_CR0,
> --
> 2.43.0
>
Apart from that nit,
Reviewed-by: Pranjal Shrivastava <praan@google.com>
Thanks,
Praan
^ permalink raw reply
* Re: [PATCH 5/5] media: nxp: imx8-isi: Add additional 32-bit RGB format support
From: Frank Li @ 2026-06-29 15:10 UTC (permalink / raw)
To: Laurent Pinchart
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, Robert Chiras
In-Reply-To: <20260629-isi-v1-5-deebfdb1b07b@oss.nxp.com>
On Mon, Jun 29, 2026 at 03:44:59PM +0800, Guoniu Zhou wrote:
> Add support for additional 32-bit RGB pixel formats (BGRA32, RGBA32,
> BGRX32, RGBX32, ARGB2101010) and extend existing ABGR32 format with
> full memory-to-memory capabilities to meet Android requirements.
>
> All formats support capture, M2M input, and M2M output operations,
> enabling complete format conversion pipelines.
>
> Signed-off-by: Guoniu Zhou <guoniu.zhou@oss.nxp.com>
> Signed-off-by: Robert Chiras <robert.chiras@nxp.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> .../media/platform/nxp/imx8-isi/imx8-isi-video.c | 59 +++++++++++++++++++++-
> 1 file changed, 58 insertions(+), 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 05b51b98344b..ef638af350fe 100644
> --- a/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
> +++ b/drivers/media/platform/nxp/imx8-isi/imx8-isi-video.c
> @@ -160,12 +160,69 @@ static const struct mxc_isi_format_info mxc_isi_formats[] = {
> }, {
> .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
> .fourcc = V4L2_PIX_FMT_ABGR32,
> - .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_CAP,
> + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT
> + | MXC_ISI_VIDEO_M2M_CAP,
> + .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_XRGB8,
> .isi_out_format = CHNL_IMG_CTRL_FORMAT_ARGB8888,
> .mem_planes = 1,
> .color_planes = 1,
> .depth = { 32 },
> .encoding = MXC_ISI_ENC_RGB,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
> + .fourcc = V4L2_PIX_FMT_BGRA32,
> + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT
> + | MXC_ISI_VIDEO_M2M_CAP,
> + .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_RGBX8,
> + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RGBA8888,
> + .mem_planes = 1,
> + .color_planes = 1,
> + .depth = { 32 },
> + .encoding = MXC_ISI_ENC_RGB,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
> + .fourcc = V4L2_PIX_FMT_RGBA32,
> + .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_out_format = CHNL_IMG_CTRL_FORMAT_ABGR8888,
> + .mem_planes = 1,
> + .color_planes = 1,
> + .depth = { 32 },
> + .encoding = MXC_ISI_ENC_RGB,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
> + .fourcc = V4L2_PIX_FMT_BGRX32,
> + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT
> + | MXC_ISI_VIDEO_M2M_CAP,
> + .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_RGBX8,
> + .isi_out_format = CHNL_IMG_CTRL_FORMAT_RGBX888,
> + .mem_planes = 1,
> + .color_planes = 1,
> + .depth = { 32 },
> + .encoding = MXC_ISI_ENC_RGB,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
> + .fourcc = V4L2_PIX_FMT_RGBX32,
> + .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_out_format = CHNL_IMG_CTRL_FORMAT_XBGR888,
> + .mem_planes = 1,
> + .color_planes = 1,
> + .depth = { 32 },
> + .encoding = MXC_ISI_ENC_RGB,
> + }, {
> + .mbus_code = MEDIA_BUS_FMT_RGB888_1X24,
> + .fourcc = V4L2_PIX_FMT_ARGB2101010,
> + .type = MXC_ISI_VIDEO_CAP | MXC_ISI_VIDEO_M2M_OUT
> + | MXC_ISI_VIDEO_M2M_CAP,
> + .isi_in_format = CHNL_MEM_RD_CTRL_IMG_TYPE_A2RGB10,
> + .isi_out_format = CHNL_IMG_CTRL_FORMAT_A2RGB10,
> + .mem_planes = 1,
> + .color_planes = 1,
> + .depth = { 32 },
> + .encoding = MXC_ISI_ENC_RGB,
> },
> /*
> * RAW formats
>
> --
> 2.34.1
>
>
^ permalink raw reply
* 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
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