* RE: [PATCH RFC 3/3] arm64: Add HOTPLUG_PARALLEL support for secondary CPUs
From: Michael Kelley @ 2026-06-12 15:45 UTC (permalink / raw)
To: Jinjie Ruan, catalin.marinas@arm.com, will@kernel.org,
tsbogend@alpha.franken.de, pjw@kernel.org, palmer@dabbelt.com,
aou@eecs.berkeley.edu, alex@ghiti.fr, tglx@kernel.org,
mingo@redhat.com, bp@alien8.de, dave.hansen@linux.intel.com,
hpa@zytor.com, peterz@infradead.org, kees@kernel.org,
nathan@kernel.org, linusw@kernel.org, ojeda@kernel.org,
david.kaplan@amd.com, lukas.bulwahn@redhat.com,
ryan.roberts@arm.com, maz@kernel.org, timothy.hayes@arm.com,
lpieralisi@kernel.org, thuth@redhat.com, oupton@kernel.org,
yeoreum.yun@arm.com, miko.lenczewski@arm.com, broonie@kernel.org,
kevin.brodsky@arm.com, james.clark@linaro.org, tabba@google.com,
mrigendra.chaubey@gmail.com, arnd@arndb.de,
anshuman.khandual@arm.com, x86@kernel.org,
linux-kernel@vger.kernel.org,
linux-arm-kernel@lists.infradead.org, linux-mips@vger.kernel.org,
linux-riscv@lists.infradead.org
In-Reply-To: <20260611133809.3854977-4-ruanjinjie@huawei.com>
From: Jinjie Ruan <ruanjinjie@huawei.com> Sent: Thursday, June 11, 2026 6:38 AM
>
> Support for parallel secondary CPU bringup is already utilized by x86,
> MIPS, and RISC-V. This patch brings this capability to the arm64
> architecture.
>
> Rework the global `secondary_data` accessed during early boot into
> a per-CPU array. This array maps logical CPU IDs to MPIDR_EL1 values,
> enabling the early boot code in head.S to resolve each secondary CPU's
> logical ID concurrently.
>
> To fully enable HOTPLUG_PARALLEL, this patch implements:
> 1) An arm64-specific arch_cpuhp_kick_ap_alive() handler.
> 2) Callbacks to cpuhp_ap_sync_alive() inside secondary_start_kernel().
>
> Successfully tested on QEMU ARM64 virt machine (KVM on, 128 vCPUs).
>
> | test kernel | secondary CPUs boot time |
> | --------------------- | -------------------- |
> | Without this patch | 155.672 |
> | cpuhp.parallel=0 | 62.897 |
> | cpuhp.parallel=1 | 166.703 |
The last two rows seem mixed up. I would expect parallel=0 to
result in a longer boot time.
Michael
>
> Signed-off-by: Jinjie Ruan <ruanjinjie@huawei.com>
> ---
> arch/arm64/Kconfig | 1 +
> arch/arm64/include/asm/smp.h | 8 ++++++++
> arch/arm64/kernel/head.S | 23 +++++++++++++++++++++++
> arch/arm64/kernel/smp.c | 27 +++++++++++++++++++++++++++
> 4 files changed, 59 insertions(+)
>
^ permalink raw reply
* Re: [PATCH v1 phy-next 7/8] soc: fsl: guts: implement the RCW override procedure
From: Conor Dooley @ 2026-06-12 15:44 UTC (permalink / raw)
To: Vladimir Oltean
Cc: linux-phy, devicetree, linuxppc-dev, linux-arm-kernel,
Ioana Ciornei, Vinod Koul, Neil Armstrong, Tanjeff Moos,
Christophe Leroy (CS GROUP), Michael Walle, Shawn Guo, Frank Li,
linux-kernel, Krzysztof Kozlowski, Rob Herring
In-Reply-To: <20260611193940.44416-8-vladimir.oltean@nxp.com>
[-- Attachment #1: Type: text/plain, Size: 15588 bytes --]
On Thu, Jun 11, 2026 at 10:39:39PM +0300, Vladimir Oltean wrote:
> From: Ioana Ciornei <ioana.ciornei@nxp.com>
>
> Add support for the RCW override procedure which enables runtime
> reconfiguration of the protocol running on a SerDes lane. The procedure
> is done through the DCFG DCSR space which now can be defined as the
> second memory region of the guts DT node.
> Support is added on the following SoCs: LS1046A, LS1088A, LS2088A.
>
> The procedure is exported to the "client" driver - the Lynx10G SerDes
> PHY driver - through the following functions:
> - fsl_guts_lane_init() used to notify the initial / boot time lane mode
> running on a SerDes lane.
> - fsl_guts_lane_validate() used to validate that changing the protocol
> on a specific lane is supported.
> - fsl_guts_lane_set_mode() which can be used to request the RCW
> procedure be executed for a specific lane.
>
> Since the RCW override procedure is different depending on the SoC, the
> private fsl_soc_data structure is updated with two new per SoC callbacks
> (.serdes_get_rcw_override() and .serdes_init_rcwcr()) which get used
> from the generic fsl_guts_lane_set_mode() function. These two callbacks
> hide all the SoC specific register offsets, masks and values so that the
> _set_mode() procedure is straightforward.
>
> Signed-off-by: Ioana Ciornei <ioana.ciornei@nxp.com>
> Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
> ---
> Cc: Conor Dooley <conor@kernel.org>
> Cc: Krzysztof Kozlowski <krzysztof.kozlowski+dt@linaro.org>
> Cc: Rob Herring <robh@kernel.org>
> Cc: devicetree@vger.kernel.org
Wrong CC list for this specific patch?
ta,
Conor.
> ---
> drivers/soc/fsl/guts.c | 286 ++++++++++++++++++++++++++++++++++++++-
> include/linux/fsl/guts.h | 20 ++-
> 2 files changed, 299 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/soc/fsl/guts.c b/drivers/soc/fsl/guts.c
> index 9f2aff07a274..23ec5750080c 100644
> --- a/drivers/soc/fsl/guts.c
> +++ b/drivers/soc/fsl/guts.c
> @@ -15,6 +15,30 @@
> #include <linux/fsl/guts.h>
>
> #define DCFG_CCSR 0
> +#define DCFG_DCSR 1
> +
> +#define MAX_NUM_LANES 8
> +#define MAX_NUM_SERDES 2
> +
> +#define LS1088A_RCWSR29_SRDS_PRTCL_S1_LNn(lane) \
> + GENMASK(19 + 4 * (3 - lane), 16 + 4 * (3 - lane))
> +#define LS1088A_RCWSR30_SRDS_PRTCL_S2_LNn(lane) \
> + GENMASK(3 + 4 * (3 - lane), 4 * (3 - lane))
> +
> +#define LS1046A_RCWSR5_SRDS_PRTCL_S1(lane) \
> + GENMASK(19 + 4 * (lane), 16 + 4 * (lane))
> +#define SRDS_PRTCL_NONE 0
> +#define SRDS_PRTCL_XFI 1
> +#define SRDS_PRTCL_2500BASEX 2
> +#define SRDS_PRTCL_100BASEX_SGMII 3
> +#define SRDS_PRTCL_QSGMII 4
> +#define SRDS_PRTCL_PCIE 5
> +
> +#define LS2088A_RCWSR30_SRDS_CLK_EN_SEL_XGMII_S1 BIT(14)
> +#define LS2088A_RCWSR30_SRDS_CLK_SEL_XGMII_Ln_S1(lane) BIT(6 + (7 - (lane)))
> +#define LS2088A_RCWSR30_SRDS_CLK_SEL_MSK GENMASK(13, 6)
> +#define SRDS_CLK_SEL_XGMII 1
> +#define SRDS_CLK_SEL_GMII 0
>
> struct fsl_soc_die_attr {
> char *die;
> @@ -22,9 +46,19 @@ struct fsl_soc_die_attr {
> u32 mask;
> };
>
> +struct fsl_soc_serdes_rcw_override {
> + int offset;
> + int mask;
> + int val;
> +};
> +
> struct fsl_soc_data {
> const char *sfp_compat;
> u32 uid_offset;
> + int (*serdes_get_rcw_override)(int index, int lane,
> + enum lynx_lane_mode lane_mode,
> + struct fsl_soc_serdes_rcw_override *override);
> + void (*serdes_init_rcwcr)(int index);
> };
>
> enum qoriq_die {
> @@ -138,9 +172,13 @@ static const struct fsl_soc_die_attr fsl_soc_die[] = {
>
> static struct fsl_soc_guts {
> struct ccsr_guts __iomem *dcfg_ccsr;
> + struct ccsr_guts __iomem *dcfg_dcsr;
> const struct fsl_soc_data *data;
> bool little_endian;
> u32 svr;
> + enum lynx_lane_mode lane_mode[MAX_NUM_SERDES][MAX_NUM_LANES];
> + bool rcwcr_init_done;
> + spinlock_t rcwcr_lock; /* serializes concurrent writes to the RCWCR */
> } soc;
>
> static unsigned int fsl_guts_read(const void __iomem *reg)
> @@ -151,6 +189,28 @@ static unsigned int fsl_guts_read(const void __iomem *reg)
> return ioread32be(reg);
> }
>
> +static void fsl_guts_write(void __iomem *reg, u32 val)
> +{
> + if (soc.little_endian)
> + iowrite32(val, reg);
> + else
> + iowrite32be(val, reg);
> +}
> +
> +/* Some fields of the Reset Configuration Word (RCW) can be overridden at
> + * runtime by writing to the RCWCRn registers contained within the DCSR space
> + * of the Device Configuration (DCFG) block. The layout of the RCWCRn registers
> + * is identical with the read-only RCWSRn from the CCSR space.
> + */
> +static void fsl_guts_rmw(int offset, u32 val, u32 mask)
> +{
> + u32 tmp = fsl_guts_read(&soc.dcfg_ccsr->rcwsr[offset]);
> +
> + tmp &= ~mask;
> + tmp |= val;
> + fsl_guts_write(&soc.dcfg_dcsr->rcwcr[offset], tmp);
> +}
> +
> static bool fsl_soc_die_match_one(u32 svr, const struct fsl_soc_die_attr *match)
> {
> return match->svr == (svr & match->mask);
> @@ -167,6 +227,97 @@ static const struct fsl_soc_die_attr *fsl_soc_die_match(
> return NULL;
> }
>
> +static int
> +fsl_guts_serdes_get_rcw_override(int serdes_idx, int lane,
> + enum lynx_lane_mode lane_mode,
> + struct fsl_soc_serdes_rcw_override *override)
> +{
> + if ((!fsl_soc_die_match_one(soc.svr, &fsl_soc_die[DIE_LS1088A]) &&
> + !fsl_soc_die_match_one(soc.svr, &fsl_soc_die[DIE_LS2088A]) &&
> + !fsl_soc_die_match_one(soc.svr, &fsl_soc_die[DIE_LS1046A])) ||
> + !soc.data || !soc.data->serdes_get_rcw_override) {
> + pr_debug("RCW override not implemented for SoC\n");
> + return -EINVAL;
> + }
> +
> + if (!soc.dcfg_dcsr) {
> + pr_debug("Device tree does not define DCFG_DCSR region necessary for RCW override\n");
> + return -EINVAL;
> + }
> +
> + return soc.data->serdes_get_rcw_override(serdes_idx, lane, lane_mode,
> + override);
> +}
> +
> +/**
> + * fsl_guts_lane_init() - Notify guts module of SerDes lane configuration
> + * @serdes_idx: zero-based SerDes block index
> + * @lane: zero-based lane index within SerDes
> + * @lane_mode: initial / boot time SerDes protocol for lane
> + *
> + * On the LS208xA SoC, the RCW override procedure needs to be aware of all link
> + * modes which are configured on a SerDes block.
> + */
> +void fsl_guts_lane_init(int serdes_idx, int lane, enum lynx_lane_mode lane_mode)
> +{
> + soc.lane_mode[serdes_idx - 1][lane] = lane_mode;
> +}
> +EXPORT_SYMBOL_NS_GPL(fsl_guts_lane_init, "FSL_GUTS");
> +
> +/**
> + * fsl_guts_lane_validate() - Validate that SerDes protocol is implemented and
> + * supported on current SoC
> + * @serdes_idx: zero-based SerDes block index
> + * @lane: zero-based lane index within SerDes
> + * @lane_mode: requested SerDes protocol
> + *
> + * Should be called before actually requesting the RCW override procedure to be
> + * applied using %fsl_guts_lane_set_mode()
> + *
> + * Return: 0 if RCW override to protocol is possible, negative error otherwise
> + */
> +int fsl_guts_lane_validate(int serdes_idx, int lane, enum lynx_lane_mode lane_mode)
> +{
> + struct fsl_soc_serdes_rcw_override override;
> +
> + return fsl_guts_serdes_get_rcw_override(serdes_idx, lane, lane_mode,
> + &override);
> +}
> +EXPORT_SYMBOL_NS_GPL(fsl_guts_lane_validate, "FSL_GUTS");
> +
> +/**
> + * fsl_guts_lane_set_mode() - apply RCW override procedure for SerDes lane
> + * @serdes_idx: zero-based SerDes block index
> + * @lane: zero-based lane index within SerDes
> + * @lane_mode: requested SerDes protocol
> + *
> + * Return: 0 on success, negative error otherwise
> + */
> +int fsl_guts_lane_set_mode(int serdes_idx, int lane, enum lynx_lane_mode lane_mode)
> +{
> + struct fsl_soc_serdes_rcw_override override;
> + int err;
> +
> + err = fsl_guts_serdes_get_rcw_override(serdes_idx, lane, lane_mode,
> + &override);
> + if (err)
> + return err;
> +
> + spin_lock(&soc.rcwcr_lock);
> +
> + if (soc.data->serdes_init_rcwcr)
> + soc.data->serdes_init_rcwcr(serdes_idx);
> +
> + fsl_guts_rmw(override.offset, override.val << __bf_shf(override.mask),
> + override.mask);
> + soc.lane_mode[serdes_idx - 1][lane] = lane_mode;
> +
> + spin_unlock(&soc.rcwcr_lock);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_NS_GPL(fsl_guts_lane_set_mode, "FSL_GUTS");
> +
> static u64 fsl_guts_get_soc_uid(const char *compat, unsigned int offset)
> {
> struct device_node *np;
> @@ -193,6 +344,128 @@ static u64 fsl_guts_get_soc_uid(const char *compat, unsigned int offset)
> return uid;
> }
>
> +static int ls1088a_serdes_get_rcw_override(int index, int lane,
> + enum lynx_lane_mode lane_mode,
> + struct fsl_soc_serdes_rcw_override *override)
> +{
> + /* The RCW override procedure has to write to different registers
> + * depending on the SerDes block index.
> + */
> + switch (index) {
> + case 1:
> + override->offset = 28;
> + override->mask = LS1088A_RCWSR29_SRDS_PRTCL_S1_LNn(lane);
> + break;
> + case 2:
> + override->offset = 29;
> + override->mask = LS1088A_RCWSR30_SRDS_PRTCL_S2_LNn(lane);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + if (lynx_lane_mode_uses_xgmii_mac(lane_mode))
> + override->val = SRDS_PRTCL_XFI;
> + else if (lynx_lane_mode_uses_gmii_mac(lane_mode))
> + override->val = SRDS_PRTCL_100BASEX_SGMII;
> + else
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int ls1046a_serdes_get_rcw_override(int index, int lane,
> + enum lynx_lane_mode lane_mode,
> + struct fsl_soc_serdes_rcw_override *override)
> +{
> + /* The RCW override procedure has to write to different registers
> + * depending on the SerDes block index.
> + */
> + switch (index) {
> + case 1:
> + override->offset = 4;
> + override->mask = LS1046A_RCWSR5_SRDS_PRTCL_S1(lane);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + if (lynx_lane_mode_uses_xgmii_mac(lane_mode))
> + override->val = SRDS_PRTCL_XFI;
> + else if (lynx_lane_mode_uses_gmii_mac(lane_mode))
> + override->val = SRDS_PRTCL_100BASEX_SGMII;
> + else
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static int ls2088a_serdes_get_rcw_override(int index, int lane,
> + enum lynx_lane_mode lane_mode,
> + struct fsl_soc_serdes_rcw_override *override)
> +{
> + switch (index) {
> + case 1:
> + override->offset = 29;
> + override->mask = LS2088A_RCWSR30_SRDS_CLK_SEL_XGMII_Ln_S1(lane);
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + if (lynx_lane_mode_uses_xgmii_mac(lane_mode))
> + override->val = SRDS_CLK_SEL_XGMII;
> + else if (lynx_lane_mode_uses_gmii_mac(lane_mode))
> + override->val = SRDS_CLK_SEL_GMII;
> + else
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +static void ls2088a_serdes_init_rcwcr(int serdes_idx)
> +{
> + u32 reg;
> + int i;
> +
> + if (serdes_idx != 1)
> + return;
> + if (soc.rcwcr_init_done)
> + return;
> +
> + /* SRDS_CLK_EN_SEL_XGMII_S1: SerDes Clock Enable Select XGMII Serdes 1:
> + * Enables to select GMII/XGMII clock according to
> + * SRDS_CLK_SEL_XGMII_Ln_S1
> + */
> + reg = LS2088A_RCWSR30_SRDS_CLK_EN_SEL_XGMII_S1;
> +
> + /* We need to configure the initial state of all lanes for
> + * the SerDes block #1
> + */
> + for (i = 0; i < MAX_NUM_LANES; i++)
> + if (lynx_lane_mode_uses_xgmii_mac(soc.lane_mode[serdes_idx - 1][i]))
> + reg |= LS2088A_RCWSR30_SRDS_CLK_SEL_XGMII_Ln_S1(i);
> +
> + fsl_guts_rmw(29, reg,
> + LS2088A_RCWSR30_SRDS_CLK_EN_SEL_XGMII_S1 |
> + LS2088A_RCWSR30_SRDS_CLK_SEL_MSK);
> +
> + soc.rcwcr_init_done = true;
> +}
> +
> +static const struct fsl_soc_data ls1088a_data = {
> + .serdes_get_rcw_override = ls1088a_serdes_get_rcw_override,
> +};
> +
> +static const struct fsl_soc_data ls1046a_data = {
> + .serdes_get_rcw_override = ls1046a_serdes_get_rcw_override,
> +};
> +
> +static const struct fsl_soc_data ls2088a_data = {
> + .serdes_get_rcw_override = ls2088a_serdes_get_rcw_override,
> + .serdes_init_rcwcr = ls2088a_serdes_init_rcwcr,
> +};
> +
> static const struct fsl_soc_data ls1028a_data = {
> .sfp_compat = "fsl,ls1028a-sfp",
> .uid_offset = 0x21c,
> @@ -221,10 +494,10 @@ static const struct of_device_id fsl_guts_of_match[] = {
> { .compatible = "fsl,mpc8572-guts", },
> { .compatible = "fsl,ls1021a-dcfg", },
> { .compatible = "fsl,ls1043a-dcfg", },
> - { .compatible = "fsl,ls2080a-dcfg", },
> - { .compatible = "fsl,ls1088a-dcfg", },
> + { .compatible = "fsl,ls2080a-dcfg", .data = &ls2088a_data},
> + { .compatible = "fsl,ls1088a-dcfg", .data = &ls1088a_data},
> { .compatible = "fsl,ls1012a-dcfg", },
> - { .compatible = "fsl,ls1046a-dcfg", },
> + { .compatible = "fsl,ls1046a-dcfg", .data = &ls1046a_data},
> { .compatible = "fsl,lx2160a-dcfg", },
> { .compatible = "fsl,ls1028a-dcfg", .data = &ls1028a_data},
> {}
> @@ -250,6 +523,8 @@ static int __init fsl_guts_init(void)
> of_node_put(np);
> return -ENOMEM;
> }
> + /* DCFG_DCSR is optional */
> + soc.dcfg_dcsr = of_iomap(np, DCFG_DCSR);
>
> soc.little_endian = of_property_read_bool(np, "little-endian");
> soc.svr = fsl_guts_read(&soc.dcfg_ccsr->svr);
> @@ -296,6 +571,8 @@ static int __init fsl_guts_init(void)
> goto err;
> }
>
> + spin_lock_init(&soc.rcwcr_lock);
> +
> pr_info("Machine: %s\n", soc_dev_attr->machine);
> pr_info("SoC family: %s\n", soc_dev_attr->family);
> pr_info("SoC ID: %s, Revision: %s\n",
> @@ -305,7 +582,8 @@ static int __init fsl_guts_init(void)
>
> err_nomem:
> ret = -ENOMEM;
> -
> + if (soc.dcfg_dcsr)
> + iounmap(soc.dcfg_dcsr);
> iounmap(soc.dcfg_ccsr);
> err:
> kfree(soc_dev_attr->family);
> diff --git a/include/linux/fsl/guts.h b/include/linux/fsl/guts.h
> index fdb55ca47a4f..176842531241 100644
> --- a/include/linux/fsl/guts.h
> +++ b/include/linux/fsl/guts.h
> @@ -13,6 +13,7 @@
>
> #include <linux/types.h>
> #include <linux/io.h>
> +#include <soc/fsl/phy-fsl-lynx.h>
>
> /*
> * Global Utility Registers.
> @@ -91,9 +92,15 @@ struct ccsr_guts {
> u32 iovselsr; /* 0x.00c0 - I/O voltage select status register
> Called 'elbcvselcr' on 86xx SOCs */
> u8 res0c4[0x100 - 0xc4];
> - u32 rcwsr[16]; /* 0x.0100 - Reset Control Word Status registers
> - There are 16 registers */
> - u8 res140[0x224 - 0x140];
> + /* 0x.0100 - read-only Reset Configuration Word Status registers in
> + * CCSR, or write-only Reset Configuration Word Control registers in
> + * DCSR. In both cases there are 32 registers.
> + */
> + union {
> + u32 rcwsr[32];
> + u32 rcwcr[32];
> + };
> + u8 res180[0x224 - 0x180];
> u32 iodelay1; /* 0x.0224 - IO delay control register 1 */
> u32 iodelay2; /* 0x.0228 - IO delay control register 2 */
> u8 res22c[0x604 - 0x22c];
> @@ -131,6 +138,13 @@ struct ccsr_guts {
> u32 srds2cr1; /* 0x.0f44 - SerDes2 Control Register 0 */
> } __attribute__ ((packed));
>
> +void fsl_guts_lane_init(int serdes_idx, int lane,
> + enum lynx_lane_mode lane_mode);
> +int fsl_guts_lane_validate(int serdes_idx, int lane,
> + enum lynx_lane_mode lane_mode);
> +int fsl_guts_lane_set_mode(int serdes_idx, int lane,
> + enum lynx_lane_mode lane_mode);
> +
> /* Alternate function signal multiplex control */
> #define MPC85xx_PMUXCR_QE(x) (0x8000 >> (x))
>
> --
> 2.34.1
>
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]
^ permalink raw reply
* Re: [PATCH v2] arm64: dts: rockchip: fix emmc reset polarity on px30-cobra
From: Quentin Schulz @ 2026-06-12 15:38 UTC (permalink / raw)
To: Jakob Unterwurzacher, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Heiko Stuebner, Jakob Unterwurzacher
Cc: stable, Heiko Stuebner, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
In-Reply-To: <20260609081728.30616-2-jakobunt@gmail.com>
Hi Jakob,
On 6/9/26 10:17 AM, Jakob Unterwurzacher wrote:
> From: Jakob Unterwurzacher <jakob.unterwurzacher@cherry.de>
>
> Technically, the reset signal is active low - it's called RST_n after all.
>
> But it is ignored completely unless RST_n_FUNCTION=1 (byte 162 in extcsd)
> is set in the emmc. It is 0 per default.
>
> For emmcs that have RST_n_FUNCTION=1 we failed like this:
>
> [ 3.074480] mmc1: Failed to initialize a non-removable card
>
> With this change they work normally.
>
> Cc: stable@vger.kernel.org
> Fixes: bb510ddc9d3e ("arm64: dts: rockchip: add px30-cobra base dtsi and board variants")
Tested-by: Quentin Schulz <quentin.schulz@cherry.de>
Thanks!
Quentin
^ permalink raw reply
* Re: [PATCH v16 0/3] of: parsing of multi #{iommu,msi}-cells in maps
From: Rob Herring @ 2026-06-12 15:35 UTC (permalink / raw)
To: Vijayanand Jitta
Cc: Nipun Gupta, Nikhil Agarwal, Joerg Roedel, Will Deacon,
Robin Murphy, Lorenzo Pieralisi, Marc Zyngier, Thomas Gleixner,
Saravana Kannan, Richard Zhu, Lucas Stach,
Krzysztof Wilczyński, Manivannan Sadhasivam, Bjorn Helgaas,
Frank Li, Sascha Hauer, Pengutronix Kernel Team, Fabio Estevam,
Juergen Gross, Stefano Stabellini, Oleksandr Tyshchenko,
linux-arm-msm, linux-kernel, iommu, linux-arm-kernel, devicetree,
linux-pci, imx, xen-devel, Charan Teja Kalla, Dmitry Baryshkov
In-Reply-To: <20260603-parse_iommu_cells-v16-0-dc509dacb19a@oss.qualcomm.com>
On Wed, Jun 03, 2026 at 12:43:11PM +0530, Vijayanand Jitta wrote:
> So far our parsing of {iommu,msi}-map properties has always blindly
> assumed that the output specifiers will always have exactly 1 cell.
> This typically does happen to be the case, but is not actually enforced
> (and the PCI msi-map binding even explicitly states support for 0 or 1
> cells) - as a result we've now ended up with dodgy DTs out in the field
> which depend on this behaviour to map a 1-cell specifier for a 2-cell
> provider, despite that being bogus per the bindings themselves.
>
> Since there is some potential use[1] in being able to map at least
> single input IDs to multi-cell output specifiers (and properly support
> 0-cell outputs as well), add support for properly parsing and using the
> target nodes' #cells values, albeit with the unfortunate complication of
> still having to work around expectations of the old behaviour too.
> -- Robin.
>
> Unlike single #{}-cell, it is complex to establish a linear relation
> between input 'id' and output specifier for multi-cell properties, thus
> it is always expected that len never going to be > 1.
>
> These changes have been tested on QEMU for the arm64 architecture and
> on the glymur platform [3].
>
> Since, this would also need update in dt-schema, raised PR[2] for the
> same.
>
> [1] https://lore.kernel.org/all/20250627-video_cb-v3-0-51e18c0ffbce@quicinc.com/
> [2] PR for iommu-map dtschema: https://github.com/devicetree-org/dt-schema/pull/184
> [3] https://lore.kernel.org/all/20260515-glymur-v6-5-f6a99cb43a24@oss.qualcomm.com/
>
> V16:
> - Patch 2: Fix potential NULL pointer dereference in of_msi_xlate()
> when msi_np is NULL. Guard the of_check_msi_parent() call with
> "if (msi_np && ...)" to handle the case where the caller passes
> NULL for msi_np, as documented. Reported by Sashiko [1].
> - Patch 2: Fix OF node refcount leak in of_msi_map_get_device_domain():
> np was never released after of_msi_xlate() transferred ownership.
> - Patch 3: Default to 1-cell output specifier when the target node
> lacks the #iommu-cells/#msi-cells property, for backward
> compatibility with controllers that predate the property
> (e.g. arm,gic-v2m-frame). Reported by Sashiko [1].
> - Patch 3: Add !cells_name to the initial parameter guard in
> of_map_id() to prevent a crash if cells_name is NULL.
> Reported by Sashiko [1].
I've applied the series, thanks.
Rob
^ permalink raw reply
* [PATCH 4/4] regulator: Add support for UGREEN NASync DH2300 MCU SATA power gate
From: Alexey Charkov @ 2026-06-12 15:34 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Lee Jones,
Heiko Stuebner, Liam Girdwood, Mark Brown
Cc: devicetree, linux-kernel, linux-arm-kernel, linux-rockchip,
Alexey Charkov
In-Reply-To: <20260612-dh2300-mcu-v1-0-ab8db1617bc0@flipper.net>
Add a driver for the SATA drive-bay power gate function of the UGREEN
NASync DH2300 embedded controller (HC32F005 MCU).
This is a simple on/off regulator, controlled by bit 0 of register 0x41,
with inverted polarity (0 = enabled, 1 = disabled). Boot-time default is
disabled, so this driver is required to use the NAS functionality.
Signed-off-by: Alexey Charkov <alchark@flipper.net>
---
MAINTAINERS | 1 +
drivers/regulator/Kconfig | 12 ++++
drivers/regulator/Makefile | 1 +
drivers/regulator/ugreen-dh2300-mcu-regulator.c | 80 +++++++++++++++++++++++++
4 files changed, 94 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 9578a06fe651..2fc84be86e46 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -27638,6 +27638,7 @@ M: Alexey Charkov <alchark@flipper.net>
S: Maintained
F: Documentation/devicetree/bindings/mfd/ugreen,dh2300-mcu.yaml
F: drivers/mfd/ugreen-dh2300-mcu.c
+F: drivers/regulator/ugreen-dh2300-mcu-regulator.c
UHID USERSPACE HID IO DRIVER
M: David Rheinsberg <david@readahead.eu>
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index a54a549196fe..e692ff864806 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -1812,6 +1812,18 @@ config REGULATOR_TWL4030
This driver supports the voltage regulators provided by
this family of companion chips.
+config REGULATOR_UGREEN_DH2300_MCU
+ tristate "UGREEN NASync DH2300 MCU SATA power regulator"
+ depends on MFD_UGREEN_DH2300_MCU
+ help
+ Say yes here to enable support for the SATA drive-bay power gate of
+ the UGREEN NASync DH2300 embedded controller. The regulator is a
+ sub-device of the ugreen-dh2300-mcu MFD core and is normally consumed
+ by the SATA controllers via their target-supply.
+
+ This driver can also be built as a module. If so, the module will be
+ called ugreen-dh2300-mcu-regulator.
+
config REGULATOR_UNIPHIER
tristate "UniPhier regulator driver"
depends on ARCH_UNIPHIER || COMPILE_TEST
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 134eee274dbf..44956d795923 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -206,6 +206,7 @@ obj-$(CONFIG_REGULATOR_TPS6594) += tps6594-regulator.o
obj-$(CONFIG_REGULATOR_TPS65132) += tps65132-regulator.o
obj-$(CONFIG_REGULATOR_TPS68470) += tps68470-regulator.o
obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o twl6030-regulator.o
+obj-$(CONFIG_REGULATOR_UGREEN_DH2300_MCU) += ugreen-dh2300-mcu-regulator.o
obj-$(CONFIG_REGULATOR_UNIPHIER) += uniphier-regulator.o
obj-$(CONFIG_REGULATOR_RZG2L_VBCTRL) += renesas-usb-vbus-regulator.o
obj-$(CONFIG_REGULATOR_VCTRL) += vctrl-regulator.o
diff --git a/drivers/regulator/ugreen-dh2300-mcu-regulator.c b/drivers/regulator/ugreen-dh2300-mcu-regulator.c
new file mode 100644
index 000000000000..69fda90f7ace
--- /dev/null
+++ b/drivers/regulator/ugreen-dh2300-mcu-regulator.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SATA drive-bay power gate for the UGREEN NASync DH2300 embedded controller
+ * (HC32F005 MCU).
+ *
+ * The microcontroller gates the SATA bay power rail through register 0x41.
+ * The polarity is inverted: writing 0 enables the rail, writing 1 disables it
+ * (the controller latches "off" out of reset).
+ */
+
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/of_regulator.h>
+
+#define UGREEN_DH2300_MCU_REG_SATA_POWER 0x41
+
+static const struct regulator_ops ugreen_dh2300_sata_ops = {
+ .enable = regulator_enable_regmap,
+ .disable = regulator_disable_regmap,
+ .is_enabled = regulator_is_enabled_regmap,
+};
+
+static const struct regulator_desc ugreen_dh2300_sata_desc = {
+ .name = "sata-power",
+ .enable_is_inverted = true,
+ .enable_mask = 0x01,
+ .enable_reg = UGREEN_DH2300_MCU_REG_SATA_POWER,
+ .supply_name = "vin",
+ .ops = &ugreen_dh2300_sata_ops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+};
+
+static int ugreen_dh2300_mcu_regulator_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct regulator_config config = { };
+ struct regulator_dev *rdev;
+ struct device_node *np;
+
+ np = of_get_child_by_name(dev->parent->of_node, "regulator");
+ if (!np)
+ return dev_err_probe(dev, -ENODEV,
+ "missing regulator child node\n");
+
+ config.dev = dev;
+ config.of_node = np;
+ config.regmap = dev_get_regmap(dev->parent, NULL);
+ if (!config.regmap) {
+ of_node_put(np);
+ return dev_err_probe(dev, -ENODEV,
+ "no regmap available from parent\n");
+ }
+
+ config.init_data = of_get_regulator_init_data(dev, np,
+ &ugreen_dh2300_sata_desc);
+
+ rdev = devm_regulator_register(dev, &ugreen_dh2300_sata_desc, &config);
+ of_node_put(np);
+ if (IS_ERR(rdev))
+ return dev_err_probe(dev, PTR_ERR(rdev),
+ "failed to register regulator\n");
+
+ return 0;
+}
+
+static struct platform_driver ugreen_dh2300_mcu_regulator_driver = {
+ .driver = {
+ .name = "ugreen-dh2300-mcu-regulator",
+ },
+ .probe = ugreen_dh2300_mcu_regulator_probe,
+};
+module_platform_driver(ugreen_dh2300_mcu_regulator_driver);
+
+MODULE_DESCRIPTION("UGREEN NASync DH2300 MCU SATA power regulator");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related
* [PATCH 3/4] mfd: Add support for UGREEN NASync DH2300 MCU
From: Alexey Charkov @ 2026-06-12 15:34 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Lee Jones,
Heiko Stuebner, Liam Girdwood, Mark Brown
Cc: devicetree, linux-kernel, linux-arm-kernel, linux-rockchip,
Alexey Charkov
In-Reply-To: <20260612-dh2300-mcu-v1-0-ab8db1617bc0@flipper.net>
Add a driver for the HC32F005 MCU used as an embedded controller on the
UGREEN NASync DH2300 NAS.
This part provides the shared I2C regmap to be used by function-specific
sub-devices, and instantiates the SATA drive-bay power gate regulator.
Implemented as an MFD to allow for other functions of the MCU to be added
later: vendor binaries imply that it also provides a hardware watchdog
and somehow serves as a wake source, but so far only the SATA power gating
function has been confirmed in absence of documentation and sources for the
vendor firmware.
Signed-off-by: Alexey Charkov <alchark@flipper.net>
---
MAINTAINERS | 1 +
drivers/mfd/Kconfig | 16 +++++++++++
drivers/mfd/Makefile | 1 +
drivers/mfd/ugreen-dh2300-mcu.c | 60 +++++++++++++++++++++++++++++++++++++++++
4 files changed, 78 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index ca27df7cd684..9578a06fe651 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -27637,6 +27637,7 @@ UGREEN DH2300 MCU MFD DRIVER
M: Alexey Charkov <alchark@flipper.net>
S: Maintained
F: Documentation/devicetree/bindings/mfd/ugreen,dh2300-mcu.yaml
+F: drivers/mfd/ugreen-dh2300-mcu.c
UHID USERSPACE HID IO DRIVER
M: David Rheinsberg <david@readahead.eu>
diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig
index 763ce6a34782..5a2ad75bd9c9 100644
--- a/drivers/mfd/Kconfig
+++ b/drivers/mfd/Kconfig
@@ -1947,6 +1947,22 @@ config MFD_TPS6594_SPI
This driver can also be built as a module. If so, the module
will be called tps6594-spi.
+config MFD_UGREEN_DH2300_MCU
+ tristate "UGREEN NASync DH2300 embedded controller"
+ depends on I2C
+ depends on OF
+ select MFD_CORE
+ select REGMAP_I2C
+ help
+ Say yes here to enable support for the HC32F005 microcontroller found
+ on the UGREEN NASync DH2300 NAS, where it acts as a board embedded
+ controller. This core driver sets up the shared register map and
+ instantiates the function sub-devices (the SATA drive-bay power
+ regulator).
+
+ This driver can also be built as a module. If so, the module will be
+ called ugreen-dh2300-mcu.
+
config TWL4030_CORE
bool "TI TWL4030/TWL5030/TWL6030/TPS659x0 Support"
depends on I2C=y
diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile
index dd4bb7e77c33..6247239bcfe1 100644
--- a/drivers/mfd/Makefile
+++ b/drivers/mfd/Makefile
@@ -109,6 +109,7 @@ obj-$(CONFIG_MFD_TPS65912_SPI) += tps65912-spi.o
obj-$(CONFIG_MFD_TPS6594) += tps6594-core.o
obj-$(CONFIG_MFD_TPS6594_I2C) += tps6594-i2c.o
obj-$(CONFIG_MFD_TPS6594_SPI) += tps6594-spi.o
+obj-$(CONFIG_MFD_UGREEN_DH2300_MCU) += ugreen-dh2300-mcu.o
obj-$(CONFIG_MENELAUS) += menelaus.o
obj-$(CONFIG_TWL4030_CORE) += twl-core.o twl4030-irq.o twl6030-irq.o
diff --git a/drivers/mfd/ugreen-dh2300-mcu.c b/drivers/mfd/ugreen-dh2300-mcu.c
new file mode 100644
index 000000000000..5184b0c98759
--- /dev/null
+++ b/drivers/mfd/ugreen-dh2300-mcu.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Core driver for the UGREEN NASync DH2300 embedded controller (HC32F005 MCU).
+ *
+ * The microcontroller sits on I2C and exposes an 8-bit register map. It is a
+ * multi-function device: SATA drive-bay power gate, hardware watchdog and
+ * possibly other functions
+ */
+
+#include <linux/i2c.h>
+#include <linux/mfd/core.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#define UGREEN_DH2300_MCU_REG_MAX 0x94
+
+static const struct regmap_config ugreen_dh2300_mcu_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = UGREEN_DH2300_MCU_REG_MAX,
+};
+
+static const struct mfd_cell ugreen_dh2300_mcu_cells[] = {
+ { .name = "ugreen-dh2300-mcu-regulator" },
+};
+
+static int ugreen_dh2300_mcu_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(client, &ugreen_dh2300_mcu_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "failed to initialise regmap\n");
+
+ return devm_mfd_add_devices(dev, PLATFORM_DEVID_AUTO,
+ ugreen_dh2300_mcu_cells,
+ ARRAY_SIZE(ugreen_dh2300_mcu_cells),
+ NULL, 0, NULL);
+}
+
+static const struct of_device_id ugreen_dh2300_mcu_of_match[] = {
+ { .compatible = "ugreen,dh2300-mcu" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ugreen_dh2300_mcu_of_match);
+
+static struct i2c_driver ugreen_dh2300_mcu_driver = {
+ .driver = {
+ .name = "ugreen-dh2300-mcu",
+ .of_match_table = ugreen_dh2300_mcu_of_match,
+ },
+ .probe = ugreen_dh2300_mcu_probe,
+};
+module_i2c_driver(ugreen_dh2300_mcu_driver);
+
+MODULE_DESCRIPTION("UGREEN NASync DH2300 embedded controller core driver");
+MODULE_LICENSE("GPL");
--
2.53.0
^ permalink raw reply related
* [PATCH 2/4] dt-bindings: mfd: Add UGREEN NASync DH2300 MCU
From: Alexey Charkov @ 2026-06-12 15:34 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Lee Jones,
Heiko Stuebner, Liam Girdwood, Mark Brown
Cc: devicetree, linux-kernel, linux-arm-kernel, linux-rockchip,
Alexey Charkov
In-Reply-To: <20260612-dh2300-mcu-v1-0-ab8db1617bc0@flipper.net>
Document the UGREEN NASync DH2300 embedded controller (HC32F005 MCU),
which is responsible for gating the SATA drive-bay power rail and
providing a hardware watchdog.
This is based on disassebly of a GPL binary from vendor firmware for which
no source code could be found, so parts of it can be inaccurate. Only
the power gating function is confirmed.
Signed-off-by: Alexey Charkov <alchark@flipper.net>
---
.../devicetree/bindings/mfd/ugreen,dh2300-mcu.yaml | 62 ++++++++++++++++++++++
MAINTAINERS | 5 ++
2 files changed, 67 insertions(+)
diff --git a/Documentation/devicetree/bindings/mfd/ugreen,dh2300-mcu.yaml b/Documentation/devicetree/bindings/mfd/ugreen,dh2300-mcu.yaml
new file mode 100644
index 000000000000..847970c609cd
--- /dev/null
+++ b/Documentation/devicetree/bindings/mfd/ugreen,dh2300-mcu.yaml
@@ -0,0 +1,62 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/mfd/ugreen,dh2300-mcu.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: UGREEN NASync DH2300 embedded controller
+
+maintainers:
+ - Alexey Charkov <alchark@flipper.net>
+
+description:
+ The UGREEN NASync DH2300 NAS carries a HC32F005 microcontroller on I2C that
+ acts as a board embedded controller. It gates power to the SATA drive bays
+ through an internal register and apparently also serves as a watchdog
+ (unconfirmed, as vendor kernel sources are unavailable, works without it)
+
+properties:
+ compatible:
+ const: ugreen,dh2300-mcu
+
+ reg:
+ maxItems: 1
+
+ regulator:
+ type: object
+ $ref: /schemas/regulator/regulator.yaml#
+ unevaluatedProperties: false
+ description:
+ The SATA drive-bay power gate controlled by the MCU.
+
+ watchdog-gpios:
+ description:
+ Optional GPIO line used to ping the hardware watchdog function of the MCU
+ maxItems: 1
+
+required:
+ - compatible
+ - reg
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/gpio/gpio.h>
+ #include <dt-bindings/pinctrl/rockchip.h>
+
+ i2c {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ embedded-controller@4c {
+ compatible = "ugreen,dh2300-mcu";
+ reg = <0x4c>;
+ watchdog-gpios = <&gpio0 RK_PA0 GPIO_ACTIVE_LOW>;
+
+ sata_power: regulator {
+ regulator-name = "sata-power";
+ vin-supply = <&vcc12v_dcin>;
+ };
+ };
+ };
diff --git a/MAINTAINERS b/MAINTAINERS
index f1caa6e5198b..ca27df7cd684 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -27633,6 +27633,11 @@ L: linux-input@vger.kernel.org
S: Maintained
F: drivers/hid/hid-udraw-ps3.c
+UGREEN DH2300 MCU MFD DRIVER
+M: Alexey Charkov <alchark@flipper.net>
+S: Maintained
+F: Documentation/devicetree/bindings/mfd/ugreen,dh2300-mcu.yaml
+
UHID USERSPACE HID IO DRIVER
M: David Rheinsberg <david@readahead.eu>
L: linux-input@vger.kernel.org
--
2.53.0
^ permalink raw reply related
* [PATCH 1/4] dt-bindings: vendor-prefixes: Add Ugreen Group Limited
From: Alexey Charkov @ 2026-06-12 15:34 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Lee Jones,
Heiko Stuebner, Liam Girdwood, Mark Brown
Cc: devicetree, linux-kernel, linux-arm-kernel, linux-rockchip,
Alexey Charkov
In-Reply-To: <20260612-dh2300-mcu-v1-0-ab8db1617bc0@flipper.net>
Add Ugreen Group Limited, a consumer technology company producing a range
of smart charging, office audio and visual, and smart storage products.
Link: https://www.ugreen.com/
Signed-off-by: Alexey Charkov <alchark@flipper.net>
---
Documentation/devicetree/bindings/vendor-prefixes.yaml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml
index dd94c50e97f9..274e52421a5b 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.yaml
+++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml
@@ -1757,6 +1757,8 @@ patternProperties:
description: Ufi Space Co., Ltd.
"^ugoos,.*":
description: Ugoos Industrial Co., Ltd.
+ "^ugreen,.*":
+ description: Ugreen Group Limited
"^ultrapower,.*":
description: Beijing Ultrapower Software Co., Ltd.
"^uni-t,.*":
--
2.53.0
^ permalink raw reply related
* [PATCH 0/4] mfd: Add support for the MCU in the UGREEN DH2300 NAS
From: Alexey Charkov @ 2026-06-12 15:34 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Lee Jones,
Heiko Stuebner, Liam Girdwood, Mark Brown
Cc: devicetree, linux-kernel, linux-arm-kernel, linux-rockchip,
Alexey Charkov
UGREEN DH2300 is a 2-bay SATA NAS based on the Rockchip RK3576 SoC. It
includes an embedded controller connected over I2C which won't let the
drives power up without its register being poked first, which precludes
the use of the device for its main purpose.
There is no public documentation or source code available, but apparently
the MCU is also responsible for a hardware watchdog function and some type
of wake functionality, so this series implements an MFD-regulator
separation right away to allow for adding these functions properly later
on.
For now though a single-bit write to the MCU seems to be sufficient to get
the drives to work with only a board DTS addition [1] - to be submitted
separately.
[1] https://github.com/flipperdevices/flipper-linux-kernel/blob/2c1c5ee609f6ed4c77e9e5428df91e98b3f50cce/arch/arm64/boot/dts/rockchip/rk3576-nasync-dh2300.dts
Signed-off-by: Alexey Charkov <alchark@flipper.net>
---
Alexey Charkov (4):
dt-bindings: vendor-prefixes: Add Ugreen Group Limited
dt-bindings: mfd: Add UGREEN NASync DH2300 MCU
mfd: Add support for UGREEN NASync DH2300 MCU
regulator: Add support for UGREEN NASync DH2300 MCU SATA power gate
.../devicetree/bindings/mfd/ugreen,dh2300-mcu.yaml | 62 +++++++++++++++++
.../devicetree/bindings/vendor-prefixes.yaml | 2 +
MAINTAINERS | 7 ++
drivers/mfd/Kconfig | 16 +++++
drivers/mfd/Makefile | 1 +
drivers/mfd/ugreen-dh2300-mcu.c | 60 ++++++++++++++++
drivers/regulator/Kconfig | 12 ++++
drivers/regulator/Makefile | 1 +
drivers/regulator/ugreen-dh2300-mcu-regulator.c | 80 ++++++++++++++++++++++
9 files changed, 241 insertions(+)
---
base-commit: ec039126b7fac4e3af35ebccaa7c6f9b6875ba81
change-id: 20260612-dh2300-mcu-5354d1f5f11a
Best regards,
--
Alexey Charkov <alchark@flipper.net>
^ permalink raw reply
* Re: [PATCH v6 5/6] KVM: arm64: Validate the offset to the mem access descriptor
From: Sebastian Ene @ 2026-06-12 15:20 UTC (permalink / raw)
To: Mostafa Saleh
Cc: op-tee, linux-kernel, kvmarm, linux-arm-kernel, maz, oupton,
joey.gouly, suzuki.poulose, catalin.marinas, jens.wiklander,
sumit.garg, vdonnefort, sudeep.holla
In-Reply-To: <20260527150236.1978655-6-smostafa@google.com>
On Wed, May 27, 2026 at 03:02:35PM +0000, Mostafa Saleh wrote:
> From: Sebastian Ene <sebastianene@google.com>
>
> Prevent the pKVM hypervisor from making assumptions that the
> endpoint memory access descriptor (EMAD) comes right after the
> FF-A memory region header.
> Prior to FF-A version 1.1 the header of the memory region
> didn't contain an offset to the endpoint memory access descriptor.
> The layout of a memory transaction looks like this from 1.1 onward:
> Type | Field name | Offset
> [ Header | ffa_mem_region | 0
> EMAD 1 | ffa_mem_region_attributes) | ffa_mem_region.ep_mem_offset
> ]
> Verify that the offset to the first endpoint memory access descriptor
> is within the mailbox buffer bounds.
>
> Also, fix one hardcoded sizeof(struct ffa_mem_region_attributes) that
> should be replaced ffa_emad_size_get() for compatibility with FFA v1.0.
>
> [@Mostafa, Add missing call to ffa_rx_release() and use fraglen
> as the max buffer size as it is the only intialised part]
>
> Fixes: 42fb33dde42b ("KVM: arm64: Use FF-A 1.1 with pKVM")
> Signed-off-by: Sebastian Ene <sebastianene@google.com>
> Signed-off-by: Mostafa Saleh <smostafa@google.com>
> ---
> arch/arm64/kvm/hyp/nvhe/ffa.c | 25 ++++++++++++++++++-------
> 1 file changed, 18 insertions(+), 7 deletions(-)
>
> diff --git a/arch/arm64/kvm/hyp/nvhe/ffa.c b/arch/arm64/kvm/hyp/nvhe/ffa.c
> index b6cf9ad82e12..a12e01883314 100644
> --- a/arch/arm64/kvm/hyp/nvhe/ffa.c
> +++ b/arch/arm64/kvm/hyp/nvhe/ffa.c
> @@ -476,10 +476,10 @@ static void __do_ffa_mem_xfer(const u64 func_id,
> DECLARE_REG(u32, fraglen, ctxt, 2);
> DECLARE_REG(u64, addr_mbz, ctxt, 3);
> DECLARE_REG(u32, npages_mbz, ctxt, 4);
> + u32 offset, nr_ranges, checked_offset, em_mem_access_off;
> struct ffa_mem_region_attributes *ep_mem_access;
> struct ffa_composite_mem_region *reg;
> struct ffa_mem_region *buf;
> - u32 offset, nr_ranges, checked_offset;
> int ret = 0;
>
> if (addr_mbz || npages_mbz || fraglen > len ||
> @@ -489,7 +489,7 @@ static void __do_ffa_mem_xfer(const u64 func_id,
> }
>
Hi Mostafa,
> if (fraglen < sizeof(struct ffa_mem_region) +
> - sizeof(struct ffa_mem_region_attributes)) {
> + ffa_emad_size_get(hyp_ffa_version)) {
> ret = FFA_RET_INVALID_PARAMETERS;
> goto out;
> }
Here sashiko complains about a compatibility break with v1.0 even though
it is not from this change. This is because the size of sizeof(struct
ffa_mem_region) was 36 bytes in v1.0 and now it is 48.
This check can cause "perfectly valid v1.0 memory transfers to be rejected"
(from sashiko).
Other than that this looks good. I will spin a new version of this
series.
> @@ -508,8 +508,13 @@ static void __do_ffa_mem_xfer(const u64 func_id,
> buf = hyp_buffers.tx;
> memcpy(buf, host_buffers.tx, fraglen);
>
> - ep_mem_access = (void *)buf +
> - ffa_mem_desc_offset(buf, 0, hyp_ffa_version);
> + em_mem_access_off = ffa_mem_desc_offset(buf, 0, hyp_ffa_version);
> + if ((u64)em_mem_access_off + ffa_emad_size_get(hyp_ffa_version) > fraglen) {
> + ret = FFA_RET_INVALID_PARAMETERS;
> + goto out_unlock;
> + }
> +
> + ep_mem_access = (void *)buf + em_mem_access_off;
> offset = ep_mem_access->composite_off;
> if (!offset || buf->ep_count != 1 || buf->sender_id != HOST_FFA_ID) {
> ret = FFA_RET_INVALID_PARAMETERS;
> @@ -574,9 +579,9 @@ static void do_ffa_mem_reclaim(struct arm_smccc_1_2_regs *res,
> DECLARE_REG(u32, handle_lo, ctxt, 1);
> DECLARE_REG(u32, handle_hi, ctxt, 2);
> DECLARE_REG(u32, flags, ctxt, 3);
> + u32 offset, len, fraglen, fragoff, em_mem_access_off;
> struct ffa_mem_region_attributes *ep_mem_access;
> struct ffa_composite_mem_region *reg;
> - u32 offset, len, fraglen, fragoff;
> struct ffa_mem_region *buf;
> int ret = 0;
> u64 handle;
> @@ -599,8 +604,14 @@ static void do_ffa_mem_reclaim(struct arm_smccc_1_2_regs *res,
> len = res->a1;
> fraglen = res->a2;
>
> - ep_mem_access = (void *)buf +
> - ffa_mem_desc_offset(buf, 0, hyp_ffa_version);
> + em_mem_access_off = ffa_mem_desc_offset(buf, 0, hyp_ffa_version);
> + if ((u64)em_mem_access_off + ffa_emad_size_get(hyp_ffa_version) > fraglen) {
> + ret = FFA_RET_INVALID_PARAMETERS;
> + ffa_rx_release(res);
> + goto out_unlock;
> + }
> +
> + ep_mem_access = (void *)buf + em_mem_access_off;
> offset = ep_mem_access->composite_off;
> /*
> * We can trust the SPMD to get this right, but let's at least
> --
> 2.54.0.746.g67dd491aae-goog
>
Thanks,
Sebastian
^ permalink raw reply
* Re: [PATCH v6 1/3] dt-bindings: imx6q-pcie: Add optional intr/aer/pme interrupts for i.MX95
From: Rob Herring @ 2026-06-12 15:13 UTC (permalink / raw)
To: hongxing.zhu
Cc: krzk+dt, conor+dt, bhelgaas, frank.li, l.stach, lpieralisi,
kwilczynski, mani, s.hauer, kernel, festevam, linux-pci,
linux-arm-kernel, devicetree, imx, linux-kernel, Richard Zhu
In-Reply-To: <20260603062510.3767610-2-hongxing.zhu@oss.nxp.com>
On Wed, Jun 03, 2026 at 02:25:08PM +0800, hongxing.zhu@oss.nxp.com wrote:
> From: Richard Zhu <hongxing.zhu@nxp.com>
>
> The i.MX95 PCIe controller introduces three additional dedicated hardware
> interrupt lines for specific events:
> - intr: general controller events
> - aer: Advanced Error Reporting events
> - pme: Power Management Events
>
> These interrupts are optional on i.MX95. PCIe basic functionality
> (enumeration, configuration, and data transfer) works correctly without
> them, as the controller can operate using only the existing msi interrupt.
>
> Earlier i.MX PCIe variants (imx6q, imx6sx, imx6qp, imx7d, imx8mm, imx8mp,
> imx8mq, imx8q) do not have these three dedicated interrupt lines.
>
> Update the binding to allow up to 5 interrupts for i.MX95, while
> restricting earlier variants to a maximum of 2 interrupts using
> conditional constraints (if/then schema). This ensures the schema
> accurately reflects the hardware capabilities of each SoC variant.
>
> Signed-off-by: Richard Zhu <hongxing.zhu@nxp.com>
> Reviewed-by: Frank Li <Frank.Li@nxp.com>
> ---
> .../bindings/pci/fsl,imx6q-pcie.yaml | 29 +++++++++++++++++++
> 1 file changed, 29 insertions(+)
>
> diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
> index e8b8131f5f23..9b5d4e59dfff 100644
> --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
> +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml
> @@ -58,12 +58,18 @@ properties:
> items:
> - description: builtin MSI controller.
> - description: builtin DMA controller.
> + - description: PCIe event interrupt.
> + - description: builtin AER SPI standalone interrupt line.
> + - description: builtin PME SPI standalone interrupt line.
>
> interrupt-names:
> minItems: 1
> items:
> - const: msi
> - const: dma
> + - const: intr
> + - const: aer
> + - const: pme
>
> reset-gpio:
> deprecated: true
> @@ -248,6 +254,29 @@ allOf:
> - const: pcie_aux
> - const: ref
> - const: extref # Optional
> + interrupts:
> + maxItems: 5
> + interrupt-names:
> + maxItems: 5
5 is already the max.
> +
> + - if:
> + properties:
> + compatible:
> + enum:
> + - fsl,imx6q-pcie
> + - fsl,imx6sx-pcie
> + - fsl,imx6qp-pcie
> + - fsl,imx7d-pcie
> + - fsl,imx8mm-pcie
> + - fsl,imx8mp-pcie
> + - fsl,imx8mq-pcie
> + - fsl,imx8q-pcie
> + then:
> + properties:
> + interrupts:
> + maxItems: 2
> + interrupt-names:
> + maxItems: 2
>
> unevaluatedProperties: false
>
> --
> 2.34.1
>
^ permalink raw reply
* Re: [PATCH v2 1/5] ASoC: dt-bindings: rockchip-spdif: Correct SPDIF clock descriptions
From: Rob Herring (Arm) @ 2026-06-12 15:12 UTC (permalink / raw)
To: phucduc.bui
Cc: Mark Brown, linux-kernel, Heiko Stuebner, Krzysztof Kozlowski,
linux-sound, Liam Girdwood, Jaroslav Kysela, Conor Dooley,
devicetree, linux-rockchip, linux-arm-kernel, Takashi Iwai
In-Reply-To: <20260602101608.45137-2-phucduc.bui@gmail.com>
On Tue, 02 Jun 2026 17:16:04 +0700, phucduc.bui@gmail.com wrote:
> From: bui duc phuc <phucduc.bui@gmail.com>
>
> Update the binding descriptions to match the actual clock usage, where
> 'mclk' is the controller clock and 'hclk' is the bus clock.
>
> Signed-off-by: bui duc phuc <phucduc.bui@gmail.com>
> ---
>
> Changes in v2:
> - Update commit message based on Krzysztof's review
>
> Documentation/devicetree/bindings/sound/rockchip-spdif.yaml | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
Acked-by: Rob Herring (Arm) <robh@kernel.org>
^ permalink raw reply
* Re: [PATCH 3/3] arm64/coco: Add pKVM as a CC platform
From: Aneesh Kumar K.V @ 2026-06-12 15:07 UTC (permalink / raw)
To: Mostafa Saleh
Cc: linux-arm-kernel, linux-kernel, akpm, catalin.marinas, will, rppt,
maz
In-Reply-To: <aivG1rEi3y4GN8kX@google.com>
Mostafa Saleh <smostafa@google.com> writes:
> On Thu, Jun 04, 2026 at 02:29:00PM +0530, Aneesh Kumar K.V wrote:
>> Mostafa Saleh <smostafa@google.com> writes:
>>
>> > pKVM does support memory encryption, expose that to the rest of
>> > the kernel through cc_platform_has()
>> >
>> > At the moment, all devices inside the guest are emulated which
>> > requires its memory to be shared back to the host (decrypted), so
>> > set force_dma_unencrypted() to always return true.
>> >
>> > Although, typically pKVM guests rely on restricted-dma-pools to
>> > bounce traffic, with this change, it is possible to solely rely on
>> > the default SWIOTLB for that (assuming the appropriate size is set
>> > from the command line)
>> >
>> > Signed-off-by: Mostafa Saleh <smostafa@google.com>
>> > ---
>> > This change is critical for the ongoing refactoring of the DMA-API[1]
>> > that will break protected guests under pKVM with this patch. That is
>> > due to this rework will make the state of the SWIOTLB and restricted
>> > dma pools depends on the value returned by cc_platform_has()
>> >
>> > [1] https://lore.kernel.org/all/20260522042815.370873-1-aneesh.kumar@kernel.org/
>> > ---
>> > arch/arm64/include/asm/hypervisor.h | 13 +++++++++++++
>> > arch/arm64/include/asm/mem_encrypt.h | 3 ++-
>> > arch/arm64/kernel/rsi.c | 12 ------------
>> > arch/arm64/mm/init.c | 15 ++++++++++++++-
>> > drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c | 3 +++
>> > 5 files changed, 32 insertions(+), 14 deletions(-)
>> >
>> > index d66291def0f4..26fe9c3f22e3 100644
> [...]
>> > --- a/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c
>> > +++ b/drivers/virt/coco/pkvm-guest/arm-pkvm-guest.c
>> > @@ -17,6 +17,7 @@
>> > #include <asm/hypervisor.h>
>> >
>> > static size_t pkvm_granule;
>> > +DEFINE_STATIC_KEY_FALSE_RO(pkvm_guest);
>> >
>>
>> Do we need EXPORT_SYMBOL on this?
>
> I was not sure about that, all users of this are in tree, I saw RME
> code have the EXPORT but did not know why?
>
arm-cca-guest is one example. I was assuming is_protected_kvm_guest()
would be a helper that could get pulled into various code paths via
force_dma_unencrypted().
If we have not found any build failures for now, we can probably avoid
that change for now.
-aneesh
^ permalink raw reply
* Re: [PATCH 11/11] ASoC: fsl: mpc5200_psc_ac97: Use guard() for mutex locks
From: Mark Brown @ 2026-06-12 15:05 UTC (permalink / raw)
To: phucduc.bui
Cc: Liam Girdwood, Jaroslav Kysela, Takashi Iwai, Shengjiu Wang,
Xiubo Li, Frank Li, Fabio Estevam, Nicolin Chen, Sascha Hauer,
Pengutronix Kernel Team, linux-sound, linux-kernel,
linux-arm-kernel, imx, linuxppc-dev
In-Reply-To: <20260612132639.78086-12-phucduc.bui@gmail.com>
[-- Attachment #1: Type: text/plain, Size: 230 bytes --]
On Fri, Jun 12, 2026 at 08:26:39PM +0700, phucduc.bui@gmail.com wrote:
> - dev_dbg(psc_dma->dev, "cold reset\n");
> + scoped_guard(mutex_lock, &psc_dma->mutex) {
> + dev_dbg(psc_dma->dev, "cold reset\n");
mutex_lock not mutex?
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* Re: [PATCH 1/2] PCI: dwc: Guard RAS DES debugfs deinit
From: Shuvam Pandey @ 2026-06-12 14:53 UTC (permalink / raw)
To: Bjorn Helgaas
Cc: Jingoo Han, Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Bjorn Helgaas, Yue Wang,
Neil Armstrong, Rob Herring, Kevin Hilman, Jerome Brunet,
Martin Blumenstingl, Fan Ni, Shradha Todi, Hanjie Lin, linux-pci,
linux-amlogic, linux-arm-kernel, linux-kernel
In-Reply-To: <20260611221000.GA524832@bhelgaas>
Hi Bjorn,
> I guess this prevents a NULL pointer dereference, right?
Yes. This is in the CONFIG_PCIE_DW_DEBUGFS teardown path when the
controller does not advertise the RAS DES capability.
In that case dwc_pcie_rasdes_debugfs_init() returns 0 before allocating
pci->debugfs->rasdes_info or initializing reg_event_lock. Later,
dwc_pcie_debugfs_deinit() still calls dwc_pcie_rasdes_debugfs_deinit()
unconditionally.
With CONFIG_DEBUG_MUTEXES, mutex_destroy() dereferences the mutex, so
calling it via &rinfo->reg_event_lock when rinfo is NULL would
dereference NULL. Without CONFIG_DEBUG_MUTEXES, mutex_destroy() is a
no-op, but there is still no RAS DES state to tear down.
So the patch makes the deinit path match the init path: if no
rasdes_info was allocated, skip the RAS DES mutex teardown.
Thanks,
Shuvam
^ permalink raw reply
* Re: [PATCH v5 09/10] dt-bindings: firmware: add arm,ras-cper
From: Rob Herring @ 2026-06-12 14:49 UTC (permalink / raw)
To: Ahmed Tiba
Cc: Jonathan Cameron, will, xueshuai, saket.dumbre, mchehab, dave,
djbw, bp, tony.luck, guohanjun, lenb, skhan, vishal.l.verma,
rafael, corbet, ira.weiny, dave.jiang, krzk+dt, catalin.marinas,
alison.schofield, conor+dt, linux-arm-kernel, Michael.Zhao2,
linux-doc, linux-kernel, linux-cxl, Dmitry.Lamerov, devicetree,
linux-acpi, linux-edac, acpica-devel
In-Reply-To: <ceb19cb6-7083-44ad-a262-a8198f489257@arm.com>
On Thu, Jun 11, 2026 at 03:22:21PM +0100, Ahmed Tiba wrote:
> On 29/05/2026 17:44, Jonathan Cameron wrote:
> > On Fri, 29 May 2026 10:50:49 +0100
> > Ahmed Tiba<ahmed.tiba@arm.com> wrote:
> > > .../devicetree/bindings/firmware/arm,ras-cper.yaml | 54 ++++++++++++++++++++++
> > > MAINTAINERS | 5 ++
> > > 2 files changed, 59 insertions(+)
> > >
> > > diff --git a/Documentation/devicetree/bindings/firmware/arm,ras-cper.yaml b/Documentation/devicetree/bindings/firmware/arm,ras-cper.yaml
> > > new file mode 100644
> > > index 000000000000..3d4de096093f
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/firmware/arm,ras-cper.yaml
> > > @@ -0,0 +1,54 @@
> > > +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> > > +%YAML 1.2
> > > +---
> > > +$id:http://devicetree.org/schemas/firmware/arm,ras-cper.yaml#
> > > +$schema:http://devicetree.org/meta-schemas/core.yaml#
> > > +
> > > +title: Arm RAS CPER provider
> > > +
> > > +maintainers:
> > > + - Ahmed Tiba<ahmed.tiba@arm.com>
> > > +
> > > +description:
> > > + Arm Reliability, Availability and Serviceability (RAS) firmware can expose
> > > + a firmware-first CPER error source directly via DeviceTree. Firmware
> > > + provides the CPER Generic Error Status block and notifies the OS through
> > > + an interrupt.
> > I'd like some spec references in here if possible.
> I can add a reference to the UEFI CPER specification for the Generic
> Error Status record format.
>
> For the firmware-first DT description itself I do not have a more specific
> public reference to cite.
Is there a platform actually using this with DT (FVP doesn't really
count)?
Rob
^ permalink raw reply
* Re: [PATCH v3 00/11] kdump: reduce vmcore size and capture time
From: Rob Herring @ 2026-06-12 14:42 UTC (permalink / raw)
To: Wandun Chen
Cc: linux-arm-kernel, linux-kernel, loongarch, linux-riscv,
devicetree, kexec, iommu, zhaomeijing, catalin.marinas, will,
chenhuacai, kernel, pjw, palmer, aou, alex, saravanak, akpm, bhe,
rppt, pasha.tatashin, pratyush, ruirui.yang, m.szyprowski,
robin.murphy, quic_obabatun
In-Reply-To: <20260527032917.3385849-1-chenwandun1@gmail.com>
On Wed, May 27, 2026 at 11:29:06AM +0800, Wandun Chen wrote:
> From: Wandun Chen <chenwandun@lixiang.com>
>
> On SoCs that carve out large firmware-owned reserved memory (GPU
> firmware, DSP, modem, camera ISP, NPU, ...), kdump currently dumps
> those carveouts as part of system RAM even though their contents are
> firmware state that is not useful for kernel crash analysis.
>
> This series introduces an opt-in 'dumpable' flag [1] on struct
> reserved_mem and uses it to filter the elfcorehdr PT_LOAD ranges on
> DT-based architectures (arm64, riscv, loongarch). By default reserved
> regions are treated as non-dumpable; CMA regions are explicitly opted
> in because their pages are returned to the buddy allocator and may
> carry key crash-analysis data.
>
> The series is organized as follows:
> Patches 1-3: Pre-existing fixes and a small prep change.
> Patches 4-5: Restructure to allow appending /memreserve/ entries.
> Patches 6-7: Add a dumpable flag and append /memreserve/ entries.
> Patch 8: Add generic kdump helpers.
> Patches 9-11: Wire the helpers into arm64, riscv and loongarch kdump
> elfcorehdr preparation.
>
> v2 --> v3:
> 1. Fix out-of-bounds issue if device tree lacks /reserved-memory node.[2]
> 2. Fix UAF issue when alloc_reserved_mem_array() fails.
> 3. Add some prepare patches.
>
> v1 --> v2:
> 1. v1 added an opt-out DT property ('linux,no-dump'). Per Rob's
> feedback [1], v2 drop that property and exclude reserve memory
> by default.
> 2. Split some prepared patches from the original patches.
> 3. Address coding-style comments on patch 5 from Rob.
>
> [1] https://lore.kernel.org/lkml/20260506144542.GA2072596-robh@kernel.org/
> [2] https://sashiko.dev/#/patchset/20260520091844.592753-1-chenwandun%40lixiang.com?part=4
>
> Wandun Chen (11):
> of: reserved_mem: handle NULL name in of_reserved_mem_lookup()
> of: reserved_mem: zero total_reserved_mem_cnt if no valid
> /reserved-memory entry
I applied these 2 for 7.2.
Rob
^ permalink raw reply
* Re: [PATCH v3 05/11] of: reserved_mem: split alloc_reserved_mem_array() from fdt_scan_reserved_mem_late()
From: Rob Herring @ 2026-06-12 14:41 UTC (permalink / raw)
To: Wandun Chen
Cc: linux-arm-kernel, linux-kernel, loongarch, linux-riscv,
devicetree, kexec, iommu, zhaomeijing, catalin.marinas, will,
chenhuacai, kernel, pjw, palmer, aou, alex, saravanak, akpm, bhe,
rppt, pasha.tatashin, pratyush, ruirui.yang, m.szyprowski,
robin.murphy, quic_obabatun
In-Reply-To: <20260527032917.3385849-6-chenwandun1@gmail.com>
On Wed, May 27, 2026 at 11:29:11AM +0800, Wandun Chen wrote:
> From: Wandun Chen <chenwandun@lixiang.com>
>
> Prepare for storing /memreserve/ entries in the reserved_mem array.
> alloc_reserved_mem_array is skipped if the device tree lacks a
> /reserved-memory node, pointer 'reserved_mem' continues to reference
> the reserved_mem_array which lives in __initdata, storing
> /memreserve/ entries into reserved_mem_array would result in metadata
> loss, and an out-of-bounds memory access will occur if the device
> tree contains more than MAX_RESERVED_REGIONS /memreserve/ entries.
>
> So split alloc_reserved_mem_array() from fdt_scan_reserved_mem_late(),
> and call alloc_reserved_mem_array() whether or not there is a
> /reserved-memory node.
>
> No functional change.
> The actual /memreserve/ population is added in a follow-up patch.
>
> Signed-off-by: Wandun Chen <chenwandun@lixiang.com>
> ---
> drivers/of/fdt.c | 7 +++++--
> drivers/of/of_private.h | 1 +
> drivers/of/of_reserved_mem.c | 6 +-----
> 3 files changed, 7 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c
> index 82f7327c59ea..83a2a474831e 100644
> --- a/drivers/of/fdt.c
> +++ b/drivers/of/fdt.c
> @@ -1284,8 +1284,11 @@ void __init unflatten_device_tree(void)
> {
> void *fdt = initial_boot_params;
>
> - /* Save the statically-placed regions in the reserved_mem array */
> - fdt_scan_reserved_mem_late();
> + /* Attempt dynamic allocation of a new reserved_mem array */
> + if (fdt && alloc_reserved_mem_array()) {
> + /* Save the statically-placed regions in the reserved_mem array */
> + fdt_scan_reserved_mem_late();
Can we make this just:
alloc_reserved_mem_array();
fdt_scan_reserved_mem_late();
We already check !fdt in fdt_scan_reserved_mem_late().
Rob
^ permalink raw reply
* Re: [PATCH 2/2] PCI: meson: Add missing remove callback
From: Shuvam Pandey @ 2026-06-12 14:39 UTC (permalink / raw)
To: Bjorn Helgaas
Cc: Jingoo Han, Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Bjorn Helgaas, Yue Wang,
Neil Armstrong, Rob Herring, Kevin Hilman, Jerome Brunet,
Martin Blumenstingl, Fan Ni, Shradha Todi, Hanjie Lin, linux-pci,
linux-amlogic, linux-arm-kernel, linux-kernel
In-Reply-To: <20260611222653.GA527114@bhelgaas>
Hi Bjorn,
> What's the user-visible effect of this? Does it avoid an oops?
> Reduce power usage?
I don't have hardware with this controller, so I have not observed an
oops from this.
The concrete effect is that driver unbind/module unload gets the
matching teardown for the successful probe path: the DesignWare host
bridge is unregistered with dw_pcie_host_deinit(), and the PHY is
powered off with meson_pcie_power_off() before devres releases the
driver's resources.
So yes, it should reduce power usage after unbind. The host bridge
teardown is also the matching cleanup for dw_pcie_host_init(), but I
don't have hardware evidence of an oops on unbind.
> Of the 34 instances of .probe() in drivers/pci/controller/dwc/, on 12
> implement .remove(), so if this fixes a problem, I'm wondering whether
> other drivers have the same problem.
Yes, that may deserve a broader driver-by-driver audit. I only checked
Meson for this patch. Some DWC drivers implement .remove(), and several
others set .suppress_bind_attrs, which affects the manual sysfs
bind/unbind path. Before this patch, the Meson driver had neither, while
meson_pcie_probe() calls meson_pcie_power_on() and registers the host
bridge with dw_pcie_host_init().
Thanks,
Shuvam
^ permalink raw reply
* Re: [PATCH] kbuild: Use --force-group-allocation when linking modules
From: Peter Collingbourne @ 2026-06-12 14:38 UTC (permalink / raw)
To: Petr Pavlu
Cc: Nathan Chancellor, Nicolas Schier, Catalin Marinas, Will Deacon,
Sami Tolvanen, Daniel Gomez, Luis Chamberlain, Aaron Tomlin,
linux-kbuild, linux-arm-kernel, linux-modules, linux-kernel
In-Reply-To: <20260612133139.1919042-1-petr.pavlu@suse.com>
On Fri, Jun 12, 2026 at 6:31 AM Petr Pavlu <petr.pavlu@suse.com> wrote:
>
> Specific code, such as outlined KASAN checks, may be placed in
> COMDAT-deduplicated sections. When linking modules as relocatable files,
> the linker by default preserves such groups, potentially leaving multiple
> copies in the resulting modules and unnecessary group metadata.
>
> Use --force-group-allocation to have the linker resolve the COMDAT groups
> and place their members as regular sections. The option is available from
> ld.bfd 2.29 and ld.lld 19.1.0.
>
> Remove the workaround in arch/arm64/include/asm/module.lds.h that was added
> for the same problem but limited to CONFIG_KASAN_SW_TAGS and .text.hot.
> Note that this code currently has no effect anyway because all .text.hot
> sections are placed in the .text output section by scripts/module.lds.S,
> since commit 1ba9f8979426 ("vmlinux.lds: Unify TEXT_MAIN, DATA_MAIN, and
> related macros").
>
> Signed-off-by: Petr Pavlu <petr.pavlu@suse.com>
Reviewed-by: Peter Collingbourne <pcc@google.com>
> ---
> Makefile | 6 ++++++
> arch/arm64/include/asm/module.lds.h | 13 -------------
> 2 files changed, 6 insertions(+), 13 deletions(-)
>
> diff --git a/Makefile b/Makefile
> index e156e2696efe..1729af0690b3 100644
> --- a/Makefile
> +++ b/Makefile
> @@ -1189,6 +1189,12 @@ KBUILD_RUSTFLAGS += $(KRUSTFLAGS)
> KBUILD_LDFLAGS_MODULE += --build-id=sha1
> LDFLAGS_vmlinux += --build-id=sha1
>
> +# Specific code, such as outlined KASAN checks, may be placed in
> +# COMDAT-deduplicated sections. Use --force-group-allocation to resolve these
> +# groups when linking modules. The option is available from ld.bfd 2.29 and
> +# ld.lld 19.1.0.
> +KBUILD_LDFLAGS_MODULE += $(call ld-option,--force-group-allocation)
> +
> KBUILD_LDFLAGS += -z noexecstack
> ifeq ($(CONFIG_LD_IS_BFD),y)
> KBUILD_LDFLAGS += $(call ld-option,--no-warn-rwx-segments)
> diff --git a/arch/arm64/include/asm/module.lds.h b/arch/arm64/include/asm/module.lds.h
> index fb944b46846d..792a0820757a 100644
> --- a/arch/arm64/include/asm/module.lds.h
> +++ b/arch/arm64/include/asm/module.lds.h
> @@ -4,19 +4,6 @@ SECTIONS {
> .text.ftrace_trampoline 0 : { BYTE(0) }
> .init.text.ftrace_trampoline 0 : { BYTE(0) }
>
> -#ifdef CONFIG_KASAN_SW_TAGS
> - /*
> - * Outlined checks go into comdat-deduplicated sections named .text.hot.
> - * Because they are in comdats they are not combined by the linker and
> - * we otherwise end up with multiple sections with the same .text.hot
> - * name in the .ko file. The kernel module loader warns if it sees
> - * multiple sections with the same name so we use this sections
> - * directive to force them into a single section and silence the
> - * warning.
> - */
> - .text.hot : { *(.text.hot) }
> -#endif
> -
> #ifdef CONFIG_UNWIND_TABLES
> /*
> * Currently, we only use unwind info at module load time, so we can
>
> base-commit: 4549871118cf616eecdd2d939f78e3b9e1dddc48
> --
> 2.54.0
>
^ permalink raw reply
* [soc:soc/arm] BUILD SUCCESS cb2c3b5e113f26f74ef05926c6ace0f83cc3d0b3
From: kernel test robot @ 2026-06-12 14:30 UTC (permalink / raw)
To: Arnd Bergmann; +Cc: linux-arm-kernel, arm
tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/soc/soc.git soc/arm
branch HEAD: cb2c3b5e113f26f74ef05926c6ace0f83cc3d0b3 MAINTAINERS: Add Axiado reviewer and Maintainers
elapsed time: 761m
configs tested: 227
configs skipped: 2
The following configs have been built successfully.
More configs may be tested in the coming days.
tested configs:
alpha allnoconfig gcc-16.1.0
alpha allyesconfig gcc-16.1.0
alpha defconfig gcc-16.1.0
arc allmodconfig clang-23
arc allmodconfig gcc-16.1.0
arc allnoconfig gcc-16.1.0
arc allyesconfig clang-23
arc allyesconfig gcc-16.1.0
arc defconfig gcc-16.1.0
arc nsim_700_defconfig gcc-16.1.0
arc randconfig-001-20260612 gcc-13.4.0
arc randconfig-002-20260612 gcc-13.4.0
arm allnoconfig clang-23
arm allnoconfig gcc-16.1.0
arm allyesconfig clang-23
arm allyesconfig gcc-16.1.0
arm axm55xx_defconfig clang-23
arm defconfig gcc-16.1.0
arm randconfig-001-20260612 gcc-13.4.0
arm randconfig-002-20260612 gcc-13.4.0
arm randconfig-003-20260612 gcc-13.4.0
arm randconfig-004-20260612 gcc-13.4.0
arm64 allmodconfig clang-23
arm64 allnoconfig gcc-16.1.0
arm64 defconfig gcc-16.1.0
arm64 randconfig-001 gcc-13.4.0
arm64 randconfig-001-20260612 gcc-13.4.0
arm64 randconfig-002 gcc-13.4.0
arm64 randconfig-002-20260612 gcc-13.4.0
arm64 randconfig-003 gcc-13.4.0
arm64 randconfig-003-20260612 gcc-13.4.0
arm64 randconfig-004 gcc-13.4.0
arm64 randconfig-004-20260612 gcc-13.4.0
csky allmodconfig gcc-16.1.0
csky allnoconfig gcc-16.1.0
csky defconfig gcc-16.1.0
csky randconfig-001 gcc-13.4.0
csky randconfig-001-20260612 gcc-13.4.0
csky randconfig-002 gcc-13.4.0
csky randconfig-002-20260612 gcc-13.4.0
hexagon allmodconfig clang-23
hexagon allmodconfig gcc-16.1.0
hexagon allnoconfig clang-23
hexagon allnoconfig gcc-16.1.0
hexagon defconfig gcc-16.1.0
hexagon randconfig-001 gcc-11.5.0
hexagon randconfig-001-20260612 gcc-11.5.0
hexagon randconfig-002 gcc-11.5.0
hexagon randconfig-002-20260612 gcc-11.5.0
i386 allmodconfig clang-22
i386 allnoconfig gcc-14
i386 allnoconfig gcc-16.1.0
i386 allyesconfig clang-22
i386 buildonly-randconfig-001-20260612 gcc-14
i386 buildonly-randconfig-002-20260612 gcc-14
i386 buildonly-randconfig-003-20260612 gcc-14
i386 buildonly-randconfig-004-20260612 gcc-14
i386 buildonly-randconfig-005-20260612 gcc-14
i386 buildonly-randconfig-006-20260612 gcc-14
i386 defconfig gcc-16.1.0
i386 randconfig-001-20260612 clang-22
i386 randconfig-002-20260612 clang-22
i386 randconfig-003-20260612 clang-22
i386 randconfig-004-20260612 clang-22
i386 randconfig-005-20260612 clang-22
i386 randconfig-006-20260612 clang-22
i386 randconfig-007-20260612 clang-22
i386 randconfig-011-20260612 clang-22
i386 randconfig-012-20260612 clang-22
i386 randconfig-013-20260612 clang-22
i386 randconfig-014-20260612 clang-22
i386 randconfig-015-20260612 clang-22
i386 randconfig-016-20260612 clang-22
i386 randconfig-017-20260612 clang-22
loongarch allmodconfig clang-19
loongarch allmodconfig clang-23
loongarch allnoconfig clang-20
loongarch allnoconfig gcc-16.1.0
loongarch defconfig clang-23
loongarch randconfig-001 gcc-11.5.0
loongarch randconfig-001-20260612 gcc-11.5.0
loongarch randconfig-002 gcc-11.5.0
loongarch randconfig-002-20260612 gcc-11.5.0
m68k allmodconfig gcc-16.1.0
m68k allnoconfig gcc-16.1.0
m68k allyesconfig clang-23
m68k allyesconfig gcc-16.1.0
m68k defconfig clang-23
microblaze allnoconfig gcc-16.1.0
microblaze allyesconfig gcc-16.1.0
microblaze defconfig clang-23
mips allmodconfig gcc-16.1.0
mips allnoconfig gcc-16.1.0
mips allyesconfig gcc-16.1.0
nios2 allmodconfig clang-20
nios2 allmodconfig gcc-11.5.0
nios2 allnoconfig clang-23
nios2 allnoconfig gcc-11.5.0
nios2 defconfig clang-23
nios2 randconfig-001 gcc-11.5.0
nios2 randconfig-001-20260612 gcc-11.5.0
nios2 randconfig-002 gcc-11.5.0
nios2 randconfig-002-20260612 gcc-11.5.0
openrisc allmodconfig clang-20
openrisc allmodconfig gcc-16.1.0
openrisc allnoconfig clang-23
openrisc allnoconfig gcc-16.1.0
openrisc defconfig gcc-16.1.0
parisc allmodconfig gcc-16.1.0
parisc allnoconfig clang-23
parisc allnoconfig gcc-16.1.0
parisc allyesconfig clang-23
parisc allyesconfig gcc-16.1.0
parisc defconfig gcc-16.1.0
parisc64 defconfig clang-23
powerpc allmodconfig gcc-16.1.0
powerpc allnoconfig clang-23
powerpc allnoconfig gcc-16.1.0
powerpc ppc64e_defconfig gcc-16.1.0
riscv allmodconfig clang-23
riscv allnoconfig clang-23
riscv allnoconfig gcc-16.1.0
riscv allyesconfig clang-23
riscv defconfig clang-23
riscv defconfig gcc-16.1.0
riscv randconfig-001-20260612 gcc-11.5.0
riscv randconfig-002-20260612 gcc-11.5.0
s390 allmodconfig clang-23
s390 allnoconfig clang-23
s390 allyesconfig gcc-16.1.0
s390 defconfig clang-18
s390 defconfig gcc-16.1.0
s390 randconfig-001-20260612 gcc-11.5.0
s390 randconfig-002-20260612 gcc-11.5.0
sh allmodconfig gcc-16.1.0
sh allnoconfig clang-23
sh allnoconfig gcc-16.1.0
sh allyesconfig clang-23
sh allyesconfig gcc-16.1.0
sh defconfig gcc-14
sh defconfig gcc-16.1.0
sh randconfig-001-20260612 gcc-11.5.0
sh randconfig-002-20260612 gcc-11.5.0
sparc allnoconfig clang-23
sparc allnoconfig gcc-16.1.0
sparc defconfig gcc-16.1.0
sparc randconfig-001 gcc-8.5.0
sparc randconfig-001-20260612 gcc-8.5.0
sparc randconfig-002 gcc-8.5.0
sparc randconfig-002-20260612 gcc-8.5.0
sparc64 allmodconfig clang-20
sparc64 defconfig clang-23
sparc64 defconfig gcc-14
sparc64 randconfig-001 gcc-8.5.0
sparc64 randconfig-001-20260612 gcc-8.5.0
sparc64 randconfig-002 gcc-8.5.0
sparc64 randconfig-002-20260612 gcc-8.5.0
um allmodconfig clang-23
um allnoconfig clang-16
um allnoconfig clang-23
um allyesconfig gcc-14
um allyesconfig gcc-16.1.0
um defconfig clang-23
um defconfig gcc-14
um i386_defconfig gcc-14
um randconfig-001 gcc-8.5.0
um randconfig-001-20260612 gcc-8.5.0
um randconfig-002 gcc-8.5.0
um randconfig-002-20260612 gcc-8.5.0
um x86_64_defconfig clang-23
um x86_64_defconfig gcc-14
x86_64 allmodconfig clang-22
x86_64 allnoconfig clang-22
x86_64 allnoconfig clang-23
x86_64 allyesconfig clang-22
x86_64 buildonly-randconfig-001 gcc-14
x86_64 buildonly-randconfig-001-20260612 gcc-14
x86_64 buildonly-randconfig-002 gcc-14
x86_64 buildonly-randconfig-002-20260612 gcc-14
x86_64 buildonly-randconfig-003 gcc-14
x86_64 buildonly-randconfig-003-20260612 gcc-14
x86_64 buildonly-randconfig-004 gcc-14
x86_64 buildonly-randconfig-004-20260612 gcc-14
x86_64 buildonly-randconfig-005 gcc-14
x86_64 buildonly-randconfig-005-20260612 gcc-14
x86_64 buildonly-randconfig-006 gcc-14
x86_64 buildonly-randconfig-006-20260612 gcc-14
x86_64 defconfig gcc-14
x86_64 kexec clang-22
x86_64 randconfig-001-20260612 clang-22
x86_64 randconfig-002-20260612 clang-22
x86_64 randconfig-003-20260612 clang-22
x86_64 randconfig-004-20260612 clang-22
x86_64 randconfig-005-20260612 clang-22
x86_64 randconfig-006-20260612 clang-22
x86_64 randconfig-011 clang-22
x86_64 randconfig-011-20260612 clang-22
x86_64 randconfig-012 clang-22
x86_64 randconfig-012-20260612 clang-22
x86_64 randconfig-013 clang-22
x86_64 randconfig-013-20260612 clang-22
x86_64 randconfig-014 clang-22
x86_64 randconfig-014-20260612 clang-22
x86_64 randconfig-015 clang-22
x86_64 randconfig-015-20260612 clang-22
x86_64 randconfig-016 clang-22
x86_64 randconfig-016-20260612 clang-22
x86_64 randconfig-071-20260612 gcc-14
x86_64 randconfig-072-20260612 gcc-14
x86_64 randconfig-073-20260612 gcc-14
x86_64 randconfig-074-20260612 gcc-14
x86_64 randconfig-075-20260612 gcc-14
x86_64 randconfig-076-20260612 gcc-14
x86_64 rhel-9.4 clang-22
x86_64 rhel-9.4-bpf gcc-14
x86_64 rhel-9.4-func clang-22
x86_64 rhel-9.4-kselftests clang-22
x86_64 rhel-9.4-kunit gcc-14
x86_64 rhel-9.4-ltp gcc-14
x86_64 rhel-9.4-rust clang-22
xtensa allnoconfig clang-23
xtensa allnoconfig gcc-16.1.0
xtensa allyesconfig clang-20
xtensa randconfig-001 gcc-8.5.0
xtensa randconfig-001-20260612 gcc-8.5.0
xtensa randconfig-002 gcc-8.5.0
xtensa randconfig-002-20260612 gcc-8.5.0
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply
* Re: [PATCH] net: macb: add TX stall timeout callback to recover from lost TSTART write
From: Théo Lebrun @ 2026-06-12 14:30 UTC (permalink / raw)
To: Andrea della Porta, Nicolai Buchwitz
Cc: netdev, Nicolas Ferre, Claudiu Beznea, Andrew Lunn,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-kernel, linux-arm-kernel, linux-rpi-kernel, Lukasz Raczylo,
Steffen Jaeckel
In-Reply-To: <DJ753ZA99KY1.1EH3EC5YWVB3@bootlin.com>
On Fri Jun 12, 2026 at 4:28 PM CEST, Théo Lebrun wrote:
> On Fri Jun 12, 2026 at 3:03 PM CEST, Andrea della Porta wrote:
>> On 14:53 Fri 12 Jun , Nicolai Buchwitz wrote:
>>> On 12.6.2026 14:51, Andrea della Porta wrote:
>>> > > The commit message describes it as RP1 specific, but it gets applied
>>> > > to all
>>> > > other variants?
>>> >
>>> > I've seen this issue happening only on RaspberryPi 5, but AFAIK it
>>> > could affect also other MACB blocks connected through PCIe, so it
>>> > may be widespread (even though it should have probably already been
>>> > noticed in the past). In the orginal driver there's no timeout callback
>>> > defined and this is much like pretgending the issue causing the timeout
>>> > to happen to go away without doing anything (whatever the cause ot the
>>> > specific hw are). So in my opinion we can just extend that to all MACB.
>>> > Or maybe we should execute the restart conditionally on
>>> > .compatible = "raspberrypi,rp1-gem"?
>>>
>>> I just observed the issue once, but other people reported it to be happen
>>> more
>>> frequently. If we can narrow down a reproducer, it would be good to test on
>>> other
>>> blocks too (like EyeQ at Théo's).|
>>>
>>> So maybe you can imagine a good repro for this issue?
>>
>> Sure, it's happening quite often during bulk dataflow, at least
>> on my RPi5.
>> It can be reproduced with the following, issued from the DUT:
>>
>> iperf -c <SERVER_IP> -P 10 -t 3000 -w 4M -i 1
>>
>> plus, of course, the related command on server side: iperf -s.
>>
>> It usually happens a couple of times withing a few hours.
>
> Thanks for the reproducer command; I'll run it next week.
> I'd be surprised if it reproduced on hardware that isn't the Pi 5.
Sorry for the two-step message. I forgot to mention I'd prefer to have
the timeout callback on all platforms: don't reserve it for Pi 5.
Thanks,
--
Théo Lebrun, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply
* Re: [PATCH v5 2/3] pwm: rp1: Add RP1 PWM controller driver
From: Sean Young @ 2026-06-12 14:29 UTC (permalink / raw)
To: Andrea della Porta
Cc: Uwe Kleine-König, linux-pwm, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Florian Fainelli,
Broadcom internal kernel review list, devicetree,
linux-rpi-kernel, linux-arm-kernel, linux-kernel, Naushir Patuck,
Stanimir Varbanov, mbrugger
In-Reply-To: <f8dd46a553351adaf9d29fbba9f98e803b672fe7.1780670224.git.andrea.porta@suse.com>
On Fri, Jun 12, 2026 at 04:01:27PM +0200, Andrea della Porta wrote:
> From: Naushir Patuck <naush@raspberrypi.com>
>
> The Raspberry Pi RP1 southbridge features an embedded PWM
> controller with 4 output channels, alongside an RPM interface
> to read the fan speed on the Raspberry Pi 5.
>
> Add the supporting driver.
>
> Signed-off-by: Naushir Patuck <naush@raspberrypi.com>
> Co-developed-by: Stanimir Varbanov <svarbanov@suse.de>
> Signed-off-by: Stanimir Varbanov <svarbanov@suse.de>
> Signed-off-by: Andrea della Porta <andrea.porta@suse.com>
> ---
> drivers/pwm/Kconfig | 9 +
> drivers/pwm/Makefile | 1 +
> drivers/pwm/pwm-rp1.c | 424 ++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 434 insertions(+)
> create mode 100644 drivers/pwm/pwm-rp1.c
>
> diff --git a/drivers/pwm/Kconfig b/drivers/pwm/Kconfig
> index 6f3147518376a..c3ddc0eb4774f 100644
> --- a/drivers/pwm/Kconfig
> +++ b/drivers/pwm/Kconfig
> @@ -625,6 +625,15 @@ config PWM_ROCKCHIP
> Generic PWM framework driver for the PWM controller found on
> Rockchip SoCs.
>
> +config PWM_RASPBERRYPI_RP1
> + tristate "RP1 PWM support"
> + depends on MISC_RP1 || COMPILE_TEST
> + depends on HAS_IOMEM
> + select REGMAP_MMIO
> + select MFD_SYSCON
> + help
> + PWM framework driver for Raspberry Pi RP1 controller.
> +
> config PWM_SAMSUNG
> tristate "Samsung PWM support"
> depends on PLAT_SAMSUNG || ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
> diff --git a/drivers/pwm/Makefile b/drivers/pwm/Makefile
> index 0dc0d2b69025d..59f29f60f9123 100644
> --- a/drivers/pwm/Makefile
> +++ b/drivers/pwm/Makefile
> @@ -56,6 +56,7 @@ obj-$(CONFIG_PWM_RENESAS_RZG2L_GPT) += pwm-rzg2l-gpt.o
> obj-$(CONFIG_PWM_RENESAS_RZ_MTU3) += pwm-rz-mtu3.o
> obj-$(CONFIG_PWM_RENESAS_TPU) += pwm-renesas-tpu.o
> obj-$(CONFIG_PWM_ROCKCHIP) += pwm-rockchip.o
> +obj-$(CONFIG_PWM_RASPBERRYPI_RP1) += pwm-rp1.o
> obj-$(CONFIG_PWM_SAMSUNG) += pwm-samsung.o
> obj-$(CONFIG_PWM_SIFIVE) += pwm-sifive.o
> obj-$(CONFIG_PWM_SL28CPLD) += pwm-sl28cpld.o
> diff --git a/drivers/pwm/pwm-rp1.c b/drivers/pwm/pwm-rp1.c
> new file mode 100644
> index 0000000000000..6382a81a5ea0f
> --- /dev/null
> +++ b/drivers/pwm/pwm-rp1.c
> @@ -0,0 +1,424 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * pwm-rp1.c
> + *
> + * Raspberry Pi RP1 PWM.
> + *
> + * Copyright © 2026 Raspberry Pi Ltd.
> + *
> + * Author: Naushir Patuck (naush@raspberrypi.com)
> + *
> + * Based on the pwm-bcm2835 driver by:
> + * Bart Tanghe <bart.tanghe@thomasmore.be>
> + *
> + * Datasheet: https://pip-assets.raspberrypi.com/categories/892-raspberry-pi-5/documents/RP-008370-DS-1-rp1-peripherals.pdf?disposition=inline
> + *
> + * Limitations:
> + * - Channels can be enabled/disabled through a global update flag, while the
> + * period and duty per-channel registers are independently updatable, and
> + * they are latched on the end of (specific channel) period strobe.
> + * This means that period and duty changes might result in glitches if the
> + * period/duty is changed exactly during an end of period strobe.
> + * - Since the duty/period registers are freely updatable (do not depend on
> + * the global update flag), setting one of them close to the period end and
> + * the other right afterwards results in a mixed output for that cycle because
> + * the write ops are not atomic.
> + * - The global update flag prevents mis-sampling of multi-bit bus signals in
> + * the PWM clock domain. This ensures that all PWM channel settings update
> + * on the same PWM clock cycle. Channels start in sync only if they share the
> + * same period.
> + * - If both duty and period are set to 0, the output is a constant low signal
> + * if polarity is normal or a constant high signal if polarity is inversed.
> + * - When disabled the output is driven to 0 if polarity is normal, or to 1
> + * if polarity is inversed.
> + * - Disabling the PWM stops the output immediately, without waiting for current
> + * period to complete first.
> + * - Channels are phase-capable, but on RPi5, the firmware can use a channel
> + * phase register to report the RPM of the fan connected to that PWM
> + * channel. As a result, phase control will be ignored for now.
> + */
> +
> +#include <linux/bitops.h>
> +#include <linux/clk.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pwm.h>
> +#include <linux/regmap.h>
> +#include <linux/mfd/syscon.h>
> +#include <linux/units.h>
> +
> +#define RP1_PWM_GLB_CTRL 0x000
> +#define RP1_PWM_GLB_CTRL_CHANNEL_ENABLE(chan) BIT(chan)
> +#define RP1_PWM_GLB_CTRL_SET_UPDATE BIT(31)
> +
> +#define RP1_PWM_CHAN_CTRL(chan) (0x014 + ((chan) * 0x10))
> +#define RP1_PWM_CHAN_CTRL_POLARITY BIT(3)
> +#define RP1_PWM_CHAN_CTRL_FIFO_POP_MASK BIT(8)
> +#define RP1_PWM_CHAN_CTRL_MODE GENMASK(2, 0)
> +enum rp1_pwm_ctrl_mode {
> + RP1_PWM_CHAN_CTRL_MODE_ZERO,
> + RP1_PWM_CHAN_CTRL_MODE_TE_MS,
> + RP1_PWM_CHAN_CTRL_MODE_PC_MS,
> + RP1_PWM_CHAN_CTRL_MODE_PD_ENC,
> + RP1_PWM_CHAN_CTRL_MODE_MSB_SER,
> + RP1_PWM_CHAN_CTRL_MODE_PPM,
> + RP1_PWM_CHAN_CTRL_MODE_LE_MS,
> + RP1_PWM_CHAN_CTRL_MODE_LSB_SER,
> +};
> +
> +#define RP1_PWM_CHAN_CTRL_DEFAULT (RP1_PWM_CHAN_CTRL_FIFO_POP_MASK + \
> + FIELD_PREP(RP1_PWM_CHAN_CTRL_MODE, \
> + RP1_PWM_CHAN_CTRL_MODE_TE_MS))
> +
> +#define RP1_PWM_RANGE(chan) (0x018 + ((chan) * 0x10))
> +#define RP1_PWM_PHASE(chan) (0x01C + ((chan) * 0x10))
> +#define RP1_PWM_DUTY(chan) (0x020 + ((chan) * 0x10))
> +
> +#define RP1_PWM_NUM_PWMS 4
> +
> +struct rp1_pwm {
> + struct regmap *regmap;
> + struct clk *clk;
> + unsigned long clk_rate;
> + bool clk_enabled;
I don't understand the point of the clk_enabled field. If the probe
function succeeds, then clk_enabled = true. It is set to false during
suspend, but after suspend the only thing which can follow is resume,
I think. In resume, we set it in to true again unconditionally. So
it is always true, no?
> +};
> +
> +struct rp1_pwm_waveform {
> + u32 period_ticks;
> + u32 duty_ticks;
> + bool enabled;
> + bool inverted_polarity;
> +};
> +
> +static const struct regmap_config rp1_pwm_regmap_config = {
> + .reg_bits = 32,
> + .val_bits = 32,
> + .reg_stride = 4,
> + .max_register = 0x60,
> +};
> +
> +static void rp1_pwm_apply_config(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> + struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
> + u32 value;
> +
> + /* update the changed registers on the next strobe to avoid glitches */
> + regmap_read(rp1->regmap, RP1_PWM_GLB_CTRL, &value);
> + value |= RP1_PWM_GLB_CTRL_SET_UPDATE;
> + regmap_write(rp1->regmap, RP1_PWM_GLB_CTRL, value);
> +}
> +
> +static int rp1_pwm_request(struct pwm_chip *chip, struct pwm_device *pwm)
> +{
> + struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
> +
> + /* init channel to reset defaults, preserving the polarity bit */
> + regmap_update_bits(rp1->regmap, RP1_PWM_CHAN_CTRL(pwm->hwpwm),
> + ~(u32)RP1_PWM_CHAN_CTRL_POLARITY, RP1_PWM_CHAN_CTRL_DEFAULT);
> + return 0;
> +}
> +
> +static int rp1_pwm_round_waveform_tohw(struct pwm_chip *chip,
> + struct pwm_device *pwm,
> + const struct pwm_waveform *wf,
> + void *_wfhw)
> +{
> + struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
> + u64 period_ticks, duty_ticks, offset_ticks;
> + struct rp1_pwm_waveform *wfhw = _wfhw;
> + u64 clk_rate = rp1->clk_rate;
> + int ret = 0;
> +
> + if (!wf->period_length_ns) {
> + wfhw->enabled = false;
> + return 0;
> + }
> +
> + period_ticks = mul_u64_u64_div_u64(wf->period_length_ns, clk_rate, NSEC_PER_SEC);
> +
> + /*
> + * The period is limited to U32_MAX, and it will be decremented by one later
> + * to allow 100% duty cycle.
> + */
> + if (period_ticks > U32_MAX) {
> + period_ticks = U32_MAX;
> + } else if (period_ticks < 2) {
> + period_ticks = 2;
> + ret = 1;
> + }
period_ticks = clamp(period_ticks, 2, U32_MAX);
Although that misses out the `ret = 1;` which I am not sure about anyway.
> +
> + duty_ticks = mul_u64_u64_div_u64(wf->duty_length_ns, clk_rate, NSEC_PER_SEC);
> + duty_ticks = min(duty_ticks, period_ticks);
> + offset_ticks = mul_u64_u64_div_u64(wf->duty_offset_ns, clk_rate, NSEC_PER_SEC);
> + if (offset_ticks >= period_ticks)
> + offset_ticks %= period_ticks;
> + if (duty_ticks && offset_ticks &&
> + duty_ticks + offset_ticks >= period_ticks) {
> + wfhw->duty_ticks = period_ticks - duty_ticks;
> + wfhw->inverted_polarity = true;
> + } else {
> + wfhw->duty_ticks = duty_ticks;
> + wfhw->inverted_polarity = false;
> + }
> + /* Account for the extra tick at the end of the period */
> + wfhw->period_ticks = period_ticks - 1;
> +
> + wfhw->enabled = true;
> +
> + return ret;
> +}
> +
> +static int rp1_pwm_round_waveform_fromhw(struct pwm_chip *chip,
> + struct pwm_device *pwm,
> + const void *_wfhw,
> + struct pwm_waveform *wf)
> +{
> + struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
> + const struct rp1_pwm_waveform *wfhw = _wfhw;
> + u64 clk_rate = rp1->clk_rate;
> + u64 ticks;
> +
> + *wf = (struct pwm_waveform){ };
> +
> + if (!wfhw->enabled)
> + return 0;
> +
> + wf->period_length_ns = DIV_ROUND_UP_ULL(((u64)wfhw->period_ticks + 1) * NSEC_PER_SEC,
> + clk_rate);
> +
> + if (!wfhw->inverted_polarity) {
> + wf->duty_length_ns = DIV_ROUND_UP_ULL((u64)wfhw->duty_ticks * NSEC_PER_SEC,
> + clk_rate);
> + } else {
> + ticks = (u64)wfhw->period_ticks + 1 - wfhw->duty_ticks;
> + wf->duty_length_ns = DIV_ROUND_UP_ULL(ticks * NSEC_PER_SEC, clk_rate);
> + wf->duty_offset_ns = wf->period_length_ns - wf->duty_length_ns;
> + }
> +
> + return 0;
> +}
> +
> +static int rp1_pwm_write_waveform(struct pwm_chip *chip,
> + struct pwm_device *pwm,
> + const void *_wfhw)
> +{
> + struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
> + const struct rp1_pwm_waveform *wfhw = _wfhw;
> + u32 value, ctrl;
> +
> + /* set polarity */
> + regmap_read(rp1->regmap, RP1_PWM_CHAN_CTRL(pwm->hwpwm), &value);
> + if (!wfhw->inverted_polarity)
> + value &= ~RP1_PWM_CHAN_CTRL_POLARITY;
> + else
> + value |= RP1_PWM_CHAN_CTRL_POLARITY;
> + regmap_write(rp1->regmap, RP1_PWM_CHAN_CTRL(pwm->hwpwm), value);
> +
> + /* early exit if disabled */
> + regmap_read(rp1->regmap, RP1_PWM_GLB_CTRL, &ctrl);
> + if (!wfhw->enabled) {
> + ctrl &= ~RP1_PWM_GLB_CTRL_CHANNEL_ENABLE(pwm->hwpwm);
> + goto exit_disable;
> + }
> +
> + /* set period and duty cycle */
> + regmap_write(rp1->regmap,
> + RP1_PWM_RANGE(pwm->hwpwm), wfhw->period_ticks);
> + regmap_write(rp1->regmap,
> + RP1_PWM_DUTY(pwm->hwpwm), wfhw->duty_ticks);
> +
> + /* enable the channel */
> + ctrl |= RP1_PWM_GLB_CTRL_CHANNEL_ENABLE(pwm->hwpwm);
> +exit_disable:
> + regmap_write(rp1->regmap, RP1_PWM_GLB_CTRL, ctrl);
> +
> + rp1_pwm_apply_config(chip, pwm);
> +
> + return 0;
> +}
> +
> +static int rp1_pwm_read_waveform(struct pwm_chip *chip,
> + struct pwm_device *pwm,
> + void *_wfhw)
> +{
> + struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
> + struct rp1_pwm_waveform *wfhw = _wfhw;
> + u32 value;
> +
> + regmap_read(rp1->regmap, RP1_PWM_GLB_CTRL, &value);
> + wfhw->enabled = !!(value & RP1_PWM_GLB_CTRL_CHANNEL_ENABLE(pwm->hwpwm));
> +
> + regmap_read(rp1->regmap, RP1_PWM_CHAN_CTRL(pwm->hwpwm), &value);
> + wfhw->inverted_polarity = !!(value & RP1_PWM_CHAN_CTRL_POLARITY);
> +
> + if (wfhw->enabled) {
> + regmap_read(rp1->regmap, RP1_PWM_RANGE(pwm->hwpwm), &wfhw->period_ticks);
> + regmap_read(rp1->regmap, RP1_PWM_DUTY(pwm->hwpwm), &wfhw->duty_ticks);
> + } else {
> + wfhw->period_ticks = 0;
> + wfhw->duty_ticks = 0;
> + }
> +
> + return 0;
> +}
> +
> +static const struct pwm_ops rp1_pwm_ops = {
> + .sizeof_wfhw = sizeof(struct rp1_pwm_waveform),
> + .request = rp1_pwm_request,
> + .round_waveform_tohw = rp1_pwm_round_waveform_tohw,
> + .round_waveform_fromhw = rp1_pwm_round_waveform_fromhw,
> + .read_waveform = rp1_pwm_read_waveform,
> + .write_waveform = rp1_pwm_write_waveform,
> +};
> +
> +static int rp1_pwm_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct device_node *np = dev->of_node;
> + unsigned long clk_rate;
> + struct pwm_chip *chip;
> + void __iomem *base;
> + struct rp1_pwm *rp1;
> + int ret;
> +
> + chip = devm_pwmchip_alloc(dev, RP1_PWM_NUM_PWMS, sizeof(*rp1));
> + if (IS_ERR(chip))
> + return PTR_ERR(chip);
> +
> + rp1 = pwmchip_get_drvdata(chip);
> +
> + base = devm_platform_ioremap_resource(pdev, 0);
> + if (IS_ERR(base))
> + return PTR_ERR(base);
> +
> + rp1->regmap = devm_regmap_init_mmio(dev, base, &rp1_pwm_regmap_config);
> + if (IS_ERR(rp1->regmap))
> + return dev_err_probe(dev, PTR_ERR(rp1->regmap), "Cannot initialize regmap\n");
> +
> + rp1->clk = devm_clk_get(dev, NULL);
> + if (IS_ERR(rp1->clk))
> + return dev_err_probe(dev, PTR_ERR(rp1->clk), "Clock not found\n");
> +
> + ret = clk_prepare_enable(rp1->clk);
> + if (ret)
> + return dev_err_probe(dev, ret, "Failed to enable clock\n");
> + rp1->clk_enabled = true;
> +
> + ret = devm_clk_rate_exclusive_get(dev, rp1->clk);
> + if (ret) {
> + dev_err_probe(dev, ret, "Failed to get exclusive rate\n");
> + goto err_disable_clk;
> + }
> +
> + clk_rate = clk_get_rate(rp1->clk);
> + if (!clk_rate) {
> + ret = dev_err_probe(dev, -EINVAL, "Failed to get clock rate\n");
> + goto err_disable_clk;
> + }
> + /*
> + * To prevent u64 overflow in period calculations:
> + * mul_u64_u64_div_u64(period_ns, clk_rate, NSEC_PER_SEC)
> + * If clk_rate > 1 GHz, the result can overflow.
> + */
> + if (clk_rate > HZ_PER_GHZ) {
> + ret = dev_err_probe(dev, -EINVAL, "Clock rate > 1 GHz is not supported\n");
> + goto err_disable_clk;
> + }
> + rp1->clk_rate = clk_rate;
> +
> + chip->ops = &rp1_pwm_ops;
Can we add the following please:
chip->atomic = true;
This means that the pwm can be controlled from atomic context (not process
context) using pwm_apply_atomic(). This is very helpful for the pwm-ir-tx
driver, which produces a much more faithful IR signal in atomic context.
Using pwm for infrared tx is much nicer than using gpio which bit bangs
the IR signal and holds the CPU with interrupts disabled for upto one second.
As far as I can see there is no sleeping code in these code paths, so we
should be fine.
Thanks,
Sean
> +
> + platform_set_drvdata(pdev, chip);
> +
> + ret = pwmchip_add(chip);
> + if (ret) {
> + dev_err_probe(dev, ret, "Failed to register PWM chip\n");
> + goto err_disable_clk;
> + }
> +
> + ret = of_syscon_register_regmap(np, rp1->regmap);
> + if (ret) {
> + dev_err_probe(dev, ret, "Failed to register syscon\n");
> + goto err_remove_chip;
> + }
> +
> + return 0;
> +
> +err_remove_chip:
> + pwmchip_remove(chip);
> +err_disable_clk:
> + clk_disable_unprepare(rp1->clk);
> +
> + return ret;
> +}
> +
> +static void rp1_pwm_remove(struct platform_device *pdev)
> +{
> + struct pwm_chip *chip = platform_get_drvdata(pdev);
> + struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
> +
> + pwmchip_remove(chip);
> +
> + if (rp1->clk_enabled) {
> + clk_disable_unprepare(rp1->clk);
> + rp1->clk_enabled = false;
> + }
> +}
> +
> +static int rp1_pwm_suspend(struct device *dev)
> +{
> + struct pwm_chip *chip = dev_get_drvdata(dev);
> + struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
> +
> + if (rp1->clk_enabled) {
> + clk_disable_unprepare(rp1->clk);
> + rp1->clk_enabled = false;
> + }
> +
> + return 0;
> +}
> +
> +static int rp1_pwm_resume(struct device *dev)
> +{
> + struct pwm_chip *chip = dev_get_drvdata(dev);
> + struct rp1_pwm *rp1 = pwmchip_get_drvdata(chip);
> + int ret;
> +
> + ret = clk_prepare_enable(rp1->clk);
> + if (ret) {
> + dev_err(dev, "Failed to enable clock on resume: %pe\n", ERR_PTR(ret));
> + return ret;
> + }
> +
> + rp1->clk_enabled = true;
> +
> + return 0;
> +}
> +
> +static DEFINE_SIMPLE_DEV_PM_OPS(rp1_pwm_pm_ops, rp1_pwm_suspend, rp1_pwm_resume);
> +
> +static const struct of_device_id rp1_pwm_of_match[] = {
> + { .compatible = "raspberrypi,rp1-pwm" },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, rp1_pwm_of_match);
> +
> +static struct platform_driver rp1_pwm_driver = {
> + .probe = rp1_pwm_probe,
> + .remove = rp1_pwm_remove,
> + .driver = {
> + .name = "rp1-pwm",
> + .of_match_table = rp1_pwm_of_match,
> + .pm = pm_ptr(&rp1_pwm_pm_ops),
> + .suppress_bind_attrs = true,
> + },
> +};
> +builtin_platform_driver(rp1_pwm_driver);
> +
> +MODULE_DESCRIPTION("RP1 PWM driver");
> +MODULE_AUTHOR("Naushir Patuck <naush@raspberrypi.com>");
> +MODULE_AUTHOR("Andrea della Porta <andrea.porta@suse.com>");
> +MODULE_LICENSE("GPL");
> --
> 2.35.3
>
^ permalink raw reply
* Re: [PATCH] net: macb: add TX stall timeout callback to recover from lost TSTART write
From: Théo Lebrun @ 2026-06-12 14:28 UTC (permalink / raw)
To: Andrea della Porta, Nicolai Buchwitz
Cc: netdev, Nicolas Ferre, Claudiu Beznea, Andrew Lunn,
David S . Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni,
linux-kernel, linux-arm-kernel, linux-rpi-kernel, Lukasz Raczylo,
Steffen Jaeckel
In-Reply-To: <aiwDsd-1IzeBzcaD@apocalypse>
On Fri Jun 12, 2026 at 3:03 PM CEST, Andrea della Porta wrote:
> On 14:53 Fri 12 Jun , Nicolai Buchwitz wrote:
>> On 12.6.2026 14:51, Andrea della Porta wrote:
>> > > The commit message describes it as RP1 specific, but it gets applied
>> > > to all
>> > > other variants?
>> >
>> > I've seen this issue happening only on RaspberryPi 5, but AFAIK it
>> > could affect also other MACB blocks connected through PCIe, so it
>> > may be widespread (even though it should have probably already been
>> > noticed in the past). In the orginal driver there's no timeout callback
>> > defined and this is much like pretgending the issue causing the timeout
>> > to happen to go away without doing anything (whatever the cause ot the
>> > specific hw are). So in my opinion we can just extend that to all MACB.
>> > Or maybe we should execute the restart conditionally on
>> > .compatible = "raspberrypi,rp1-gem"?
>>
>> I just observed the issue once, but other people reported it to be happen
>> more
>> frequently. If we can narrow down a reproducer, it would be good to test on
>> other
>> blocks too (like EyeQ at Théo's).|
>>
>> So maybe you can imagine a good repro for this issue?
>
> Sure, it's happening quite often during bulk dataflow, at least
> on my RPi5.
> It can be reproduced with the following, issued from the DUT:
>
> iperf -c <SERVER_IP> -P 10 -t 3000 -w 4M -i 1
>
> plus, of course, the related command on server side: iperf -s.
>
> It usually happens a couple of times withing a few hours.
Thanks for the reproducer command; I'll run it next week.
I'd be surprised if it reproduced on hardware that isn't the Pi 5.
Thanks,
--
Théo Lebrun, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
^ permalink raw reply
* [PATCH v1 4/4] KVM: arm64: Add hyp_printk event to nVHE/pKVM hyp
From: Vincent Donnefort @ 2026-06-12 14:22 UTC (permalink / raw)
To: maz, oliver.upton, joey.gouly, suzuki.poulose, yuzenghui,
catalin.marinas, will, rostedt
Cc: linux-arm-kernel, kvmarm, kernel-team, tabba, qerret,
Vincent Donnefort
In-Reply-To: <20260612142245.1015744-1-vdonnefort@google.com>
Create an event to allow developers to log pretty much anything into the
hypervisor tracing buffer with trace_hyp_printk(), just like the kernel
tracing has the function trace_printk().
trace_hyp_printk("foobar");
trace_hyp_printk("foo=%d", foo);
trace_hyp_printk("foo=%d bar=0x%016llx", foo, bar);
To ensure writing into the tracing buffer is fast, store the string
format into a kernel-accessible ELF section. The hypervisor only has to
write into the event the string ID, which is the delta between the
hyp_string_fmt and the start of the ELF section.
To not waste tracing buffer data, use a dynamic size. Each
argument is 8 bytes and the in-kernel printing function can simply know
how many of them there are by looking at the event length.
Signed-off-by: Vincent Donnefort <vdonnefort@google.com>
diff --git a/arch/arm64/include/asm/kvm_hypevents.h b/arch/arm64/include/asm/kvm_hypevents.h
index 743c49bd878f..8465b523cb1d 100644
--- a/arch/arm64/include/asm/kvm_hypevents.h
+++ b/arch/arm64/include/asm/kvm_hypevents.h
@@ -57,4 +57,18 @@ HYP_EVENT(selftest,
),
RE_PRINTK("id=%llu", __entry->id)
);
+
+/*
+ * trace_hyp_printk() has too many specificities to be declared with HYP_EVENT().
+ * However, we can use a REMOTE_EVENT macro to automatically do the declaration
+ * for the kernel side.
+ */
+REMOTE_EVENT_CUSTOM_PRINTK(hyp_printk,
+ 0, /* id will be overwritten during hyp event init */
+ RE_STRUCT(
+ re_field(u16, fmt_id)
+ re_field(u64, args[])
+ ),
+ __hyp_trace_printk(evt, seq)
+);
#endif
diff --git a/arch/arm64/include/asm/kvm_hyptrace.h b/arch/arm64/include/asm/kvm_hyptrace.h
index de133b735f72..46097105fdd8 100644
--- a/arch/arm64/include/asm/kvm_hyptrace.h
+++ b/arch/arm64/include/asm/kvm_hyptrace.h
@@ -23,4 +23,12 @@ extern struct remote_event __hyp_events_end[];
extern struct hyp_event_id __hyp_event_ids_start[];
extern struct hyp_event_id __hyp_event_ids_end[];
+#define HYP_STRING_FMT_MAX_SIZE 128
+
+struct hyp_string_fmt {
+ const char fmt[HYP_STRING_FMT_MAX_SIZE];
+};
+
+extern struct hyp_string_fmt __hyp_string_fmts_start[];
+extern struct hyp_string_fmt __hyp_string_fmts_end[];
#endif
diff --git a/arch/arm64/kernel/image-vars.h b/arch/arm64/kernel/image-vars.h
index d4c7d45ae6bc..ec03621d7a81 100644
--- a/arch/arm64/kernel/image-vars.h
+++ b/arch/arm64/kernel/image-vars.h
@@ -141,6 +141,7 @@ KVM_NVHE_ALIAS(__hyp_rodata_end);
#ifdef CONFIG_NVHE_EL2_TRACING
KVM_NVHE_ALIAS(__hyp_event_ids_start);
KVM_NVHE_ALIAS(__hyp_event_ids_end);
+KVM_NVHE_ALIAS(__hyp_string_fmts_start);
#endif
/* pKVM static key */
diff --git a/arch/arm64/kernel/vmlinux.lds.S b/arch/arm64/kernel/vmlinux.lds.S
index e1ac876200a3..b6d62642b6bd 100644
--- a/arch/arm64/kernel/vmlinux.lds.S
+++ b/arch/arm64/kernel/vmlinux.lds.S
@@ -324,6 +324,10 @@ SECTIONS
__hyp_events_start = .;
*(SORT(_hyp_events.*))
__hyp_events_end = .;
+
+ __hyp_string_fmts_start = .;
+ *(_hyp_string_fmts)
+ __hyp_string_fmts_end = .;
}
#endif
/*
diff --git a/arch/arm64/kvm/hyp/include/nvhe/define_events.h b/arch/arm64/kvm/hyp/include/nvhe/define_events.h
index 776d4c6cb702..370e8c2d39fe 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/define_events.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/define_events.h
@@ -10,5 +10,3 @@
#define HYP_EVENT_MULTI_READ
#include <asm/kvm_hypevents.h>
#undef HYP_EVENT_MULTI_READ
-
-#undef HYP_EVENT
diff --git a/arch/arm64/kvm/hyp/include/nvhe/trace.h b/arch/arm64/kvm/hyp/include/nvhe/trace.h
index 8813ff250f8e..3d0b5c634bb3 100644
--- a/arch/arm64/kvm/hyp/include/nvhe/trace.h
+++ b/arch/arm64/kvm/hyp/include/nvhe/trace.h
@@ -46,6 +46,69 @@ static inline pid_t __tracing_get_vcpu_pid(struct kvm_cpu_context *host_ctxt)
void *tracing_reserve_entry(unsigned long length);
void tracing_commit_entry(void);
+/*
+ * The trace_hyp_printk boilerplate is too fiddly to be declared with
+ * HYP_EVENT():
+ *
+ * The string format is stored into a kernel-accessible ELF section. The
+ * hypervisor only writes the format ID.
+ *
+ * The function has a variadic prototype. We have no easy way to know each
+ * argument width so they must all cast to u64.
+ */
+#define REMOTE_EVENT_CUSTOM_PRINTK(...)
+
+#define __TO_U64_0()
+#define __TO_U64_1(x) , (u64)(x)
+#define __TO_U64_2(x, ...) , (u64)(x) __TO_U64_1(__VA_ARGS__)
+#define __TO_U64_3(x, ...) , (u64)(x) __TO_U64_2(__VA_ARGS__)
+#define __TO_U64_4(x, ...) , (u64)(x) __TO_U64_3(__VA_ARGS__)
+#define __TO_U64_5(x, ...) , (u64)(x) __TO_U64_4(__VA_ARGS__)
+#define __TO_U64_6(x, ...) , (u64)(x) __TO_U64_5(__VA_ARGS__)
+#define __TO_U64_7(x, ...) , (u64)(x) __TO_U64_6(__VA_ARGS__)
+#define __TO_U64_8(x, ...) , (u64)(x) __TO_U64_7(__VA_ARGS__)
+
+#define __TO_U64_X(N, ...) CONCATENATE(__TO_U64_, N)(__VA_ARGS__)
+#define __TO_U64(...) __TO_U64_X(COUNT_ARGS(__VA_ARGS__), ##__VA_ARGS__)
+
+REMOTE_EVENT_FORMAT(hyp_printk, HE_STRUCT(he_field(u16, fmt_id) he_field(u64, args[])));
+extern struct hyp_event_id hyp_event_id_hyp_printk;
+
+static __always_inline void __trace_hyp_printk(struct hyp_string_fmt *fmt, int nr_args, ...)
+{
+ struct remote_event_format_hyp_printk *entry;
+ va_list va;
+ int i;
+
+ if (!atomic_read(&hyp_event_id_hyp_printk.enabled))
+ return;
+
+ entry = tracing_reserve_entry(struct_size(entry, args, nr_args));
+ if (!entry)
+ return;
+
+ entry->hdr.id = hyp_event_id_hyp_printk.id;
+ entry->fmt_id = fmt - __hyp_string_fmts_start;
+
+ va_start(va, nr_args);
+ for (i = 0; i < nr_args; i++)
+ entry->args[i] = va_arg(va, u64);
+ va_end(va);
+
+ tracing_commit_entry();
+}
+
+
+#define trace_hyp_printk(__fmt, __args...) \
+do { \
+ static struct hyp_string_fmt __used __section("_hyp_string_fmts") fmt = { \
+ .fmt = __fmt \
+ }; \
+ BUILD_BUG_ON(sizeof(__fmt) > HYP_STRING_FMT_MAX_SIZE); \
+ /* __TO_U64 prepends a comma if there are arguments */ \
+ __trace_hyp_printk(&fmt, COUNT_ARGS(__args) __TO_U64(__args)); \
+} while (0)
+
int __tracing_load(unsigned long desc_va, size_t desc_size);
void __tracing_unload(void);
int __tracing_enable(bool enable);
@@ -58,6 +121,8 @@ static inline void *tracing_reserve_entry(unsigned long length) { return NULL; }
static inline void tracing_commit_entry(void) { }
#define HYP_EVENT(__name, __proto, __struct, __assign, __printk) \
static inline void trace_##__name(__proto) {}
+#define REMOTE_EVENT_CUSTOM_PRINTK(...)
+#define trace_hyp_printk(fmt, args...) do { } while (0)
static inline int __tracing_load(unsigned long desc_va, size_t desc_size) { return -ENODEV; }
static inline void __tracing_unload(void) { }
diff --git a/arch/arm64/kvm/hyp/nvhe/events.c b/arch/arm64/kvm/hyp/nvhe/events.c
index add9383aadb5..12223d2e3618 100644
--- a/arch/arm64/kvm/hyp/nvhe/events.c
+++ b/arch/arm64/kvm/hyp/nvhe/events.c
@@ -9,6 +9,12 @@
#include <nvhe/define_events.h>
+/*
+ * The hyp_printk event is not declared with HYP_EVENT in kvm_hypevents.h,
+ * so we manually add the boilerplate here.
+ */
+HYP_EVENT(hyp_printk, 0, 0, 0, 0);
+
int __tracing_enable_event(unsigned short id, bool enable)
{
struct hyp_event_id *event_id = &__hyp_event_ids_start[id];
diff --git a/arch/arm64/kvm/hyp_trace.c b/arch/arm64/kvm/hyp_trace.c
index 821bc93ecdd1..187a8a295d6f 100644
--- a/arch/arm64/kvm/hyp_trace.c
+++ b/arch/arm64/kvm/hyp_trace.c
@@ -391,6 +391,9 @@ static struct trace_remote_callbacks trace_remote_callbacks = {
static const char *__hyp_enter_exit_reason_str(u8 reason);
+struct remote_event_format_hyp_printk;
+static void __hyp_trace_printk(struct remote_event_format_hyp_printk *entry, struct trace_seq *seq);
+
#include "define_hypevents.h"
static const char *__hyp_enter_exit_reason_str(u8 reason)
@@ -409,6 +412,61 @@ static const char *__hyp_enter_exit_reason_str(u8 reason)
return strs[min(reason, HYP_REASON_UNKNOWN)];
}
+static void __hyp_trace_printk(struct remote_event_format_hyp_printk *entry, struct trace_seq *seq)
+{
+ const char *fmt = (const char *)(&__hyp_string_fmts_start[entry->fmt_id]);
+ struct ring_buffer_event *evt = (void *)entry - RB_EVNT_HDR_SIZE;
+ int nr_args;
+
+ trace_seq_putc(seq, ' ');
+
+ if ((void *)fmt >= (void *)__hyp_string_fmts_end) {
+ trace_seq_printf(seq, "Unknown hyp_string_fmt ID %d\n", entry->fmt_id);
+ return;
+ }
+
+ nr_args = (ring_buffer_event_length(evt) -
+ offsetof(struct remote_event_format_hyp_printk, args)) / sizeof(entry->args[0]);
+ switch (nr_args) {
+ case 0:
+ trace_seq_printf(seq, fmt);
+ break;
+ case 1:
+ trace_seq_printf(seq, fmt, entry->args[0]);
+ break;
+ case 2:
+ trace_seq_printf(seq, fmt, entry->args[0], entry->args[1]);
+ break;
+ case 3:
+ trace_seq_printf(seq, fmt, entry->args[0], entry->args[1], entry->args[2]);
+ break;
+ case 4:
+ trace_seq_printf(seq, fmt, entry->args[0], entry->args[1], entry->args[2],
+ entry->args[3]);
+ break;
+ case 5:
+ trace_seq_printf(seq, fmt, entry->args[0], entry->args[1], entry->args[2],
+ entry->args[3], entry->args[4]);
+ break;
+ case 6:
+ trace_seq_printf(seq, fmt, entry->args[0], entry->args[1], entry->args[2],
+ entry->args[3], entry->args[4], entry->args[5]);
+ break;
+ case 7:
+ trace_seq_printf(seq, fmt, entry->args[0], entry->args[1], entry->args[2],
+ entry->args[3], entry->args[4], entry->args[5], entry->args[6]);
+ break;
+ default:
+ trace_seq_printf(seq, fmt, entry->args[0], entry->args[1],
+ entry->args[2], entry->args[3], entry->args[4], entry->args[5],
+ entry->args[6], entry->args[7]);
+ break;
+ }
+
+ if (seq->buffer[trace_seq_used(seq) - 1] != '\n')
+ trace_seq_putc(seq, '\n');
+}
+
static void __init hyp_trace_init_events(void)
{
struct hyp_event_id *hyp_event_id = __hyp_event_ids_start;
--
2.54.0.1136.gdb2ca164c4-goog
^ 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