[RFC PATCH 35/38] arm_mpam: Add workaround for T241-MPAM-1

Ben Horgan ben.horgan at arm.com
Wed Dec 10 04:20:19 PST 2025


Hi James,

On 12/5/25 21:58, James Morse wrote:
> From: Shanker Donthineni <sdonthineni at nvidia.com>
> 
> The MPAM bandwidth partitioning controls will not be correctly configured,
> and hardware will retain default configuration register values, meaning
> generally that bandwidth will remain unprovisioned.
> 
> To address the issue, follow the below steps after updating the MBW_MIN
> and/or MBW_MAX registers.
> 
>  - Perform 64b reads from all 12 bridge MPAM shadow registers at offsets
>    (0x360048 + slice*0x10000 + partid*8). These registers are read-only.
>  - Continue iterating until all 12 shadow register values match in a loop.
>    pr_warn_once if the values fail to match within the loop count 1000.
>  - Perform 64b writes with the value 0x0 to the two spare registers at
>    offsets 0x1b0000 and 0x1c0000.
> 
> In the hardware, writes to the MPAMCFG_MBW_MAX MPAMCFG_MBW_MIN registers
> are transformed into broadcast writes to the 12 shadow registers. The
> final two writes to the spare registers cause a final rank of downstream
> micro-architectural MPAM registers to be updated from the shadow copies.
> The intervening loop to read the 12 shadow registers helps avoid a race
> condition where writes to the spare registers occur before all shadow
> registers have been updated.
> 
> Signed-off-by: Shanker Donthineni <sdonthineni at nvidia.com>
> [ morse: Merged the min/max update into a single
>   mpam_quirk_post_config_change() helper. Stashed the t241_id in the msc
>   instead of carrying the physical address around. Test the msc quirk bit
>   instead of a static key. ]
> Signed-off-by: James Morse <james.morse at arm.com>
> ---
>  Documentation/arch/arm64/silicon-errata.rst |  2 +
>  drivers/resctrl/mpam_devices.c              | 87 +++++++++++++++++++++
>  drivers/resctrl/mpam_internal.h             |  9 +++
>  3 files changed, 98 insertions(+)
> 
> diff --git a/Documentation/arch/arm64/silicon-errata.rst b/Documentation/arch/arm64/silicon-errata.rst
> index a7ec57060f64..4e86b85fe3d6 100644
> --- a/Documentation/arch/arm64/silicon-errata.rst
> +++ b/Documentation/arch/arm64/silicon-errata.rst
> @@ -246,6 +246,8 @@ stable kernels.
>  +----------------+-----------------+-----------------+-----------------------------+
>  | NVIDIA         | T241 GICv3/4.x  | T241-FABRIC-4   | N/A                         |
>  +----------------+-----------------+-----------------+-----------------------------+
> +| NVIDIA         | T241 MPAM       | T241-MPAM-1     | N/A                         |
> ++----------------+-----------------+-----------------+-----------------------------+
>  +----------------+-----------------+-----------------+-----------------------------+
>  | Freescale/NXP  | LS2080A/LS1043A | A-008585        | FSL_ERRATUM_A008585         |
>  +----------------+-----------------+-----------------+-----------------------------+
> diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
> index f0f6f9b55ad4..f1f03ceade0a 100644
> --- a/drivers/resctrl/mpam_devices.c
> +++ b/drivers/resctrl/mpam_devices.c
> @@ -29,6 +29,16 @@
>  
>  #include "mpam_internal.h"
>  
> +/* Values for the T241 errata workaround */
> +#define T241_CHIPS_MAX			4
> +#define T241_CHIP_NSLICES		12
> +#define T241_SPARE_REG0_OFF		0x1b0000
> +#define T241_SPARE_REG1_OFF		0x1c0000
> +#define T241_CHIP_ID(phys)		FIELD_GET(GENMASK_ULL(44, 43), phys)
> +#define T241_SHADOW_REG_OFF(sidx, pid)	(0x360048 + (sidx) * 0x10000 + (pid) * 8)
> +#define SMCCC_SOC_ID_T241		0x036b0241
> +static void __iomem *t241_scratch_regs[T241_CHIPS_MAX];
> +
>  /*
>   * mpam_list_lock protects the SRCU lists when writing. Once the
>   * mpam_enabled key is enabled these lists are read-only,
> @@ -630,7 +640,44 @@ static struct mpam_msc_ris *mpam_get_or_create_ris(struct mpam_msc *msc,
>  	return ERR_PTR(-ENOENT);
>  }
>  
> +static void mpam_enable_quirk_nvidia_t241(struct mpam_msc *msc,
> +					  const struct mpam_quirk *quirk)
> +{
> +	s32 soc_id = arm_smccc_get_soc_id_version();
> +	struct resource *r;
> +	phys_addr_t phys;
> +
> +	/*
> +	 * A mapping to a device other than the MSC is needed, check
> +	 * SOC_ID is  NVIDIA T241 chip (036b:0241)
> +	 */
> +	if (soc_id < 0 || soc_id != SMCCC_SOC_ID_T241)
> +		return;
> +
> +	r = platform_get_resource(msc->pdev, IORESOURCE_MEM, 0);
> +	if (!r)
> +		return;
> +
> +	/* Find the internal registers base addr from the CHIP ID */
> +	msc->t241_id = T241_CHIP_ID(r->start);
> +	phys = FIELD_PREP(GENMASK_ULL(45, 44), msc->t241_id) | 0x19000000ULL;
> +
> +	t241_scratch_regs[msc->t241_id] = ioremap(phys, SZ_8M);
> +	if (WARN_ON_ONCE(!t241_scratch_regs[msc->t241_id]))
> +		return;
> +
> +	mpam_set_quirk(quirk->workaround, msc);
> +	pr_info_once("Enabled workaround for NVIDIA T241 erratum T241-MPAM-1\n");
> +}
> +
>  static const struct mpam_quirk mpam_quirks[] = {
> +	{
> +	/* NVIDIA t241 erratum T241-MPAM-1 */
> +	.init       = mpam_enable_quirk_nvidia_t241,
> +	.iidr       = MPAM_IIDR_NVIDIA_T421,

MPAM_IIDR_NVIDIA_T421 -> MPAM_IIDR_NVIDIA_T241

> +	.iidr_mask  = MPAM_IIDR_MATCH_ONE,
> +	.workaround = T241_SCRUB_SHADOW_REGS,
> +	},
>  	{ NULL }, /* Sentinel */
>  };
>  
> @@ -1372,6 +1419,44 @@ static void mpam_reset_msc_bitmap(struct mpam_msc *msc, u16 reg, u16 wd)
>  	__mpam_write_reg(msc, reg, bm);
>  }
>  
> +static void mpam_apply_t241_erratum(struct mpam_msc_ris *ris, u16 partid)
> +{
> +	int sidx, i, lcount = 1000;
> +	void __iomem *regs;
> +	u64 val0, val;
> +
> +	regs = t241_scratch_regs[ris->vmsc->msc->t241_id];
> +
> +	for (i = 0; i < lcount; i++) {
> +		/* Read the shadow register at index 0 */
> +		val0 = readq_relaxed(regs + T241_SHADOW_REG_OFF(0, partid));
> +
> +		/* Check if all the shadow registers have the same value */
> +		for (sidx = 1; sidx < T241_CHIP_NSLICES; sidx++) {
> +			val = readq_relaxed(regs +
> +					    T241_SHADOW_REG_OFF(sidx, partid));
> +			if (val != val0)
> +				break;
> +		}
> +		if (sidx == T241_CHIP_NSLICES)
> +			break;
> +	}
> +
> +	if (i == lcount)
> +		pr_warn_once("t241: inconsistent values in shadow regs");
> +
> +	/* Write a value zero to spare registers to take effect of MBW conf */
> +	writeq_relaxed(0, regs + T241_SPARE_REG0_OFF);
> +	writeq_relaxed(0, regs + T241_SPARE_REG1_OFF);
> +}
> +
> +static void mpam_quirk_post_config_change(struct mpam_msc_ris *ris, u16 partid,
> +					  struct mpam_config *cfg)
> +{
> +	if (mpam_has_quirk(T241_SCRUB_SHADOW_REGS, ris->vmsc->msc))
> +		mpam_apply_t241_erratum(ris, partid);
> +}
> +
>  /* Called via IPI. Call while holding an SRCU reference */
>  static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
>  				      struct mpam_config *cfg)
> @@ -1455,6 +1540,8 @@ static void mpam_reprogram_ris_partid(struct mpam_msc_ris *ris, u16 partid,
>  		mpam_write_partsel_reg(msc, PRI, pri_val);
>  	}
>  
> +	mpam_quirk_post_config_change(ris, partid, cfg);
> +
>  	mutex_unlock(&msc->part_sel_lock);
>  }
>  
> diff --git a/drivers/resctrl/mpam_internal.h b/drivers/resctrl/mpam_internal.h
> index de3e5faa12b2..70b78cfd1f5b 100644
> --- a/drivers/resctrl/mpam_internal.h
> +++ b/drivers/resctrl/mpam_internal.h
> @@ -133,6 +133,9 @@ struct mpam_msc {
>  	void __iomem		*mapped_hwpage;
>  	size_t			mapped_hwpage_sz;
>  
> +	/* Values only used on some platforms for quirks */
> +	u32			t241_id;
> +
>  	struct mpam_garbage	garbage;
>  };
>  
> @@ -219,6 +222,7 @@ struct mpam_props {
>  
>  /* Workaround bits for msc->quirks */
>  enum mpam_device_quirks {
> +	T241_SCRUB_SHADOW_REGS,
>  	MPAM_QUIRK_LAST,
>  };
>  
> @@ -239,6 +243,11 @@ struct mpam_quirk {
>  				FIELD_PREP_CONST(MPAMF_IIDR_REVISION,    0xf  ) | \
>  				FIELD_PREP_CONST(MPAMF_IIDR_IMPLEMENTER, 0xfff)
>  
> +#define MPAM_IIDR_NVIDIA_T421   FIELD_PREP_CONST(MPAMF_IIDR_PRODUCTID,   0x241) | \

MPAM_IIDR_NVIDIA_T421 -> MPAM_IIDR_NVIDIA_T241

> +				FIELD_PREP_CONST(MPAMF_IIDR_VARIANT,     0    ) | \
> +				FIELD_PREP_CONST(MPAMF_IIDR_REVISION,    0    ) | \
> +				FIELD_PREP_CONST(MPAMF_IIDR_IMPLEMENTER, 0x36b)
> +
>  
>  /* The values for MSMON_CFG_MBWU_FLT.RWBW */
>  enum mon_filter_options {

Thanks,

Ben




More information about the linux-arm-kernel mailing list