* Re: [PATCH v6 05/25] iommu/arm-smmu-v3: Move IDR parsing to common functions
From: Mostafa Saleh @ 2026-05-11 11:53 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: linux-arm-kernel, linux-kernel, kvmarm, iommu, catalin.marinas,
will, maz, oliver.upton, joey.gouly, suzuki.poulose, yuzenghui,
joro, jean-philippe, mark.rutland, qperret, tabba, vdonnefort,
sebastianene, keirf
In-Reply-To: <20260509233443.GK9285@ziepe.ca>
On Sun, May 10, 2026 at 12:34 AM Jason Gunthorpe <jgg@ziepe.ca> wrote:
>
> On Thu, May 07, 2026 at 10:13:13AM +0000, Mostafa Saleh wrote:
> > > I was thinking more like
> > >
> > > #define arm_smmu arm_pkvm_smmu
> > >
> > > Before the pkvm compile of the shared code.
> >
> > Oh, so we rely on arm_pkvm_smmu and arm_smmu having the same field
> > names and types?
>
> Just for the shared code, yes.
>
> For instance this patch would require the features field be present in
> both, and then all you do is directly copy all the smmu->features
> logic into the shared file and that's it.
>
> > I don’t think that’s better for maintainability, I can look into it
> > if Robin and Will are OK with that.
>
> I think maintainability here means pkvm minimizes how much it changes
> the code of the main driver.
>
> Copying a bunch of functions into a shared .c file exactly as it is,
> then compiling the shared file with some #ifdef'ery at is going to be
> long term better than trying to mangle the whole thing to avoid using
> any of the core types and not directly share the code, IMHO.
>
There isn't #ifdef'ery at the moment, that's what I was trying to
avoid by introducing shared code.
I believe that can be treated as a library as most of the shared code
is about HW description (commands, invalidation, IDR probing...)
> Like my tlbi point for instance, it is not a good idea to rip out a
> little bit of the invalidation logic then open code a pkvm
> version. Use the exact driver code. But to do this you need to pass an
> arm_smmu through to to the pkvm specific queue management functions.
>
What concerns me is how fragile that is, any change in the main struct
can easily break the hypervisor, unlike if we have a clear shared code
and defined API that is used by 2 entities.
I will think more about this before v7 and see how intrusive it is.
> > > How does pkvm compilation work anyhow? Is it all rolled into a unique
> > > link, or do you have to worry about symbol conflicts with the main
> > > kernel?
> >
> > All pkvm symbols are prefixed with __kvm_nvhe_ in the main kernel.
>
> So automatically with objcopy or something like that during the build?
>
Exactly: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/kvm/hyp/nvhe/Makefile#n96
Thanks,
Mostafa
> Jason
^ permalink raw reply
* Re: [RFC PATCH] KVM: arm64: vgic: Skip vCPU trylock for pre-init register access
From: David Woodhouse @ 2026-05-11 11:52 UTC (permalink / raw)
To: Yao Yuan
Cc: Marc Zyngier, Oliver Upton, Joey Gouly, Suzuki K Poulose,
Zenghui Yu, Catalin Marinas, Will Deacon, Sascha Bischoff,
Paolo Bonzini, Jonathan Cameron, Eric Auger,
Raghavendra Rao Ananta, Maxim Levitsky, linux-arm-kernel, kvmarm,
kvm
In-Reply-To: <3ijnoudux4iyddpd25yr53s2zqrslplixjqfod225ix43y4z4a@heoklijpq45f>
[-- Attachment #1: Type: text/plain, Size: 1454 bytes --]
On Mon, 2026-05-11 at 19:03 +0800, Yao Yuan wrote:
>
> > + /*
> > + * If the VGIC becomes initialized between the above check and taking
> > + * config_lock, drop config_lock to lock the VCPUS.
> > + */
> > + if (vgic_initialized(dev->kvm) && !vcpus_locked) {
> > + mutex_unlock(&dev->kvm->arch.config_lock);
> > + if (kvm_trylock_all_vcpus(dev->kvm)) {
> > + mutex_unlock(&dev->kvm->lock);
> > + return -EBUSY;
> > + }
> > + mutex_lock(&dev->kvm->arch.config_lock);
> > + vcpus_locked = true;
>
> Question: Why not do vgic_initialized(dev->kvm) and
> reg_allowed_pre_init(attr) checking after take config_lock,
> and depends on the checking result to decide trylocak all
> vcpus or not ?
Yeah... I didn't like that either.
If we attempt to take the vCPU locks while *holding* config_lock, that
gives us a classic ABBA deadlock with the code paths that hold the vCPU
locks first.
Well, maybe it doesn't because we'd only be doing a *trylock*... but
lockdep wouldn't know that and it's fugly as hell. So I settled on the
above.
The long term fix, as I noted, is to kill kvm_trylock_all_vcpus() with
fire in *all* cases, not just the !vgic_initialized() case. On x86 we'd
do this with something like KVM_REQ_MCLOCK_INPROGRESS to kick vCPUs out
of guest mode to prevent concurrent access. Which isn't my favourite
code *either* but at least it works without imposing the awfulness on
the userspace API.
[-- Attachment #2: smime.p7s --]
[-- Type: application/pkcs7-signature, Size: 5069 bytes --]
^ permalink raw reply
* Re: [PATCH] arm64: defconfig: Enable MT7921S support
From: Gary Bisson @ 2026-05-11 11:51 UTC (permalink / raw)
To: Matthias Brugger, AngeloGioacchino Del Regno
Cc: linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <20260310-mtkdefconfig-v1-1-717eb3e6eea7@gmail.com>
Hi,
On Tue, Mar 10, 2026 at 04:52:12PM +0100, Gary Bisson wrote:
> Enable MT7921S which is the SDIO variant of the MT7921, also called
> Filogic 320 [1].
> This driver is now used by Tungsten510/700 platforms which are now
> included upstream.
>
> [1] https://www.mediatek.com/products/broadband-wifi/mediatek-filogic-320
>
> Signed-off-by: Gary Bisson <bisson.gary@gmail.com>
> ---
> arch/arm64/configs/defconfig | 2 ++
> 1 file changed, 2 insertions(+)
>
> diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
> index b67d5b1fc45b..d469ad15dc69 100644
> --- a/arch/arm64/configs/defconfig
> +++ b/arch/arm64/configs/defconfig
> @@ -200,6 +200,7 @@ CONFIG_BT_HCIUART_QCA=y
> CONFIG_BT_HCIUART_MRVL=y
> CONFIG_BT_MRVL=m
> CONFIG_BT_MRVL_SDIO=m
> +CONFIG_BT_MTKSDIO=m
> CONFIG_BT_QCOMSMD=m
> CONFIG_BT_NXPUART=m
> CONFIG_CFG80211=m
> @@ -462,6 +463,7 @@ CONFIG_MWIFIEX_SDIO=m
> CONFIG_MWIFIEX_PCIE=m
> CONFIG_MWIFIEX_USB=m
> CONFIG_MT7921E=m
> +CONFIG_MT7921S=m
> CONFIG_RSI_91X=m
> CONFIG_WL18XX=m
> CONFIG_WLCORE_SDIO=m
>
> ---
> base-commit: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f
> change-id: 20260310-mtkdefconfig-5d9626f242c7
>
> Best regards,
Gentle ping on this request. Let me know if anything is missing.
Regards,
Gary
^ permalink raw reply
* Re: [PATCH v6 1/2] media: dt-bindings: Add CSI Pixel Formatter DT bindings
From: Marco Felsch @ 2026-05-11 11:45 UTC (permalink / raw)
To: Guoniu Zhou
Cc: Mauro Carvalho Chehab, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Shawn Guo, Sascha Hauer, Pengutronix Kernel Team,
Fabio Estevam, Laurent Pinchart, Frank Li, imx,
Krzysztof Kozlowski, devicetree, linux-kernel, Guoniu Zhou,
linux-arm-kernel, linux-media
In-Reply-To: <20260511-csi_formatter-v6-1-01028e312e2b@oss.nxp.com>
On 26-05-11, Guoniu Zhou wrote:
> From: Guoniu Zhou <guoniu.zhou@nxp.com>
>
> The i.MX9 CSI pixel formatting module uses packet info, pixel and
> non-pixel data from the CSI-2 host controller and reformat them to
> match Pixel Link(PL) definition.
Sorry for chiming in very late, but can you please provide on which
i.MX9 devices this formatting module is present? I've checked the i.MX93
reference manual and found no info instead I found a CAMERA_MUX register
which does something similiar but is not the same. Please provide a more
specific compatible if this IP is only be present on i.MX95 devices.
Regards,
Marco
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
> Signed-off-by: Guoniu Zhou <guoniu.zhou@nxp.com>
> ---
> .../bindings/media/fsl,imx9-csi-formatter.yaml | 87 ++++++++++++++++++++++
> 1 file changed, 87 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/media/fsl,imx9-csi-formatter.yaml b/Documentation/devicetree/bindings/media/fsl,imx9-csi-formatter.yaml
> new file mode 100644
> index 000000000000..774d37d2b987
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/media/fsl,imx9-csi-formatter.yaml
> @@ -0,0 +1,87 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/media/fsl,imx9-csi-formatter.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: i.MX9 CSI Pixel Formatter
> +
> +maintainers:
> + - Guoniu Zhou <guoniu.zhou@nxp.com>
> +
> +description:
> + The CSI pixel formatting module uses packet info, pixel and non-pixel
> + data from the CSI-2 host controller and reformat them to match Pixel
> + Link(PL) definition.
> +
> +properties:
> + compatible:
> + const: fsl,imx9-csi-formatter
> +
> + reg:
> + maxItems: 1
> +
> + clocks:
> + maxItems: 1
> +
> + power-domains:
> + maxItems: 1
> +
> + ports:
> + $ref: /schemas/graph.yaml#/properties/ports
> +
> + properties:
> + port@0:
> + $ref: /schemas/graph.yaml#/$defs/port-base
> + unevaluatedProperties: false
> + description: MIPI CSI-2 RX IDI interface
> +
> + properties:
> + endpoint:
> + $ref: video-interfaces.yaml#
> + unevaluatedProperties: false
> +
> + port@1:
> + $ref: /schemas/graph.yaml#/properties/port
> + description: Pixel Link Interface
> +
> +required:
> + - compatible
> + - reg
> + - clocks
> + - power-domains
> + - ports
> +
> +additionalProperties: false
> +
> +examples:
> + - |
> + #include <dt-bindings/clock/nxp,imx95-clock.h>
> +
> + formatter@20 {
> + compatible = "fsl,imx9-csi-formatter";
> + reg = <0x20 0x100>;
> + clocks = <&cameramix_csr IMX95_CLK_CAMBLK_CSI2_FOR0>;
> + power-domains = <&scmi_devpd 3>;
> +
> + ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + port@0 {
> + reg = <0>;
> +
> + endpoint {
> + remote-endpoint = <&mipi_csi_0_out>;
> + };
> + };
> +
> + port@1 {
> + reg = <1>;
> +
> + endpoint {
> + remote-endpoint = <&isi_in_2>;
> + };
> + };
> + };
> + };
>
> --
> 2.34.1
>
>
>
--
#gernperDu
#CallMeByMyFirstName
Pengutronix e.K. | |
Steuerwalder Str. 21 | https://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-9 |
^ permalink raw reply
* Re: [PATCH v6 04/25] iommu/arm-smmu-v3: Move TLB range invalidation into common code
From: Mostafa Saleh @ 2026-05-11 11:45 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: linux-arm-kernel, linux-kernel, kvmarm, iommu, catalin.marinas,
will, maz, oliver.upton, joey.gouly, suzuki.poulose, yuzenghui,
joro, jean-philippe, mark.rutland, qperret, tabba, vdonnefort,
sebastianene, keirf
In-Reply-To: <20260509232931.GJ9285@ziepe.ca>
On Sat, May 09, 2026 at 08:29:31PM -0300, Jason Gunthorpe wrote:
> On Thu, May 07, 2026 at 09:40:00AM +0000, Mostafa Saleh wrote:
> > But that doesn’t solve the problem, which is: At some point, whether
> > eagerly from the page table code, through gather sync or a fancy
> > invalidation array, the driver will need to populate a range
> > invalidation command (tg, ttl, scale...) and this logic is better
> > shared with the main driver which is this patch does.
>
> My point is this patch doesn't share enough. If you do need to issue
> invalidations then share everything below the top level tlbi entry
> point and don't try to make a pkvm version of the entire logic just by
> ripping out the range logic.
>
> There is no reason for pkvm to need a different algorithm
> here. Especially when you get to supporting ATS and multiple devices
> and smmus you may as well just use the whole thing.
>
> Which is why I suggested to copy the entire call chain into a shared
> file
Agh, actually this seires doesn't deal with ATS, which I think is
wrong, propably we have to issue CMDQ_OP_ATC_INV for the whole
space on every S2 invalidation which has to be done per-SID and
as it can't be done by VMID :/ or just hide ATS support from host for
now, I will look into more for v7.
But anyway, we don’t have to share any logic, the kernel driver
is quite complicated as it is designed for a different use-case.
Doing that makes the hypervisor unnecessary complicated and
oversharing this logic makes the kernel driver less maintainable IMO.
The hypervisor only needs to share the architecture spec of
populating commands.
If that’s a problem. I am happy to re-write the RIL part in the
hypervisor. It's not that complicated, but I thought it makes sense
to share as it is a HW contract.
Otherwise, we can break this part into some macros that are called
by this function and the hypervisor but that's maybe over-engineered.
Thanks,
Mostafa
>
> Jason
^ permalink raw reply
* Re: [PATCH v2 3/3] arm64: dts: allwinner: A133: add support for Baijie Helper A133 board
From: Andre Przywara @ 2026-05-11 11:44 UTC (permalink / raw)
To: Alexander Sverdlin, linux-sunxi
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Chen-Yu Tsai,
Jernej Skrabec, Samuel Holland, devicetree, linux-arm-kernel,
linux-kernel
In-Reply-To: <20260510201644.4143710-4-alexander.sverdlin@gmail.com>
Hi Alexander,
thanks for upstreaming the DTs!
On 5/10/26 22:16, Alexander Sverdlin wrote:
> Baijie Helper A133 board is a development board around Baijie A133 Core
> SBC. Features:
>
> - 1/2/4GiB LPDDR4 DRAM
> - 8/16/32GiB eMMC
> - AXP707 PMIC
> - 2 USB 2.0 ports
> - MicroSD slot and on-board eMMC module
> - Gigabit Ethernet
> - Bluetooth
> - WiFi
>
> Add initial support for both the Helper and Core boards, including UART,
> PMU, eMMC, USB, Ethernet.
>
> Signed-off-by: Alexander Sverdlin <alexander.sverdlin@gmail.com>
> ---
>
> Changelog:
> v2:
> - introduced baijie,helper-a133-core compatible for the Core (SoM) board
>
> arch/arm64/boot/dts/allwinner/Makefile | 1 +
> .../dts/allwinner/sun50i-a133-baije-core.dtsi | 162 ++++++++++++++++++
> .../allwinner/sun50i-a133-baijie-helper.dts | 94 ++++++++++
> 3 files changed, 257 insertions(+)
> create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a133-baije-core.dtsi
> create mode 100644 arch/arm64/boot/dts/allwinner/sun50i-a133-baijie-helper.dts
>
> diff --git a/arch/arm64/boot/dts/allwinner/Makefile b/arch/arm64/boot/dts/allwinner/Makefile
> index d116864b6c2b..926dfa851100 100644
> --- a/arch/arm64/boot/dts/allwinner/Makefile
> +++ b/arch/arm64/boot/dts/allwinner/Makefile
> @@ -18,6 +18,7 @@ dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-sopine-baseboard.dtb
> dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a64-teres-i.dtb
> dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h64-remix-mini-pc.dtb
> dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a100-allwinner-perf1.dtb
> +dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a133-baijie-helper.dtb
> dtb-$(CONFIG_ARCH_SUNXI) += sun50i-a133-liontron-h-a133l.dtb
> dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h5-bananapi-m2-plus.dtb
> dtb-$(CONFIG_ARCH_SUNXI) += sun50i-h5-bananapi-m2-plus-v1.2.dtb
> diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a133-baije-core.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a133-baije-core.dtsi
> new file mode 100644
> index 000000000000..65b094f30bf5
> --- /dev/null
> +++ b/arch/arm64/boot/dts/allwinner/sun50i-a133-baije-core.dtsi
> @@ -0,0 +1,162 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Copyright (c) 2025 Arm Ltd.
Please put your own copyright here, even if that has been largely copied
from an existing file.
> + */
> +
> +/dts-v1/;
> +
> +#include "sun50i-a100.dtsi"
> +#include "sun50i-a100-cpu-opp.dtsi"
> +
> +/{
> + compatible = "baijie,helper-a133-core",
> + "allwinner,sun50i-a100";
> +
> + aliases {
> + serial1 = &uart1; /* BT module */
Do we really need an alias for the BT UART? And is the BT module
supported already? Then please add a child node to the UART node.
> + };
> +};
> +
> +&cpu0 {
> + cpu-supply = <®_dcdc2>;
> +};
> +
> +&pio {
The order of those referenced nodes is alphabetically by the label name.
So this one goes further down.
> + vcc-pb-supply = <®_dcdc1>;
> + vcc-pc-supply = <®_eldo1>;
> + vcc-pd-supply = <®_dcdc1>;
> + vcc-pe-supply = <®_dldo2>;
> + vcc-pf-supply = <®_dcdc1>;
> + vcc-pg-supply = <®_dldo1>;
> + vcc-ph-supply = <®_dcdc1>;
> +};
> +
Isn't the WiFi/BT module on the SoM? Then please mention and enable MMC1
here. Provide the child node for the WiFi chip, even if there is no
upstream support in the kernel for it yet.
> +&mmc2 {
> + vmmc-supply = <®_dcdc1>;
> + vqmmc-supply = <®_eldo1>;
> + cap-mmc-hw-reset;
> + non-removable;
> + bus-width = <8>;
> + mmc-ddr-1_8v;
> + mmc-hs200-1_8v;
> + status = "okay";
> +};
> +
> +&r_i2c0 {
> + status = "okay";
> +
> + axp803: pmic@34 {
> + compatible = "x-powers,axp803";
> + reg = <0x34>;
> + interrupt-parent = <&r_intc>;
> + interrupts = <0 IRQ_TYPE_LEVEL_LOW>;
> + };
> +};
> +
> +#include "axp803.dtsi"
> +
> +&ac_power_supply {
> + status = "okay";
> +};
> +
> +®_aldo1 {
What is aldo1 used for, actually? I don't see this referenced anywhere.
I guess the kernel turns that off after booting?
If you have access to the schematic, please check that. If that's for
some peripheral not yet supported, please note the user anyway, ideally
by an explaining regulator-name, or by a comment. Also if it's used for
any of the required SoC VDD pins. See the Liontron .dts for comparison.
> + regulator-always-on;
> + regulator-min-microvolt = <700000>;
> + regulator-max-microvolt = <3300000>;
Please don't provide ranges here (those are in the driver already), the
point of this entry is to set the voltage required by the board. (And
yes, the vendor DTBs alway get this wrong). Typically there are fixed
requirements, so use the same value for min and max.
Same for the others below.
> +};
> +
> +®_aldo2 {
> + regulator-always-on;
For always-on regulators we definitely need an explanation. Does the
board stop booting if you remove this line?
Maybe it's for DRAM? Can you say what voltage it is, either from the
reset default, or set by the bootloader?
> + regulator-min-microvolt = <700000>;
> + regulator-max-microvolt = <3300000>;
> +};
> +
> +®_aldo3 {
> + regulator-always-on;
> + regulator-min-microvolt = <700000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-enable-ramp-delay = <1000>;
> +};
> +
> +®_dcdc1 {
> + regulator-always-on;
> + regulator-min-microvolt = <1600000>;
> + regulator-max-microvolt = <3400000>;
> + regulator-name = "vcc-3v3";
> +};
> +
> +®_dcdc2 {
> + regulator-always-on;
> + regulator-min-microvolt = <500000>;
> + regulator-max-microvolt = <1300000>;
This one is an exception, because the voltage is to be adjusted at
runtime. But the voltage range needs to be tighter, the A133 datasheet
puts the valid VDD_CPU range between 810mV and 1200mV.
Compare to the Liontron dts file here.
> + regulator-name = "vdd-cpu";
> +};
> +
> +®_dcdc3 {
> + regulator-always-on;
> + regulator-min-microvolt = <500000>;
> + regulator-max-microvolt = <1300000>;
> +};
> +
> +®_dcdc4 {
> + regulator-always-on;
> + regulator-min-microvolt = <500000>;
> + regulator-max-microvolt = <1300000>;
> + regulator-name = "vdd-sys";
> +};
> +
> +®_dcdc5 {
> + regulator-always-on;
> + regulator-min-microvolt = <800000>;
> + regulator-max-microvolt = <1840000>;
> + regulator-name = "vcc-dram";
> +};
> +
> +/* DCDC6 unused */
> +
> +®_dldo1 {
> + regulator-min-microvolt = <700000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-enable-ramp-delay = <1000>;
> +};
> +
> +®_dldo2 {
> + regulator-min-microvolt = <700000>;
> + regulator-max-microvolt = <3400000>;
> + regulator-enable-ramp-delay = <1000>;
> +};
> +
> +®_dldo3 {
> + regulator-min-microvolt = <700000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-enable-ramp-delay = <1000>;
> + regulator-name = "avdd-csi";
> +};
> +
> +®_dldo4 {
> + regulator-min-microvolt = <700000>;
> + regulator-max-microvolt = <3300000>;
> + regulator-enable-ramp-delay = <1000>;
> +};
> +
> +®_eldo1 {
> + regulator-min-microvolt = <700000>;
> + regulator-max-microvolt = <1900000>;
> + regulator-enable-ramp-delay = <1000>;
> +};
> +
> +®_eldo2 {
> + regulator-min-microvolt = <700000>;
> + regulator-max-microvolt = <1900000>;
> + regulator-enable-ramp-delay = <1000>;
> + regulator-name = "dvdd-csi";
> +};
> +
> +/* ELDO3 unused */
> +
> +®_fldo1 {
> + regulator-always-on;
> + regulator-min-microvolt = <700000>;
> + regulator-max-microvolt = <1450000>;
> + regulator-name = "vdd-cpus-usb";
> +};
> diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a133-baijie-helper.dts b/arch/arm64/boot/dts/allwinner/sun50i-a133-baijie-helper.dts
> new file mode 100644
> index 000000000000..ccbca5d0a40c
> --- /dev/null
> +++ b/arch/arm64/boot/dts/allwinner/sun50i-a133-baijie-helper.dts
> @@ -0,0 +1,94 @@
> +// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
> +/*
> + * Copyright (c) 2025 Arm Ltd.
Your own copyright, please.
> + */
> +
> +/dts-v1/;
> +
> +#include "sun50i-a133-baije-core.dtsi"
> +
> +#include <dt-bindings/gpio/gpio.h>
> +#include <dt-bindings/leds/common.h>
> +
> +/{
> + model = "HelperBoard A133";
> + compatible = "baijie,helper-a133",
> + "baijie,helper-a133-core",
> + "allwinner,sun50i-a100";
> +
> + aliases {
> + serial0 = &uart0;
> + };
> +
> + chosen {
> + stdout-path = "serial0:115200n8";
> + };
> +
> + leds {
> + compatible = "gpio-leds";
> +
> + led {
> + function = LED_FUNCTION_INDICATOR;
> + color = <LED_COLOR_ID_GREEN>;
> + gpios = <&pio 7 13 GPIO_ACTIVE_LOW>; /* PH13 */
> + };
> + };
I see quite some buttons on the board. I guess they are connected to
GPIOs? Can you please describe them then, using gpio-keys? Look at
sun50i-h5-orangepi-pc2.dts for an example.
And you should provide a top level 5V regulator here, to be the root of
the regulator tree. Look at reg_vcc5v in the Liontron .dts.
> +};
> +
> +&mmc0 {
> + vmmc-supply = <®_dcdc1>;
> + cd-gpios = <&pio 5 6 GPIO_ACTIVE_LOW>; /* PF6 */
> + bus-width = <4>;
> + status = "okay";
> +};
> +
> +&uart0 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&uart0_pb_pins>;
> + status = "okay";
> +};
> +
> +&rgmii0_pins {
> + drive-strength = <30>;
> +};
> +
> +&emac0 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&rgmii0_pins>;
> + phy-handle = <ð_phy>;
> + phy-mode = "rgmii-id";
> + allwinner,rx-delay-ps = <200>;
> + allwinner,tx-delay-ps = <200>;
> + status = "okay";
> +};
> +
> +&mdio0 {
> + reset-gpios = <&pio 7 11 GPIO_ACTIVE_LOW>; /* PH11 */
> + reset-delay-us = <10000>;
> + reset-post-delay-us = <150000>;
> +
> + eth_phy: ethernet-phy@1 {
The typical label here would be rgmii_phy. The node name is correct, though.
> + compatible = "ethernet-phy-ieee802.3-c22";
> + reg = <1>;
> + };
> +};
> +
So from the pictures I found online it looks like there is an USB-C port
labelled "OTG", so can you please add an &usbotg reference here and
describe that port.
> +&usbphy {
Are the two USB ports always powered?
And anyway, I see a *dual* USB-A socket on the pictures online, in
addition to the USB-OTG port. So where does the third USB come from? The
A133 only supports one host USB port plus the one OTG port. So is there
an USB hub chip on the board?
> + status = "okay";
> +};
> +
> +&ehci0 {
(and again, please order those nodes alphabetically)
Cheers,
Andre.
> + status = "okay";
> +};
> +
> +&ohci0 {
> + status = "okay";
> +};
> +
> +&ehci1 {
> + status = "okay";
> +};
> +
> +&ohci1 {
> + status = "okay";
> +};
^ permalink raw reply
* [PATCH v12 12/28] coresight: etm4x: Hook CPU PM callbacks
From: Leo Yan @ 2026-05-11 11:10 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
Add a helper etm4_pm_save_needed() to detect if need save context for
self-hosted PM mode and hook pm_save_disable() and pm_restore_enable()
callbacks through etm4_cs_pm_ops structure. With this, ETMv4 PM
save/restore is integrated into the core layer's CPU PM.
Organize etm4_cs_ops and etm4_cs_pm_ops together for more readable.
The CPU PM notifier in the ETMv4 driver is no longer needed, remove it
along with its registration and unregistration code.
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: James Clark <james.clark@linaro.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-etm4x-core.c | 73 +++++++---------------
1 file changed, 23 insertions(+), 50 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index 7011a20d20042cfbeaac51797cd8592275bbf1ca..5cd70f0f3710a5ec6406a89c034d30230f782b13 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -1195,11 +1195,6 @@ static const struct coresight_ops_source etm4_source_ops = {
.pause_perf = etm4_pause_perf,
};
-static const struct coresight_ops etm4_cs_ops = {
- .trace_id = coresight_etm_get_trace_id,
- .source_ops = &etm4_source_ops,
-};
-
static bool cpu_supports_sysreg_trace(void)
{
u64 dfr0 = read_sysreg_s(SYS_ID_AA64DFR0_EL1);
@@ -1853,6 +1848,11 @@ static int etm4_dying_cpu(unsigned int cpu)
return 0;
}
+static inline bool etm4_pm_save_needed(struct etmv4_drvdata *drvdata)
+{
+ return !!drvdata->save_state;
+}
+
static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
{
int i, ret = 0;
@@ -1995,11 +1995,12 @@ static int __etm4_cpu_save(struct etmv4_drvdata *drvdata)
return ret;
}
-static int etm4_cpu_save(struct etmv4_drvdata *drvdata)
+static int etm4_cpu_save(struct coresight_device *csdev)
{
+ struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
int ret = 0;
- if (!drvdata->save_state)
+ if (!etm4_pm_save_needed(drvdata))
return 0;
/*
@@ -2112,47 +2113,27 @@ static void __etm4_cpu_restore(struct etmv4_drvdata *drvdata)
etm4_cs_lock(drvdata, csa);
}
-static void etm4_cpu_restore(struct etmv4_drvdata *drvdata)
+static void etm4_cpu_restore(struct coresight_device *csdev)
{
- if (!drvdata->save_state)
+ struct etmv4_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+ if (!etm4_pm_save_needed(drvdata))
return;
if (coresight_get_mode(drvdata->csdev))
__etm4_cpu_restore(drvdata);
}
-static int etm4_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd,
- void *v)
-{
- struct etmv4_drvdata *drvdata;
- unsigned int cpu = smp_processor_id();
-
- if (!etmdrvdata[cpu])
- return NOTIFY_OK;
-
- drvdata = etmdrvdata[cpu];
-
- if (WARN_ON_ONCE(drvdata->cpu != cpu))
- return NOTIFY_BAD;
-
- switch (cmd) {
- case CPU_PM_ENTER:
- if (etm4_cpu_save(drvdata))
- return NOTIFY_BAD;
- break;
- case CPU_PM_EXIT:
- case CPU_PM_ENTER_FAILED:
- etm4_cpu_restore(drvdata);
- break;
- default:
- return NOTIFY_DONE;
- }
-
- return NOTIFY_OK;
-}
+static const struct coresight_ops etm4_cs_ops = {
+ .trace_id = coresight_etm_get_trace_id,
+ .source_ops = &etm4_source_ops,
+};
-static struct notifier_block etm4_cpu_pm_nb = {
- .notifier_call = etm4_cpu_pm_notify,
+static const struct coresight_ops etm4_cs_pm_ops = {
+ .trace_id = coresight_etm_get_trace_id,
+ .source_ops = &etm4_source_ops,
+ .pm_save_disable = etm4_cpu_save,
+ .pm_restore_enable = etm4_cpu_restore,
};
/* Setup PM. Deals with error conditions and counts */
@@ -2160,16 +2141,12 @@ static int __init etm4_pm_setup(void)
{
int ret;
- ret = cpu_pm_register_notifier(&etm4_cpu_pm_nb);
- if (ret)
- return ret;
-
ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING,
"arm/coresight4:starting",
etm4_starting_cpu, etm4_dying_cpu);
if (ret)
- goto unregister_notifier;
+ return ret;
ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
"arm/coresight4:online",
@@ -2183,15 +2160,11 @@ static int __init etm4_pm_setup(void)
/* failed dyn state - remove others */
cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
-
-unregister_notifier:
- cpu_pm_unregister_notifier(&etm4_cpu_pm_nb);
return ret;
}
static void etm4_pm_clear(void)
{
- cpu_pm_unregister_notifier(&etm4_cpu_pm_nb);
cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
if (hp_online) {
cpuhp_remove_state_nocalls(hp_online);
@@ -2270,7 +2243,7 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg)
desc.type = CORESIGHT_DEV_TYPE_SOURCE;
desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_PROC;
- desc.ops = &etm4_cs_ops;
+ desc.ops = etm4_pm_save_needed(drvdata) ? &etm4_cs_pm_ops : &etm4_cs_ops;
desc.pdata = pdata;
desc.dev = dev;
desc.groups = coresight_etmv4_groups;
--
2.34.1
^ permalink raw reply related
* [PATCH v3 0/4] KVM: arm64: vgic: Fix IGROUPR writability and IIDR revision control
From: David Woodhouse @ 2026-05-11 11:30 UTC (permalink / raw)
To: Paolo Bonzini
Cc: Jonathan Corbet, Shuah Khan, Marc Zyngier, Oliver Upton,
Joey Gouly, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Will Deacon, Jonathan Cameron, Sascha Bischoff, Eric Auger,
Raghavendra Rao Ananta, Maxim Levitsky, David Woodhouse,
Kees Cook, Timothy Hayes, Arnd Bergmann, kvm, linux-doc,
linux-kernel, linux-arm-kernel, kvmarm, linux-kselftest,
Peter Maydell, qemu-arm, qemu-devel
Maintaining precise guest compatibility across host kernel upgrades —
and even downgrades, since rollback is sometimes necessary — is not
optional. That *shouldn't* need saying, but maybe it does:
https://lore.kernel.org/all/6856b269d2af706eae397e0cf9c1231f89d9a932.camel@infradead.org/
This series fixes the GICv2/v3 IGROUPR writability model to be
consistently controlled by the GICD_IIDR implementation revision,
replacing the ad-hoc v2_groups_user_writable flag.
Before commit d53c2c29ae0d ("KVM: arm/arm64: vgic: Allow configuration
of interrupt groups"), IGROUPR was read-only on both GICv2 and GICv3.
That commit made it unconditionally guest-writable, but provided no
mechanism for userspace to restore the previous behaviour for guests
that were launched on older kernels (which which might need to be
migrated back to older kernels).
The v2_groups_user_writable flag (added by 32f8777ed92d) attempted to
gate GICv2 userspace IGROUPR writes until userspace wrote the IIDR,
but the guest write path was never gated, creating an inconsistency
where the guest could modify groups that userspace couldn't save or
restore. QEMU never writes GICD_IIDR, so its GICv2 IGROUPR
save/restore (QEMU commit eb8b9530b0c) appears to be silently broken.
This series:
- Allows userspace to set IIDR revision 1, to restore the original
read-only IGROUPR behaviour for both GICv2 and GICv3 (patch 1)
- Removes v2_groups_user_writable and makes both guest and userspace
writability follow the IIDR revision directly (patch 3)
- Adds selftests covering IIDR revision semantics and a QEMU-style
save/restore scenario (patches 2, 4)
Tested on EC2 c7g.metal (GICv3 native) and under QEMU-TCG (GICv2).
David Woodhouse (4):
KVM: arm64: vgic: Allow userspace to set IIDR revision 1
KVM: arm64: selftests: Add vgic IIDR revision test
KVM: arm64: vgic: Remove v2_groups_user_writable and use IIDR revision directly
KVM: arm64: selftests: Add GICv2 IGROUPR writability test
arch/arm64/kvm/vgic/vgic-mmio-v2.c | 15 +-
arch/arm64/kvm/vgic/vgic-mmio-v3.c | 4 +
arch/arm64/kvm/vgic/vgic-mmio.c | 4 +
include/kvm/arm_vgic.h | 4 +-
tools/testing/selftests/kvm/Makefile.kvm | 2 +
.../testing/selftests/kvm/arm64/vgic_group_iidr.c | 118 +++++++++++
tools/testing/selftests/kvm/arm64/vgic_group_v2.c | 226 +++++++++++++++++++++
7 files changed, 361 insertions(+), 12 deletions(-)
^ permalink raw reply
* [PATCH v3 3/4] KVM: arm64: vgic: Remove v2_groups_user_writable and use IIDR revision directly
From: David Woodhouse @ 2026-05-11 11:30 UTC (permalink / raw)
To: Paolo Bonzini
Cc: Jonathan Corbet, Shuah Khan, Marc Zyngier, Oliver Upton,
Joey Gouly, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Will Deacon, Jonathan Cameron, Sascha Bischoff, Eric Auger,
Raghavendra Rao Ananta, Maxim Levitsky, David Woodhouse,
Kees Cook, Timothy Hayes, Arnd Bergmann, kvm, linux-doc,
linux-kernel, linux-arm-kernel, kvmarm, linux-kselftest,
Peter Maydell, qemu-arm, qemu-devel
In-Reply-To: <20260511113558.3325004-1-dwmw2@infradead.org>
From: David Woodhouse <dwmw@amazon.co.uk>
The v2_groups_user_writable flag was introduced to gate GICv2 userspace
IGROUPR writes until userspace explicitly wrote the IIDR, signalling
awareness of the group semantics. However, the guest write path through
vgic_mmio_write_group() was never gated by this flag, allowing a GICv2
guest to modify interrupt groups regardless of whether userspace had
opted in.
Rather than adding the same flag check to the guest path, remove the
flag entirely and make both guest and userspace IGROUPR writability
follow the IIDR implementation revision directly. Groups are writable
when the revision is >= 2, which is the case when userspace explicitly
sets the IIDR to revision 2 or 3. When userspace does not write the
IIDR, vgic_init() defaults to KVM_VGIC_IMP_REV_LATEST (currently 3),
so the behaviour is unchanged for userspace that doesn't set the IIDR.
This also fixes the inconsistency where GICv2 userspace could not write
IGROUPR at the default revision, even though the guest could.
As far as I can tell, QEMU commit eb8b9530b0c ("hw/intc/arm_gic_kvm.c:
Save and restore GICD_IGROUPRn state") made QEMU attempt to save/restore
the GICD_IGROUPR registers (which, again, are guest-writable but not
userspace-writable by default) without ever actually setting GICD_IIDR.
Fixes: 32f8777ed92d ("KVM: arm/arm64: vgic: Let userspace opt-in to writable v2 IGROUPR")
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/arm64/kvm/vgic/vgic-mmio-v2.c | 16 +++++-----------
include/kvm/arm_vgic.h | 3 ---
2 files changed, 5 insertions(+), 14 deletions(-)
diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v2.c b/arch/arm64/kvm/vgic/vgic-mmio-v2.c
index e5714f7fd2ec..e5fc673a1ea9 100644
--- a/arch/arm64/kvm/vgic/vgic-mmio-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-mmio-v2.c
@@ -84,21 +84,15 @@ static int vgic_mmio_uaccess_write_v2_misc(struct kvm_vcpu *vcpu,
return -EINVAL;
/*
- * If we observe a write to GICD_IIDR we know that userspace
- * has been updated and has had a chance to cope with older
- * kernels (VGICv2 IIDR.Revision == 0) incorrectly reporting
- * interrupts as group 1, and therefore we now allow groups to
- * be user writable. Doing this by default would break
- * migration from old kernels to new kernels with legacy
- * userspace.
+ * Allow userspace to select the GICv2 IIDR revision.
+ * Group writability follows the revision directly:
+ * groups are guest/user writable for revision >= 2.
*/
reg = FIELD_GET(GICD_IIDR_REVISION_MASK, val);
switch (reg) {
+ case KVM_VGIC_IMP_REV_1:
case KVM_VGIC_IMP_REV_2:
case KVM_VGIC_IMP_REV_3:
- vcpu->kvm->arch.vgic.v2_groups_user_writable = true;
- fallthrough;
- case KVM_VGIC_IMP_REV_1:
dist->implementation_rev = reg;
return 0;
default:
@@ -114,7 +108,7 @@ static int vgic_mmio_uaccess_write_v2_group(struct kvm_vcpu *vcpu,
gpa_t addr, unsigned int len,
unsigned long val)
{
- if (vcpu->kvm->arch.vgic.v2_groups_user_writable)
+ if (vgic_get_implementation_rev(vcpu) >= KVM_VGIC_IMP_REV_2)
vgic_mmio_write_group(vcpu, addr, len, val);
return 0;
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 16811ec03d54..a9490e43d98d 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -377,9 +377,6 @@ struct vgic_dist {
#define KVM_VGIC_IMP_REV_3 3 /* GICv3 GICR_CTLR.{IW,CES,RWP} */
#define KVM_VGIC_IMP_REV_LATEST KVM_VGIC_IMP_REV_3
- /* Userspace can write to GICv2 IGROUPR */
- bool v2_groups_user_writable;
-
/* Do injected MSIs require an additional device ID? */
bool msis_require_devid;
--
2.51.0
^ permalink raw reply related
* [PATCH v3 4/4] KVM: arm64: selftests: Add GICv2 IGROUPR writability test
From: David Woodhouse @ 2026-05-11 11:30 UTC (permalink / raw)
To: Paolo Bonzini
Cc: Jonathan Corbet, Shuah Khan, Marc Zyngier, Oliver Upton,
Joey Gouly, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Will Deacon, Jonathan Cameron, Sascha Bischoff, Eric Auger,
Raghavendra Rao Ananta, Maxim Levitsky, David Woodhouse,
Kees Cook, Timothy Hayes, Arnd Bergmann, kvm, linux-doc,
linux-kernel, linux-arm-kernel, kvmarm, linux-kselftest,
Peter Maydell, qemu-arm, qemu-devel
In-Reply-To: <20260511113558.3325004-1-dwmw2@infradead.org>
From: David Woodhouse <dwmw@amazon.co.uk>
Test that GICv2 IGROUPR writability is consistently gated by the IIDR
implementation revision for both guest and userspace paths:
Default (no IIDR write): implementation_rev defaults to 3, groups
writable from both guest and userspace.
Rev 1: IGROUPR reads as zero (group 0), writes ignored from both
guest and userspace.
Rev 2: IGROUPR is writable from both guest and userspace.
This test requires GICv2 emulation support (GICv3 with GICv2 compat
CPU interface) and will be skipped on hardware without it.
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../selftests/kvm/arm64/vgic_group_v2.c | 226 ++++++++++++++++++
2 files changed, 227 insertions(+)
create mode 100644 tools/testing/selftests/kvm/arm64/vgic_group_v2.c
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 8cadfed4d79a..6bc295d0b776 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -178,6 +178,7 @@ TEST_GEN_PROGS_arm64 += arm64/set_id_regs
TEST_GEN_PROGS_arm64 += arm64/smccc_filter
TEST_GEN_PROGS_arm64 += arm64/vcpu_width_config
TEST_GEN_PROGS_arm64 += arm64/vgic_group_iidr
+TEST_GEN_PROGS_arm64 += arm64/vgic_group_v2
TEST_GEN_PROGS_arm64 += arm64/vgic_init
TEST_GEN_PROGS_arm64 += arm64/vgic_irq
TEST_GEN_PROGS_arm64 += arm64/vgic_lpi_stress
diff --git a/tools/testing/selftests/kvm/arm64/vgic_group_v2.c b/tools/testing/selftests/kvm/arm64/vgic_group_v2.c
new file mode 100644
index 000000000000..f2b384a816ba
--- /dev/null
+++ b/tools/testing/selftests/kvm/arm64/vgic_group_v2.c
@@ -0,0 +1,226 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * vgic_group_v2.c - Test GICv2 IGROUPR behaviour across IIDR revisions
+ *
+ * Validate that the GICD_IIDR implementation revision controls GICv2
+ * IGROUPR writability for both guest and userspace:
+ * Default (no IIDR write): groups writable (implementation_rev defaults to 3)
+ * Rev 1: IGROUPR reads as zero (group 0), writes ignored
+ * Rev 2: IGROUPR is guest and userspace configurable
+ */
+#include <linux/sizes.h>
+
+#include "test_util.h"
+#include "kvm_util.h"
+#include "processor.h"
+#include "gic.h"
+#include "gic_v3.h"
+#include "vgic.h"
+
+#define NR_IRQS 64
+
+#define V2_DIST_BASE 0x8000000ULL
+#define V2_CPU_BASE 0x8010000ULL
+#define V2_DIST_GVA ((volatile void *)V2_DIST_BASE)
+
+#define SPI_IGROUPR (GICD_IGROUPR + (32 / 32) * 4)
+
+static uint64_t shared_rev;
+static uint64_t guest_result;
+
+static void guest_code(void)
+{
+ uint32_t before, after;
+
+ before = readl(V2_DIST_GVA + SPI_IGROUPR);
+ writel(0x5a5a5a5a, V2_DIST_GVA + SPI_IGROUPR);
+ after = readl(V2_DIST_GVA + SPI_IGROUPR);
+
+ guest_result = ((uint64_t)before << 32) | after;
+ GUEST_DONE();
+}
+
+static int create_v2_gic(struct kvm_vm *vm)
+{
+ uint32_t nr_irqs = NR_IRQS;
+ uint64_t addr;
+ int gic_fd;
+
+ gic_fd = __kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V2);
+ if (gic_fd < 0)
+ return gic_fd;
+
+ addr = V2_DIST_BASE;
+ kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
+ KVM_VGIC_V2_ADDR_TYPE_DIST, &addr);
+ addr = V2_CPU_BASE;
+ kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR,
+ KVM_VGIC_V2_ADDR_TYPE_CPU, &addr);
+
+ virt_map(vm, V2_DIST_BASE, V2_DIST_BASE,
+ vm_calc_num_guest_pages(vm->mode, SZ_64K));
+ virt_map(vm, V2_CPU_BASE, V2_CPU_BASE,
+ vm_calc_num_guest_pages(vm->mode, SZ_64K));
+
+ kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS,
+ 0, &nr_irqs);
+ return gic_fd;
+}
+
+static void run_test(int set_iidr_rev)
+{
+ struct kvm_vcpu *vcpus[1];
+ struct kvm_vm *vm;
+ struct ucall uc;
+ uint32_t before, after, igroupr, iidr;
+ int gic_fd;
+ bool expect_writable;
+
+ if (set_iidr_rev >= 0)
+ pr_info("Testing GICv2 IIDR revision %d\n", set_iidr_rev);
+ else
+ pr_info("Testing GICv2 IIDR default (no write)\n");
+
+ test_disable_default_vgic();
+ vm = vm_create_with_vcpus(1, guest_code, vcpus);
+
+ gic_fd = create_v2_gic(vm);
+ TEST_REQUIRE(gic_fd >= 0);
+
+ if (set_iidr_rev >= 0) {
+ kvm_device_attr_get(gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
+ GICD_IIDR, &iidr);
+ iidr &= ~GICD_IIDR_REVISION_MASK;
+ iidr |= set_iidr_rev << GICD_IIDR_REVISION_SHIFT;
+ kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
+ GICD_IIDR, &iidr);
+ }
+
+ kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
+ KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
+
+ /*
+ * Default (no IIDR write) gets implementation_rev=3 from vgic_init(),
+ * so groups should be writable. Rev 1 = not writable. Rev 2+ = writable.
+ */
+ expect_writable = (set_iidr_rev != 1);
+
+ /* Test userspace IGROUPR write */
+ igroupr = 0xa5a5a5a5;
+ kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
+ SPI_IGROUPR, &igroupr);
+ igroupr = 0;
+ kvm_device_attr_get(gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
+ SPI_IGROUPR, &igroupr);
+
+ if (expect_writable)
+ TEST_ASSERT(igroupr == 0xa5a5a5a5,
+ "Userspace write should succeed: got 0x%08x", igroupr);
+ else
+ TEST_ASSERT(igroupr == 0x00000000,
+ "Userspace write should be ignored: got 0x%08x", igroupr);
+
+ /* Reset IGROUPR to 0 via userspace for rev 2+ before guest test */
+ if (expect_writable) {
+ igroupr = 0;
+ kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
+ SPI_IGROUPR, &igroupr);
+ }
+
+ /* Test guest IGROUPR write */
+ sync_global_to_guest(vm, guest_result);
+ vcpu_run(vcpus[0]);
+
+ switch (get_ucall(vcpus[0], &uc)) {
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT(uc);
+ break;
+ case UCALL_DONE:
+ break;
+ default:
+ TEST_FAIL("Unexpected ucall %lu", uc.cmd);
+ }
+
+ sync_global_from_guest(vm, guest_result);
+ before = guest_result >> 32;
+ after = guest_result & 0xffffffff;
+
+ TEST_ASSERT(before == 0x00000000,
+ "Initial IGROUPR should be 0 (group 0): got 0x%08x", before);
+
+ if (expect_writable)
+ TEST_ASSERT(after == 0x5a5a5a5a,
+ "Guest write should succeed: got 0x%08x", after);
+ else
+ TEST_ASSERT(after == 0x00000000,
+ "Guest write should be ignored: got 0x%08x", after);
+
+ close(gic_fd);
+ kvm_vm_free(vm);
+}
+
+/*
+ * Test QEMU-style save/restore: the guest writes IGROUPR, then userspace
+ * reads it back (save) and writes it again (restore) — all without ever
+ * writing GICD_IIDR. This exercises the bug where v2_groups_user_writable
+ * gated userspace writes but not guest writes, so userspace could observe
+ * guest-modified groups but couldn't restore them.
+ */
+static void run_save_restore_test(void)
+{
+ struct kvm_vcpu *vcpus[1];
+ struct kvm_vm *vm;
+ struct ucall uc;
+ uint32_t igroupr;
+ int gic_fd;
+
+ pr_info("Testing GICv2 IGROUPR save/restore (no IIDR write)\n");
+
+ test_disable_default_vgic();
+ vm = vm_create_with_vcpus(1, guest_code, vcpus);
+
+ gic_fd = create_v2_gic(vm);
+ TEST_REQUIRE(gic_fd >= 0);
+
+ /* Do NOT write GICD_IIDR — mimicking QEMU */
+
+ kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL,
+ KVM_DEV_ARM_VGIC_CTRL_INIT, NULL);
+
+ /* Let the guest write 0x5a5a5a5a to IGROUPR */
+ sync_global_to_guest(vm, guest_result);
+ vcpu_run(vcpus[0]);
+ TEST_ASSERT(get_ucall(vcpus[0], &uc) == UCALL_DONE,
+ "Guest failed");
+
+ /* Save: userspace reads IGROUPR — should see guest's write */
+ igroupr = 0;
+ kvm_device_attr_get(gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
+ SPI_IGROUPR, &igroupr);
+ TEST_ASSERT(igroupr == 0x5a5a5a5a,
+ "Save: expected 0x5a5a5a5a, got 0x%08x", igroupr);
+
+ /* Restore: userspace writes a different value — should succeed */
+ igroupr = 0x12345678;
+ kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
+ SPI_IGROUPR, &igroupr);
+
+ /* Verify: read back should reflect the restore */
+ igroupr = 0;
+ kvm_device_attr_get(gic_fd, KVM_DEV_ARM_VGIC_GRP_DIST_REGS,
+ SPI_IGROUPR, &igroupr);
+ TEST_ASSERT(igroupr == 0x12345678,
+ "Restore: expected 0x12345678, got 0x%08x", igroupr);
+
+ close(gic_fd);
+ kvm_vm_free(vm);
+}
+
+int main(int argc, char *argv[])
+{
+ run_test(-1); /* default */
+ run_test(1); /* rev 1 */
+ run_test(2); /* rev 2 */
+ run_save_restore_test();
+ return 0;
+}
--
2.51.0
^ permalink raw reply related
* [PATCH v3 1/4] KVM: arm64: vgic: Allow userspace to set IIDR revision 1
From: David Woodhouse @ 2026-05-11 11:30 UTC (permalink / raw)
To: Paolo Bonzini
Cc: Jonathan Corbet, Shuah Khan, Marc Zyngier, Oliver Upton,
Joey Gouly, Suzuki K Poulose, Zenghui Yu, Catalin Marinas,
Will Deacon, Jonathan Cameron, Sascha Bischoff, Eric Auger,
Raghavendra Rao Ananta, Maxim Levitsky, David Woodhouse,
Kees Cook, Timothy Hayes, Arnd Bergmann, kvm, linux-doc,
linux-kernel, linux-arm-kernel, kvmarm, linux-kselftest,
Peter Maydell, qemu-arm, qemu-devel
In-Reply-To: <20260511113558.3325004-1-dwmw2@infradead.org>
From: David Woodhouse <dwmw@amazon.co.uk>
In order to preserve guest compatibility across kernel upgrades, allow
userspace to select GICD_IIDR revision 1. This allows compatibility with
the original behaviour from before commit d53c2c29ae0d ("KVM: arm/arm64:
vgic: Allow configuration of interrupt groups") where interrupt groups
are not guest-configurable.
When revision 1 is selected:
- GICv2: IGROUPR reads as zero (group 0), writes are ignored
- GICv3: IGROUPR reads as all-ones (group 1), writes are ignored
- v2_groups_user_writable is not set
This is implemented by checking the implementation revision in
vgic_mmio_write_group() and suppressing writes when the revision is
below 2. The read side needs no change since the per-IRQ group reset
values already match the expected behaviour.
For GICv2, commit 32f8777ed92d7 ("KVM: arm/arm64: vgic: Let userspace
opt-in to writable v2 IGROUPR") introduced a confusing model where
IGROUPR registers were not writable from userspace by default until the
IIDR was written — even if it was written to the *same* as its default
value (which, in fact, was the only thing that userspace *could* set it
to before commit a0e6ae45af17 fixed the IIDR write path). Furthermore,
even when the v2_groups_user_writable flag wasn't set, the *guest*
could still actually write to the registers... but userspace couldn't
save/restore them. That default behaviour for GICv2 remains unchanged;
it can be fixed in a future commit.
Fixes: d53c2c29ae0d ("KVM: arm/arm64: vgic: Allow configuration of interrupt groups")
Signed-off-by: David Woodhouse <dwmw@amazon.co.uk>
---
arch/arm64/kvm/vgic/vgic-mmio-v2.c | 3 +++
arch/arm64/kvm/vgic/vgic-mmio-v3.c | 4 ++++
arch/arm64/kvm/vgic/vgic-mmio.c | 4 ++++
include/kvm/arm_vgic.h | 1 +
4 files changed, 12 insertions(+)
diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v2.c b/arch/arm64/kvm/vgic/vgic-mmio-v2.c
index 0643e333db35..e5714f7fd2ec 100644
--- a/arch/arm64/kvm/vgic/vgic-mmio-v2.c
+++ b/arch/arm64/kvm/vgic/vgic-mmio-v2.c
@@ -20,6 +20,7 @@
* Revision 1: Report GICv2 interrupts as group 0 instead of group 1
* Revision 2: Interrupt groups are guest-configurable and signaled using
* their configured groups.
+ * Revision 3: GICv2 behaviour is unchanged from revision 2.
*/
static unsigned long vgic_mmio_read_v2_misc(struct kvm_vcpu *vcpu,
@@ -96,6 +97,8 @@ static int vgic_mmio_uaccess_write_v2_misc(struct kvm_vcpu *vcpu,
case KVM_VGIC_IMP_REV_2:
case KVM_VGIC_IMP_REV_3:
vcpu->kvm->arch.vgic.v2_groups_user_writable = true;
+ fallthrough;
+ case KVM_VGIC_IMP_REV_1:
dist->implementation_rev = reg;
return 0;
default:
diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v3.c b/arch/arm64/kvm/vgic/vgic-mmio-v3.c
index 5913a20d8301..0130db71cfc9 100644
--- a/arch/arm64/kvm/vgic/vgic-mmio-v3.c
+++ b/arch/arm64/kvm/vgic/vgic-mmio-v3.c
@@ -74,8 +74,11 @@ bool vgic_supports_direct_sgis(struct kvm *kvm)
/*
* The Revision field in the IIDR have the following meanings:
*
+ * Revision 1: Interrupt groups are not guest-configurable.
+ * IGROUPR reads as all-ones (group 1), writes ignored.
* Revision 2: Interrupt groups are guest-configurable and signaled using
* their configured groups.
+ * Revision 3: GICR_CTLR.{IR,CES} are advertised.
*/
static unsigned long vgic_mmio_read_v3_misc(struct kvm_vcpu *vcpu,
@@ -196,6 +199,7 @@ static int vgic_mmio_uaccess_write_v3_misc(struct kvm_vcpu *vcpu,
reg = FIELD_GET(GICD_IIDR_REVISION_MASK, val);
switch (reg) {
+ case KVM_VGIC_IMP_REV_1:
case KVM_VGIC_IMP_REV_2:
case KVM_VGIC_IMP_REV_3:
dist->implementation_rev = reg;
diff --git a/arch/arm64/kvm/vgic/vgic-mmio.c b/arch/arm64/kvm/vgic/vgic-mmio.c
index 74d76dec9730..1b662744ec5b 100644
--- a/arch/arm64/kvm/vgic/vgic-mmio.c
+++ b/arch/arm64/kvm/vgic/vgic-mmio.c
@@ -73,6 +73,10 @@ void vgic_mmio_write_group(struct kvm_vcpu *vcpu, gpa_t addr,
int i;
unsigned long flags;
+ /* Revision 1 and below: groups are not guest-configurable. */
+ if (vgic_get_implementation_rev(vcpu) < KVM_VGIC_IMP_REV_2)
+ return;
+
for (i = 0; i < len * 8; i++) {
struct vgic_irq *irq = vgic_get_vcpu_irq(vcpu, intid + i);
diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h
index 1388dc6028a9..16811ec03d54 100644
--- a/include/kvm/arm_vgic.h
+++ b/include/kvm/arm_vgic.h
@@ -372,6 +372,7 @@ struct vgic_dist {
/* Implementation revision as reported in the GICD_IIDR */
u32 implementation_rev;
+#define KVM_VGIC_IMP_REV_1 1 /* GICv2 interrupts as group 0 */
#define KVM_VGIC_IMP_REV_2 2 /* GICv2 restorable groups */
#define KVM_VGIC_IMP_REV_3 3 /* GICv3 GICR_CTLR.{IW,CES,RWP} */
#define KVM_VGIC_IMP_REV_LATEST KVM_VGIC_IMP_REV_3
--
2.51.0
^ permalink raw reply related
* [PATCH v12 5/5] mfd: Add support for MediaTek SPMI PMICs and MT6363/73
From: AngeloGioacchino Del Regno @ 2026-05-11 10:13 UTC (permalink / raw)
To: linux-mediatek
Cc: lee, robh, krzk+dt, conor+dt, matthias.bgg,
angelogioacchino.delregno, lgirdwood, broonie, devicetree,
linux-kernel, linux-arm-kernel, kernel, wenst,
Nícolas F. R. A. Prado
In-Reply-To: <20260511101355.122478-1-angelogioacchino.delregno@collabora.com>
This driver adds support for the MediaTek SPMI PMICs and their
interrupt controller (which is present in 95% of the cases).
Other than probing all of the sub-devices of a SPMI PMIC, this
sets up a regmap from the relevant SPMI bus and initializes an
interrupt controller with its irq domain and irqchip to handle
chained interrupts, with the SPMI bus itself being its parent
irq controller, and the PMIC being the outmost device.
This driver hence holds all of the information about a specific
PMIC's interrupts and will properly handle them, calling the
ISR for any subdevice that requested an interrupt.
As for the interrupt spec, this driver wants either three or
two interrupt cells, but in the case 3 were given it ignores
the first one: this is because of how this first revision of
of the MediaTek SPMI 2.0 Controller works, which doesn't hold
hold irq number information in its register, but delegates
that to the SPMI device - it's possible that this will change
in the future with a newer revision of the controller IP, and
this is the main reason for that.
To make use of this implementation, this driver also adds the
required bits to support MediaTek MT6363 and MT6373 SPMI PMICs.
Reviewed-by: Nícolas F. R. A. Prado <nfraprado@collabora.com>
Signed-off-by: AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
---
drivers/mfd/Kconfig | 16 +
drivers/mfd/Makefile | 1 +
drivers/mfd/mtk-spmi-pmic.c | 427 ++++++++++++++++++++++
include/linux/mfd/mt63x3_spmi/registers.h | 34 ++
4 files changed, 478 insertions(+)
create mode 100644 drivers/mfd/mtk-spmi-pmic.c
create mode 100644 include/linux/mfd/mt63x3_spmi/registers.h
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 7192c9d1d268..3e9acdf648b7 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1148,6 +1148,22 @@ config MFD_MT6397
accessing the device; additional drivers must be enabled in order
to use the functionality of the device.
+config MFD_MTK_SPMI_PMIC
+ tristate "MediaTek SPMI PMICs"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on OF
+ depends on SPMI
+ select REGMAP_SPMI
+ help
+ Say yes here to enable support for MediaTek's SPMI PMICs.
+ These PMICs made their first appearance in board designs using the
+ MediaTek Dimensity 9400 series of SoCs.
+ Note that this will only be useful when paired with descriptions
+ of the independent functions as child nodes in the device tree.
+
+ Say M here if you want to include support for the MediaTek SPMI
+ PMICs as a module. The module will be called "mtk-spmi-pmic".
+
config MFD_MENF21BMC
tristate "MEN 14F021P00 Board Management Controller Support"
depends on I2C
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index e75e8045c28a..e00d283450c6 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -190,6 +190,7 @@ obj-$(CONFIG_MFD_MT6360) += mt6360-core.o
obj-$(CONFIG_MFD_MT6370) += mt6370.o
mt6397-objs := mt6397-core.o mt6397-irq.o mt6358-irq.o
obj-$(CONFIG_MFD_MT6397) += mt6397.o
+obj-$(CONFIG_MFD_MTK_SPMI_PMIC) += mtk-spmi-pmic.o
obj-$(CONFIG_RZ_MTU3) += rz-mtu3.o
obj-$(CONFIG_ABX500_CORE) += abx500-core.o
diff --git a/drivers/mfd/mtk-spmi-pmic.c b/drivers/mfd/mtk-spmi-pmic.c
new file mode 100644
index 000000000000..d1fc8156e696
--- /dev/null
+++ b/drivers/mfd/mtk-spmi-pmic.c
@@ -0,0 +1,427 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MediaTek SPMI PMICs Driver
+ *
+ * Copyright (c) 2024 MediaTek Inc.
+ * Copyright (c) 2025 Collabora Ltd
+ *
+ * Authors:
+ * AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/gfp.h>
+#include <linux/irq.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
+#include <linux/kernel.h>
+#include <linux/mfd/mt63x3_spmi/registers.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+#include <linux/spmi.h>
+#include <linux/types.h>
+
+#define MTK_SPMI_PMIC_VAL_BITS 8
+#define MTK_SPMI_PMIC_REG_CHIP_ID 0xb
+#define MTK_SPMI_PMIC_RCS_IRQ_DONE 0x41b
+
+/**
+ * struct mtk_spmi_pmic_irq_group - Group of interrupts in SPMI PMIC
+ * @num_int_regs: Number of registers for this group of interrupts
+ * @con_reg: PMIC Interrupt Group Control 0 register
+ * @sta_reg: PMIC Interrupt Group Status 0 register
+ * @group_num: PMIC Interrupt Group number - also corresponds to the
+ * status bit in the global IRQ Control register
+ */
+struct mtk_spmi_pmic_irq_grp {
+ u8 hwirq_base;
+ u8 num_int_regs;
+ u16 con_reg;
+ u16 sta_reg;
+ u8 group_num;
+};
+
+/**
+ * struct mtk_spmi_pmic_variant - SPMI PMIC variant-specific data
+ * @pmic_irq: Group of interrupts in SPMI PMIC
+ * @num_groups: Number of groups of interrupts
+ * @con_reg_len: Length in bytes of Control registers, depends on
+ * existence of SET and CLR registers in the layout
+ * @irq_grp_reg: Global interrupt status register, explains which
+ * group needs attention because of a group IRQ;
+ * if this is zero, it means that there is only one
+ * group and the device has no irqgroup register
+ * @chip_id_reg: Chip ID Register
+ */
+struct mtk_spmi_pmic_variant {
+ const struct mtk_spmi_pmic_irq_grp *pmic_irq;
+ u8 num_groups;
+ u8 con_reg_len;
+ u8 irq_grp_reg;
+ u8 chip_id_reg;
+};
+
+/**
+ * struct mtk_spmi_pmic - Main driver structure
+ * @variant: SPMI PMIC variant-specific data
+ * @dev: Handle to SPMI Device
+ * @dom: IRQ Domain of the PMIC's interrupt controller
+ * @regmap: Handle to PMIC regmap
+ * @irq: PMIC chained interrupt
+ */
+struct mtk_spmi_pmic {
+ const struct mtk_spmi_pmic_variant *variant;
+ struct device *dev;
+ struct irq_domain *dom;
+ struct regmap *regmap;
+ int irq;
+};
+
+static void mtk_spmi_pmic_irq_set_unmasking(struct irq_data *d, bool unmask)
+{
+ struct mtk_spmi_pmic *pmic = irq_data_get_irq_chip_data(d);
+ const struct mtk_spmi_pmic_variant *variant = pmic->variant;
+ struct regmap *regmap = pmic->regmap;
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ unsigned short i;
+
+ for (i = 0; i < variant->num_groups; i++) {
+ const struct mtk_spmi_pmic_irq_grp *irq_grp = &variant->pmic_irq[i];
+ u32 con_reg;
+ u8 irq_en_bit;
+
+ if (hwirq < irq_grp->hwirq_base)
+ continue;
+
+ con_reg = irq_grp->con_reg + (variant->con_reg_len * i);
+ irq_en_bit = hwirq - irq_grp->hwirq_base;
+ regmap_assign_bits(regmap, con_reg, BIT(irq_en_bit), unmask);
+
+ break;
+ }
+}
+
+static void mtk_spmi_pmic_irq_mask(struct irq_data *d)
+{
+ mtk_spmi_pmic_irq_set_unmasking(d, false);
+}
+
+static void mtk_spmi_pmic_irq_unmask(struct irq_data *d)
+{
+ mtk_spmi_pmic_irq_set_unmasking(d, true);
+}
+
+static struct irq_chip mtk_spmi_pmic_irq_chip = {
+ .name = "mtk-spmi-pmic",
+ .irq_mask = mtk_spmi_pmic_irq_mask,
+ .irq_unmask = mtk_spmi_pmic_irq_unmask,
+ .flags = IRQCHIP_SKIP_SET_WAKE,
+};
+
+static struct lock_class_key mtk_spmi_pmic_irq_lock_class, mtk_spmi_pmic_irq_request_class;
+
+static int mtk_spmi_pmic_irq_translate(struct irq_domain *d, struct irq_fwspec *fwspec,
+ unsigned long *out_hwirq, unsigned int *out_type)
+{
+ struct mtk_spmi_pmic *pmic = d->host_data;
+ u32 intsize = fwspec->param_count;
+ u32 *intspec = fwspec->param;
+ unsigned int irq_type_index;
+ unsigned int irq_num_index;
+
+ /*
+ * Interrupt cell index - For interrupt size 3:
+ * [0] - SID Interrupt number
+ * [1] - SPMI PMIC (Sub-)Device Interrupt number
+ * [2] - Interrupt Type mask
+ *
+ * When only two cells are specified the SID Interrupt is not present.
+ */
+ if (intsize != 2 && intsize != 3) {
+ dev_err(pmic->dev, "Expected IRQ specifier of size 2 or 3, got %u\n", intsize);
+ return -EINVAL;
+ }
+
+ /* irq_num_index refers to the SPMI (Sub-)Device Interrupt number */
+ irq_num_index = intsize - 2;
+ irq_type_index = irq_num_index + 1;
+
+ /*
+ * For 3 cells, the IRQ number in intspec[0] is ignored on purpose here!
+ *
+ * This is because of how at least the first revision of the SPMI 2.0
+ * controller works in MediaTek SoCs: the controller will raise an
+ * interrupt for each SID (but doesn't know the details), and the
+ * specific IRQ number that got raised must be read from the PMIC or
+ * its sub-device driver.
+ * It's possible that this will change in the future with a newer
+ * revision of the SPMI controller, and this is why the device tree
+ * holds the full interrupt specifier.
+ *
+ * out_hwirq: SPMI PMIC (Sub-)Device Interrupt number
+ * out_type: Interrupt type sense mask
+ */
+ *out_hwirq = intspec[irq_num_index];
+ *out_type = intspec[irq_type_index] & IRQ_TYPE_SENSE_MASK;
+
+ return 0;
+}
+
+static int mtk_spmi_pmic_irq_alloc(struct irq_domain *d, unsigned int virq,
+ unsigned int nr_irqs, void *data)
+{
+ struct mtk_spmi_pmic *pmic = d->host_data;
+ struct irq_fwspec *fwspec = data;
+ irq_hw_number_t hwirq;
+ unsigned int irqtype;
+ int i, ret;
+
+ ret = mtk_spmi_pmic_irq_translate(d, fwspec, &hwirq, &irqtype);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < nr_irqs; i++) {
+ irq_set_lockdep_class(virq, &mtk_spmi_pmic_irq_lock_class,
+ &mtk_spmi_pmic_irq_request_class);
+ irq_domain_set_info(d, virq, hwirq, &mtk_spmi_pmic_irq_chip,
+ pmic, handle_level_irq, NULL, NULL);
+ }
+
+ return 0;
+}
+
+
+static const struct irq_domain_ops mtk_spmi_pmic_irq_domain_ops = {
+ .alloc = mtk_spmi_pmic_irq_alloc,
+ .free = irq_domain_free_irqs_common,
+ .translate = mtk_spmi_pmic_irq_translate,
+};
+
+static int mtk_spmi_pmic_handle_group_irq(struct mtk_spmi_pmic *pmic, int group)
+{
+ const struct mtk_spmi_pmic_irq_grp *irq_grp = &pmic->variant->pmic_irq[group];
+ struct regmap *regmap = pmic->regmap;
+ struct device *dev = pmic->dev;
+ int i, ret;
+
+ for (i = 0; i < irq_grp->num_int_regs; i++) {
+ u32 status, saved_status;
+
+ ret = regmap_read(regmap, irq_grp->sta_reg + i, &status);
+ if (ret) {
+ dev_err(dev, "Could not read IRQ status register: %d", ret);
+ return ret;
+ }
+
+ if (status == 0)
+ continue;
+
+ saved_status = status;
+ do {
+ irq_hw_number_t hwirq;
+ u8 bit = __ffs(status);
+
+ /* Each register has 8 bits: this is the first IRQ of this group */
+ hwirq = MTK_SPMI_PMIC_VAL_BITS * i;
+
+ /* Offset by this group's start interrupt */
+ hwirq += irq_grp->hwirq_base;
+
+ /* Finally, offset by the fired IRQ's bit number */
+ hwirq += bit;
+
+ status &= ~BIT(bit);
+
+ generic_handle_domain_irq_safe(pmic->dom, hwirq);
+ } while (status);
+
+ /* Clear the interrupts by writing the previous status */
+ regmap_write(regmap, irq_grp->sta_reg + i, saved_status);
+ }
+
+ return 0;
+}
+
+static void mtk_spmi_pmic_handle_chained_irq(struct irq_desc *desc)
+{
+ struct mtk_spmi_pmic *pmic = irq_desc_get_handler_data(desc);
+ const struct mtk_spmi_pmic_variant *variant = pmic->variant;
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+ struct regmap *regmap = pmic->regmap;
+ bool irq_handled = false;
+ int i, ret;
+ u32 val;
+
+ chained_irq_enter(chip, desc);
+
+ /* If irq_grp_reg is present there are multiple IRQ groups */
+ if (variant->irq_grp_reg > 0) {
+ ret = regmap_read(regmap, variant->irq_grp_reg, &val);
+ if (ret)
+ handle_bad_irq(desc);
+
+ /* This is very unlikely to happen */
+ if (val == 0) {
+ chained_irq_exit(chip, desc);
+ return;
+ }
+ } else {
+ val = BIT(0);
+ }
+
+ for (i = 0; i < variant->num_groups; i++) {
+ const struct mtk_spmi_pmic_irq_grp *irq_grp = &variant->pmic_irq[i];
+ u8 group_bit = BIT(irq_grp[i].group_num);
+
+ if (val & group_bit) {
+ ret = mtk_spmi_pmic_handle_group_irq(pmic, i);
+ if (ret == 0)
+ irq_handled = true;
+ }
+ }
+
+ /* The RCS flag has to be cleared even if the IRQ was not handled. */
+ ret = regmap_write(regmap, MTK_SPMI_PMIC_RCS_IRQ_DONE, 1);
+ if (ret)
+ dev_warn(pmic->dev, "Could not clear RCS flag!\n");
+
+ if (!irq_handled)
+ handle_bad_irq(desc);
+
+ chained_irq_exit(chip, desc);
+}
+
+static void mtk_spmi_pmic_irq_remove(void *data)
+{
+ struct mtk_spmi_pmic *pmic = (struct mtk_spmi_pmic *)data;
+
+ irq_set_chained_handler_and_data(pmic->irq, NULL, NULL);
+ irq_domain_remove(pmic->dom);
+}
+
+static int mtk_spmi_pmic_irq_init(struct device *dev, struct regmap *regmap,
+ const struct mtk_spmi_pmic_variant *variant)
+{
+ struct fwnode_handle *fwnode = of_fwnode_handle(dev->of_node);
+ struct mtk_spmi_pmic *pmic;
+ int ret;
+
+ pmic = devm_kzalloc(dev, sizeof(*pmic), GFP_KERNEL);
+ if (!pmic)
+ return -ENOMEM;
+
+ pmic->irq = of_irq_get(dev->of_node, 0);
+ if (pmic->irq < 0)
+ return dev_err_probe(dev, pmic->irq, "Cannot get IRQ\n");
+
+ pmic->dev = dev;
+ pmic->regmap = regmap;
+ pmic->variant = variant;
+
+ pmic->dom = irq_domain_create_tree(fwnode, &mtk_spmi_pmic_irq_domain_ops, pmic);
+ if (!pmic->dom)
+ return dev_err_probe(dev, -ENOMEM, "Cannot create IRQ domain\n");
+
+ ret = devm_add_action_or_reset(dev, mtk_spmi_pmic_irq_remove, pmic);
+ if (ret) {
+ irq_domain_remove(pmic->dom);
+ return ret;
+ }
+
+ irq_set_chained_handler_and_data(pmic->irq, mtk_spmi_pmic_handle_chained_irq, pmic);
+
+ return 0;
+}
+
+#define MTK_SPMI_PMIC_IRQ_GROUP(pmic, group_name, group_index, first_irq, last_irq) \
+{ \
+ .hwirq_base = first_irq, \
+ .num_int_regs = ((last_irq - first_irq) / MTK_SPMI_PMIC_VAL_BITS) + 1, \
+ .con_reg = pmic##_REG_##group_name##_TOP_INT_CON0, \
+ .sta_reg = pmic##_REG_##group_name##_TOP_INT_STATUS0, \
+ .group_num = group_index, \
+}
+
+static const struct mtk_spmi_pmic_irq_grp mt6363_irq_groups[] = {
+ MTK_SPMI_PMIC_IRQ_GROUP(MT6363, BUCK, 0, 0, 9),
+ MTK_SPMI_PMIC_IRQ_GROUP(MT6363, LDO, 1, 16, 40),
+ MTK_SPMI_PMIC_IRQ_GROUP(MT6363, PSC, 2, 48, 57),
+ MTK_SPMI_PMIC_IRQ_GROUP(MT6363, MISC, 3, 64, 79),
+ MTK_SPMI_PMIC_IRQ_GROUP(MT6363, HK, 4, 80, 87),
+ MTK_SPMI_PMIC_IRQ_GROUP(MT6363, BM, 6, 88, 107)
+};
+
+static const struct mtk_spmi_pmic_irq_grp mt6373_irq_groups[] = {
+ MTK_SPMI_PMIC_IRQ_GROUP(MT6373, BUCK, 0, 0, 9),
+ MTK_SPMI_PMIC_IRQ_GROUP(MT6373, LDO, 1, 16, 39),
+ MTK_SPMI_PMIC_IRQ_GROUP(MT6373, MISC, 3, 56, 71),
+};
+
+static const struct mtk_spmi_pmic_variant mt6363_variant = {
+ .pmic_irq = mt6363_irq_groups,
+ .num_groups = ARRAY_SIZE(mt6363_irq_groups),
+ .con_reg_len = 3,
+ .irq_grp_reg = MT6363_REG_TOP_INT_STATUS1,
+ .chip_id_reg = MTK_SPMI_PMIC_REG_CHIP_ID,
+};
+
+static const struct mtk_spmi_pmic_variant mt6373_variant = {
+ .pmic_irq = mt6373_irq_groups,
+ .num_groups = ARRAY_SIZE(mt6373_irq_groups),
+ .con_reg_len = 3,
+ .irq_grp_reg = MT6373_REG_TOP_INT_STATUS1,
+ .chip_id_reg = MTK_SPMI_PMIC_REG_CHIP_ID,
+};
+
+static const struct regmap_config mtk_spmi_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = MTK_SPMI_PMIC_VAL_BITS,
+ .max_register = 0xffff,
+ .fast_io = true,
+};
+
+static int mtk_spmi_pmic_probe(struct spmi_device *sdev)
+{
+ const struct mtk_spmi_pmic_variant *variant;
+ struct device *dev = &sdev->dev;
+ struct regmap *regmap;
+ int ret;
+
+ regmap = devm_regmap_init_spmi_ext(sdev, &mtk_spmi_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ variant = (const struct mtk_spmi_pmic_variant *)device_get_match_data(dev);
+ if (variant && variant->num_groups) {
+ ret = mtk_spmi_pmic_irq_init(dev, regmap, variant);
+ if (ret)
+ return ret;
+ }
+
+ return devm_of_platform_populate(dev);
+}
+
+static const struct of_device_id mtk_pmic_spmi_id_table[] = {
+ { .compatible = "mediatek,mt6363", .data = &mt6363_variant },
+ { .compatible = "mediatek,mt6373", .data = &mt6373_variant },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mtk_pmic_spmi_id_table);
+
+static struct spmi_driver mtk_spmi_pmic_driver = {
+ .probe = mtk_spmi_pmic_probe,
+ .driver = {
+ .name = "mtk-spmi-pmic",
+ .of_match_table = mtk_pmic_spmi_id_table,
+ },
+};
+module_spmi_driver(mtk_spmi_pmic_driver);
+
+MODULE_AUTHOR("AngeloGioacchino Del Regno <angelogioacchino.delregno@collabora.com>");
+MODULE_DESCRIPTION("MediaTek SPMI PMIC driver");
+MODULE_LICENSE("GPL");
diff --git a/include/linux/mfd/mt63x3_spmi/registers.h b/include/linux/mfd/mt63x3_spmi/registers.h
new file mode 100644
index 000000000000..808927280b40
--- /dev/null
+++ b/include/linux/mfd/mt63x3_spmi/registers.h
@@ -0,0 +1,34 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2021 MediaTek Inc.
+ * Copyright (c) 2025 Collabora Ltd
+ */
+
+#ifndef __MFD_MT63X3_SPMI_REGISTERS_H__
+#define __MFD_MT63X3_SPMI_REGISTERS_H__
+
+/* MT6363 PMIC Registers */
+#define MT6363_REG_MISC_TOP_INT_CON0 0x37
+#define MT6363_REG_MISC_TOP_INT_STATUS0 0x43
+#define MT6363_REG_TOP_INT_STATUS1 0x4e
+#define MT6363_REG_PSC_TOP_INT_CON0 0x90f
+#define MT6363_REG_PSC_TOP_INT_STATUS0 0x91b
+#define MT6363_REG_BM_TOP_INT_CON0 0xc24
+#define MT6363_REG_BM_TOP_INT_STATUS0 0xc36
+#define MT6363_REG_HK_TOP_INT_CON0 0xf92
+#define MT6363_REG_HK_TOP_INT_STATUS0 0xf9e
+#define MT6363_REG_BUCK_TOP_INT_CON0 0x1411
+#define MT6363_REG_BUCK_TOP_INT_STATUS0 0x141d
+#define MT6363_REG_LDO_TOP_INT_CON0 0x1b11
+#define MT6363_REG_LDO_TOP_INT_STATUS0 0x1b29
+
+/* MT6373 PMIC Registers */
+#define MT6373_REG_MISC_TOP_INT_CON0 0x3c
+#define MT6373_REG_MISC_TOP_INT_STATUS0 0x48
+#define MT6373_REG_TOP_INT_STATUS1 0x53
+#define MT6373_REG_BUCK_TOP_INT_CON0 0x1411
+#define MT6373_REG_BUCK_TOP_INT_STATUS0 0x141d
+#define MT6373_REG_LDO_TOP_INT_CON0 0x1b10
+#define MT6373_REG_LDO_TOP_INT_STATUS0 0x1b22
+
+#endif /* __MFD_MT63X3_SPMI_REGISTERS_H__ */
--
2.53.0
^ permalink raw reply related
* Re: [PATCH 2/3] [v5 net-next] p54spi: convert to devicetree
From: Bartosz Golaszewski @ 2026-05-11 11:30 UTC (permalink / raw)
To: Arnd Bergmann
Cc: netdev, Arnd Bergmann, Aaro Koskinen, Andreas Kemnade,
Bartosz Golaszewski, Benoît Cousson, David S. Miller,
Dmitry Torokhov, Eric Dumazet, Felipe Balbi, Jakub Kicinski,
Johannes Berg, Kevin Hilman, Krzysztof Kozlowski, Linus Walleij,
Paolo Abeni, Rob Herring, Roger Quadros, Tony Lindgren,
linux-wireless, devicetree, linux-kernel, linux-arm-kernel,
linux-gpio, linux-omap, Christian Lamparter
In-Reply-To: <20260507212451.3333185-3-arnd@kernel.org>
On Thu, 7 May 2026 23:24:50 +0200, Arnd Bergmann <arnd@kernel.org> said:
> From: Arnd Bergmann <arnd@arndb.de>
>
> The Prism54 SPI driver hardcodes GPIO numbers and expects users to
> pass them as module parameters, apparently a relic from its life as a
> staging driver. This works because there is only one user, the Nokia
> N8x0 tablet.
>
> Convert this to the gpio descriptor interface and DT based probing
> to improve this and simplify the code at the same time.
>
> Acked-by: Christian Lamparter <chunkeey@gmail.com>
> Reviewed-by: Linus Walleij <linusw@kernel.org>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
> ---
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
^ permalink raw reply
* Re: [PATCH v4 2/2] arm64: dts: imx8dxl: Add SolidRun SoM and HummingBoard
From: Vladimir Oltean @ 2026-05-11 11:24 UTC (permalink / raw)
To: Josua Mayer
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Shawn Guo,
Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Andrew Lunn, Vladimir Oltean, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Yazan Shhady, Mikhail Anikin,
Alexander Dahl, devicetree, linux-kernel, imx, linux-arm-kernel,
Conor Dooley, Krzysztof Kozlowski, netdev
In-Reply-To: <20260511-imx8dxl-sr-som-v4-2-64381b3bf80d@solid-run.com>
On Mon, May 11, 2026 at 12:11:31PM +0200, Josua Mayer wrote:
> +&eqos {
> + /* delays are added by connected ethernet-switch cpu port */
> + phy-mode = "rgmii";
Documentation/devicetree/bindings/net/ethernet-controller.yaml says:
# Informative
# ===========
#
# 'phy-modes' & 'phy-connection-type' properties 'rgmii', 'rgmii-id',
# 'rgmii-rxid', and 'rgmii-txid' are frequently used wrongly by
# developers. This informative section clarifies their usage.
#
# The RGMII specification requires a 2ns delay between the data and
# clock signals on the RGMII bus. How this delay is implemented is not
# specified.
#
# One option is to make the clock traces on the PCB longer than the
# data traces. A sufficient difference in length can provide the 2ns
# delay. If both the RX and TX delays are implemented in this manner,
# 'rgmii' should be used, so indicating the PCB adds the delays.
#
# If the PCB does not add these delays via extra long traces,
# 'rgmii-id' should be used. Here, 'id' refers to 'internal delay',
# where either the MAC or PHY adds the delay.
#
# If only one of the two delays are implemented via extra long clock
# lines, either 'rgmii-rxid' or 'rgmii-txid' should be used,
# indicating the MAC or PHY should implement one of the delays
# internally, while the PCB implements the other delay.
#
# Device Tree describes hardware, and in this case, it describes the
# PCB between the MAC and the PHY, if the PCB implements delays or
# not.
#
# In practice, very few PCBs make use of extra long clock lines. Hence
# any RGMII phy mode other than 'rgmii-id' is probably wrong, and is
# unlikely to be accepted during review without details provided in
# the commit description and comments in the .dts file.
My understanding from the above is that when the RGMII delays are
provided by the switch, the eqos should have phy-mode = "rgmii-id".
> + pinctrl-0 = <&eqos_pins>;
> + pinctrl-names = "default";
> + status = "okay";
> +
> + fixed-link {
> + full-duplex;
> + speed = <1000>;
> + };
> +};
> +
> +&lpspi0 {
> + cs-gpios = <&lsio_gpio1 8 GPIO_ACTIVE_LOW>, <&lsio_gpio1 7 GPIO_ACTIVE_LOW>;
> + pinctrl-0 = <&lpspi0_pins>, <&switch_pins>;
> + pinctrl-names = "default";
> + status = "okay";
> +
> + ethernet-switch@0 {
> + compatible = "nxp,sja1110a";
> + reg = <0>;
> + reset-gpios = <&lsio_gpio4 3 GPIO_ACTIVE_LOW>;
> + spi-max-frequency = <4000000>;
> +
> + ethernet-ports {
> + #address-cells = <1>;
> + #size-cells = <0>;
> +
> + /* 100Base-TX on connector J26 */
> + port@1 {
Some people might prefer seeing ethernet-port@N instead of port@N, to
fully avoid confusion with dtschema/schemas/graph.yaml (although, the
container node being ethernet-ports now, the confusion should be
avoidable).
Also, if you ever perform device tree fixups from the bootloader (like
for setting status = "disabled" to status = "okay" for the ports that go
to addon boards), please never hardcode fixup paths, but always use eth
aliases to get to them. This is in case the port node names ever change,
to avoid bootloader regressions.
> + reg = <0x1>;
> + phy-handle = <&switch_port1_base_tx_phy>;
> + phy-mode = "internal";
> + };
^ permalink raw reply
* Re: [PATCH v6 08/25] KVM: arm64: iommu: Shadow host stage-2 page table
From: Mostafa Saleh @ 2026-05-11 11:24 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: linux-arm-kernel, linux-kernel, kvmarm, iommu, catalin.marinas,
will, maz, oliver.upton, joey.gouly, suzuki.poulose, yuzenghui,
joro, jean-philippe, mark.rutland, qperret, tabba, vdonnefort,
sebastianene, keirf
In-Reply-To: <20260509232714.GI9285@ziepe.ca>
On Sat, May 09, 2026 at 08:27:14PM -0300, Jason Gunthorpe wrote:
> On Mon, May 04, 2026 at 12:28:55PM +0000, Mostafa Saleh wrote:
> > So far this is the list of requirements/changes needed share the
> > stage-2 page table (besides the obvious: same page table format,
> > granularity, endianness...)
> >
> > 1) HW BBM is not supported in the hypervisor page table, that’s
> > because it can generate TLB conflict aborts, which the hypervisor
> > can not handle because of the limited syndrome information.
> > We can rely on FEAT_BBML3 which was newly introduced to work
> > around that, it’s quite niche and not supported in KVM yet or
> > have an allow list similar to the kernel
> > (as in cpu_supports_bbml2_noabort()) which also limits the number
> > of CPUs that can run this.
>
> Do you think pkvm will need BBM? Hitless replace of a PTE is already a
> pretty advanced feature and the SMMU has its own support matrix there
> too. Is it for shared/private conversion?
Yes, we can break block on memory donation which is transfer of
ownership to the hypervisor or a guest.
>
> > 2) Handling page faults, devices must be able to stall and let the
> > hypervisor handle the page fault (which has to proxy through the
> > kernel as the hypervisor doesn’t handle interrupts), this includes
> > also IO page faults which are hard to get right from the HW which
> > and may lead to system stability issues or lockups.
>
> No.. once you turn on IO like this you don't have page faults
> anymore. Everything must be permantently mapped into the SMMU view, it
> can never be made non-present and you must run without page
> faults. That's what you have in the io-pgtable constructed table,
> right?
Exactly, but the CPU page table doesn’t guarantee that, so we either
have to handle page faults in the IOMMU, or completely change how KVM
deals with stage-2 if we want to share the page table with the CPU.
>
> > Alternatively, we can pin the stage-2 pages, that would require some
> > hypercalls, hacks to the driver/IOMMU API and possibly new semantics
> > in the DMA-API for IDENTITY devices as they will still need to pin
> > the pages as they are actually in stage-2 translation and not bypass.
>
> ?? Then how does this series work?
This series works fine as it shadows the page table and doesn't share it
with the CPU, so it fully populates the address space.
>
> > 3) SMMUv3 must be coherent.
>
> Yes for sure.
>
> > 4) Support BTM/DVM for TLB invalidation, otherwise some hooks are
> > still required (although not io-pgtable-arm)
>
> SW needs to forward invalidations, BTM is rare..
>
> > IMO, 1, 2 are the most tricky parts. It's more work and runs on very
> > limited systems, However, it can be implemented as an optimization)
> > which is my plan.
>
> I think unless you can do it without these HW features (excluding 3)
> don't bother.
I am looking into this now, but as I mentioned that will be a separate
RFC following this one as an optimization for advanced HW.
Thanks,
Mostafa
>
> > I am not sure how CCA deals with that, I’d expect they have a lot of
> > constraints on CPUs/SMMUs and DMA capable devices on those systems.
>
> 3 is not supported. The entire S2 is permanently mapped and doesn't
> really change alot at runtime. No page faults, not sure if the RMM
> private/shard conversion would require BMM..
>
> Jason
^ permalink raw reply
* Re: [PATCH v3 2/4] arm_mpam: Check whether the config array is allocated before destroying it
From: Ben Horgan @ 2026-05-11 11:23 UTC (permalink / raw)
To: James Morse, linux-kernel, linux-arm-kernel, zengheng4
Cc: wangkefeng.wang, xry111, catalin.marinas, yang, reinette.chatre,
will, thuth, mrigendra.chaubey, fenghuay, ahmed.genidi
In-Reply-To: <20260508162341.3762549-3-james.morse@arm.com>
Hi James,
On 5/8/26 17:23, James Morse wrote:
> __destroy_component_cfg() is called to free the configuration array.
> It uses the embedded 'garbage' structure, which means the array has
> to be allocated.
>
> If __destroy_component_cfg() is called from mpam_disable() before the
> configuration was ever allocated, then a NULL pointer is dereferenced.
>
> Check for this case and return early if the configuration is not
> allocated.
>
> __destroy_component_cfg() also frees the mbwu_state as this is allocated
> by __allocate_component_cfg(). As the mbwu_state is allocated after
> comp->cfg is set, and is also under mpam_list_lock, only the first
> pointer needs checking.
>
> Fixes: 3bd04fe7d807bb ("arm_mpam: Extend reset logic to allow devices to be reset any time")
> Signed-off-by: James Morse <james.morse@arm.com>
I checked on the FVP and with this and the previous patch and an early mpam_disable
due to an unsupported MSC version doesn't cause any problems. Additionally, the kunit
tests still run and pass.
Reviewed-by: Ben Horgan <ben.horgan@arm.com>
Thanks,
Ben
^ permalink raw reply
* Re: [PATCH v3 1/4] arm_mpam: Fix false positive assert failure during mpam_disable()
From: Ben Horgan @ 2026-05-11 11:19 UTC (permalink / raw)
To: James Morse, linux-kernel, linux-arm-kernel, zengheng4
Cc: wangkefeng.wang, xry111, catalin.marinas, yang, reinette.chatre,
will, thuth, mrigendra.chaubey, fenghuay, ahmed.genidi
In-Reply-To: <20260508162341.3762549-2-james.morse@arm.com>
Hi James,
On 5/8/26 17:23, James Morse wrote:
> mpam_assert_partid_sizes_fixed() is used to document that the caller
> doesn't expect the discovered PARTID size to change while it is walking
> a list sized by PARTID. Typically the MSC state is not written to until
> all the MSC have been discovered and this value is set.
>
> However, if discovering the MSC fails and schedules mpam_disable(),
> then the MSC state is written to reset it. In this case the
> discovered PARTID size may be become smaller - but only PARTID 0
> will be used once resctrl_exit() has been called.
>
> Skip the WARN_ON_ONCE() if mpam_disable_reason has been set.
>
> Fixes: 3bd04fe7d807bb ("arm_mpam: Extend reset logic to allow devices to be reset at any time")
> Signed-off-by: James Morse <james.morse@arm.com>
> ---
> drivers/resctrl/mpam_devices.c | 10 ++++++++--
> 1 file changed, 8 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
> index 41b14344b16f..bef5e9e9e844 100644
> --- a/drivers/resctrl/mpam_devices.c
> +++ b/drivers/resctrl/mpam_devices.c
> @@ -164,11 +164,17 @@ static void mpam_free_garbage(void)
> /*
> * Once mpam is enabled, new requestors cannot further reduce the available
> * partid. Assert that the size is fixed, and new requestors will be turned
> - * away.
> + * away. This is needed when walking over structures sized by PARTID.
> + *
> + * During mpam_disable() these structures are not fixed, but the MSC state
> + * is still reset using whatever sizes have been discovered so far. As only
> + * PARTID 0 will be used after mpam_disable(), any race would be benign.
> + * Skip the check if a mpam_disable_reason has been set.
> */
> static void mpam_assert_partid_sizes_fixed(void)
> {
> - WARN_ON_ONCE(!partid_max_published);
> + if (!mpam_disable_reason)
> + WARN_ON_ONCE(!partid_max_published);
A little bit nasty to use mpam_disable_reason this way but a simple better way eludes me.
Reviewed-by: Ben Horgan <ben.horgan@arm.com>
Thanks,
Ben
> }
>
> static u32 __mpam_read_reg(struct mpam_msc *msc, u16 reg)
^ permalink raw reply
* Re: [PATCH v6 06/25] iommu/io-pgtable-arm: Rework to use the iommu-pages API
From: Mostafa Saleh @ 2026-05-11 11:16 UTC (permalink / raw)
To: Jason Gunthorpe
Cc: linux-arm-kernel, linux-kernel, kvmarm, iommu, catalin.marinas,
will, maz, oliver.upton, joey.gouly, suzuki.poulose, yuzenghui,
joro, jean-philippe, mark.rutland, qperret, tabba, vdonnefort,
sebastianene, keirf
In-Reply-To: <20260509232155.GH9285@ziepe.ca>
On Sat, May 09, 2026 at 08:21:55PM -0300, Jason Gunthorpe wrote:
> On Mon, May 04, 2026 at 12:19:37PM +0000, Mostafa Saleh wrote:
> > > So.. I suggest you update it to use the iommu_pages API, #ifdef out
> > > the allocator so the pkvm pkvm doesn't need to deal with it. Then
> > > compile a special iommu-pages for the pkvm side presenting the same
> > > API.
> >
> > I see, we still need to leave the DMA-API calls for the custom config,
> > as I am not sure if it can use pages not backed by the vmemmap, I
> > pushed that into a separate function so it’s easily compiled out.
>
> Yeah..
>
> > Without this patch, now it looks like:
>
> Seems reasonable, and then i'd probably just put something like
> #define dma_map(...)
> #define dma_umap(...)
>
> To effectively take it out of the pkvm build
>
> Then have a pkvm compile of iommu-pages to provide the functions in a
> pkvm compatible way. I guess you can't actually fully do this, but
> can do enough to support this file at least.
>
> > > You should have a pkvm shim header that provides
> > > kmalloc/kfree/virt_to_phys in the normal way and just #include that in
> > > io-pgtable when doing a pkvm build instead of hacking up all the code.
> >
> > Ok, I can do that in another change, but I believe it's better to
> > change the usage in this file to arm_lpae_*(virt_to_phys...) so it's
> > clear which parts are intended for that.
>
> IDK, why? virt_to_phys() is part of the iommu-pages API, I'd just
> leave it.. If you want to narrow it then #define it for pkvm when
> compiling this file..
It is not going to be part of the iommu-pages API, I meant in
io-pgtable-arm, we will use something arm_lpae_virt_to_phys()...
which is then implemented differently for pkvm.
Thanks,
Mostafa
>
> Jason
^ permalink raw reply
* [PATCH v12 22/28] coresight: sysfs: Use source's path pointer for path control
From: Leo Yan @ 2026-05-11 11:11 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
Since the path pointer is stored in the source's structure, retrieve it
directly when disabling the path.
As a result, the global variables used for caching path pointers are no
longer needed. Remove them to simplify the code.
Tested-by: James Clark <james.clark@linaro.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-sysfs.c | 82 +++------------------------
1 file changed, 9 insertions(+), 73 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c
index 7ef3f13ebac55b920458b6cd202bdb533796ba2d..0aebafcb8d0e8e699652244af5202e7c4dc4e9b1 100644
--- a/drivers/hwtracing/coresight/coresight-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-sysfs.c
@@ -5,26 +5,12 @@
*/
#include <linux/device.h>
-#include <linux/idr.h>
#include <linux/kernel.h>
#include <linux/property.h>
#include "coresight-priv.h"
#include "coresight-trace-id.h"
-/*
- * Use IDR to map the hash of the source's device name
- * to the pointer of path for the source. The idr is for
- * the sources which aren't associated with CPU.
- */
-static DEFINE_IDR(path_idr);
-
-/*
- * When operating Coresight drivers from the sysFS interface, only a single
- * path can exist from a tracer (associated to a CPU) to a sink.
- */
-static DEFINE_PER_CPU(struct coresight_path *, tracer_path);
-
ssize_t coresight_simple_show_pair(struct device *_dev,
struct device_attribute *attr, char *buf)
{
@@ -167,11 +153,10 @@ static int coresight_validate_source_sysfs(struct coresight_device *csdev,
int coresight_enable_sysfs(struct coresight_device *csdev)
{
- int cpu, ret = 0;
+ int ret = 0;
struct coresight_device *sink;
struct coresight_path *path;
enum coresight_dev_subtype_source subtype;
- u32 hash;
subtype = csdev->subtype.source_subtype;
@@ -223,37 +208,6 @@ int coresight_enable_sysfs(struct coresight_device *csdev)
if (ret)
goto err_source;
- switch (subtype) {
- case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
- /*
- * When working from sysFS it is important to keep track
- * of the paths that were created so that they can be
- * undone in 'coresight_disable()'. Since there can only
- * be a single session per tracer (when working from sysFS)
- * a per-cpu variable will do just fine.
- */
- cpu = csdev->cpu;
- per_cpu(tracer_path, cpu) = path;
- break;
- case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
- case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:
- case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:
- /*
- * Use the hash of source's device name as ID
- * and map the ID to the pointer of the path.
- */
- hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));
- ret = idr_alloc_u32(&path_idr, path, &hash, hash, GFP_KERNEL);
- if (ret) {
- coresight_disable_source_sysfs(csdev, NULL);
- goto err_source;
- }
- break;
- default:
- /* We can't be here */
- break;
- }
-
out:
mutex_unlock(&coresight_mutex);
return ret;
@@ -269,9 +223,8 @@ EXPORT_SYMBOL_GPL(coresight_enable_sysfs);
void coresight_disable_sysfs(struct coresight_device *csdev)
{
- int cpu, ret;
- struct coresight_path *path = NULL;
- u32 hash;
+ struct coresight_path *path;
+ int ret;
mutex_lock(&coresight_mutex);
@@ -279,32 +232,15 @@ void coresight_disable_sysfs(struct coresight_device *csdev)
if (ret)
goto out;
+ /*
+ * coresight_disable_source_sysfs() clears the 'csdev->path' pointer
+ * when disabling the source. Retrieve the path pointer here.
+ */
+ path = csdev->path;
+
if (!coresight_disable_source_sysfs(csdev, NULL))
goto out;
- switch (csdev->subtype.source_subtype) {
- case CORESIGHT_DEV_SUBTYPE_SOURCE_PROC:
- cpu = csdev->cpu;
- path = per_cpu(tracer_path, cpu);
- per_cpu(tracer_path, cpu) = NULL;
- break;
- case CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE:
- case CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM:
- case CORESIGHT_DEV_SUBTYPE_SOURCE_OTHERS:
- hash = hashlen_hash(hashlen_string(NULL, dev_name(&csdev->dev)));
- /* Find the path by the hash. */
- path = idr_find(&path_idr, hash);
- if (path == NULL) {
- pr_err("Path is not found for %s\n", dev_name(&csdev->dev));
- goto out;
- }
- idr_remove(&path_idr, hash);
- break;
- default:
- /* We can't be here */
- break;
- }
-
coresight_disable_path(path);
coresight_release_path(path);
--
2.34.1
^ permalink raw reply related
* [PATCH v12 28/28] coresight: sysfs: Validate CPU online status for per-CPU sources
From: Leo Yan @ 2026-05-11 11:11 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
The current SysFS flow first enables the links and sink, then rolls back
to disable them if the source fails to enable. This failure can occur if
the associated CPU is offline, which causes the SMP call to fail.
Validate whether the associated CPU is online for a per-CPU tracer.
If the CPU is offline, return -ENODEV and bail out.
Tested-by: James Clark <james.clark@linaro.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-sysfs.c | 3 +++
1 file changed, 3 insertions(+)
diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c
index 6f11738b67303eb826c0577eedc418694a0bb1ca..a7735bee9e47a709642e6fa97a33c8ef4cf0b926 100644
--- a/drivers/hwtracing/coresight/coresight-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-sysfs.c
@@ -168,6 +168,9 @@ static int coresight_validate_source_sysfs(struct coresight_device *csdev,
return -EINVAL;
}
+ if (coresight_is_percpu_source(csdev) && !cpu_online(csdev->cpu))
+ return -ENODEV;
+
return 0;
}
--
2.34.1
^ permalink raw reply related
* [PATCH v12 20/28] coresight: etm4x: Set active path on target CPU
From: Leo Yan @ 2026-05-11 11:11 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
Set the path pointer on the target CPU during ETM enable and disable.
This ensures the device mode and path pointer are updated together and
observed atomically by the CPU PM notifier.
Tested-by: James Clark <james.clark@linaro.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-etm4x-core.c | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index fef1270439e9d770fc0459e9ab8cab7cb40827ee..343ba9ce946a7ea3776c06d43364cdce823e2c80 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -238,6 +238,7 @@ void etm4_release_trace_id(struct etmv4_drvdata *drvdata)
struct etm4_enable_arg {
struct etmv4_drvdata *drvdata;
+ struct coresight_path *path;
int rc;
};
@@ -625,8 +626,12 @@ static void etm4_enable_sysfs_smp_call(void *info)
arg->rc = etm4_enable_hw(arg->drvdata);
/* The tracer didn't start */
- if (arg->rc)
+ if (arg->rc) {
coresight_set_mode(csdev, CS_MODE_DISABLED);
+ return;
+ }
+
+ csdev->path = arg->path;
}
/*
@@ -894,9 +899,13 @@ static int etm4_enable_perf(struct coresight_device *csdev,
out:
/* Failed to start tracer; roll back to DISABLED mode */
- if (ret)
+ if (ret) {
coresight_set_mode(csdev, CS_MODE_DISABLED);
- return ret;
+ return ret;
+ }
+
+ csdev->path = path;
+ return 0;
}
static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_path *path)
@@ -926,6 +935,7 @@ static int etm4_enable_sysfs(struct coresight_device *csdev, struct coresight_pa
* ensures that register writes occur when cpu is powered.
*/
arg.drvdata = drvdata;
+ arg.path = path;
ret = smp_call_function_single(drvdata->cpu,
etm4_enable_sysfs_smp_call, &arg, 1);
if (!ret)
@@ -1067,6 +1077,7 @@ static void etm4_disable_sysfs_smp_call(void *info)
etm4_disable_hw(drvdata);
+ drvdata->csdev->path = NULL;
coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
}
@@ -1096,6 +1107,7 @@ static int etm4_disable_perf(struct coresight_device *csdev,
/* TRCVICTLR::SSSTATUS, bit[9] */
filters->ssstatus = (control & BIT(9));
+ drvdata->csdev->path = NULL;
coresight_set_mode(drvdata->csdev, CS_MODE_DISABLED);
/*
--
2.34.1
^ permalink raw reply related
* [PATCH v12 27/28] coresight: Move CPU hotplug callbacks to core layer
From: Leo Yan @ 2026-05-11 11:11 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
This commit moves CPU hotplug callbacks from ETMv4 driver to core layer.
The motivation is the core layer can control all components on an
activated path rather but not only managing tracer in ETMv4 driver.
The perf event layer will disable CoreSight PMU event 'cs_etm' when
hotplug off a CPU. That means a perf mode will be always converted to
disabled mode in CPU hotplug. Arm CoreSight CPU hotplug callbacks only
need to handle the Sysfs mode and ignore the perf mode.
Add a 'mode' argument to coresight_pm_get_active_path() so it only
returns active paths for the relevant mode. Define the enum with bit
flags so it is safe for bitwise operations.
Change CPUHP_AP_ARM_CORESIGHT_STARTING to CPUHP_AP_ARM_CORESIGHT_ONLINE
so that the CPU hotplug callback runs in the online state and thread
context, allowing coresight_disable_sysfs() to be called directly to
disable the path.
Tested-by: James Clark <james.clark@linaro.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-core.c | 48 ++++++++++++++++++----
drivers/hwtracing/coresight/coresight-etm3x-core.c | 40 ------------------
drivers/hwtracing/coresight/coresight-etm4x-core.c | 37 -----------------
include/linux/coresight.h | 6 +--
include/linux/cpuhotplug.h | 2 +-
5 files changed, 43 insertions(+), 90 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 424727ed12b2fd26be1267ce3e88bab9e3858772..3ee78d977549fcc6ab2e8e2caca01f7a2fb07955 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -1841,7 +1841,7 @@ static void coresight_release_device_list(void)
}
}
-static struct coresight_path *coresight_cpu_get_active_path(void)
+static struct coresight_path *coresight_cpu_get_active_path(enum cs_mode mode)
{
struct coresight_device *source;
bool is_active = false;
@@ -1850,17 +1850,17 @@ static struct coresight_path *coresight_cpu_get_active_path(void)
if (!source)
return NULL;
- if (coresight_get_mode(source) != CS_MODE_DISABLED)
+ if (coresight_get_mode(source) & mode)
is_active = true;
coresight_put_percpu_source_ref(source);
/*
- * It is expected to run in atomic context, so it cannot be preempted
- * to disable the path. Here returns the active path pointer without
- * concern that its state may change. Since the build path has taken
- * a reference on the component, the path can be safely used by the
- * caller.
+ * It is expected to run in atomic context or with the CPU lock held for
+ * sysfs mode, so it cannot be preempted to disable the path. Here
+ * returns the active path pointer without concern that its state may
+ * change. Since the build path has taken a reference on the component,
+ * the path can be safely used by the caller.
*/
return is_active ? source->path : NULL;
}
@@ -1979,7 +1979,8 @@ static void coresight_pm_restore(struct coresight_path *path)
static int coresight_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd,
void *v)
{
- struct coresight_path *path = coresight_cpu_get_active_path();
+ struct coresight_path *path =
+ coresight_cpu_get_active_path(CS_MODE_SYSFS | CS_MODE_PERF);
int ret;
ret = coresight_pm_is_needed(path);
@@ -2006,13 +2007,42 @@ static struct notifier_block coresight_cpu_pm_nb = {
.notifier_call = coresight_cpu_pm_notify,
};
+static int coresight_dying_cpu(unsigned int cpu)
+{
+ struct coresight_path *path;
+
+ /*
+ * The perf event layer will disable PMU events in the CPU
+ * hotplug. Here only handles SYSFS case.
+ */
+ path = coresight_cpu_get_active_path(CS_MODE_SYSFS);
+ if (!path)
+ return 0;
+
+ coresight_disable_sysfs(coresight_get_source(path));
+ return 0;
+}
+
static int __init coresight_pm_setup(void)
{
- return cpu_pm_register_notifier(&coresight_cpu_pm_nb);
+ int ret;
+
+ ret = cpu_pm_register_notifier(&coresight_cpu_pm_nb);
+ if (ret)
+ return ret;
+
+ ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_ONLINE,
+ "arm/coresight-core:dying",
+ NULL, coresight_dying_cpu);
+ if (ret)
+ cpu_pm_unregister_notifier(&coresight_cpu_pm_nb);
+
+ return ret;
}
static void coresight_pm_cleanup(void)
{
+ cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_ONLINE);
cpu_pm_unregister_notifier(&coresight_cpu_pm_nb);
}
diff --git a/drivers/hwtracing/coresight/coresight-etm3x-core.c b/drivers/hwtracing/coresight/coresight-etm3x-core.c
index c6fe8b25b855a4119110fee4162f55c0154c3d05..862ad0786699c41433eae8683406b3c1340a6cb6 100644
--- a/drivers/hwtracing/coresight/coresight-etm3x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm3x-core.c
@@ -699,35 +699,6 @@ static int etm_online_cpu(unsigned int cpu)
return 0;
}
-static int etm_starting_cpu(unsigned int cpu)
-{
- if (!etmdrvdata[cpu])
- return 0;
-
- spin_lock(&etmdrvdata[cpu]->spinlock);
- if (!etmdrvdata[cpu]->os_unlock) {
- etm_os_unlock(etmdrvdata[cpu]);
- etmdrvdata[cpu]->os_unlock = true;
- }
-
- if (coresight_get_mode(etmdrvdata[cpu]->csdev))
- etm_enable_hw(etmdrvdata[cpu]);
- spin_unlock(&etmdrvdata[cpu]->spinlock);
- return 0;
-}
-
-static int etm_dying_cpu(unsigned int cpu)
-{
- if (!etmdrvdata[cpu])
- return 0;
-
- spin_lock(&etmdrvdata[cpu]->spinlock);
- if (coresight_get_mode(etmdrvdata[cpu]->csdev))
- etm_disable_hw(etmdrvdata[cpu]);
- spin_unlock(&etmdrvdata[cpu]->spinlock);
- return 0;
-}
-
static bool etm_arch_supported(u8 arch)
{
switch (arch) {
@@ -795,13 +766,6 @@ static int __init etm_hp_setup(void)
{
int ret;
- ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING,
- "arm/coresight:starting",
- etm_starting_cpu, etm_dying_cpu);
-
- if (ret)
- return ret;
-
ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
"arm/coresight:online",
etm_online_cpu, NULL);
@@ -812,15 +776,11 @@ static int __init etm_hp_setup(void)
return 0;
}
- /* failed dyn state - remove others */
- cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
-
return ret;
}
static void etm_hp_clear(void)
{
- cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
if (hp_online) {
cpuhp_remove_state_nocalls(hp_online);
hp_online = 0;
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index 343ba9ce946a7ea3776c06d43364cdce823e2c80..14bb31bd6a0b979051dd17963218c00165a0ebb8 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -1833,33 +1833,6 @@ static int etm4_online_cpu(unsigned int cpu)
return 0;
}
-static int etm4_starting_cpu(unsigned int cpu)
-{
- if (!etmdrvdata[cpu])
- return 0;
-
- raw_spin_lock(&etmdrvdata[cpu]->spinlock);
- if (!etmdrvdata[cpu]->os_unlock)
- etm4_os_unlock(etmdrvdata[cpu]);
-
- if (coresight_get_mode(etmdrvdata[cpu]->csdev))
- etm4_enable_hw(etmdrvdata[cpu]);
- raw_spin_unlock(&etmdrvdata[cpu]->spinlock);
- return 0;
-}
-
-static int etm4_dying_cpu(unsigned int cpu)
-{
- if (!etmdrvdata[cpu])
- return 0;
-
- raw_spin_lock(&etmdrvdata[cpu]->spinlock);
- if (coresight_get_mode(etmdrvdata[cpu]->csdev))
- etm4_disable_hw(etmdrvdata[cpu]);
- raw_spin_unlock(&etmdrvdata[cpu]->spinlock);
- return 0;
-}
-
static inline bool etm4_pm_save_needed(struct etmv4_drvdata *drvdata)
{
return !!drvdata->save_state;
@@ -2120,13 +2093,6 @@ static int __init etm4_pm_setup(void)
{
int ret;
- ret = cpuhp_setup_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING,
- "arm/coresight4:starting",
- etm4_starting_cpu, etm4_dying_cpu);
-
- if (ret)
- return ret;
-
ret = cpuhp_setup_state_nocalls(CPUHP_AP_ONLINE_DYN,
"arm/coresight4:online",
etm4_online_cpu, NULL);
@@ -2137,14 +2103,11 @@ static int __init etm4_pm_setup(void)
return 0;
}
- /* failed dyn state - remove others */
- cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
return ret;
}
static void etm4_pm_clear(void)
{
- cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
if (hp_online) {
cpuhp_remove_state_nocalls(hp_online);
hp_online = 0;
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 76ef4c0965125cd11830df0151a6707d3e3b638d..add0579cad884c62b8c8e5ff82264966ff0613b7 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -344,9 +344,9 @@ struct coresight_path {
};
enum cs_mode {
- CS_MODE_DISABLED,
- CS_MODE_SYSFS,
- CS_MODE_PERF,
+ CS_MODE_DISABLED = 0,
+ CS_MODE_SYSFS = BIT(0),
+ CS_MODE_PERF = BIT(1),
};
#define coresight_ops(csdev) csdev->ops
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index 22ba327ec2278c132572950848ade2b814787eb5..0fb3a2a62eb001bcc813422eba2ce8fbf92c260a 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -180,7 +180,6 @@ enum cpuhp_state {
CPUHP_AP_DUMMY_TIMER_STARTING,
CPUHP_AP_ARM_XEN_STARTING,
CPUHP_AP_ARM_XEN_RUNSTATE_STARTING,
- CPUHP_AP_ARM_CORESIGHT_STARTING,
CPUHP_AP_ARM_CORESIGHT_CTI_STARTING,
CPUHP_AP_ARM64_ISNDEP_STARTING,
CPUHP_AP_SMPCFD_DYING,
@@ -200,6 +199,7 @@ enum cpuhp_state {
CPUHP_AP_IRQ_AFFINITY_ONLINE,
CPUHP_AP_BLK_MQ_ONLINE,
CPUHP_AP_ARM_MVEBU_SYNC_CLOCKS,
+ CPUHP_AP_ARM_CORESIGHT_ONLINE,
CPUHP_AP_X86_INTEL_EPB_ONLINE,
CPUHP_AP_PERF_ONLINE,
CPUHP_AP_PERF_X86_ONLINE,
--
2.34.1
^ permalink raw reply related
* [PATCH v12 26/28] coresight: sysfs: Increment refcount only for software source
From: Leo Yan @ 2026-05-11 11:11 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
Except for software sources (e.g. STM), other sources treat multiple
enables as equivalent to a single enable. The device mode already
tracks the binary state, so it is redundant to operate refcount.
Introduce a helper coresight_is_software_source() for check software
source. Refactor to maintain the refcount only for software sources.
This simplifies future CPU PM handling without refcount logic.
Tested-by: James Clark <james.clark@linaro.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-sysfs.c | 39 +++++++++++++++++----------
include/linux/coresight.h | 6 +++++
2 files changed, 31 insertions(+), 14 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c
index 0aebafcb8d0e8e699652244af5202e7c4dc4e9b1..6f11738b67303eb826c0577eedc418694a0bb1ca 100644
--- a/drivers/hwtracing/coresight/coresight-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-sysfs.c
@@ -39,6 +39,26 @@ ssize_t coresight_simple_show32(struct device *_dev,
}
EXPORT_SYMBOL_GPL(coresight_simple_show32);
+static void coresight_source_get_refcnt(struct coresight_device *csdev)
+{
+ /*
+ * There could be multiple applications driving the software
+ * source. So keep the refcount for each such user when the
+ * source is already enabled.
+ *
+ * No need to increment the reference counter for other source
+ * types, as multiple enables are the same as a single enable.
+ */
+ if (coresight_is_software_source(csdev))
+ csdev->refcnt++;
+}
+
+static void coresight_source_put_refcnt(struct coresight_device *csdev)
+{
+ if (coresight_is_software_source(csdev))
+ csdev->refcnt--;
+}
+
static int coresight_enable_source_sysfs(struct coresight_device *csdev,
enum cs_mode mode,
struct coresight_path *path)
@@ -57,14 +77,14 @@ static int coresight_enable_source_sysfs(struct coresight_device *csdev,
return ret;
}
- csdev->refcnt++;
+ coresight_source_get_refcnt(csdev);
return 0;
}
/**
- * coresight_disable_source_sysfs - Drop the reference count by 1 and disable
- * the device if there are no users left.
+ * coresight_disable_source_sysfs - Drop the reference count by 1 for software
+ * sources. Disable the device if there are no users left.
*
* @csdev: The coresight device to disable
* @data: Opaque data to pass on to the disable function of the source device.
@@ -79,7 +99,7 @@ static bool coresight_disable_source_sysfs(struct coresight_device *csdev,
if (coresight_get_mode(csdev) != CS_MODE_SYSFS)
return false;
- csdev->refcnt--;
+ coresight_source_put_refcnt(csdev);
if (csdev->refcnt == 0) {
coresight_disable_source(csdev, data);
return true;
@@ -156,9 +176,6 @@ int coresight_enable_sysfs(struct coresight_device *csdev)
int ret = 0;
struct coresight_device *sink;
struct coresight_path *path;
- enum coresight_dev_subtype_source subtype;
-
- subtype = csdev->subtype.source_subtype;
mutex_lock(&coresight_mutex);
@@ -173,13 +190,7 @@ int coresight_enable_sysfs(struct coresight_device *csdev)
* doesn't hold coresight_mutex.
*/
if (coresight_get_mode(csdev) == CS_MODE_SYSFS) {
- /*
- * There could be multiple applications driving the software
- * source. So keep the refcount for each such user when the
- * source is already enabled.
- */
- if (subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE)
- csdev->refcnt++;
+ coresight_source_get_refcnt(csdev);
goto out;
}
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 58d474b269806d32cad6ed87da96550b06f1f30f..76ef4c0965125cd11830df0151a6707d3e3b638d 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -611,6 +611,12 @@ static inline bool coresight_is_percpu_source(struct coresight_device *csdev)
(csdev->subtype.source_subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_PROC);
}
+static inline bool coresight_is_software_source(struct coresight_device *csdev)
+{
+ return csdev && coresight_is_device_source(csdev) &&
+ (csdev->subtype.source_subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE);
+}
+
static inline bool coresight_is_percpu_sink(struct coresight_device *csdev)
{
return csdev && (csdev->type == CORESIGHT_DEV_TYPE_SINK) &&
--
2.34.1
^ permalink raw reply related
* [PATCH v12 24/28] coresight: Add PM callbacks for sink device
From: Leo Yan @ 2026-05-11 11:11 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
Unlike system level sinks, per-CPU sinks may lose power during CPU idle
states. Currently, this applies specifically to TRBE. This commit
invokes save and restore callbacks for the sink in the CPU PM notifier.
If the sink provides PM callbacks but the source does not, this is
unsafe because the sink cannot be disabled safely unless the source
can also be controlled, so veto low power entry to avoid lockups.
Tested-by: James Clark <james.clark@linaro.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-core.c | 35 ++++++++++++++++++++++++++--
1 file changed, 33 insertions(+), 2 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index 674fc375ff44e732405563af7be9dc8fae118e41..424727ed12b2fd26be1267ce3e88bab9e3858772 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -1868,7 +1868,7 @@ static struct coresight_path *coresight_cpu_get_active_path(void)
/* Return: 1 if PM is required, 0 if skip, or a negative error */
static int coresight_pm_is_needed(struct coresight_path *path)
{
- struct coresight_device *source;
+ struct coresight_device *source, *sink;
if (this_cpu_read(percpu_pm_failed))
return -EIO;
@@ -1877,7 +1877,8 @@ static int coresight_pm_is_needed(struct coresight_path *path)
return 0;
source = coresight_get_source(path);
- if (!source)
+ sink = coresight_get_sink(path);
+ if (!source || !sink)
return 0;
/* pm_save_disable() and pm_restore_enable() must be paired */
@@ -1885,16 +1886,35 @@ static int coresight_pm_is_needed(struct coresight_path *path)
coresight_ops(source)->pm_restore_enable)
return 1;
+ /*
+ * It is not permitted that the source has no callbacks while the sink
+ * does, as the sink cannot be disabled without disabling the source,
+ * which may lead to lockups. Fix this by enabling self-hosted PM
+ * mode for ETM (see etm4_probe()).
+ */
+ if (coresight_ops(sink)->pm_save_disable &&
+ coresight_ops(sink)->pm_restore_enable) {
+ pr_warn_once("coresight PM failed: source has no PM callbacks; "
+ "cannot safely control sink\n");
+ return -EINVAL;
+ }
+
return 0;
}
static int coresight_pm_device_save(struct coresight_device *csdev)
{
+ if (!csdev || !coresight_ops(csdev)->pm_save_disable)
+ return 0;
+
return coresight_ops(csdev)->pm_save_disable(csdev);
}
static void coresight_pm_device_restore(struct coresight_device *csdev)
{
+ if (!csdev || !coresight_ops(csdev)->pm_restore_enable)
+ return;
+
coresight_ops(csdev)->pm_restore_enable(csdev);
}
@@ -1913,15 +1933,24 @@ static int coresight_pm_save(struct coresight_path *path)
to = list_prev_entry(coresight_path_last_node(path), link);
coresight_disable_path_from_to(path, from, to);
+ /*
+ * Save the sink. Most sinks do not implement a save callback to avoid
+ * latency from memory copying. We assume the sink's save and restore
+ * always succeed.
+ */
+ coresight_pm_device_save(coresight_get_sink(path));
return 0;
}
static void coresight_pm_restore(struct coresight_path *path)
{
struct coresight_device *source = coresight_get_source(path);
+ struct coresight_device *sink = coresight_get_sink(path);
struct coresight_node *from, *to;
int ret;
+ coresight_pm_device_restore(sink);
+
from = coresight_path_first_node(path);
/* Enable up to the node before sink */
to = list_prev_entry(coresight_path_last_node(path), link);
@@ -1934,6 +1963,8 @@ static void coresight_pm_restore(struct coresight_path *path)
return;
path_failed:
+ coresight_pm_device_save(sink);
+
pr_err("Failed in coresight PM restore on CPU%d: %d\n",
smp_processor_id(), ret);
--
2.34.1
^ permalink raw reply related
* [PATCH v12 25/28] coresight: trbe: Save and restore state across CPU low power state
From: Leo Yan @ 2026-05-11 11:11 UTC (permalink / raw)
To: Suzuki K Poulose, Mike Leach, James Clark, Yeoreum Yun,
Mark Rutland, Will Deacon, Yabin Cui, Keita Morisaki, Jie Gan,
Yuanfang Zhang, Greg Kroah-Hartman, Alexander Shishkin,
Tamas Petz, Thomas Gleixner, Peter Zijlstra
Cc: coresight, linux-arm-kernel, Leo Yan
In-Reply-To: <20260511-arm_coresight_path_power_management_improvement-v12-0-1c9dcb1de8c9@arm.com>
From: Yabin Cui <yabinc@google.com>
TRBE context can be lost when a CPU enters low power states. If a trace
source is restored while TRBE is not, tracing may run without an active
sink, which can lead to hangs on some devices (e.g., Pixel 9).
The save and restore flows are described in the section K5.5 "Context
switching" of Arm ARM (ARM DDI 0487 L.a). This commit adds save and
restore callbacks with following the software usages defined in the
architecture manual.
During the restore flow, since TRBLIMITR_EL1.E resets to 0 on a warm
reset, the trace buffer unit is disabled when idle resume, it is safe to
restore base/pointer/status registers first and program TRBLIMITR_EL1
last.
Signed-off-by: Yabin Cui <yabinc@google.com>
Tested-by: James Clark <james.clark@linaro.org>
Reviewed-by: Yeoreum Yun <yeoreum.yun@arm.com>
Reviewed-by: James Clark <james.clark@linaro.org>
Co-developed-by: Leo Yan <leo.yan@arm.com>
Tested-by: Jie Gan <jie.gan@oss.qualcomm.com>
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/hwtracing/coresight/coresight-trbe.c | 59 +++++++++++++++++++++++++++-
1 file changed, 58 insertions(+), 1 deletion(-)
diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c
index 14e35b9660d76e47619cc6026b94929b3bb3e02b..c7cbca45f2debd4047b93283ea9fe5dd9e1f2ebf 100644
--- a/drivers/hwtracing/coresight/coresight-trbe.c
+++ b/drivers/hwtracing/coresight/coresight-trbe.c
@@ -116,6 +116,20 @@ static int trbe_errata_cpucaps[] = {
*/
#define TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES 256
+/*
+ * struct trbe_save_state: Register values representing TRBE state
+ * @trblimitr - Trace Buffer Limit Address Register value
+ * @trbbaser - Trace Buffer Base Register value
+ * @trbptr - Trace Buffer Write Pointer Register value
+ * @trbsr - Trace Buffer Status Register value
+ */
+struct trbe_save_state {
+ u64 trblimitr;
+ u64 trbbaser;
+ u64 trbptr;
+ u64 trbsr;
+};
+
/*
* struct trbe_cpudata: TRBE instance specific data
* @trbe_flag - TRBE dirty/access flag support
@@ -134,6 +148,7 @@ struct trbe_cpudata {
enum cs_mode mode;
struct trbe_buf *buf;
struct trbe_drvdata *drvdata;
+ struct trbe_save_state save_state;
DECLARE_BITMAP(errata, TRBE_ERRATA_MAX);
};
@@ -1189,6 +1204,46 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev)
return IRQ_HANDLED;
}
+static int arm_trbe_save(struct coresight_device *csdev)
+{
+ struct trbe_cpudata *cpudata = dev_get_drvdata(&csdev->dev);
+ struct trbe_save_state *state = &cpudata->save_state;
+
+ state->trblimitr = read_sysreg_s(SYS_TRBLIMITR_EL1);
+
+ /* Disable the unit, ensure the writes to memory are complete */
+ if (state->trblimitr & TRBLIMITR_EL1_E)
+ trbe_drain_and_disable_local(cpudata);
+
+ state->trbbaser = read_sysreg_s(SYS_TRBBASER_EL1);
+ state->trbptr = read_sysreg_s(SYS_TRBPTR_EL1);
+ state->trbsr = read_sysreg_s(SYS_TRBSR_EL1);
+ return 0;
+}
+
+static void arm_trbe_restore(struct coresight_device *csdev)
+{
+ struct trbe_cpudata *cpudata = dev_get_drvdata(&csdev->dev);
+ struct trbe_save_state *state = &cpudata->save_state;
+
+ write_sysreg_s(state->trbbaser, SYS_TRBBASER_EL1);
+ write_sysreg_s(state->trbptr, SYS_TRBPTR_EL1);
+ write_sysreg_s(state->trbsr, SYS_TRBSR_EL1);
+
+ if (!(state->trblimitr & TRBLIMITR_EL1_E)) {
+ write_sysreg_s(state->trblimitr, SYS_TRBLIMITR_EL1);
+ } else {
+ /*
+ * The section K5.5 Context switching, Arm ARM (ARM DDI 0487
+ * L.a), S_PKLXF requires a Context synchronization event to
+ * guarantee the Trace Buffer Unit will observe the new values
+ * of the system registers.
+ */
+ isb();
+ set_trbe_enabled(cpudata, state->trblimitr);
+ }
+}
+
static const struct coresight_ops_sink arm_trbe_sink_ops = {
.enable = arm_trbe_enable,
.disable = arm_trbe_disable,
@@ -1198,7 +1253,9 @@ static const struct coresight_ops_sink arm_trbe_sink_ops = {
};
static const struct coresight_ops arm_trbe_cs_ops = {
- .sink_ops = &arm_trbe_sink_ops,
+ .pm_save_disable = arm_trbe_save,
+ .pm_restore_enable = arm_trbe_restore,
+ .sink_ops = &arm_trbe_sink_ops,
};
static ssize_t align_show(struct device *dev, struct device_attribute *attr, char *buf)
--
2.34.1
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox