* [PATCH 4/5] arm64: dts: freescale: imx93-phyboard-nash: Add gpio-line-names
From: Florijan Plohl @ 2026-04-02 10:56 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, upstream
In-Reply-To: <20260402105613.1303871-1-florijan.plohl@norik.com>
Add gpio-line-names for GPIOs with a defined board-level
function on the PHYTEC phyBOARD-Nash-i.MX93.
Signed-off-by: Florijan Plohl <florijan.plohl@norik.com>
---
.../dts/freescale/imx93-phyboard-nash.dts | 31 +++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/imx93-phyboard-nash.dts b/arch/arm64/boot/dts/freescale/imx93-phyboard-nash.dts
index eac389ed30f3..b82192f25498 100644
--- a/arch/arm64/boot/dts/freescale/imx93-phyboard-nash.dts
+++ b/arch/arm64/boot/dts/freescale/imx93-phyboard-nash.dts
@@ -141,6 +141,37 @@ &flexcan1 {
status = "okay";
};
+&gpio1 {
+ gpio-line-names = "", "USER_LED", "I2C2_SCL", "I2C2_SDA";
+};
+
+&gpio2 {
+ gpio-line-names = "SPI6_CS0", "", "", "", "",
+ "", "", "", "", "",
+ "", "", "", "", "",
+ "", "", "TPM_nIRQ", "", "",
+ "", "", "", "", "",
+ "", "", "", "I2C3_SDA", "I2C3_SCL";
+};
+
+&gpio3 {
+ gpio-line-names = "SD2_nCD", "", "", "", "",
+ "", "", "SD2_nRESET", "", "",
+ "", "", "", "", "",
+ "", "", "", "", "",
+ "", "", "", "", "",
+ "", "nENET1_INT";
+};
+
+&gpio4 {
+ gpio-line-names = "", "", "", "", "",
+ "", "", "", "", "",
+ "", "", "", "", "",
+ "", "nCAN_EN", "", "", "",
+ "", "", "", "RESET_PHY", "",
+ "", "RTC_nINT", "PMIC_IRQ_B";
+};
+
/* I2C2 */
&lpi2c2 {
clock-frequency = <400000>;
--
2.43.0
^ permalink raw reply related
* [PATCH 1/5] arm64: dts: freescale: imx91-phycore-som: Add gpio-line-names
From: Florijan Plohl @ 2026-04-02 10:56 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Frank Li,
Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam
Cc: devicetree, imx, linux-arm-kernel, linux-kernel, upstream
Add gpio-line-names for GPIOs with a defined board-level
function on the PHYTEC phyCORE-i.MX91 SoM.
Signed-off-by: Florijan Plohl <florijan.plohl@norik.com>
---
.../boot/dts/freescale/imx91-phycore-som.dtsi | 22 +++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/arch/arm64/boot/dts/freescale/imx91-phycore-som.dtsi b/arch/arm64/boot/dts/freescale/imx91-phycore-som.dtsi
index 29a428a052b0..b9a453f6b290 100644
--- a/arch/arm64/boot/dts/freescale/imx91-phycore-som.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx91-phycore-som.dtsi
@@ -88,6 +88,28 @@ ethphy1: ethernet-phy@1 {
};
};
+&gpio1 {
+ gpio-line-names = "", "USER_LED";
+};
+
+&gpio2 {
+ gpio-line-names = "", "", "", "", "",
+ "", "", "", "", "",
+ "", "", "", "", "",
+ "", "", "", "", "",
+ "", "", "", "", "",
+ "", "", "", "I2C3_SDA", "I2C3_SCL";
+};
+
+&gpio4 {
+ gpio-line-names = "", "", "", "", "",
+ "", "", "", "", "",
+ "", "", "", "", "",
+ "", "", "", "", "",
+ "", "", "", "RESET_PHY", "",
+ "", "", "PMIC_IRQ_B";
+};
+
/* I2C3 */
&lpi2c3 {
clock-frequency = <400000>;
--
2.43.0
^ permalink raw reply related
* Re: [PATCH v5 3/3] arm64,ppc64le/kdump: pass dm-crypt keys to kdump kernel
From: Sourabh Jain @ 2026-04-02 10:54 UTC (permalink / raw)
To: Coiby Xu, kexec, linux-arm-kernel, linuxppc-dev, devicetree
Cc: Arnaud Lefebvre, Baoquan he, Dave Young, Kairui Song, Pingfan Liu,
Andrew Morton, Krzysztof Kozlowski, Rob Herring, Thomas Staudt,
Will Deacon, Christophe Leroy (CS GROUP), Catalin Marinas,
Madhavan Srinivasan, Michael Ellerman, Nicholas Piggin,
Saravana Kannan, open list
In-Reply-To: <20260225060347.718905-4-coxu@redhat.com>
On 25/02/26 11:33, Coiby Xu wrote:
> CONFIG_CRASH_DM_CRYPT has been introduced to support LUKS-encrypted
> device dump target by addressing two challenges [1],
> - Kdump kernel may not be able to decrypt the LUKS partition. For some
> machines, a system administrator may not have a chance to enter the
> password to decrypt the device in kdump initramfs after the 1st kernel
> crashes
>
> - LUKS2 by default use the memory-hard Argon2 key derivation function
> which is quite memory-consuming compared to the limited memory reserved
> for kdump.
>
> To also enable this feature for ARM64 and PowerPC, the missing piece is
> to let the kdump kernel know where to find the dm-crypt keys which are
> randomly stored in memory reserved for kdump. Introduce a new device
> tree property dmcryptkeys [2] as similar to elfcorehdr to pass the
> memory address of the stored info of dm-crypt keys to the kdump kernel.
> Since this property is only needed by the kdump kernel, it won't be
> exposed to user space.
>
> [1] https://lore.kernel.org/all/20250502011246.99238-1-coxu@redhat.com/
> [2] https://github.com/devicetree-org/dt-schema/pull/181
>
> Cc: Arnaud Lefebvre <arnaud.lefebvre@clever-cloud.com>
> Cc: Baoquan he <bhe@redhat.com>
> Cc: Dave Young <dyoung@redhat.com>
> Cc: Kairui Song <ryncsn@gmail.com>
> Cc: Pingfan Liu <kernelfans@gmail.com>
> Cc: Andrew Morton <akpm@linux-foundation.org>
> Cc: Krzysztof Kozlowski <krzk@kernel.org>
> Cc: Rob Herring <robh@kernel.org>
> Cc: Thomas Staudt <tstaudt@de.ibm.com>
> Cc: Sourabh Jain <sourabhjain@linux.ibm.com>
> Cc: Will Deacon <will@kernel.org>
> Cc: Christophe Leroy (CS GROUP) <chleroy@kernel.org>
> Signed-off-by: Coiby Xu <coxu@redhat.com>
> ---
> arch/arm64/kernel/machine_kexec_file.c | 4 ++++
> arch/powerpc/kexec/elf_64.c | 4 ++++
> drivers/of/fdt.c | 21 +++++++++++++++++++++
> drivers/of/kexec.c | 19 +++++++++++++++++++
> 4 files changed, 48 insertions(+)
>
> diff --git a/arch/arm64/kernel/machine_kexec_file.c b/arch/arm64/kernel/machine_kexec_file.c
> index fba260ad87a9..e31fabed378a 100644
> --- a/arch/arm64/kernel/machine_kexec_file.c
> +++ b/arch/arm64/kernel/machine_kexec_file.c
> @@ -134,6 +134,10 @@ int load_other_segments(struct kimage *image,
>
> kexec_dprintk("Loaded elf core header at 0x%lx bufsz=0x%lx memsz=0x%lx\n",
> image->elf_load_addr, kbuf.bufsz, kbuf.memsz);
> +
> + ret = crash_load_dm_crypt_keys(image);
> + if (ret)
> + goto out_err;
> }
> #endif
>
> diff --git a/arch/powerpc/kexec/elf_64.c b/arch/powerpc/kexec/elf_64.c
> index 5d6d616404cf..ea50a072debf 100644
> --- a/arch/powerpc/kexec/elf_64.c
> +++ b/arch/powerpc/kexec/elf_64.c
> @@ -79,6 +79,10 @@ static void *elf64_load(struct kimage *image, char *kernel_buf,
> goto out;
> }
>
> + ret = crash_load_dm_crypt_keys(image);
> + if (ret)
> + goto out;
> +
> /* Setup cmdline for kdump kernel case */
> modified_cmdline = setup_kdump_cmdline(image, cmdline,
> cmdline_len);
> diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
> index 331646d667b9..2967e4aff807 100644
> --- a/drivers/of/fdt.c
> +++ b/drivers/of/fdt.c
> @@ -866,6 +866,26 @@ static void __init early_init_dt_check_for_elfcorehdr(unsigned long node)
> elfcorehdr_addr, elfcorehdr_size);
> }
>
> +static void __init early_init_dt_check_for_dmcryptkeys(unsigned long node)
> +{
> + const char *prop_name = "linux,dmcryptkeys";
> + const __be32 *prop;
> +
> + if (!IS_ENABLED(CONFIG_CRASH_DM_CRYPT))
> + return;
> +
> + pr_debug("Looking for dmcryptkeys property... ");
> +
> + prop = of_get_flat_dt_prop(node, prop_name, NULL);
> + if (!prop)
> + return;
> +
> + dm_crypt_keys_addr = dt_mem_next_cell(dt_root_addr_cells, &prop);
> +
> + /* Property only accessible to crash dump kernel */
> + fdt_delprop(initial_boot_params, node, prop_name);
> +}
> +
> static unsigned long chosen_node_offset = -FDT_ERR_NOTFOUND;
>
> /*
> @@ -1097,6 +1117,7 @@ int __init early_init_dt_scan_chosen(char *cmdline)
>
> early_init_dt_check_for_initrd(node);
> early_init_dt_check_for_elfcorehdr(node);
> + early_init_dt_check_for_dmcryptkeys(node);
>
> rng_seed = of_get_flat_dt_prop(node, "rng-seed", &l);
> if (rng_seed && l > 0) {
> diff --git a/drivers/of/kexec.c b/drivers/of/kexec.c
> index c4cf3552c018..fbd253f0d3c5 100644
> --- a/drivers/of/kexec.c
> +++ b/drivers/of/kexec.c
> @@ -423,6 +423,25 @@ void *of_kexec_alloc_and_setup_fdt(const struct kimage *image,
> if (ret)
> goto out;
>
> + if (image->dm_crypt_keys_addr != 0) {
> + ret = fdt_appendprop_addrrange(fdt, 0, chosen_node,
> + "linux,dmcryptkeys",
> + image->dm_crypt_keys_addr,
> + image->dm_crypt_keys_sz);
> +
> + if (ret)
> + goto out;
> +
> + /*
> + * Avoid dmcryptkeys from being stomped on in kdump kernel by
> + * setting up memory reserve map.
> + */
> + ret = fdt_add_mem_rsv(fdt, image->dm_crypt_keys_addr,
> + image->dm_crypt_keys_sz);
> + if (ret)
> + goto out;
> + }
> +
> #ifdef CONFIG_CRASH_DUMP
> /* add linux,usable-memory-range */
> ret = fdt_appendprop_addrrange(fdt, 0, chosen_node,
The above changes look good to me.
Feel free to add:
Reviewed-by: Sourabh Jain <sourabhjain@linux.ibm.com>
But while reading crash_load_dm_crypt_keys() I noticed a possibility of a
double free at the address pointed by `keys_header`:
In crash_load_dm_crypt_keys()/crash_dump_dm_crypt.c
snip...
kbuf.buffer = keys_header;
snip....
r = kexec_add_buffer(&kbuf);
if (r) {
pr_err("Failed to call kexec_add_buffer, ret=%d\n", r);
kvfree((void *)kbuf.buffer); <---
First Free
return r;
}
Since `keys_header` is not reset, the next call to build_keys_header()
will cause a double free at `keys_header`.
static int build_keys_header(void)
{
snip...
if (keys_header != NULL)
kvfree(keys_header);
snip...
}
What do you think?
- Sourabh Jain
^ permalink raw reply
* RE: [PATCH 1/8] hv: Select CONFIG_SYSFB only for CONFIG_HYPERV_VMBUS
From: Saurabh Singh Sengar @ 2026-04-02 10:50 UTC (permalink / raw)
To: Thomas Zimmermann, javierm@redhat.com, arnd@arndb.de,
ardb@kernel.org, ilias.apalodimas@linaro.org,
chenhuacai@kernel.org, kernel@xen0n.name,
maarten.lankhorst@linux.intel.com, mripard@kernel.org,
airlied@gmail.com, simona@ffwll.ch, KY Srinivasan, Haiyang Zhang,
wei.liu@kernel.org, Dexuan Cui, Long Li, deller@gmx.de
Cc: linux-arm-kernel@lists.infradead.org, loongarch@lists.linux.dev,
linux-efi@vger.kernel.org, linux-riscv@lists.infradead.org,
dri-devel@lists.freedesktop.org, linux-hyperv@vger.kernel.org,
linux-fbdev@vger.kernel.org, Michael Kelley, Saurabh Sengar,
stable@vger.kernel.org
In-Reply-To: <20260402092305.208728-2-tzimmermann@suse.de>
> Hyperv's sysfb access only exists in the VMBUS support. Therefore only select
> CONFIG_SYSFB for CONFIG_HYPERV_VMBUS. Avoids sysfb code on systems
> that don't need it.
>
> Signed-off-by: Thomas Zimmermann <tzimmermann@suse.de>
> Fixes: 96959283a58d ("Drivers: hv: Always select CONFIG_SYSFB for Hyper-V
> guests")
> Cc: Michael Kelley <mhklinux@outlook.com>
> Cc: Saurabh Sengar <ssengar@linux.microsoft.com>
> Cc: Wei Liu <wei.liu@kernel.org>
> Cc: "K. Y. Srinivasan" <kys@microsoft.com>
> Cc: Haiyang Zhang <haiyangz@microsoft.com>
> Cc: Dexuan Cui <decui@microsoft.com>
> Cc: Long Li <longli@microsoft.com>
> Cc: linux-hyperv@vger.kernel.org
> Cc: <stable@vger.kernel.org> # v6.16+
> ---
> drivers/hv/Kconfig | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/drivers/hv/Kconfig b/drivers/hv/Kconfig index
> 7937ac0cbd0f..2d0b3fcb0ff8 100644
> --- a/drivers/hv/Kconfig
> +++ b/drivers/hv/Kconfig
> @@ -9,7 +9,6 @@ config HYPERV
> select PARAVIRT
> select X86_HV_CALLBACK_VECTOR if X86
> select OF_EARLY_FLATTREE if OF
> - select SYSFB if EFI && !HYPERV_VTL_MODE
> select IRQ_MSI_LIB if X86
> help
> Select this option to run Linux as a Hyper-V client operating @@ -62,6
> +61,7 @@ config HYPERV_VMBUS
> tristate "Microsoft Hyper-V VMBus driver"
> depends on HYPERV
> default HYPERV
> + select SYSFB if EFI && !HYPERV_VTL_MODE
> help
> Select this option to enable Hyper-V Vmbus driver.
>
> --
> 2.53.0
Reviewed-by: Saurabh Sengar <ssengar@linux.microsoft.com>
^ permalink raw reply
* Re: [PATCH V3 7/7] arm64/hw_breakpoint: Enable FEAT_Debugv8p9
From: Mark Rutland @ 2026-04-02 10:37 UTC (permalink / raw)
To: Rob Herring
Cc: Anshuman Khandual, linux-arm-kernel, linux-kernel,
Jonathan Corbet, Marc Zyngier, Oliver Upton, James Morse,
Suzuki K Poulose, Catalin Marinas, Will Deacon, Mark Brown,
kvmarm
In-Reply-To: <20260331225800.GA2082670-robh@kernel.org>
On Tue, Mar 31, 2026 at 05:58:00PM -0500, Rob Herring wrote:
> On Mon, Dec 16, 2024 at 10:58:29AM +0000, Mark Rutland wrote:
> > On Mon, Dec 16, 2024 at 09:38:31AM +0530, Anshuman Khandual wrote:
> > > Currently there can be maximum 16 breakpoints, and 16 watchpoints available
> > > on a given platform - as detected from ID_AA64DFR0_EL1.[BRPs|WRPs] register
> > > fields. But these breakpoint, and watchpoints can be extended further up to
> > > 64 via a new arch feature FEAT_Debugv8p9.
> > >
> > > This first enables banked access for the breakpoint and watchpoint register
> > > set via MDSELR_EL1, extended exceptions via MDSCR_EL1.EMBWE and determining
> > > available breakpoints and watchpoints in the platform from ID_AA64DFR1_EL1,
> > > when FEAT_Debugv8p9 is enabled.
> >
> > [...]
>
> Well, this series has landed on my plate...
Sorry about that; thanks for taking a look!
> > > +static u64 read_wb_reg(int reg, int n)
> > > +{
> > > + unsigned long flags;
> > > + u64 val;
> > > +
> > > + if (!is_debug_v8p9_enabled())
> > > + return __read_wb_reg(reg, n);
> > > +
> > > + /*
> > > + * Bank selection in MDSELR_EL1, followed by an indexed read from
> > > + * breakpoint (or watchpoint) registers cannot be interrupted, as
> > > + * that might cause misread from the wrong targets instead. Hence
> > > + * this requires mutual exclusion.
> > > + */
> > > + local_irq_save(flags);
> > > + write_sysreg_s(SYS_FIELD_PREP(MDSELR_EL1, BANK, n / MAX_PER_BANK), SYS_MDSELR_EL1);
> > > + isb();
> > > + val = __read_wb_reg(reg, n % MAX_PER_BANK);
> > > + local_irq_restore(flags);
> > > + return val;
> > > +}
> > > NOKPROBE_SYMBOL(read_wb_reg);
> >
> > I don't believe that disabling interrupts here is sufficient. On the
> > last version I asked about the case of racing with a watchpoint handler:
> >
> > | For example, what prevents watchpoint_handler() from firing in the
> > | middle of arch_install_hw_breakpoint() or
> > | arch_uninstall_hw_breakpoint()?
> >
> > ... and disabling interrupts cannot prevent that, because
> > local_irq_{save,restore}() do not affect the behaviour of watchpoints or
> > breakpoints.
>
> I think the answer is we just need NOKPROBE_SYMBOL() annotation on
> hw_breakpoint_control() (what arch_install_hw_breakpoint() and
> arch_uninstall_hw_breakpoint() wrap).
Ok. I couldn'y spot where we prevent placing HW breakpoints on
NOKPROBE_SYMBOL() functions, but if we do enforce that, something like
that sounds ok.
I suspect we'd need to make that noinstr to also prevent ftrace
instrumentation, unless ftrace also inhibits itself for
NOKPROBE_SYMBOL() functions.
> We also need that on __read_wb_reg
> and __read_wb_reg though I would think those are folded into the calling
> functions by the compiler. Interestly, the x86 code doesn't use the
> annotation at all.
IIUC, it looks like they *can* take debug NMIs during
arch_install_hw_breakpoint() and arch_uninstall_hw_breakpoint(), which
is why they have ordering constraints for modifying the percpu 'cpu_dr7'
variable, and their actual DR7 register (which IIUC has the enable
controls for each HW breakpoint).
That said, the use of 'bp_per_reg' looks suspect given their
arch_install_hw_breakpoint() and arch_uninstall_hw_breakpoint() modify
that non-atomically.
We could consider allowing breakpoints on those functions, but I'm not
sure whether that's possible for us, and (as noted below) it might be
better to transiently disable breakpoints/watchpoints.
IIRC on x86, breakpoint exceptions are taken *after* execution of the
instruction that triggered them, so the handler doesn't have to
manipulate single-step, and can safely ignore a breakpoint exception
without the risk of getting stuck taking the breakpoint repeatedly.
> I initially thought the IRQ disabling is also still needed as IRQ
> handlers can trigger breakpoints. However, the x86 version of
> arch_install_hw_breakpoint() contains a lockdep_assert_irqs_disabled(),
> so it seems for that case interrupts are already disabled. And in debug
> exceptions, we disable interrupts. So I think the interrupt disabling
> can be dropped.
I'd expect that the core perf code disables interrupts before calling
arch_install_hw_breakpoint() or arch_uninstall_hw_breakpoint(), and this
would be necessary for perf to serialise against IPIs that manipulate
the perf_event_context.
I agree that when we actually take the breakpoint, we'll mask all
exceptions, and so it's not necessary to mask IRQs there.
So a first step is probably to add that lockdep assert.
> > Please can you try to answer the questions I asked last time, i.e.
> >
> > | What prevents a race with an exception handler? e.g.
> > |
> > | * Does the structure of the code prevent that somehow?
>
> If you can't set a breakpoint/watchpoint in NOKPROBE_SYMBOL() annotated
> code, you can't race.
As above, I agree (with caveats), but I couldn't spot where this is
enforced.
> However, there's no such annotation for data. It looks like the kernel
> policy is "don't do that" or disable all breakpoints/watchpoints.
If we have to transiently disable watchpoints/breakpoints when
manipulating the relevant HW registers, that sounds fine to me.
> > |
> > | * What context(s) does this code execute in?
> > | - Are debug exceptions always masked?
>
> No.
>
> > | - Do we disable breakpoints/watchpoints around (some) manipulation of
> > | the relevant registers?
>
> Yes, with NOKPROBE_SYMBOL().
Thanks for digging into this; much appreciated!
Mark.
^ permalink raw reply
* Re: [PATCH v13 5/7] qcom-tgu: Add support to configure next action
From: Jie Gan @ 2026-04-02 10:28 UTC (permalink / raw)
To: Songwei Chai, andersson, alexander.shishkin, mike.leach,
konrad.dybcio, suzuki.poulose, james.clark, krzk+dt, conor+dt
Cc: linux-kernel, linux-arm-kernel, linux-arm-msm, coresight,
devicetree, gregkh
In-Reply-To: <20260402092838.341295-6-songwei.chai@oss.qualcomm.com>
On 4/2/2026 5:28 PM, Songwei Chai wrote:
> Add "select" node for each step to determine if another step is taken,
> trigger(s) are generated, counters/timers incremented/decremented, etc.
>
> Reviewed-by: Jie Gan <jie.gan@oss.qualcomm.com>
> Signed-off-by: Songwei Chai <songwei.chai@oss.qualcomm.com>
> ---
> .../ABI/testing/sysfs-bus-amba-devices-tgu | 7 +++
> drivers/hwtracing/qcom/tgu.c | 53 ++++++++++++++++++-
> drivers/hwtracing/qcom/tgu.h | 27 ++++++++++
> 3 files changed, 85 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
> index 4ef0d696d3d0..786cb852bbe5 100644
> --- a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
> +++ b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
> @@ -21,3 +21,10 @@ KernelVersion: 7.1
> Contact: Jinlong Mao <jinlong.mao@oss.qualcomm.com>, Songwei Chai <songwei.chai@oss.qualcomm.com>
> Description:
> (RW) Set/Get the decode mode with specific step for TGU.
> +
> +What: /sys/bus/amba/devices/<tgu-name>/step[0:7]_condition_select/reg[0:3]
> +Date: April 2026
> +KernelVersion: 7.1
> +Contact: Jinlong Mao <jinlong.mao@oss.qualcomm.com>, Songwei Chai <songwei.chai@oss.qualcomm.com>
> +Description:
> + (RW) Set/Get the next action with specific step for TGU.
> diff --git a/drivers/hwtracing/qcom/tgu.c b/drivers/hwtracing/qcom/tgu.c
> index 5b37eb10f863..4112e6a691d6 100644
> --- a/drivers/hwtracing/qcom/tgu.c
> +++ b/drivers/hwtracing/qcom/tgu.c
> @@ -29,6 +29,9 @@ static int calculate_array_location(struct tgu_drvdata *drvdata,
> case TGU_CONDITION_DECODE:
> return step_index * (drvdata->num_condition_decode) +
> reg_index;
> + case TGU_CONDITION_SELECT:
> + return step_index * (drvdata->num_condition_select) +
> + reg_index;
> default:
> break;
> }
> @@ -71,6 +74,9 @@ static ssize_t tgu_dataset_show(struct device *dev,
> case TGU_CONDITION_DECODE:
> return sysfs_emit(buf, "0x%x\n",
> drvdata->value_table->condition_decode[index]);
> + case TGU_CONDITION_SELECT:
> + return sysfs_emit(buf, "0x%x\n",
> + drvdata->value_table->condition_select[index]);
> default:
> break;
> }
> @@ -112,6 +118,10 @@ static ssize_t tgu_dataset_store(struct device *dev,
> tgu_drvdata->value_table->condition_decode[index] = val;
> ret = size;
> break;
> + case TGU_CONDITION_SELECT:
> + tgu_drvdata->value_table->condition_select[index] = val;
> + ret = size;
> + break;
> default:
> ret = -EINVAL;
> break;
> @@ -146,6 +156,13 @@ static umode_t tgu_node_visible(struct kobject *kobject,
> if (tgu_attr->reg_num < drvdata->num_condition_decode)
> return attr->mode;
> break;
> + case TGU_CONDITION_SELECT:
> + /* 'default' register is at the end of 'select' region */
> + if (tgu_attr->reg_num == drvdata->num_condition_select - 1)
> + attr->name = "default";
> + if (tgu_attr->reg_num < drvdata->num_condition_select)
> + return attr->mode;
> + break;
> default:
> break;
> }
> @@ -184,6 +201,18 @@ static ssize_t tgu_write_all_hw_regs(struct tgu_drvdata *drvdata)
> drvdata->base + CONDITION_DECODE_STEP(i, j));
> }
> }
> +
> + for (i = 0; i < drvdata->num_step; i++) {
> + for (j = 0; j < drvdata->num_condition_select; j++) {
> + index = check_array_location(drvdata, i,
> + TGU_CONDITION_SELECT, j);
> + if (index == -EINVAL)
> + goto exit;
> +
> + writel(drvdata->value_table->condition_select[index],
> + drvdata->base + CONDITION_SELECT_STEP(i, j));
> + }
> + }
> /* Enable TGU to program the triggers */
> writel(1, drvdata->base + TGU_CONTROL);
> exit:
> @@ -223,6 +252,8 @@ static void tgu_set_conditions(struct tgu_drvdata *drvdata)
>
> devid = readl(drvdata->base + TGU_DEVID);
> drvdata->num_condition_decode = TGU_DEVID_CONDITIONS(devid);
> + /* select region has an additional 'default' register */
> + drvdata->num_condition_select = TGU_DEVID_CONDITIONS(devid) + 1;
> }
>
> static int tgu_enable(struct device *dev)
> @@ -366,6 +397,14 @@ static const struct attribute_group *tgu_attr_groups[] = {
> CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(5),
> CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(6),
> CONDITION_DECODE_ATTRIBUTE_GROUP_INIT(7),
> + CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(0),
> + CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(1),
> + CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(2),
> + CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(3),
> + CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(4),
> + CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(5),
> + CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(6),
> + CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(7),
> NULL,
> };
>
> @@ -373,8 +412,8 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
> {
> struct device *dev = &adev->dev;
> struct tgu_drvdata *drvdata;
> - unsigned int *priority, *condition;
> - size_t priority_size, condition_size;
> + unsigned int *priority, *condition, *select;
> + size_t priority_size, condition_size, select_size;
> int ret;
>
> drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
> @@ -425,6 +464,16 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
>
> drvdata->value_table->condition_decode = condition;
>
> + select_size = drvdata->num_condition_select * drvdata->num_step;
> +
> + select = devm_kcalloc(dev, select_size,
> + sizeof(*(drvdata->value_table->condition_select)),
> + GFP_KERNEL);
> + if (!select)
> + return -ENOMEM;
> +
> + drvdata->value_table->condition_select = select;
> +
> drvdata->enabled = false;
>
> pm_runtime_put(&adev->dev);
> diff --git a/drivers/hwtracing/qcom/tgu.h b/drivers/hwtracing/qcom/tgu.h
> index 987ea07bd618..ac46a2875209 100644
> --- a/drivers/hwtracing/qcom/tgu.h
> +++ b/drivers/hwtracing/qcom/tgu.h
> @@ -52,6 +52,7 @@
> #define STEP_OFFSET 0x1D8
> #define PRIORITY_START_OFFSET 0x0074
> #define CONDITION_DECODE_OFFSET 0x0050
> +#define CONDITION_SELECT_OFFSET 0x0060
> #define PRIORITY_OFFSET 0x60
> #define REG_OFFSET 0x4
>
> @@ -63,6 +64,9 @@
> #define CONDITION_DECODE_STEP(step, decode) \
> (CONDITION_DECODE_OFFSET + REG_OFFSET * decode + STEP_OFFSET * step)
>
> +#define CONDITION_SELECT_STEP(step, select) \
> + (CONDITION_SELECT_OFFSET + REG_OFFSET * select + STEP_OFFSET * step)
> +
> #define tgu_dataset_rw(name, step_index, type, reg_num) \
> (&((struct tgu_attribute[]){ { \
> __ATTR(name, 0644, tgu_dataset_show, tgu_dataset_store), \
> @@ -76,6 +80,8 @@
> reg_num)
> #define STEP_DECODE(step_index, reg_num) \
> tgu_dataset_rw(reg##reg_num, step_index, TGU_CONDITION_DECODE, reg_num)
> +#define STEP_SELECT(step_index, reg_num) \
> + tgu_dataset_rw(reg##reg_num, step_index, TGU_CONDITION_SELECT, reg_num)
>
> #define STEP_PRIORITY_LIST(step_index, priority) \
> {STEP_PRIORITY(step_index, 0, priority), \
> @@ -107,6 +113,15 @@
> NULL \
> }
>
> +#define STEP_SELECT_LIST(n) \
> + {STEP_SELECT(n, 0), \
> + STEP_SELECT(n, 1), \
> + STEP_SELECT(n, 2), \
> + STEP_SELECT(n, 3), \
> + STEP_SELECT(n, 4), \
> + NULL \
> + }
> +
> #define PRIORITY_ATTRIBUTE_GROUP_INIT(step, priority)\
> (&(const struct attribute_group){\
> .attrs = (struct attribute*[])STEP_PRIORITY_LIST(step, priority),\
> @@ -121,12 +136,21 @@
> .name = "step" #step "_condition_decode" \
> })
>
> +#define CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(step)\
> + (&(const struct attribute_group){\
> + .attrs = (struct attribute*[])STEP_SELECT_LIST(step),\
> + .is_visible = tgu_node_visible,\
> + .name = "step" #step "_condition_select" \
> + })
> +
> +
remove extra blank line.
Thanks,
Jie
> enum operation_index {
> TGU_PRIORITY0,
> TGU_PRIORITY1,
> TGU_PRIORITY2,
> TGU_PRIORITY3,
> TGU_CONDITION_DECODE,
> + TGU_CONDITION_SELECT,
> };
>
> /* Maximum priority that TGU supports */
> @@ -142,6 +166,7 @@ struct tgu_attribute {
> struct value_table {
> unsigned int *priority;
> unsigned int *condition_decode;
> + unsigned int *condition_select;
> };
>
> static inline void TGU_LOCK(void __iomem *addr)
> @@ -172,6 +197,7 @@ static inline void TGU_UNLOCK(void __iomem *addr)
> * @num_reg: Maximum number of registers
> * @num_step: Maximum step size
> * @num_condition_decode: Maximum number of condition_decode
> + * @num_condition_select: Maximum number of condition_select
> *
> * This structure defines the data associated with a TGU device,
> * including its base address, device pointers, clock, spinlock for
> @@ -187,6 +213,7 @@ struct tgu_drvdata {
> int num_reg;
> int num_step;
> int num_condition_decode;
> + int num_condition_select;
> };
>
> #endif
^ permalink raw reply
* Re: [PATCH v13 6/7] qcom-tgu: Add timer/counter functionality for TGU
From: Jie Gan @ 2026-04-02 10:26 UTC (permalink / raw)
To: Songwei Chai, andersson, alexander.shishkin, mike.leach,
konrad.dybcio, suzuki.poulose, james.clark, krzk+dt, conor+dt
Cc: linux-kernel, linux-arm-kernel, linux-arm-msm, coresight,
devicetree, gregkh
In-Reply-To: <20260402092838.341295-7-songwei.chai@oss.qualcomm.com>
On 4/2/2026 5:28 PM, Songwei Chai wrote:
> Add counter and timer node for each step which could be
> programed if they are to be utilized in trigger event/sequence.
>
> Reviewed-by: Jie Gan <jie.gan@oss.qualcomm.com>
> Signed-off-by: Songwei Chai <songwei.chai@oss.qualcomm.com>
> ---
> .../ABI/testing/sysfs-bus-amba-devices-tgu | 14 +++
> drivers/hwtracing/qcom/tgu.c | 116 +++++++++++++++++-
> drivers/hwtracing/qcom/tgu.h | 56 +++++++++
> 3 files changed, 184 insertions(+), 2 deletions(-)
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
> index 786cb852bbe5..7a3573e03e27 100644
> --- a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
> +++ b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
> @@ -28,3 +28,17 @@ KernelVersion: 7.1
> Contact: Jinlong Mao <jinlong.mao@oss.qualcomm.com>, Songwei Chai <songwei.chai@oss.qualcomm.com>
> Description:
> (RW) Set/Get the next action with specific step for TGU.
> +
> +What: /sys/bus/amba/devices/<tgu-name>/step[0:7]_timer/reg[0:1]
> +Date: April 2026
> +KernelVersion: 7.1
> +Contact: Jinlong Mao <jinlong.mao@oss.qualcomm.com>, Songwei Chai <songwei.chai@oss.qualcomm.com>
> +Description:
> + (RW) Set/Get the timer value with specific step for TGU.
> +
> +What: /sys/bus/amba/devices/<tgu-name>/step[0:7]_counter/reg[0:1]
> +Date: April 2026
> +KernelVersion: 7.1
> +Contact: Jinlong Mao <jinlong.mao@oss.qualcomm.com>, Songwei Chai <songwei.chai@oss.qualcomm.com>
> +Description:
> + (RW) Set/Get the counter value with specific step for TGU.
> diff --git a/drivers/hwtracing/qcom/tgu.c b/drivers/hwtracing/qcom/tgu.c
> index 4112e6a691d6..4a529520b428 100644
> --- a/drivers/hwtracing/qcom/tgu.c
> +++ b/drivers/hwtracing/qcom/tgu.c
> @@ -32,6 +32,10 @@ static int calculate_array_location(struct tgu_drvdata *drvdata,
> case TGU_CONDITION_SELECT:
> return step_index * (drvdata->num_condition_select) +
> reg_index;
> + case TGU_COUNTER:
> + return step_index * (drvdata->num_counter) + reg_index;
> + case TGU_TIMER:
> + return step_index * (drvdata->num_timer) + reg_index;
> default:
> break;
> }
> @@ -77,6 +81,12 @@ static ssize_t tgu_dataset_show(struct device *dev,
> case TGU_CONDITION_SELECT:
> return sysfs_emit(buf, "0x%x\n",
> drvdata->value_table->condition_select[index]);
> + case TGU_TIMER:
> + return sysfs_emit(buf, "0x%x\n",
> + drvdata->value_table->timer[index]);
> + case TGU_COUNTER:
> + return sysfs_emit(buf, "0x%x\n",
> + drvdata->value_table->counter[index]);
> default:
> break;
> }
> @@ -122,6 +132,14 @@ static ssize_t tgu_dataset_store(struct device *dev,
> tgu_drvdata->value_table->condition_select[index] = val;
> ret = size;
> break;
> + case TGU_TIMER:
> + tgu_drvdata->value_table->timer[index] = val;
> + ret = size;
> + break;
> + case TGU_COUNTER:
> + tgu_drvdata->value_table->counter[index] = val;
> + ret = size;
> + break;
> default:
> ret = -EINVAL;
> break;
> @@ -163,6 +181,18 @@ static umode_t tgu_node_visible(struct kobject *kobject,
> if (tgu_attr->reg_num < drvdata->num_condition_select)
> return attr->mode;
> break;
> + case TGU_COUNTER:
> + if (!drvdata->num_counter)
> + break;
> + if (tgu_attr->reg_num < drvdata->num_counter)
> + return attr->mode;
> + break;
> + case TGU_TIMER:
> + if (!drvdata->num_timer)
> + break;
> + if (tgu_attr->reg_num < drvdata->num_timer)
> + return attr->mode;
> + break;
> default:
> break;
> }
> @@ -213,6 +243,30 @@ static ssize_t tgu_write_all_hw_regs(struct tgu_drvdata *drvdata)
> drvdata->base + CONDITION_SELECT_STEP(i, j));
> }
> }
> +
> + for (i = 0; i < drvdata->num_step; i++) {
> + for (j = 0; j < drvdata->num_timer; j++) {
> + index = check_array_location(drvdata, i, TGU_TIMER, j);
> +
> + if (index == -EINVAL)
> + goto exit;
> +
> + writel(drvdata->value_table->timer[index],
> + drvdata->base + TIMER_COMPARE_STEP(i, j));
> + }
> + }
> +
> + for (i = 0; i < drvdata->num_step; i++) {
> + for (j = 0; j < drvdata->num_counter; j++) {
> + index = check_array_location(drvdata, i, TGU_COUNTER, j);
> +
> + if (index == -EINVAL)
> + goto exit;
> +
> + writel(drvdata->value_table->counter[index],
> + drvdata->base + COUNTER_COMPARE_STEP(i, j));
> + }
> + }
> /* Enable TGU to program the triggers */
> writel(1, drvdata->base + TGU_CONTROL);
> exit:
> @@ -256,6 +310,27 @@ static void tgu_set_conditions(struct tgu_drvdata *drvdata)
> drvdata->num_condition_select = TGU_DEVID_CONDITIONS(devid) + 1;
> }
>
> +static void tgu_set_timer_counter(struct tgu_drvdata *drvdata)
> +{
> + int num_timers = 0, num_counters = 0;
> + u32 devid2;
> +
> + devid2 = readl(drvdata->base + CORESIGHT_DEVID2);
> +
> + if (TGU_DEVID2_TIMER0(devid2))
> + num_timers++;
> + if (TGU_DEVID2_TIMER1(devid2))
> + num_timers++;
> +
> + if (TGU_DEVID2_COUNTER0(devid2))
> + num_counters++;
> + if (TGU_DEVID2_COUNTER1(devid2))
> + num_counters++;
> +
> + drvdata->num_timer = num_timers;
> + drvdata->num_counter = num_counters;
> +}
> +
> static int tgu_enable(struct device *dev)
> {
> struct tgu_drvdata *drvdata = dev_get_drvdata(dev);
> @@ -405,6 +480,22 @@ static const struct attribute_group *tgu_attr_groups[] = {
> CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(5),
> CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(6),
> CONDITION_SELECT_ATTRIBUTE_GROUP_INIT(7),
> + TIMER_ATTRIBUTE_GROUP_INIT(0),
> + TIMER_ATTRIBUTE_GROUP_INIT(1),
> + TIMER_ATTRIBUTE_GROUP_INIT(2),
> + TIMER_ATTRIBUTE_GROUP_INIT(3),
> + TIMER_ATTRIBUTE_GROUP_INIT(4),
> + TIMER_ATTRIBUTE_GROUP_INIT(5),
> + TIMER_ATTRIBUTE_GROUP_INIT(6),
> + TIMER_ATTRIBUTE_GROUP_INIT(7),
> + COUNTER_ATTRIBUTE_GROUP_INIT(0),
> + COUNTER_ATTRIBUTE_GROUP_INIT(1),
> + COUNTER_ATTRIBUTE_GROUP_INIT(2),
> + COUNTER_ATTRIBUTE_GROUP_INIT(3),
> + COUNTER_ATTRIBUTE_GROUP_INIT(4),
> + COUNTER_ATTRIBUTE_GROUP_INIT(5),
> + COUNTER_ATTRIBUTE_GROUP_INIT(6),
> + COUNTER_ATTRIBUTE_GROUP_INIT(7),
> NULL,
> };
>
> @@ -412,8 +503,8 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
> {
> struct device *dev = &adev->dev;
> struct tgu_drvdata *drvdata;
> - unsigned int *priority, *condition, *select;
> - size_t priority_size, condition_size, select_size;
> + unsigned int *priority, *condition, *select, *timer, *counter;
> + size_t priority_size, condition_size, select_size, timer_size, counter_size;
> int ret;
>
> drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
> @@ -432,6 +523,7 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
> tgu_set_reg_number(drvdata);
> tgu_set_steps(drvdata);
> tgu_set_conditions(drvdata);
> + tgu_set_timer_counter(drvdata);
>
> ret = sysfs_create_groups(&dev->kobj, tgu_attr_groups);
> if (ret) {
> @@ -474,6 +566,26 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
>
> drvdata->value_table->condition_select = select;
>
> + timer_size = drvdata->num_step * drvdata->num_timer;
> +
> + timer = devm_kcalloc(dev, timer_size,
> + sizeof(*(drvdata->value_table->timer)),
> + GFP_KERNEL);
> + if (!timer)
> + return -ENOMEM;
> +
> + drvdata->value_table->timer = timer;
> +
> + counter_size = drvdata->num_step * drvdata->num_counter;
> +
> + counter = devm_kcalloc(dev, counter_size,
> + sizeof(*(drvdata->value_table->counter)),
> + GFP_KERNEL);
> + if (!counter)
> + return -ENOMEM;
> +
> + drvdata->value_table->counter = counter;
> +
> drvdata->enabled = false;
>
> pm_runtime_put(&adev->dev);
> diff --git a/drivers/hwtracing/qcom/tgu.h b/drivers/hwtracing/qcom/tgu.h
> index ac46a2875209..5dfef0afbad6 100644
> --- a/drivers/hwtracing/qcom/tgu.h
> +++ b/drivers/hwtracing/qcom/tgu.h
> @@ -11,6 +11,7 @@
> #define TGU_LAR 0xfb0
> #define TGU_UNLOCK_OFFSET 0xc5acce55
> #define TGU_DEVID 0xfc8
> +#define CORESIGHT_DEVID2 0xfc0
>
> #define TGU_DEVID_SENSE_INPUT(devid_val) \
> ((int)FIELD_GET(GENMASK(17, 10), devid_val))
> @@ -18,6 +19,16 @@
> ((int)FIELD_GET(GENMASK(6, 3), devid_val))
> #define TGU_DEVID_CONDITIONS(devid_val) \
> ((int)FIELD_GET(GENMASK(2, 0), devid_val))
> +#define TGU_DEVID2_TIMER0(devid_val) \
> + ((int)FIELD_GET(GENMASK(23, 18), devid_val))
> +#define TGU_DEVID2_TIMER1(devid_val) \
> + ((int)FIELD_GET(GENMASK(17, 13), devid_val))
> +#define TGU_DEVID2_COUNTER0(devid_val) \
> + ((int)FIELD_GET(GENMASK(11, 6), devid_val))
> +#define TGU_DEVID2_COUNTER1(devid_val) \
> + ((int)FIELD_GET(GENMASK(5, 0), devid_val))
> +
> +
> #define TGU_BITS_PER_SIGNAL 4
> #define LENGTH_REGISTER 32
>
> @@ -53,6 +64,8 @@
> #define PRIORITY_START_OFFSET 0x0074
> #define CONDITION_DECODE_OFFSET 0x0050
> #define CONDITION_SELECT_OFFSET 0x0060
> +#define TIMER_START_OFFSET 0x0040
> +#define COUNTER_START_OFFSET 0x0048
> #define PRIORITY_OFFSET 0x60
> #define REG_OFFSET 0x4
>
> @@ -67,6 +80,12 @@
> #define CONDITION_SELECT_STEP(step, select) \
> (CONDITION_SELECT_OFFSET + REG_OFFSET * select + STEP_OFFSET * step)
>
> +#define TIMER_COMPARE_STEP(step, timer) \
> + (TIMER_START_OFFSET + REG_OFFSET * timer + STEP_OFFSET * step)
> +
> +#define COUNTER_COMPARE_STEP(step, counter) \
> + (COUNTER_START_OFFSET + REG_OFFSET * counter + STEP_OFFSET * step)
> +
> #define tgu_dataset_rw(name, step_index, type, reg_num) \
> (&((struct tgu_attribute[]){ { \
> __ATTR(name, 0644, tgu_dataset_show, tgu_dataset_store), \
> @@ -82,6 +101,10 @@
> tgu_dataset_rw(reg##reg_num, step_index, TGU_CONDITION_DECODE, reg_num)
> #define STEP_SELECT(step_index, reg_num) \
> tgu_dataset_rw(reg##reg_num, step_index, TGU_CONDITION_SELECT, reg_num)
> +#define STEP_TIMER(step_index, reg_num) \
> + tgu_dataset_rw(reg##reg_num, step_index, TGU_TIMER, reg_num)
> +#define STEP_COUNTER(step_index, reg_num) \
> + tgu_dataset_rw(reg##reg_num, step_index, TGU_COUNTER, reg_num)
>
> #define STEP_PRIORITY_LIST(step_index, priority) \
> {STEP_PRIORITY(step_index, 0, priority), \
> @@ -122,6 +145,18 @@
> NULL \
> }
>
> +#define STEP_TIMER_LIST(n) \
> + {STEP_TIMER(n, 0), \
> + STEP_TIMER(n, 1), \
> + NULL \
> + }
> +
> +#define STEP_COUNTER_LIST(n) \
> + {STEP_COUNTER(n, 0), \
> + STEP_COUNTER(n, 1), \
> + NULL \
> + }
> +
> #define PRIORITY_ATTRIBUTE_GROUP_INIT(step, priority)\
> (&(const struct attribute_group){\
> .attrs = (struct attribute*[])STEP_PRIORITY_LIST(step, priority),\
> @@ -143,6 +178,19 @@
> .name = "step" #step "_condition_select" \
> })
>
> +#define TIMER_ATTRIBUTE_GROUP_INIT(step)\
> + (&(const struct attribute_group){\
> + .attrs = (struct attribute*[])STEP_TIMER_LIST(step),\
> + .is_visible = tgu_node_visible,\
> + .name = "step" #step "_timer" \
> + })
> +
> +#define COUNTER_ATTRIBUTE_GROUP_INIT(step)\
> + (&(const struct attribute_group){\
> + .attrs = (struct attribute*[])STEP_COUNTER_LIST(step),\
> + .is_visible = tgu_node_visible,\
> + .name = "step" #step "_counter" \
> + })
>
> enum operation_index {
> TGU_PRIORITY0,
> @@ -151,6 +199,8 @@ enum operation_index {
> TGU_PRIORITY3,
> TGU_CONDITION_DECODE,
> TGU_CONDITION_SELECT,
> + TGU_TIMER,
> + TGU_COUNTER
[Nit] TGU_COUNTER,
Thanks,
Jie
> };
>
> /* Maximum priority that TGU supports */
> @@ -167,6 +217,8 @@ struct value_table {
> unsigned int *priority;
> unsigned int *condition_decode;
> unsigned int *condition_select;
> + unsigned int *timer;
> + unsigned int *counter;
> };
>
> static inline void TGU_LOCK(void __iomem *addr)
> @@ -198,6 +250,8 @@ static inline void TGU_UNLOCK(void __iomem *addr)
> * @num_step: Maximum step size
> * @num_condition_decode: Maximum number of condition_decode
> * @num_condition_select: Maximum number of condition_select
> + * @num_timer: Maximum number of timers
> + * @num_counter: Maximum number of counters
> *
> * This structure defines the data associated with a TGU device,
> * including its base address, device pointers, clock, spinlock for
> @@ -214,6 +268,8 @@ struct tgu_drvdata {
> int num_step;
> int num_condition_decode;
> int num_condition_select;
> + int num_timer;
> + int num_counter;
> };
>
> #endif
^ permalink raw reply
* Re: [PATCH v13 3/7] qcom-tgu: Add signal priority support
From: Jie Gan @ 2026-04-02 10:23 UTC (permalink / raw)
To: Songwei Chai, andersson, alexander.shishkin, mike.leach,
konrad.dybcio, suzuki.poulose, james.clark, krzk+dt, conor+dt
Cc: linux-kernel, linux-arm-kernel, linux-arm-msm, coresight,
devicetree, gregkh
In-Reply-To: <20260402092838.341295-4-songwei.chai@oss.qualcomm.com>
On 4/2/2026 5:28 PM, Songwei Chai wrote:
> Like circuit of a Logic analyzer, in TGU, the requirement could be
> configured in each step and the trigger will be created once the
> requirements are met. Add priority functionality here to sort the
> signals into different priorities. The signal which is wanted could
> be configured in each step's priority node, the larger number means
> the higher priority and the signal with higher priority will be sensed
> more preferentially.
>
> Reviewed-by: Jie Gan <jie.gan@oss.qualcomm.com>
> Signed-off-by: Songwei Chai <songwei.chai@oss.qualcomm.com>
> ---
> .../ABI/testing/sysfs-bus-amba-devices-tgu | 7 +
> drivers/hwtracing/qcom/tgu.c | 161 ++++++++++++++++++
> drivers/hwtracing/qcom/tgu.h | 114 +++++++++++++
> 3 files changed, 282 insertions(+)
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
> index f877a00fcaa5..223873789ca6 100644
> --- a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
> +++ b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
> @@ -7,3 +7,10 @@ Description:
> Accepts only one of the 2 values - 0 or 1.
> 0 : disable TGU.
> 1 : enable TGU.
> +
> +What: /sys/bus/amba/devices/<tgu-name>/step[0:7]_priority[0:3]/reg[0:17]
> +Date: April 2026
> +KernelVersion: 7.1
> +Contact: Jinlong Mao <jinlong.mao@oss.qualcomm.com>, Songwei Chai <songwei.chai@oss.qualcomm.com>
> +Description:
> + (RW) Set/Get the sensed signal with specific step and priority for TGU.
> diff --git a/drivers/hwtracing/qcom/tgu.c b/drivers/hwtracing/qcom/tgu.c
> index 49c8f710b931..7d69986c3e3d 100644
> --- a/drivers/hwtracing/qcom/tgu.c
> +++ b/drivers/hwtracing/qcom/tgu.c
> @@ -14,14 +14,123 @@
>
> #include "tgu.h"
>
> +static int calculate_array_location(struct tgu_drvdata *drvdata,
> + int step_index, int operation_index,
> + int reg_index)
> +{
> + return operation_index * (drvdata->num_step) * (drvdata->num_reg) +
> + step_index * (drvdata->num_reg) + reg_index;
> +}
> +
> +static ssize_t tgu_dataset_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct tgu_drvdata *drvdata = dev_get_drvdata(dev);
> + struct tgu_attribute *tgu_attr =
> + container_of(attr, struct tgu_attribute, attr);
> + int index;
> +
> + index = calculate_array_location(drvdata, tgu_attr->step_index,
> + tgu_attr->operation_index,
> + tgu_attr->reg_num);
> +
> + return sysfs_emit(buf, "0x%x\n",
> + drvdata->value_table->priority[index]);
> +}
> +
> +static ssize_t tgu_dataset_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf, size_t size)
> +{
> + struct tgu_drvdata *tgu_drvdata = dev_get_drvdata(dev);
> + struct tgu_attribute *tgu_attr =
> + container_of(attr, struct tgu_attribute, attr);
> + unsigned long val;
> + int index;
> + int ret;
> +
> + ret = kstrtoul(buf, 0, &val);
> + if (ret)
> + return ret;
> +
> + guard(spinlock)(&tgu_drvdata->lock);
> + index = calculate_array_location(tgu_drvdata, tgu_attr->step_index,
> + tgu_attr->operation_index,
> + tgu_attr->reg_num);
> +
> + tgu_drvdata->value_table->priority[index] = val;
> +
> + return size;
> +}
> +
> +static umode_t tgu_node_visible(struct kobject *kobject,
> + struct attribute *attr,
> + int n)
> +{
> + struct device *dev = kobj_to_dev(kobject);
> + struct tgu_drvdata *drvdata = dev_get_drvdata(dev);
> + struct device_attribute *dev_attr =
> + container_of(attr, struct device_attribute, attr);
> + struct tgu_attribute *tgu_attr =
> + container_of(dev_attr, struct tgu_attribute, attr);
> +
> + if (tgu_attr->step_index >= drvdata->num_step)
> + return SYSFS_GROUP_INVISIBLE;
> +
> + if (tgu_attr->reg_num >= drvdata->num_reg)
> + return 0;
> +
> + return attr->mode;
> +}
> +
> static void tgu_write_all_hw_regs(struct tgu_drvdata *drvdata)
> {
> + int i, j, k, index;
> +
> TGU_UNLOCK(drvdata->base);
> + for (i = 0; i < drvdata->num_step; i++) {
> + for (j = 0; j < MAX_PRIORITY; j++) {
> + for (k = 0; k < drvdata->num_reg; k++) {
> + index = calculate_array_location(
> + drvdata, i, j, k);
> +
> + writel(drvdata->value_table->priority[index],
> + drvdata->base +
> + PRIORITY_REG_STEP(i, j, k));
> + }
> + }
> + }
> /* Enable TGU to program the triggers */
> writel(1, drvdata->base + TGU_CONTROL);
> TGU_LOCK(drvdata->base);
> }
>
> +static void tgu_set_reg_number(struct tgu_drvdata *drvdata)
> +{
> + int num_sense_input;
> + int num_reg;
> + u32 devid;
> +
> + devid = readl(drvdata->base + TGU_DEVID);
> +
> + num_sense_input = TGU_DEVID_SENSE_INPUT(devid);
> + num_reg = (num_sense_input * TGU_BITS_PER_SIGNAL) / LENGTH_REGISTER;
> +
> + if ((num_sense_input * TGU_BITS_PER_SIGNAL) % LENGTH_REGISTER)
> + num_reg++;
> +
> + drvdata->num_reg = num_reg;
> +}
> +
> +static void tgu_set_steps(struct tgu_drvdata *drvdata)
> +{
> + u32 devid;
> +
> + devid = readl(drvdata->base + TGU_DEVID);
> +
> + drvdata->num_step = TGU_DEVID_STEPS(devid);
> +}
> +
> static int tgu_enable(struct device *dev)
> {
> struct tgu_drvdata *drvdata = dev_get_drvdata(dev);
> @@ -121,6 +230,38 @@ static const struct attribute_group tgu_common_grp = {
>
> static const struct attribute_group *tgu_attr_groups[] = {
> &tgu_common_grp,
> + PRIORITY_ATTRIBUTE_GROUP_INIT(0, 0),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(0, 1),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(0, 2),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(0, 3),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(1, 0),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(1, 1),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(1, 2),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(1, 3),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(2, 0),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(2, 1),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(2, 2),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(2, 3),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(3, 0),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(3, 1),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(3, 2),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(3, 3),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(4, 0),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(4, 1),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(4, 2),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(4, 3),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(5, 0),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(5, 1),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(5, 2),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(5, 3),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(6, 0),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(6, 1),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(6, 2),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(6, 3),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(7, 0),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(7, 1),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(7, 2),
> + PRIORITY_ATTRIBUTE_GROUP_INIT(7, 3),
> NULL,
> };
>
> @@ -128,6 +269,8 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
> {
> struct device *dev = &adev->dev;
> struct tgu_drvdata *drvdata;
> + unsigned int *priority;
> + size_t priority_size;
> int ret;
>
> drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
> @@ -143,12 +286,30 @@ static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
>
> spin_lock_init(&drvdata->lock);
>
> + tgu_set_reg_number(drvdata);
> + tgu_set_steps(drvdata);
> +
> ret = sysfs_create_groups(&dev->kobj, tgu_attr_groups);
> if (ret) {
> dev_err(dev, "failed to create sysfs groups: %d\n", ret);
> return ret;
> }
>
> + drvdata->value_table =
> + devm_kzalloc(dev, sizeof(*drvdata->value_table), GFP_KERNEL);
> + if (!drvdata->value_table)
> + return -ENOMEM;
> +
> + priority_size = MAX_PRIORITY * drvdata->num_reg * drvdata->num_step;
> +
> + priority = devm_kcalloc(dev, priority_size,
> + sizeof(*drvdata->value_table->priority),
> + GFP_KERNEL);
> + if (!priority)
> + return -ENOMEM;
> +
> + drvdata->value_table->priority = priority;
> +
> drvdata->enabled = false;
>
> pm_runtime_put(&adev->dev);
> diff --git a/drivers/hwtracing/qcom/tgu.h b/drivers/hwtracing/qcom/tgu.h
> index dd7533b9d735..df570c89ffd7 100644
> --- a/drivers/hwtracing/qcom/tgu.h
> +++ b/drivers/hwtracing/qcom/tgu.h
> @@ -10,6 +10,114 @@
> #define TGU_CONTROL 0x0000
> #define TGU_LAR 0xfb0
> #define TGU_UNLOCK_OFFSET 0xc5acce55
> +#define TGU_DEVID 0xfc8
> +
> +#define TGU_DEVID_SENSE_INPUT(devid_val) \
> + ((int)FIELD_GET(GENMASK(17, 10), devid_val))
> +#define TGU_DEVID_STEPS(devid_val) \
> + ((int)FIELD_GET(GENMASK(6, 3), devid_val))
> +#define TGU_BITS_PER_SIGNAL 4
> +#define LENGTH_REGISTER 32
> +
> +/*
> + * TGU configuration space Step configuration
> + * offset table space layout
> + * x-------------------------x$ x-------------x$
> + * | |$ | |$
> + * | | | reserve |$
> + * | | | |$
> + * |coresight management | |-------------|base+n*0x1D8+0x1F4$
> + * | registe | |---> |prioroty[3] |$
s/registe/registers
s/prioroty/priority
Thanks,
Jie
> + * | | | |-------------|base+n*0x1D8+0x194$
> + * | | | |prioroty[2] |$
> + * |-------------------------| | |-------------|base+n*0x1D8+0x134$
> + * | | | |prioroty[1] |$
> + * | step[7] | | |-------------|base+n*0x1D8+0xD4$
> + * |-------------------------|->base+0x40+7*0x1D8 | |prioroty[0] |$
> + * | | | |-------------|base+n*0x1D8+0x74$
> + * | ... | | | condition |$
> + * | | | | select |$
> + * |-------------------------|->base+0x40+1*0x1D8 | |-------------|base+n*0x1D8+0x60$
> + * | | | | condition |$
> + * | step[0] |--------------------> | decode |$
> + * |-------------------------|-> base+0x40 |-------------|base+n*0x1D8+0x50$
> + * | | | |$
> + * | Control and status space| |Timer/Counter|$
> + * | space | | |$
> + * x-------------------------x->base x-------------x base+n*0x1D8+0x40$
> + *
> + */
> +#define STEP_OFFSET 0x1D8
> +#define PRIORITY_START_OFFSET 0x0074
> +#define PRIORITY_OFFSET 0x60
> +#define REG_OFFSET 0x4
> +
> +/* Calculate compare step addresses */
> +#define PRIORITY_REG_STEP(step, priority, reg)\
> + (PRIORITY_START_OFFSET + PRIORITY_OFFSET * priority +\
> + REG_OFFSET * reg + STEP_OFFSET * step)
> +
> +#define tgu_dataset_rw(name, step_index, type, reg_num) \
> + (&((struct tgu_attribute[]){ { \
> + __ATTR(name, 0644, tgu_dataset_show, tgu_dataset_store), \
> + step_index, \
> + type, \
> + reg_num, \
> + } })[0].attr.attr)
> +
> +#define STEP_PRIORITY(step_index, reg_num, priority) \
> + tgu_dataset_rw(reg##reg_num, step_index, TGU_PRIORITY##priority, \
> + reg_num)
> +
> +#define STEP_PRIORITY_LIST(step_index, priority) \
> + {STEP_PRIORITY(step_index, 0, priority), \
> + STEP_PRIORITY(step_index, 1, priority), \
> + STEP_PRIORITY(step_index, 2, priority), \
> + STEP_PRIORITY(step_index, 3, priority), \
> + STEP_PRIORITY(step_index, 4, priority), \
> + STEP_PRIORITY(step_index, 5, priority), \
> + STEP_PRIORITY(step_index, 6, priority), \
> + STEP_PRIORITY(step_index, 7, priority), \
> + STEP_PRIORITY(step_index, 8, priority), \
> + STEP_PRIORITY(step_index, 9, priority), \
> + STEP_PRIORITY(step_index, 10, priority), \
> + STEP_PRIORITY(step_index, 11, priority), \
> + STEP_PRIORITY(step_index, 12, priority), \
> + STEP_PRIORITY(step_index, 13, priority), \
> + STEP_PRIORITY(step_index, 14, priority), \
> + STEP_PRIORITY(step_index, 15, priority), \
> + STEP_PRIORITY(step_index, 16, priority), \
> + STEP_PRIORITY(step_index, 17, priority), \
> + NULL \
> + }
> +
> +#define PRIORITY_ATTRIBUTE_GROUP_INIT(step, priority)\
> + (&(const struct attribute_group){\
> + .attrs = (struct attribute*[])STEP_PRIORITY_LIST(step, priority),\
> + .is_visible = tgu_node_visible,\
> + .name = "step" #step "_priority" #priority \
> + })
> +
> +enum operation_index {
> + TGU_PRIORITY0,
> + TGU_PRIORITY1,
> + TGU_PRIORITY2,
> + TGU_PRIORITY3,
> +};
> +
> +/* Maximum priority that TGU supports */
> +#define MAX_PRIORITY 4
> +
> +struct tgu_attribute {
> + struct device_attribute attr;
> + u32 step_index;
> + enum operation_index operation_index;
> + u32 reg_num;
> +};
> +
> +struct value_table {
> + unsigned int *priority;
> +};
>
> static inline void TGU_LOCK(void __iomem *addr)
> {
> @@ -35,6 +143,9 @@ static inline void TGU_UNLOCK(void __iomem *addr)
> * @dev: Pointer to the associated device structure
> * @lock: Spinlock for handling concurrent access to private data
> * @enabled: Flag indicating whether the TGU device is enabled
> + * @value_table: Store given value based on relevant parameters
> + * @num_reg: Maximum number of registers
> + * @num_step: Maximum step size
> *
> * This structure defines the data associated with a TGU device,
> * including its base address, device pointers, clock, spinlock for
> @@ -46,6 +157,9 @@ struct tgu_drvdata {
> struct device *dev;
> spinlock_t lock;
> bool enabled;
> + struct value_table *value_table;
> + int num_reg;
> + int num_step;
> };
>
> #endif
^ permalink raw reply
* Re: [PATCH v13 2/7] qcom-tgu: Add TGU driver
From: Jie Gan @ 2026-04-02 10:18 UTC (permalink / raw)
To: Songwei Chai, andersson, alexander.shishkin, mike.leach,
konrad.dybcio, suzuki.poulose, james.clark, krzk+dt, conor+dt
Cc: linux-kernel, linux-arm-kernel, linux-arm-msm, coresight,
devicetree, gregkh
In-Reply-To: <20260402092838.341295-3-songwei.chai@oss.qualcomm.com>
On 4/2/2026 5:28 PM, Songwei Chai wrote:
> Add driver to support device TGU (Trigger Generation Unit).
> TGU is a Data Engine which can be utilized to sense a plurality of
> signals and create a trigger into the CTI or generate interrupts to
> processors. Add probe/enable/disable functions for tgu.
>
> Signed-off-by: Songwei Chai <songwei.chai@oss.qualcomm.com>
> ---
> .../ABI/testing/sysfs-bus-amba-devices-tgu | 9 +
> drivers/Makefile | 1 +
> drivers/hwtracing/Kconfig | 2 +
> drivers/hwtracing/qcom/Kconfig | 18 ++
> drivers/hwtracing/qcom/Makefile | 3 +
> drivers/hwtracing/qcom/tgu.c | 193 ++++++++++++++++++
> drivers/hwtracing/qcom/tgu.h | 51 +++++
> 7 files changed, 277 insertions(+)
> create mode 100644 Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
> create mode 100644 drivers/hwtracing/qcom/Kconfig
> create mode 100644 drivers/hwtracing/qcom/Makefile
> create mode 100644 drivers/hwtracing/qcom/tgu.c
> create mode 100644 drivers/hwtracing/qcom/tgu.h
>
> diff --git a/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
> new file mode 100644
> index 000000000000..f877a00fcaa5
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-amba-devices-tgu
> @@ -0,0 +1,9 @@
> +What: /sys/bus/amba/devices/<tgu-name>/enable_tgu
> +Date: April 2026
> +KernelVersion: 7.1
> +Contact: Jinlong Mao <jinlong.mao@oss.qualcomm.com>, Songwei Chai <songwei.chai@oss.qualcomm.com>
> +Description:
> + (RW) Set/Get the enable/disable status of TGU
> + Accepts only one of the 2 values - 0 or 1.
> + 0 : disable TGU.
> + 1 : enable TGU.
> diff --git a/drivers/Makefile b/drivers/Makefile
> index 53fbd2e0acdd..82b712a12a26 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -177,6 +177,7 @@ obj-$(CONFIG_RAS) += ras/
> obj-$(CONFIG_USB4) += thunderbolt/
> obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/
> obj-y += hwtracing/intel_th/
> +obj-y += hwtracing/qcom/
> obj-$(CONFIG_STM) += hwtracing/stm/
> obj-$(CONFIG_HISI_PTT) += hwtracing/ptt/
> obj-y += android/
> diff --git a/drivers/hwtracing/Kconfig b/drivers/hwtracing/Kconfig
> index 911ee977103c..8a640218eed8 100644
> --- a/drivers/hwtracing/Kconfig
> +++ b/drivers/hwtracing/Kconfig
> @@ -7,4 +7,6 @@ source "drivers/hwtracing/intel_th/Kconfig"
>
> source "drivers/hwtracing/ptt/Kconfig"
>
> +source "drivers/hwtracing/qcom/Kconfig"
> +
> endmenu
> diff --git a/drivers/hwtracing/qcom/Kconfig b/drivers/hwtracing/qcom/Kconfig
> new file mode 100644
> index 000000000000..d6f6d4b0f28e
> --- /dev/null
> +++ b/drivers/hwtracing/qcom/Kconfig
> @@ -0,0 +1,18 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +#
> +# QCOM specific hwtracing drivers
> +#
> +menu "Qualcomm specific hwtracing drivers"
> +
> +config QCOM_TGU
> + tristate "QCOM Trigger Generation Unit driver"
depends on ARCH_QCOM || COMPILE_TEST
depends on ARM_AMBA
> + help
> + This driver provides support for Trigger Generation Unit that is
> + used to detect patterns or sequences on a given set of signals.
> + TGU is used to monitor a particular bus within a given region to
> + detect illegal transaction sequences or slave responses. It is also
> + used to monitor a data stream to detect protocol violations and to
> + provide a trigger point for centering data around a specific event
> + within the trace data buffer.
> +
> +endmenu
> diff --git a/drivers/hwtracing/qcom/Makefile b/drivers/hwtracing/qcom/Makefile
> new file mode 100644
> index 000000000000..5a0a868c1ea0
> --- /dev/null
> +++ b/drivers/hwtracing/qcom/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-$(CONFIG_QCOM_TGU) += tgu.o
> diff --git a/drivers/hwtracing/qcom/tgu.c b/drivers/hwtracing/qcom/tgu.c
> new file mode 100644
> index 000000000000..49c8f710b931
> --- /dev/null
> +++ b/drivers/hwtracing/qcom/tgu.c
> @@ -0,0 +1,193 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#include <linux/amba/bus.h>
> +#include <linux/device.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/pm_runtime.h>
> +
> +#include "tgu.h"
> +
> +static void tgu_write_all_hw_regs(struct tgu_drvdata *drvdata)
> +{
> + TGU_UNLOCK(drvdata->base);
> + /* Enable TGU to program the triggers */
> + writel(1, drvdata->base + TGU_CONTROL);
> + TGU_LOCK(drvdata->base);
> +}
> +
> +static int tgu_enable(struct device *dev)
> +{
> + struct tgu_drvdata *drvdata = dev_get_drvdata(dev);
> +
> + guard(spinlock)(&drvdata->lock);
> + drvdata->enabled = true;
> +
> + tgu_write_all_hw_regs(drvdata);
> +
> + return 0;
> +}
> +
> +static void tgu_do_disable(struct tgu_drvdata *drvdata)
> +{
> + TGU_UNLOCK(drvdata->base);
> + writel(0, drvdata->base + TGU_CONTROL);
> + TGU_LOCK(drvdata->base);
> +
> + drvdata->enabled = false;
> +}
> +
> +static void tgu_disable(struct device *dev)
> +{
> + struct tgu_drvdata *drvdata = dev_get_drvdata(dev);
> +
> + guard(spinlock)(&drvdata->lock);
> + if (!drvdata->enabled)
> + return;
> +
> + tgu_do_disable(drvdata);
> +}
> +
> +static ssize_t enable_tgu_show(struct device *dev,
> + struct device_attribute *attr, char *buf)
> +{
> + struct tgu_drvdata *drvdata = dev_get_drvdata(dev);
> + bool enabled;
> +
> + guard(spinlock)(&drvdata->lock);
> + enabled = drvdata->enabled;
> +
> + return sysfs_emit(buf, "%d\n", !!enabled);
> +}
> +
> +/* enable_tgu_store - Configure Trace and Gating Unit (TGU) triggers. */
> +static ssize_t enable_tgu_store(struct device *dev,
> + struct device_attribute *attr,
> + const char *buf,
> + size_t size)
> +{
> + struct tgu_drvdata *drvdata = dev_get_drvdata(dev);
> + unsigned long val;
> + int ret;
> +
> + ret = kstrtoul(buf, 0, &val);
> + if (ret || val > 1)
> + return -EINVAL;
> +
> + if (val) {
> + scoped_guard(spinlock, &drvdata->lock) {
> + if (drvdata->enabled)
> + return -EBUSY;
> + }
> +
> + ret = pm_runtime_resume_and_get(dev);
> + if (ret)
> + return ret;
> +
> + ret = tgu_enable(dev);
> + if (ret) {
> + pm_runtime_put(dev);
> + return ret;
> + }
> + } else {
> + scoped_guard(spinlock, &drvdata->lock) {
> + if (!drvdata->enabled)
> + return -EINVAL;
> + }
> +
> + tgu_disable(dev);
> + pm_runtime_put(dev);
> + }
> +
> + return size;
> +}
> +static DEVICE_ATTR_RW(enable_tgu);
> +
> +static struct attribute *tgu_common_attrs[] = {
> + &dev_attr_enable_tgu.attr,
> + NULL,
> +};
> +
> +static const struct attribute_group tgu_common_grp = {
> + .attrs = tgu_common_attrs,
> + NULL,
> +};
> +
> +static const struct attribute_group *tgu_attr_groups[] = {
> + &tgu_common_grp,
> + NULL,
> +};
> +
> +static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
> +{
> + struct device *dev = &adev->dev;
> + struct tgu_drvdata *drvdata;
> + int ret;
> +
> + drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
> + if (!drvdata)
> + return -ENOMEM;
> +
> + drvdata->dev = &adev->dev;
> + dev_set_drvdata(dev, drvdata);
> +
> + drvdata->base = devm_ioremap_resource(dev, &adev->res);
> + if (IS_ERR(drvdata->base))
> + return PTR_ERR(drvdata->base);
pm_runtime_enable is called in amba_probe, and amba_probe calls
pm_runtime_put_noidle only when tgu_probe succeeds. We need call the
pm_runtime_put in error path to reset the runtime refcount.
> +
> + spin_lock_init(&drvdata->lock);
> +
> + ret = sysfs_create_groups(&dev->kobj, tgu_attr_groups);
> + if (ret) {
> + dev_err(dev, "failed to create sysfs groups: %d\n", ret);
> + return ret;
> + }
> +
> + drvdata->enabled = false;
> +
> + pm_runtime_put(&adev->dev);
> +
> + return 0;
> +}
> +
> +static void tgu_remove(struct amba_device *adev)
> +{
> + struct device *dev = &adev->dev;
> +
> + sysfs_remove_groups(&dev->kobj, tgu_attr_groups);
> +
> + tgu_disable(dev);
> +}
> +
> +static const struct amba_id tgu_ids[] = {
> + {
> + .id = 0x000f0e00,
> + .mask = 0x000fffff,
> + },
> + { 0, 0, NULL },
> +};
> +
> +MODULE_DEVICE_TABLE(amba, tgu_ids);
> +
> +static struct amba_driver tgu_driver = {
> + .drv = {
> + .name = "qcom-tgu",
> + .suppress_bind_attrs = true,
> + },
> + .probe = tgu_probe,
> + .remove = tgu_remove,
> + .id_table = tgu_ids,
> +};
> +
> +module_amba_driver(tgu_driver);
> +
> +MODULE_AUTHOR("Songwei Chai <songwei.chai@oss.qualcomm.com>");
> +MODULE_AUTHOR("Jinlong Mao <jinlong.mao@oss.qualcomm.com>");
> +MODULE_DESCRIPTION("Qualcomm Trigger Generation Unit driver");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/hwtracing/qcom/tgu.h b/drivers/hwtracing/qcom/tgu.h
> new file mode 100644
> index 000000000000..dd7533b9d735
> --- /dev/null
> +++ b/drivers/hwtracing/qcom/tgu.h
> @@ -0,0 +1,51 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#ifndef _QCOM_TGU_H
> +#define _QCOM_TGU_H
> +
> +/* Register addresses */
> +#define TGU_CONTROL 0x0000
> +#define TGU_LAR 0xfb0
nit: wrong indent here (you can check in vim)
Thanks,
Jie
> +#define TGU_UNLOCK_OFFSET 0xc5acce55
> +
> +static inline void TGU_LOCK(void __iomem *addr)
> +{
> + do {
> + /* Wait for things to settle */
> + mb();
> + writel_relaxed(0x0, addr + TGU_LAR);
> + } while (0);
> +}
> +
> +static inline void TGU_UNLOCK(void __iomem *addr)
> +{
> + do {
> + writel_relaxed(TGU_UNLOCK_OFFSET, addr + TGU_LAR);
> + /* Make sure everyone has seen this */
> + mb();
> + } while (0);
> +}
> +
> +/**
> + * struct tgu_drvdata - Data structure for a TGU (Trigger Generator Unit)
> + * @base: Memory-mapped base address of the TGU device
> + * @dev: Pointer to the associated device structure
> + * @lock: Spinlock for handling concurrent access to private data
> + * @enabled: Flag indicating whether the TGU device is enabled
> + *
> + * This structure defines the data associated with a TGU device,
> + * including its base address, device pointers, clock, spinlock for
> + * synchronization, trigger data pointers, maximum limits for various
> + * trigger-related parameters, and enable status.
> + */
> +struct tgu_drvdata {
> + void __iomem *base;
> + struct device *dev;
> + spinlock_t lock;
> + bool enabled;
> +};
> +
> +#endif
^ permalink raw reply
* [PATCH v4 4/4] arm64: errata: Work around early CME DVMSync acknowledgement
From: Catalin Marinas @ 2026-04-02 10:12 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: Will Deacon, James Morse, Mark Rutland, Mark Brown
In-Reply-To: <20260402101246.3870036-1-catalin.marinas@arm.com>
C1-Pro acknowledges DVMSync messages before completing the SME/CME
memory accesses. Work around this by issuing an IPI to the affected CPUs
if they are running in EL0 with SME enabled.
Note that we avoid the local DSB in the IPI handler as the kernel runs
with SCTLR_EL1.IESB=1. This is sufficient to complete SME memory
accesses at EL0 on taking an exception to EL1. On the return to user
path, no barrier is necessary either. See the comment in
sme_set_active() and the more detailed explanation in the link below.
To avoid a potential IPI flood from malicious applications (e.g.
madvise(MADV_PAGEOUT) in a tight loop), track where a process is active
via mm_cpumask() and only interrupt those CPUs.
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Link: https://lore.kernel.org/r/ablEXwhfKyJW1i7l@J2N7QTR9R3
Cc: Will Deacon <will@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: James Morse <james.morse@arm.com>
Cc: Mark Brown <broonie@kernel.org>
---
Documentation/arch/arm64/silicon-errata.rst | 2 +
arch/arm64/Kconfig | 12 +++
arch/arm64/include/asm/cpucaps.h | 2 +
arch/arm64/include/asm/fpsimd.h | 21 +++++
arch/arm64/include/asm/tlbbatch.h | 10 ++-
arch/arm64/include/asm/tlbflush.h | 72 ++++++++++++++++-
arch/arm64/kernel/cpu_errata.c | 30 +++++++
arch/arm64/kernel/entry-common.c | 3 +
arch/arm64/kernel/fpsimd.c | 90 +++++++++++++++++++++
arch/arm64/kernel/process.c | 37 +++++++++
arch/arm64/tools/cpucaps | 1 +
11 files changed, 276 insertions(+), 4 deletions(-)
diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst
index 4c300caad901..282ad4257983 100644
--- a/Documentation/arch/arm64/silicon-errata.rst
+++ b/Documentation/arch/arm64/silicon-errata.rst
@@ -202,6 +202,8 @@ stable kernels.
+----------------+-----------------+-----------------+-----------------------------+
| ARM | Neoverse-V3AE | #3312417 | ARM64_ERRATUM_3194386 |
+----------------+-----------------+-----------------+-----------------------------+
+| ARM | C1-Pro | #4193714 | ARM64_ERRATUM_4193714 |
++----------------+-----------------+-----------------+-----------------------------+
| ARM | MMU-500 | #841119,826419 | ARM_SMMU_MMU_500_CPRE_ERRATA|
| | | #562869,1047329 | |
+----------------+-----------------+-----------------+-----------------------------+
diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
index 38dba5f7e4d2..9b419f1a9ae6 100644
--- a/arch/arm64/Kconfig
+++ b/arch/arm64/Kconfig
@@ -1175,6 +1175,18 @@ config ARM64_ERRATUM_4311569
If unsure, say Y.
+config ARM64_ERRATUM_4193714
+ bool "C1-Pro: 4193714: SME DVMSync early acknowledgement"
+ depends on ARM64_SME
+ default y
+ help
+ Enable workaround for C1-Pro acknowledging the DVMSync before
+ the SME memory accesses are complete. This will cause TLB
+ maintenance for processes using SME to also issue an IPI to
+ the affected CPUs.
+
+ If unsure, say Y.
+
config CAVIUM_ERRATUM_22375
bool "Cavium erratum 22375, 24313"
default y
diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h
index 177c691914f8..0b1b78a4c03e 100644
--- a/arch/arm64/include/asm/cpucaps.h
+++ b/arch/arm64/include/asm/cpucaps.h
@@ -64,6 +64,8 @@ cpucap_is_possible(const unsigned int cap)
return IS_ENABLED(CONFIG_ARM64_WORKAROUND_REPEAT_TLBI);
case ARM64_WORKAROUND_SPECULATIVE_SSBS:
return IS_ENABLED(CONFIG_ARM64_ERRATUM_3194386);
+ case ARM64_WORKAROUND_4193714:
+ return IS_ENABLED(CONFIG_ARM64_ERRATUM_4193714);
case ARM64_MPAM:
/*
* KVM MPAM support doesn't rely on the host kernel supporting MPAM.
diff --git a/arch/arm64/include/asm/fpsimd.h b/arch/arm64/include/asm/fpsimd.h
index 1d2e33559bd5..d9d00b45ab11 100644
--- a/arch/arm64/include/asm/fpsimd.h
+++ b/arch/arm64/include/asm/fpsimd.h
@@ -428,6 +428,24 @@ static inline size_t sme_state_size(struct task_struct const *task)
return __sme_state_size(task_get_sme_vl(task));
}
+void sme_enable_dvmsync(void);
+void sme_set_active(void);
+void sme_clear_active(void);
+
+static inline void sme_enter_from_user_mode(void)
+{
+ if (alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714) &&
+ test_thread_flag(TIF_SME))
+ sme_clear_active();
+}
+
+static inline void sme_exit_to_user_mode(void)
+{
+ if (alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714) &&
+ test_thread_flag(TIF_SME))
+ sme_set_active();
+}
+
#else
static inline void sme_user_disable(void) { BUILD_BUG(); }
@@ -456,6 +474,9 @@ static inline size_t sme_state_size(struct task_struct const *task)
return 0;
}
+static inline void sme_enter_from_user_mode(void) { }
+static inline void sme_exit_to_user_mode(void) { }
+
#endif /* ! CONFIG_ARM64_SME */
/* For use by EFI runtime services calls only */
diff --git a/arch/arm64/include/asm/tlbbatch.h b/arch/arm64/include/asm/tlbbatch.h
index fedb0b87b8db..6297631532e5 100644
--- a/arch/arm64/include/asm/tlbbatch.h
+++ b/arch/arm64/include/asm/tlbbatch.h
@@ -2,11 +2,17 @@
#ifndef _ARCH_ARM64_TLBBATCH_H
#define _ARCH_ARM64_TLBBATCH_H
+#include <linux/cpumask.h>
+
struct arch_tlbflush_unmap_batch {
+#ifdef CONFIG_ARM64_ERRATUM_4193714
/*
- * For arm64, HW can do tlb shootdown, so we don't
- * need to record cpumask for sending IPI
+ * Track CPUs that need SME DVMSync on completion of this batch.
+ * Otherwise, the arm64 HW can do tlb shootdown, so we don't need to
+ * record cpumask for sending IPI
*/
+ cpumask_var_t cpumask;
+#endif
};
#endif /* _ARCH_ARM64_TLBBATCH_H */
diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h
index 262791191935..3aa19831d0c2 100644
--- a/arch/arm64/include/asm/tlbflush.h
+++ b/arch/arm64/include/asm/tlbflush.h
@@ -80,6 +80,71 @@ static inline unsigned long get_trans_granule(void)
}
}
+#ifdef CONFIG_ARM64_ERRATUM_4193714
+
+void sme_do_dvmsync(const struct cpumask *mask);
+
+static inline void sme_dvmsync(struct mm_struct *mm)
+{
+ if (!alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714))
+ return;
+
+ sme_do_dvmsync(mm_cpumask(mm));
+}
+
+static inline void sme_dvmsync_add_pending(struct arch_tlbflush_unmap_batch *batch,
+ struct mm_struct *mm)
+{
+ if (!alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714))
+ return;
+
+ /*
+ * Order the mm_cpumask() read after the hardware DVMSync.
+ */
+ dsb(ish);
+ if (cpumask_empty(mm_cpumask(mm)))
+ return;
+
+ /*
+ * Allocate the batch cpumask on first use. If this fails, fall back to
+ * an immediate IPI for this mm.
+ */
+ if (!cpumask_available(batch->cpumask) &&
+ !zalloc_cpumask_var(&batch->cpumask, GFP_ATOMIC)) {
+ sme_do_dvmsync(mm_cpumask(mm));
+ return;
+ }
+
+ cpumask_or(batch->cpumask, batch->cpumask, mm_cpumask(mm));
+}
+
+static inline void sme_dvmsync_batch(struct arch_tlbflush_unmap_batch *batch)
+{
+ if (!alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714))
+ return;
+
+ if (!cpumask_available(batch->cpumask))
+ return;
+
+ sme_do_dvmsync(batch->cpumask);
+ cpumask_clear(batch->cpumask);
+}
+
+#else
+
+static inline void sme_dvmsync(struct mm_struct *mm)
+{
+}
+static inline void sme_dvmsync_add_pending(struct arch_tlbflush_unmap_batch *batch,
+ struct mm_struct *mm)
+{
+}
+static inline void sme_dvmsync_batch(struct arch_tlbflush_unmap_batch *batch)
+{
+}
+
+#endif /* CONFIG_ARM64_ERRATUM_4193714 */
+
/*
* Level-based TLBI operations.
*
@@ -189,12 +254,14 @@ static inline void __tlbi_sync_s1ish(struct mm_struct *mm)
{
dsb(ish);
__repeat_tlbi_sync(vale1is, 0);
+ sme_dvmsync(mm);
}
-static inline void __tlbi_sync_s1ish_batch(void)
+static inline void __tlbi_sync_s1ish_batch(struct arch_tlbflush_unmap_batch *batch)
{
dsb(ish);
__repeat_tlbi_sync(vale1is, 0);
+ sme_dvmsync_batch(batch);
}
static inline void __tlbi_sync_s1ish_kernel(void)
@@ -397,7 +464,7 @@ static inline bool arch_tlbbatch_should_defer(struct mm_struct *mm)
*/
static inline void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch)
{
- __tlbi_sync_s1ish_batch();
+ __tlbi_sync_s1ish_batch(batch);
}
/*
@@ -602,6 +669,7 @@ static inline void arch_tlbbatch_add_pending(struct arch_tlbflush_unmap_batch *b
struct mm_struct *mm, unsigned long start, unsigned long end)
{
__flush_tlb_range_nosync(mm, start, end, PAGE_SIZE, true, 3);
+ sme_dvmsync_add_pending(batch, mm);
}
static inline bool __pte_flags_need_flush(ptdesc_t oldval, ptdesc_t newval)
diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
index 5c0ab6bfd44a..5377e4c2eba2 100644
--- a/arch/arm64/kernel/cpu_errata.c
+++ b/arch/arm64/kernel/cpu_errata.c
@@ -11,6 +11,7 @@
#include <asm/cpu.h>
#include <asm/cputype.h>
#include <asm/cpufeature.h>
+#include <asm/fpsimd.h>
#include <asm/kvm_asm.h>
#include <asm/smp_plat.h>
@@ -575,6 +576,23 @@ static const struct midr_range erratum_spec_ssbs_list[] = {
};
#endif
+#ifdef CONFIG_ARM64_ERRATUM_4193714
+static bool has_sme_dvmsync_erratum(const struct arm64_cpu_capabilities *entry,
+ int scope)
+{
+ if (!id_aa64pfr1_sme(read_sanitised_ftr_reg(SYS_ID_AA64PFR1_EL1)))
+ return false;
+
+ return is_affected_midr_range(entry, scope);
+}
+
+static void cpu_enable_sme_dvmsync(const struct arm64_cpu_capabilities *__unused)
+{
+ if (this_cpu_has_cap(ARM64_WORKAROUND_4193714))
+ sme_enable_dvmsync();
+}
+#endif
+
#ifdef CONFIG_AMPERE_ERRATUM_AC03_CPU_38
static const struct midr_range erratum_ac03_cpu_38_list[] = {
MIDR_ALL_VERSIONS(MIDR_AMPERE1),
@@ -901,6 +919,18 @@ const struct arm64_cpu_capabilities arm64_errata[] = {
.matches = need_arm_si_l1_workaround_4311569,
},
#endif
+#ifdef CONFIG_ARM64_ERRATUM_4193714
+ {
+ .desc = "C1-Pro SME DVMSync early acknowledgement",
+ .capability = ARM64_WORKAROUND_4193714,
+ .type = ARM64_CPUCAP_LOCAL_CPU_ERRATUM,
+ .matches = has_sme_dvmsync_erratum,
+ .cpu_enable = cpu_enable_sme_dvmsync,
+ /* C1-Pro r0p0 - r1p2 (the latter only when REVIDR_EL1[0]==0) */
+ .midr_range = MIDR_RANGE(MIDR_C1_PRO, 0, 0, 1, 2),
+ MIDR_FIXED(MIDR_CPU_VAR_REV(1, 2), BIT(0)),
+ },
+#endif
#ifdef CONFIG_ARM64_WORKAROUND_SPECULATIVE_UNPRIV_LOAD
{
.desc = "ARM errata 2966298, 3117295",
diff --git a/arch/arm64/kernel/entry-common.c b/arch/arm64/kernel/entry-common.c
index 3625797e9ee8..fb1e374af622 100644
--- a/arch/arm64/kernel/entry-common.c
+++ b/arch/arm64/kernel/entry-common.c
@@ -21,6 +21,7 @@
#include <asm/daifflags.h>
#include <asm/esr.h>
#include <asm/exception.h>
+#include <asm/fpsimd.h>
#include <asm/irq_regs.h>
#include <asm/kprobes.h>
#include <asm/mmu.h>
@@ -67,6 +68,7 @@ static __always_inline void arm64_enter_from_user_mode(struct pt_regs *regs)
{
enter_from_user_mode(regs);
mte_disable_tco_entry(current);
+ sme_enter_from_user_mode();
}
/*
@@ -80,6 +82,7 @@ static __always_inline void arm64_exit_to_user_mode(struct pt_regs *regs)
local_irq_disable();
exit_to_user_mode_prepare_legacy(regs);
local_daif_mask();
+ sme_exit_to_user_mode();
mte_check_tfsr_exit();
exit_to_user_mode();
}
diff --git a/arch/arm64/kernel/fpsimd.c b/arch/arm64/kernel/fpsimd.c
index 9de1d8a604cb..7b497ba5395f 100644
--- a/arch/arm64/kernel/fpsimd.c
+++ b/arch/arm64/kernel/fpsimd.c
@@ -15,6 +15,7 @@
#include <linux/compiler.h>
#include <linux/cpu.h>
#include <linux/cpu_pm.h>
+#include <linux/cpumask.h>
#include <linux/ctype.h>
#include <linux/kernel.h>
#include <linux/linkage.h>
@@ -28,6 +29,7 @@
#include <linux/sched/task_stack.h>
#include <linux/signal.h>
#include <linux/slab.h>
+#include <linux/smp.h>
#include <linux/stddef.h>
#include <linux/sysctl.h>
#include <linux/swab.h>
@@ -1358,6 +1360,94 @@ void do_sve_acc(unsigned long esr, struct pt_regs *regs)
put_cpu_fpsimd_context();
}
+#ifdef CONFIG_ARM64_ERRATUM_4193714
+
+/*
+ * SME/CME erratum handling
+ */
+static cpumask_var_t sme_dvmsync_cpus;
+static DEFINE_RAW_SPINLOCK(sme_dvmsync_init_lock);
+
+/*
+ * These helpers are only called from non-preemptible contexts, so
+ * smp_processor_id() is safe here.
+ */
+void sme_set_active(void)
+{
+ unsigned int cpu = smp_processor_id();
+
+ if (!cpumask_test_cpu(cpu, sme_dvmsync_cpus))
+ return;
+
+ cpumask_set_cpu(cpu, mm_cpumask(current->mm));
+
+ /*
+ * A subsequent (post ERET) SME access may use a stale address
+ * translation. On C1-Pro, a TLBI+DSB on a different CPU will wait for
+ * the completion of cpumask_set_cpu() above as it appears in program
+ * order before the SME access. The post-TLBI+DSB read of mm_cpumask()
+ * will lead to the IPI being issued.
+ *
+ * https://lore.kernel.org/r/ablEXwhfKyJW1i7l@J2N7QTR9R3
+ */
+}
+
+void sme_clear_active(void)
+{
+ unsigned int cpu = smp_processor_id();
+
+ if (!cpumask_test_cpu(cpu, sme_dvmsync_cpus))
+ return;
+
+ /*
+ * With SCTLR_EL1.IESB enabled, the SME memory transactions are
+ * completed on entering EL1.
+ */
+ cpumask_clear_cpu(cpu, mm_cpumask(current->mm));
+}
+
+static void sme_dvmsync_ipi(void *unused)
+{
+ /*
+ * With SCTLR_EL1.IESB on, taking an exception is sufficient to ensure
+ * the completion of the SME memory accesses, so no need for an
+ * explicit DSB.
+ */
+}
+
+void sme_do_dvmsync(const struct cpumask *mask)
+{
+ /*
+ * This is called from the TLB maintenance functions after the DSB ISH
+ * to send the hardware DVMSync message. If this CPU sees the mask as
+ * empty, the remote CPU executing sme_set_active() would have seen
+ * the DVMSync and no IPI required.
+ */
+ if (cpumask_empty(mask))
+ return;
+
+ preempt_disable();
+ smp_call_function_many(mask, sme_dvmsync_ipi, NULL, true);
+ preempt_enable();
+}
+
+void sme_enable_dvmsync(void)
+{
+ /*
+ * stop_machine() will invoke this function concurrently on all
+ * affected CPUs. Serialise the initialisation.
+ */
+ raw_spin_lock(&sme_dvmsync_init_lock);
+ if (!cpumask_available(sme_dvmsync_cpus) &&
+ !zalloc_cpumask_var(&sme_dvmsync_cpus, GFP_ATOMIC))
+ panic("Unable to allocate cpumasks for the SME DVMSync erratum");
+ raw_spin_unlock(&sme_dvmsync_init_lock);
+
+ cpumask_set_cpu(smp_processor_id(), sme_dvmsync_cpus);
+}
+
+#endif /* CONFIG_ARM64_ERRATUM_4193714 */
+
/*
* Trapped SME access
*
diff --git a/arch/arm64/kernel/process.c b/arch/arm64/kernel/process.c
index 489554931231..88426d8ae11c 100644
--- a/arch/arm64/kernel/process.c
+++ b/arch/arm64/kernel/process.c
@@ -26,6 +26,7 @@
#include <linux/reboot.h>
#include <linux/interrupt.h>
#include <linux/init.h>
+#include <linux/cpumask.h>
#include <linux/cpu.h>
#include <linux/elfcore.h>
#include <linux/pm.h>
@@ -339,8 +340,41 @@ void flush_thread(void)
flush_gcs();
}
+#ifdef CONFIG_ARM64_ERRATUM_4193714
+
+static int arch_dup_tlbbatch_mask(struct task_struct *dst)
+{
+ if (!alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714))
+ return 0;
+
+ if (!zalloc_cpumask_var(&dst->tlb_ubc.arch.cpumask, GFP_KERNEL))
+ return -ENOMEM;
+
+ return 0;
+}
+
+static void arch_release_tlbbatch_mask(struct task_struct *tsk)
+{
+ if (alternative_has_cap_unlikely(ARM64_WORKAROUND_4193714))
+ free_cpumask_var(tsk->tlb_ubc.arch.cpumask);
+}
+
+#else
+
+static int arch_dup_tlbbatch_mask(struct task_struct *dst)
+{
+ return 0;
+}
+
+static void arch_release_tlbbatch_mask(struct task_struct *tsk)
+{
+}
+
+#endif /* CONFIG_ARM64_ERRATUM_4193714 */
+
void arch_release_task_struct(struct task_struct *tsk)
{
+ arch_release_tlbbatch_mask(tsk);
fpsimd_release_task(tsk);
}
@@ -356,6 +390,9 @@ int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
*dst = *src;
+ if (arch_dup_tlbbatch_mask(dst))
+ return -ENOMEM;
+
/*
* Drop stale reference to src's sve_state and convert dst to
* non-streaming FPSIMD mode.
diff --git a/arch/arm64/tools/cpucaps b/arch/arm64/tools/cpucaps
index 7261553b644b..8946be60a409 100644
--- a/arch/arm64/tools/cpucaps
+++ b/arch/arm64/tools/cpucaps
@@ -105,6 +105,7 @@ WORKAROUND_2077057
WORKAROUND_2457168
WORKAROUND_2645198
WORKAROUND_2658417
+WORKAROUND_4193714
WORKAROUND_4311569
WORKAROUND_AMPERE_AC03_CPU_38
WORKAROUND_AMPERE_AC04_CPU_23
^ permalink raw reply related
* [PATCH v4 3/4] arm64: cputype: Add C1-Pro definitions
From: Catalin Marinas @ 2026-04-02 10:12 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: Will Deacon, James Morse, Mark Rutland, Mark Brown
In-Reply-To: <20260402101246.3870036-1-catalin.marinas@arm.com>
Add cputype definitions for C1-Pro. These will be used for errata
detection in subsequent patches.
These values can be found in "Table A-303: MIDR_EL1 bit descriptions" in
issue 07 of the C1-Pro TRM:
https://documentation-service.arm.com/static/6930126730f8f55a656570af
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will@kernel.org>
Cc: James Morse <james.morse@arm.com>
---
arch/arm64/include/asm/cputype.h | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h
index 08860d482e60..7b518e81dd15 100644
--- a/arch/arm64/include/asm/cputype.h
+++ b/arch/arm64/include/asm/cputype.h
@@ -98,6 +98,7 @@
#define ARM_CPU_PART_CORTEX_A725 0xD87
#define ARM_CPU_PART_CORTEX_A720AE 0xD89
#define ARM_CPU_PART_NEOVERSE_N3 0xD8E
+#define ARM_CPU_PART_C1_PRO 0xD8B
#define APM_CPU_PART_XGENE 0x000
#define APM_CPU_VAR_POTENZA 0x00
@@ -189,6 +190,7 @@
#define MIDR_CORTEX_A725 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A725)
#define MIDR_CORTEX_A720AE MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A720AE)
#define MIDR_NEOVERSE_N3 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_NEOVERSE_N3)
+#define MIDR_C1_PRO MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_C1_PRO)
#define MIDR_THUNDERX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX)
#define MIDR_THUNDERX_81XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_81XX)
#define MIDR_THUNDERX_83XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_83XX)
^ permalink raw reply related
* [PATCH v4 0/4] arm64: Work around C1-Pro erratum 4193714 (CVE-2026-0995)
From: Catalin Marinas @ 2026-04-02 10:12 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: Will Deacon, James Morse, Mark Rutland, Mark Brown
Here's version 4 of the workaround for C1-Pro erratum 4193714. Version 3
was posted here:
https://lore.kernel.org/r/20260323162408.4163113-1-catalin.marinas@arm.com
The most significant change since v3 was the drop of the global
sme_dvmsync_cpus mask in favour of mm_cpumask(). Sashiko reported a
potential DoS if a malicious app does an madvise(MADV_PAGEOUT)
continuously. This would trigger a flood of IPIs to unrelated apps that
run at EL0 with SME on. We knew about this and deemed it low risk but I
thought I'd do some testing. On a Raspberry Pi 4 (no SME but hacked the
kernel to assume workaround always on) I can get about 30% drop in the
victim throughput due to the IPIs.
Since using mm_cpumask() to track which threads of an app run in
user-space with SME enabled, we can drop the MMCF_SME_DVMSYNC flag as
well. One downside is that arch_tlbbatch_add_pending() now needs a DSB
before checking the mm_cpumask() if the workaround is enabled. The
actual IPI is still batched. On hardware where this erratum is present
(small number of CPUs), I don't expect this to be noticeable.
Other changes since v3:
- Included acks from Mark Rutland but dropped it for the forth patch as
it was heavily reworked.
- Dropped the KVM workaround due to conflicts with next. I'll rebase
this patch it after -rc1. I also dropped the KVM and SMCCC people from
cc until the KVM part will be posted.
- Added a raw_spin_lock() around the sme_enable_dvmsync() function and
cpumask allocation. This function is called simultaneously on multiple
CPUs via stop_machine(). In practice, CONFIG_CPUMASK_OFFSTACK is
likely off on such hardware and no allocation needed.
I'll push the patches provisionally to for-next/c1-pro-erratum-4193714
for some exposure in -next. It doesn't necessarily mean they'll go
upstream for 7.1-rc1.
Erratum description:
Arm C1-Pro prior to r1p3 has an erratum (4193714) where a TLBI+DSB
sequence might fail to ensure the completion of all outstanding SME
(Scalable Matrix Extension) memory accesses. The DVMSync message is
acknowledged before the SME accesses have fully completed, potentially
allowing pages to be reused before all in-flight accesses are done.
The workaround consists of executing a DSB locally (via IPI)
on all affected CPUs running with SME enabled, after the TLB
invalidation. This ensures the SME accesses have completed before the
IPI is acknowledged.
This has been assigned CVE-2026-0995:
https://developer.arm.com/documentation/111823/latest/
Catalin Marinas (4):
arm64: tlb: Introduce __tlbi_sync_s1ish_{kernel,batch}() for TLB
maintenance
arm64: tlb: Pass the corresponding mm to __tlbi_sync_s1ish()
arm64: cputype: Add C1-Pro definitions
arm64: errata: Work around early CME DVMSync acknowledgement
Documentation/arch/arm64/silicon-errata.rst | 2 +
arch/arm64/Kconfig | 12 +++
arch/arm64/include/asm/cpucaps.h | 2 +
arch/arm64/include/asm/cputype.h | 2 +
arch/arm64/include/asm/fpsimd.h | 21 +++++
arch/arm64/include/asm/tlbbatch.h | 10 ++-
arch/arm64/include/asm/tlbflush.h | 96 +++++++++++++++++++--
arch/arm64/kernel/cpu_errata.c | 30 +++++++
arch/arm64/kernel/entry-common.c | 3 +
arch/arm64/kernel/fpsimd.c | 90 +++++++++++++++++++
arch/arm64/kernel/process.c | 37 ++++++++
arch/arm64/kernel/sys_compat.c | 2 +-
arch/arm64/tools/cpucaps | 1 +
13 files changed, 297 insertions(+), 11 deletions(-)
^ permalink raw reply
* [PATCH v4 2/4] arm64: tlb: Pass the corresponding mm to __tlbi_sync_s1ish()
From: Catalin Marinas @ 2026-04-02 10:12 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: Will Deacon, James Morse, Mark Rutland, Mark Brown
In-Reply-To: <20260402101246.3870036-1-catalin.marinas@arm.com>
The mm structure will be used for workarounds that need limiting to
specific tasks.
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will@kernel.org>
---
arch/arm64/include/asm/tlbflush.h | 8 ++++----
arch/arm64/kernel/sys_compat.c | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h
index f41eebf00990..262791191935 100644
--- a/arch/arm64/include/asm/tlbflush.h
+++ b/arch/arm64/include/asm/tlbflush.h
@@ -185,7 +185,7 @@ do { \
* Complete broadcast TLB maintenance issued by the host which invalidates
* stage 1 information in the host's own translation regime.
*/
-static inline void __tlbi_sync_s1ish(void)
+static inline void __tlbi_sync_s1ish(struct mm_struct *mm)
{
dsb(ish);
__repeat_tlbi_sync(vale1is, 0);
@@ -323,7 +323,7 @@ static inline void flush_tlb_mm(struct mm_struct *mm)
asid = __TLBI_VADDR(0, ASID(mm));
__tlbi(aside1is, asid);
__tlbi_user(aside1is, asid);
- __tlbi_sync_s1ish();
+ __tlbi_sync_s1ish(mm);
mmu_notifier_arch_invalidate_secondary_tlbs(mm, 0, -1UL);
}
@@ -377,7 +377,7 @@ static inline void flush_tlb_page(struct vm_area_struct *vma,
unsigned long uaddr)
{
flush_tlb_page_nosync(vma, uaddr);
- __tlbi_sync_s1ish();
+ __tlbi_sync_s1ish(vma->vm_mm);
}
static inline bool arch_tlbbatch_should_defer(struct mm_struct *mm)
@@ -532,7 +532,7 @@ static inline void __flush_tlb_range(struct vm_area_struct *vma,
{
__flush_tlb_range_nosync(vma->vm_mm, start, end, stride,
last_level, tlb_level);
- __tlbi_sync_s1ish();
+ __tlbi_sync_s1ish(vma->vm_mm);
}
static inline void local_flush_tlb_contpte(struct vm_area_struct *vma,
diff --git a/arch/arm64/kernel/sys_compat.c b/arch/arm64/kernel/sys_compat.c
index b9d4998c97ef..03fde2677d5b 100644
--- a/arch/arm64/kernel/sys_compat.c
+++ b/arch/arm64/kernel/sys_compat.c
@@ -37,7 +37,7 @@ __do_compat_cache_op(unsigned long start, unsigned long end)
* We pick the reserved-ASID to minimise the impact.
*/
__tlbi(aside1is, __TLBI_VADDR(0, 0));
- __tlbi_sync_s1ish();
+ __tlbi_sync_s1ish(current->mm);
}
ret = caches_clean_inval_user_pou(start, start + chunk);
^ permalink raw reply related
* [PATCH v4 1/4] arm64: tlb: Introduce __tlbi_sync_s1ish_{kernel,batch}() for TLB maintenance
From: Catalin Marinas @ 2026-04-02 10:12 UTC (permalink / raw)
To: linux-arm-kernel; +Cc: Will Deacon, James Morse, Mark Rutland, Mark Brown
In-Reply-To: <20260402101246.3870036-1-catalin.marinas@arm.com>
Add __tlbi_sync_s1ish_kernel() similar to __tlbi_sync_s1ish() and use it
for kernel TLB maintenance. Also use this function in flush_tlb_all()
which is only used in relation to kernel mappings. Subsequent patches
can differentiate between workarounds that apply to user only or both
user and kernel.
A subsequent patch will add mm_struct to __tlbi_sync_s1ish(). Since
arch_tlbbatch_flush() is not specific to an mm, add a corresponding
__tlbi_sync_s1ish_batch() helper.
Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
Acked-by: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will@kernel.org>
---
arch/arm64/include/asm/tlbflush.h | 20 ++++++++++++++++----
1 file changed, 16 insertions(+), 4 deletions(-)
diff --git a/arch/arm64/include/asm/tlbflush.h b/arch/arm64/include/asm/tlbflush.h
index 1416e652612b..f41eebf00990 100644
--- a/arch/arm64/include/asm/tlbflush.h
+++ b/arch/arm64/include/asm/tlbflush.h
@@ -191,6 +191,18 @@ static inline void __tlbi_sync_s1ish(void)
__repeat_tlbi_sync(vale1is, 0);
}
+static inline void __tlbi_sync_s1ish_batch(void)
+{
+ dsb(ish);
+ __repeat_tlbi_sync(vale1is, 0);
+}
+
+static inline void __tlbi_sync_s1ish_kernel(void)
+{
+ dsb(ish);
+ __repeat_tlbi_sync(vale1is, 0);
+}
+
/*
* Complete broadcast TLB maintenance issued by hyp code which invalidates
* stage 1 translation information in any translation regime.
@@ -299,7 +311,7 @@ static inline void flush_tlb_all(void)
{
dsb(ishst);
__tlbi(vmalle1is);
- __tlbi_sync_s1ish();
+ __tlbi_sync_s1ish_kernel();
isb();
}
@@ -385,7 +397,7 @@ static inline bool arch_tlbbatch_should_defer(struct mm_struct *mm)
*/
static inline void arch_tlbbatch_flush(struct arch_tlbflush_unmap_batch *batch)
{
- __tlbi_sync_s1ish();
+ __tlbi_sync_s1ish_batch();
}
/*
@@ -568,7 +580,7 @@ static inline void flush_tlb_kernel_range(unsigned long start, unsigned long end
dsb(ishst);
__flush_tlb_range_op(vaale1is, start, pages, stride, 0,
TLBI_TTL_UNKNOWN, false, lpa2_is_enabled());
- __tlbi_sync_s1ish();
+ __tlbi_sync_s1ish_kernel();
isb();
}
@@ -582,7 +594,7 @@ static inline void __flush_tlb_kernel_pgtable(unsigned long kaddr)
dsb(ishst);
__tlbi(vaae1is, addr);
- __tlbi_sync_s1ish();
+ __tlbi_sync_s1ish_kernel();
isb();
}
^ permalink raw reply related
* [PATCH V2 8/8] arm64: dts: imx95: Move power supply properties to Root Port node
From: Sherry Sun @ 2026-04-02 10:10 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, Frank.Li, s.hauer, kernel, festevam,
lpieralisi, kwilczynski, mani, bhelgaas, hongxing.zhu, l.stach
Cc: imx, linux-pci, linux-arm-kernel, devicetree, linux-kernel
In-Reply-To: <20260402101007.208419-1-sherry.sun@nxp.com>
Move the vpcie-supply and vpcie3v3aux-supply properties from the PCIe
controller nodes to the Root Port child nodes to support the new PCI
pwrctrl framework.
Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
---
arch/arm64/boot/dts/freescale/imx95-15x15-evk.dts | 4 ++--
arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts | 8 ++++----
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/arch/arm64/boot/dts/freescale/imx95-15x15-evk.dts b/arch/arm64/boot/dts/freescale/imx95-15x15-evk.dts
index 7d820a0f80b2..0d1cdfd54cce 100644
--- a/arch/arm64/boot/dts/freescale/imx95-15x15-evk.dts
+++ b/arch/arm64/boot/dts/freescale/imx95-15x15-evk.dts
@@ -555,8 +555,6 @@ &pcie0 {
pinctrl-names = "default";
/* This property is deprecated, use reset-gpios from the Root Port node. */
reset-gpio = <&gpio5 13 GPIO_ACTIVE_LOW>;
- vpcie-supply = <®_m2_pwr>;
- vpcie3v3aux-supply = <®_m2_pwr>;
supports-clkreq;
status = "disabled";
};
@@ -570,6 +568,8 @@ &pcie0_ep {
&pcie0_port0 {
reset-gpios = <&gpio5 13 GPIO_ACTIVE_LOW>;
+ vpcie-supply = <®_m2_pwr>;
+ vpcie3v3aux-supply = <®_m2_pwr>;
};
&sai1 {
diff --git a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
index 6f193cf04119..77c3a87d9065 100644
--- a/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
+++ b/arch/arm64/boot/dts/freescale/imx95-19x19-evk.dts
@@ -542,8 +542,6 @@ &pcie0 {
pinctrl-names = "default";
/* This property is deprecated, use reset-gpios from the Root Port node. */
reset-gpio = <&i2c7_pcal6524 5 GPIO_ACTIVE_LOW>;
- vpcie-supply = <®_pcie0>;
- vpcie3v3aux-supply = <®_pcie0>;
supports-clkreq;
status = "okay";
};
@@ -557,6 +555,8 @@ &pcie0_ep {
&pcie0_port0 {
reset-gpios = <&i2c7_pcal6524 5 GPIO_ACTIVE_LOW>;
+ vpcie-supply = <®_pcie0>;
+ vpcie3v3aux-supply = <®_pcie0>;
};
&pcie1 {
@@ -564,8 +564,6 @@ &pcie1 {
pinctrl-names = "default";
/* This property is deprecated, use reset-gpios from the Root Port node. */
reset-gpio = <&i2c7_pcal6524 16 GPIO_ACTIVE_LOW>;
- vpcie-supply = <®_slot_pwr>;
- vpcie3v3aux-supply = <®_slot_pwr>;
status = "okay";
};
@@ -578,6 +576,8 @@ &pcie1_ep {
&pcie1_port0 {
reset-gpios = <&i2c7_pcal6524 16 GPIO_ACTIVE_LOW>;
+ vpcie-supply = <®_slot_pwr>;
+ vpcie3v3aux-supply = <®_slot_pwr>;
};
&sai1 {
--
2.37.1
^ permalink raw reply related
* [PATCH V2 7/8] arm64: dts: imx8dxl/qm/qxp: Move power supply properties to Root Port node
From: Sherry Sun @ 2026-04-02 10:10 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, Frank.Li, s.hauer, kernel, festevam,
lpieralisi, kwilczynski, mani, bhelgaas, hongxing.zhu, l.stach
Cc: imx, linux-pci, linux-arm-kernel, devicetree, linux-kernel
In-Reply-To: <20260402101007.208419-1-sherry.sun@nxp.com>
Move the vpcie-supply and vpcie3v3aux-supply properties from the PCIe
controller nodes to the Root Port child nodes to support the new PCI
pwrctrl framework.
Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
---
arch/arm64/boot/dts/freescale/imx8dxl-evk.dts | 4 ++--
arch/arm64/boot/dts/freescale/imx8qm-mek.dts | 4 ++--
arch/arm64/boot/dts/freescale/imx8qxp-mek.dts | 4 ++--
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts b/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts
index 39108a915f96..66b2d496b73f 100644
--- a/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts
+++ b/arch/arm64/boot/dts/freescale/imx8dxl-evk.dts
@@ -677,8 +677,6 @@ &pcie0 {
pinctrl-names = "default";
/* This property is deprecated, use reset-gpios from the Root Port node. */
reset-gpio = <&lsio_gpio4 0 GPIO_ACTIVE_LOW>;
- vpcie-supply = <®_pcieb>;
- vpcie3v3aux-supply = <®_pcieb>;
status = "okay";
};
@@ -694,6 +692,8 @@ &pcie0_ep {
&pcieb_port0 {
reset-gpios = <&lsio_gpio4 0 GPIO_ACTIVE_LOW>;
+ vpcie-supply = <®_pcieb>;
+ vpcie3v3aux-supply = <®_pcieb>;
};
&sai0 {
diff --git a/arch/arm64/boot/dts/freescale/imx8qm-mek.dts b/arch/arm64/boot/dts/freescale/imx8qm-mek.dts
index f706c86137c0..5e725ad8aef9 100644
--- a/arch/arm64/boot/dts/freescale/imx8qm-mek.dts
+++ b/arch/arm64/boot/dts/freescale/imx8qm-mek.dts
@@ -812,14 +812,14 @@ &pciea {
pinctrl-names = "default";
/* This property is deprecated, use reset-gpios from the Root Port node. */
reset-gpio = <&lsio_gpio4 29 GPIO_ACTIVE_LOW>;
- vpcie-supply = <®_pciea>;
- vpcie3v3aux-supply = <®_pciea>;
supports-clkreq;
status = "okay";
};
&pciea_port0 {
reset-gpios = <&lsio_gpio4 29 GPIO_ACTIVE_LOW>;
+ vpcie-supply = <®_pciea>;
+ vpcie3v3aux-supply = <®_pciea>;
};
&pcieb {
diff --git a/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts b/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts
index 489e174df4c4..4a4e9bcca9d0 100644
--- a/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts
+++ b/arch/arm64/boot/dts/freescale/imx8qxp-mek.dts
@@ -732,8 +732,6 @@ &pcie0 {
pinctrl-names = "default";
/* This property is deprecated, use reset-gpios from the Root Port node. */
reset-gpios = <&lsio_gpio4 0 GPIO_ACTIVE_LOW>;
- vpcie-supply = <®_pcieb>;
- vpcie3v3aux-supply = <®_pcieb>;
supports-clkreq;
status = "okay";
};
@@ -749,6 +747,8 @@ &pcie0_ep {
&pcieb_port0 {
reset-gpios = <&lsio_gpio4 0 GPIO_ACTIVE_LOW>;
+ vpcie-supply = <®_pcieb>;
+ vpcie3v3aux-supply = <®_pcieb>;
};
&scu_key {
--
2.37.1
^ permalink raw reply related
* [PATCH V2 6/8] arm64: dts: imx8mq-evk: Move power supply properties to Root Port node
From: Sherry Sun @ 2026-04-02 10:10 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, Frank.Li, s.hauer, kernel, festevam,
lpieralisi, kwilczynski, mani, bhelgaas, hongxing.zhu, l.stach
Cc: imx, linux-pci, linux-arm-kernel, devicetree, linux-kernel
In-Reply-To: <20260402101007.208419-1-sherry.sun@nxp.com>
Move the vpcie-supply and vpcie3v3aux-supply properties from the PCIe
controller node to the Root Port child node to support the new PCI
pwrctrl framework.
Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
---
arch/arm64/boot/dts/freescale/imx8mq-evk.dts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/boot/dts/freescale/imx8mq-evk.dts b/arch/arm64/boot/dts/freescale/imx8mq-evk.dts
index e7d87ea81b69..75d9b25d1f0e 100644
--- a/arch/arm64/boot/dts/freescale/imx8mq-evk.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mq-evk.dts
@@ -403,8 +403,6 @@ &pcie1 {
<&pcie0_refclk>,
<&clk IMX8MQ_CLK_PCIE2_PHY>,
<&clk IMX8MQ_CLK_PCIE2_AUX>;
- vpcie-supply = <®_pcie1>;
- vpcie3v3aux-supply = <®_pcie1>;
vph-supply = <&vgen5_reg>;
supports-clkreq;
status = "okay";
@@ -422,6 +420,8 @@ &pcie1_ep {
&pcie1_port0 {
reset-gpios = <&gpio5 12 GPIO_ACTIVE_LOW>;
+ vpcie-supply = <®_pcie1>;
+ vpcie3v3aux-supply = <®_pcie1>;
};
&pgc_gpu {
--
2.37.1
^ permalink raw reply related
* [PATCH V2 5/8] arm64: dts: imx8mp-evk: Move power supply properties to Root Port node
From: Sherry Sun @ 2026-04-02 10:10 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, Frank.Li, s.hauer, kernel, festevam,
lpieralisi, kwilczynski, mani, bhelgaas, hongxing.zhu, l.stach
Cc: imx, linux-pci, linux-arm-kernel, devicetree, linux-kernel
In-Reply-To: <20260402101007.208419-1-sherry.sun@nxp.com>
Move the vpcie-supply and vpcie3v3aux-supply properties from the PCIe
controller node to the Root Port child node to support the new PCI
pwrctrl framework.
Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
---
arch/arm64/boot/dts/freescale/imx8mp-evk.dts | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm64/boot/dts/freescale/imx8mp-evk.dts b/arch/arm64/boot/dts/freescale/imx8mp-evk.dts
index a7f3acdc36d1..cb2b820cf3bc 100644
--- a/arch/arm64/boot/dts/freescale/imx8mp-evk.dts
+++ b/arch/arm64/boot/dts/freescale/imx8mp-evk.dts
@@ -772,8 +772,6 @@ &pcie0 {
pinctrl-0 = <&pinctrl_pcie0>;
/* This property is deprecated, use reset-gpios from the Root Port node. */
reset-gpio = <&gpio2 7 GPIO_ACTIVE_LOW>;
- vpcie-supply = <®_pcie0>;
- vpcie3v3aux-supply = <®_pcie0>;
supports-clkreq;
status = "disabled";
};
@@ -786,6 +784,8 @@ &pcie0_ep {
&pcie0_port0 {
reset-gpios = <&gpio2 7 GPIO_ACTIVE_LOW>;
+ vpcie-supply = <®_pcie0>;
+ vpcie3v3aux-supply = <®_pcie0>;
};
&pwm1 {
--
2.37.1
^ permalink raw reply related
* [PATCH V2 4/8] arm64: dts: imx8mm-evk: Move power supply property to Root Port node
From: Sherry Sun @ 2026-04-02 10:10 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, Frank.Li, s.hauer, kernel, festevam,
lpieralisi, kwilczynski, mani, bhelgaas, hongxing.zhu, l.stach
Cc: imx, linux-pci, linux-arm-kernel, devicetree, linux-kernel
In-Reply-To: <20260402101007.208419-1-sherry.sun@nxp.com>
Move the vpcie-supply property from the PCIe controller node to the Root
Port child node to support the new PCI pwrctrl framework.
Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
---
arch/arm64/boot/dts/freescale/imx8mm-evk.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm64/boot/dts/freescale/imx8mm-evk.dtsi b/arch/arm64/boot/dts/freescale/imx8mm-evk.dtsi
index e03aba825c18..ba7fa0815d13 100644
--- a/arch/arm64/boot/dts/freescale/imx8mm-evk.dtsi
+++ b/arch/arm64/boot/dts/freescale/imx8mm-evk.dtsi
@@ -542,7 +542,6 @@ &pcie0 {
assigned-clock-rates = <10000000>, <250000000>;
assigned-clock-parents = <&clk IMX8MM_SYS_PLL2_50M>,
<&clk IMX8MM_SYS_PLL2_250M>;
- vpcie-supply = <®_pcie0>;
supports-clkreq;
status = "okay";
};
@@ -562,6 +561,7 @@ &pcie0_ep {
&pcie0_port0 {
reset-gpios = <&gpio4 21 GPIO_ACTIVE_LOW>;
+ vpcie-supply = <®_pcie0>;
};
&sai2 {
--
2.37.1
^ permalink raw reply related
* [PATCH V2 3/8] arm: dts: imx6sx-sdb: Move power supply property to Root Port node
From: Sherry Sun @ 2026-04-02 10:10 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, Frank.Li, s.hauer, kernel, festevam,
lpieralisi, kwilczynski, mani, bhelgaas, hongxing.zhu, l.stach
Cc: imx, linux-pci, linux-arm-kernel, devicetree, linux-kernel
In-Reply-To: <20260402101007.208419-1-sherry.sun@nxp.com>
Move the vpcie-supply property from the PCIe controller node to the Root
Port child node to support the new PCI pwrctrl framework.
Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
---
arch/arm/boot/dts/nxp/imx/imx6sx-sdb.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/nxp/imx/imx6sx-sdb.dtsi b/arch/arm/boot/dts/nxp/imx/imx6sx-sdb.dtsi
index 338de4d144b2..7633ba2139d3 100644
--- a/arch/arm/boot/dts/nxp/imx/imx6sx-sdb.dtsi
+++ b/arch/arm/boot/dts/nxp/imx/imx6sx-sdb.dtsi
@@ -284,12 +284,12 @@ &pcie {
pinctrl-0 = <&pinctrl_pcie>;
/* This property is deprecated, use reset-gpios from the Root Port node. */
reset-gpio = <&gpio2 0 GPIO_ACTIVE_LOW>;
- vpcie-supply = <®_pcie_gpio>;
status = "okay";
};
&pcie_port0 {
reset-gpios = <&gpio2 0 GPIO_ACTIVE_LOW>;
+ vpcie-supply = <®_pcie_gpio>;
};
&lcdif1 {
--
2.37.1
^ permalink raw reply related
* [PATCH V2 2/8] arm: dts: imx6qdl-sabresd: Move power supply property to Root Port node
From: Sherry Sun @ 2026-04-02 10:10 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, Frank.Li, s.hauer, kernel, festevam,
lpieralisi, kwilczynski, mani, bhelgaas, hongxing.zhu, l.stach
Cc: imx, linux-pci, linux-arm-kernel, devicetree, linux-kernel
In-Reply-To: <20260402101007.208419-1-sherry.sun@nxp.com>
Move the vpcie-supply property from the PCIe controller node to the Root
Port child node to support the new PCI pwrctrl framework.
Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
---
arch/arm/boot/dts/nxp/imx/imx6qdl-sabresd.dtsi | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/nxp/imx/imx6qdl-sabresd.dtsi b/arch/arm/boot/dts/nxp/imx/imx6qdl-sabresd.dtsi
index fe9046c03ddd..e52205d7c487 100644
--- a/arch/arm/boot/dts/nxp/imx/imx6qdl-sabresd.dtsi
+++ b/arch/arm/boot/dts/nxp/imx/imx6qdl-sabresd.dtsi
@@ -756,12 +756,12 @@ &pcie {
pinctrl-0 = <&pinctrl_pcie>;
/* This property is deprecated, use reset-gpios from the Root Port node. */
reset-gpio = <&gpio7 12 GPIO_ACTIVE_LOW>;
- vpcie-supply = <®_pcie>;
status = "okay";
};
&pcie_port0 {
reset-gpios = <&gpio7 12 GPIO_ACTIVE_LOW>;
+ vpcie-supply = <®_pcie>;
};
&pwm1 {
--
2.37.1
^ permalink raw reply related
* [PATCH V2 1/8] PCI: imx6: Integrate new pwrctrl API for pci-imx6
From: Sherry Sun @ 2026-04-02 10:10 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, Frank.Li, s.hauer, kernel, festevam,
lpieralisi, kwilczynski, mani, bhelgaas, hongxing.zhu, l.stach
Cc: imx, linux-pci, linux-arm-kernel, devicetree, linux-kernel
In-Reply-To: <20260402101007.208419-1-sherry.sun@nxp.com>
Integrate the PCI pwrctrl framework into the pci-imx6 driver to provide
standardized power management for PCIe devices.
Legacy regulator handling (vpcie-supply at controller level) is
maintained for backward compatibility with existing device trees.
New device trees should specify power supplies at the Root Port
level to utilize the pwrctrl framework.
Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
---
drivers/pci/controller/dwc/Kconfig | 1 +
drivers/pci/controller/dwc/pci-imx6.c | 24 +++++++++++++++++++++++-
2 files changed, 24 insertions(+), 1 deletion(-)
diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig
index f2fde13107f2..327b0dc65550 100644
--- a/drivers/pci/controller/dwc/Kconfig
+++ b/drivers/pci/controller/dwc/Kconfig
@@ -114,6 +114,7 @@ config PCI_IMX6_HOST
depends on PCI_MSI
select PCIE_DW_HOST
select PCI_IMX6
+ select PCI_PWRCTRL_GENERIC
help
Enables support for the PCIe controller in the i.MX SoCs to
work in Root Complex mode. The PCI controller on i.MX is based
diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c
index b44563309d40..0078cd7bbf9e 100644
--- a/drivers/pci/controller/dwc/pci-imx6.c
+++ b/drivers/pci/controller/dwc/pci-imx6.c
@@ -20,6 +20,7 @@
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/pci.h>
+#include <linux/pci-pwrctrl.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
@@ -1314,6 +1315,7 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
return ret;
}
+ /* Legacy regulator handling for DT backward compatibility. */
if (imx_pcie->vpcie) {
ret = regulator_enable(imx_pcie->vpcie);
if (ret) {
@@ -1323,10 +1325,22 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
}
}
+ ret = pci_pwrctrl_create_devices(dev);
+ if (ret) {
+ dev_err(dev, "failed to create pwrctrl devices\n");
+ goto err_reg_disable;
+ }
+
+ ret = pci_pwrctrl_power_on_devices(dev);
+ if (ret) {
+ dev_err(dev, "failed to power on pwrctrl devices\n");
+ goto err_pwrctrl_destroy;
+ }
+
ret = imx_pcie_clk_enable(imx_pcie);
if (ret) {
dev_err(dev, "unable to enable pcie clocks: %d\n", ret);
- goto err_reg_disable;
+ goto err_pwrctrl_power_off;
}
if (pp->bridge && imx_check_flag(imx_pcie, IMX_PCIE_FLAG_HAS_LUT)) {
@@ -1385,6 +1399,11 @@ static int imx_pcie_host_init(struct dw_pcie_rp *pp)
phy_exit(imx_pcie->phy);
err_clk_disable:
imx_pcie_clk_disable(imx_pcie);
+err_pwrctrl_power_off:
+ pci_pwrctrl_power_off_devices(dev);
+err_pwrctrl_destroy:
+ if (ret != -EPROBE_DEFER)
+ pci_pwrctrl_destroy_devices(dev);
err_reg_disable:
if (imx_pcie->vpcie)
regulator_disable(imx_pcie->vpcie);
@@ -1403,6 +1422,7 @@ static void imx_pcie_host_exit(struct dw_pcie_rp *pp)
}
imx_pcie_clk_disable(imx_pcie);
+ pci_pwrctrl_power_off_devices(pci->dev);
if (imx_pcie->vpcie)
regulator_disable(imx_pcie->vpcie);
}
@@ -1911,6 +1931,8 @@ static void imx_pcie_shutdown(struct platform_device *pdev)
/* bring down link, so bootloader gets clean state in case of reboot */
imx_pcie_assert_core_reset(imx_pcie);
imx_pcie_assert_perst(imx_pcie, true);
+ pci_pwrctrl_power_off_devices(&pdev->dev);
+ pci_pwrctrl_destroy_devices(&pdev->dev);
}
static const struct imx_pcie_drvdata drvdata[] = {
--
2.37.1
^ permalink raw reply related
* [PATCH V2 0/8] PCI: imx6: Integrate pwrctrl API and update device trees
From: Sherry Sun @ 2026-04-02 10:09 UTC (permalink / raw)
To: robh, krzk+dt, conor+dt, Frank.Li, s.hauer, kernel, festevam,
lpieralisi, kwilczynski, mani, bhelgaas, hongxing.zhu, l.stach
Cc: imx, linux-pci, linux-arm-kernel, devicetree, linux-kernel
Note: This patch set depends on my previous patch set [1] which adds
Root Port device tree nodes and support parsing the reset property in
new Root Port binding in pci-imx6 driver.
This series integrates the PCI pwrctrl framework into the pci-imx6
driver and updates i.MX EVK board device trees to support it.
Patches 2-8 update device trees for i.MX EVK boards which maintained
by NXP to move power supply properties from the PCIe controller node
to the Root Port child node, which is required for pwrctrl framework.
Affected boards:
- i.MX6Q/DL SABRESD
- i.MX6SX SDB
- i.MX8MM EVK
- i.MX8MP EVK
- i.MX8MQ EVK
- i.MX8DXL/QM/QXP EVK
- i.MX95 15x15/19x19 EVK
The driver maintains legacy regulator handling for device trees that
haven't been updated yet. Both old and new device tree structures are
supported.
[1] https://lore.kernel.org/all/20260318062916.2747472-1-sherry.sun@nxp.com/
Signed-off-by: Sherry Sun <sherry.sun@nxp.com>
---
Changes in V2:
1. After commit 2d8c5098b847 ("PCI/pwrctrl: Do not power off on pwrctrl
device removal"), the pwrctrl drivers no longer power off devices
during removal. Update pci-imx6 driver's shutdown callback in patch#1
to explicitly call pci_pwrctrl_power_off_devices() before
pci_pwrctrl_destroy_devices() to ensure devices are properly powered
off.
---
Sherry Sun (8):
PCI: imx6: Integrate new pwrctrl API for pci-imx6
arm: dts: imx6qdl-sabresd: Move power supply property to Root Port
node
arm: dts: imx6sx-sdb: Move power supply property to Root Port node
arm64: dts: imx8mm-evk: Move power supply property to Root Port node
arm64: dts: imx8mp-evk: Move power supply properties to Root Port node
arm64: dts: imx8mq-evk: Move power supply properties to Root Port node
arm64: dts: imx8dxl/qm/qxp: Move power supply properties to Root Port
node
arm64: dts: imx95: Move power supply properties to Root Port node
.../arm/boot/dts/nxp/imx/imx6qdl-sabresd.dtsi | 2 +-
arch/arm/boot/dts/nxp/imx/imx6sx-sdb.dtsi | 2 +-
arch/arm64/boot/dts/freescale/imx8dxl-evk.dts | 4 ++--
arch/arm64/boot/dts/freescale/imx8mm-evk.dtsi | 2 +-
arch/arm64/boot/dts/freescale/imx8mp-evk.dts | 4 ++--
arch/arm64/boot/dts/freescale/imx8mq-evk.dts | 4 ++--
arch/arm64/boot/dts/freescale/imx8qm-mek.dts | 4 ++--
arch/arm64/boot/dts/freescale/imx8qxp-mek.dts | 4 ++--
.../boot/dts/freescale/imx95-15x15-evk.dts | 4 ++--
.../boot/dts/freescale/imx95-19x19-evk.dts | 8 +++----
drivers/pci/controller/dwc/Kconfig | 1 +
drivers/pci/controller/dwc/pci-imx6.c | 24 ++++++++++++++++++-
12 files changed, 43 insertions(+), 20 deletions(-)
--
2.37.1
^ permalink raw reply
* Re: [PATCH v1 07/27] KVM: arm64: Provide arm64 KVM API for non-native architectures
From: Marc Zyngier @ 2026-04-02 10:08 UTC (permalink / raw)
To: Steffen Eiden
Cc: kvm, kvmarm, linux-arm-kernel, linux-kernel, linux-s390,
Andreas Grapentin, Arnd Bergmann, Catalin Marinas,
Christian Borntraeger, Claudio Imbrenda, David Hildenbrand,
Gautam Gala, Hendrik Brueckner, Janosch Frank, Joey Gouly,
Nina Schoetterl-Glausch, Oliver Upton, Paolo Bonzini,
Suzuki K Poulose, Ulrich Weigand, Will Deacon, Zenghui Yu
In-Reply-To: <20260402042125.3948963-8-seiden@linux.ibm.com>
On Thu, 02 Apr 2026 05:21:03 +0100,
Steffen Eiden <seiden@linux.ibm.com> wrote:
Drive-by comment as I was idly going through this patch.
[...]
> diff --git a/include/kvm/arm64/kvm_host.h b/include/kvm/arm64/kvm_host.h
> new file mode 100644
> index 000000000000..3a434f47497b
> --- /dev/null
> +++ b/include/kvm/arm64/kvm_host.h
[...]
> +static inline bool kvm_supports_32bit_el0(void)
> +{
> + return false;
> +}
> +
This looks wrong. The original file still has:
#define kvm_supports_32bit_el0() \
(system_supports_32bit_el0() && \
!static_branch_unlikely(&arm64_mismatched_32bit_el0))
which should not be tampered with. I guess we are simply lucky that
the preprocessor is braindead enough to make this sort of things go
unnoticed, but I'd expect this predicate to be directly provided by
the s390 code one way or another.
Thanks,
M.
--
Without deviation from the norm, progress is not possible.
^ permalink raw reply
* Re: [PATCH v1 00/27] KVM: s390: Introduce arm64 KVM
From: Christian Borntraeger @ 2026-04-02 10:07 UTC (permalink / raw)
To: David Hildenbrand (Arm), Steffen Eiden, kvm, kvmarm,
linux-arm-kernel, linux-kernel, linux-s390
Cc: Andreas Grapentin, Arnd Bergmann, Catalin Marinas,
Claudio Imbrenda, Gautam Gala, Hendrik Brueckner, Janosch Frank,
Joey Gouly, Marc Zyngier, Nina Schoetterl-Glausch, Oliver Upton,
Paolo Bonzini, Suzuki K Poulose, Ulrich Weigand, Will Deacon,
Zenghui Yu
In-Reply-To: <3898bcc1-f02d-44ae-b13a-4ea52a5a998d@kernel.org>
Am 02.04.26 um 10:53 schrieb David Hildenbrand (Arm):
>>
>> KVM on s390:
>> The SAE (Start Arm Execution) instruction is introduced as the
>> s390 mechanism for running Arm64 guests, and a new kvm-arm64 module is
>> built up incrementally.
>>
>> Upcoming patch series will introduce system-register handling, interrupt
>> support, hypercalls, and additional features such as PMU.
>
> Pretty cool stuff.
>
> What's the rough timeline for the other work?
Over the next months. The idea was to split this into consumable chunks and start
with those things where a lot of people have to agree (code movement, code sharing
and shared maintainership). This will certainly evolve depending on patch feedback
and merge progress.
>
> Regarding I/O, I guess it is primarily VIRTIO (VIRTIO_PCI) for these VMs
> only?
yes, virtio-pci.
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox