* Re: [PATCH RFC v7 7/9] PM / devfreq: Introduce the QCOM SCMI Memlat devfreq driver
From: Bjorn Andersson @ 2026-07-02 17:21 UTC (permalink / raw)
To: Pragnesh Papaniya
Cc: Sudeep Holla, Cristian Marussi, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, Sibi Sankar, MyungJoo Ham, Kyungmin Park,
Chanwoo Choi, Dmitry Osipenko, Thierry Reding, Jonathan Hunter,
Konrad Dybcio, Rajendra Nayak, Pankaj Patil, linux-arm-msm,
linux-kernel, arm-scmi, linux-arm-kernel, devicetree, linux-pm,
linux-tegra, Amir Vajid, Ramakrishna Gottimukkula
In-Reply-To: <20260610-rfc_v7_scmi_memlat-v7-7-f3f68c608f25@oss.qualcomm.com>
On Wed, Jun 10, 2026 at 02:21:34PM +0530, Pragnesh Papaniya wrote:
> From: Sibi Sankar <sibi.sankar@oss.qualcomm.com>
>
> On Qualcomm Glymur, Mahua and X1E/X1P (Hamoa) SoCs, the memlat governor and
> the mechanism to control the various caches and RAM is hosted on the CPU
> Control Processor (CPUCP), and configuration and control of this governor
> is exposed through the QCOM SCMI Generic Extension Protocol, addressed via
> the "MEMLAT" algorithm string.
>
This explains that there's a bunch of functionality running on CPUCP and
there's a "MEMLAT" string.
> Introduce a devfreq SCMI client driver that uses the MEMLAT algorithm
> string to detect memory-latency-bound workloads and control the
> frequency/level of the memory buses (DDR, LLCC and DDR_QOS).
You established that there's stuff running in the firmware, now we're
introducing a client driver to control memory buses.
But where did you explain how these two "facts" are related? Why is
there a client driver, what is the actual distribution of roles in this
dance?
> Model each bus
> as a devfreq device using the remote devfreq governor. This provides basic
> insight into device operation via trans_stat and allows further tuning of
> the remote governor's parameters from userspace.
>
Does this mean that the driver is "optional", and only exists to give
insight and a way to tune the firmware operation?
If that's the case, why is it a devfreq driver?
Please write your commit messages (and patches) such that people outside
your immediate team can understand why the patches are needed and what
the code is supposed to do.
> Co-developed-by: Amir Vajid <amir.vajid@oss.qualcomm.com>
> Signed-off-by: Amir Vajid <amir.vajid@oss.qualcomm.com>
> Co-developed-by: Ramakrishna Gottimukkula <ramakrishna.gottimukkula@oss.qualcomm.com>
> Signed-off-by: Ramakrishna Gottimukkula <ramakrishna.gottimukkula@oss.qualcomm.com>
> Signed-off-by: Sibi Sankar <sibi.sankar@oss.qualcomm.com>
> Co-developed-by: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com>
> Signed-off-by: Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com>
> ---
> drivers/devfreq/Kconfig | 13 +
> drivers/devfreq/Makefile | 1 +
> drivers/devfreq/scmi-qcom-memlat-cfg.h | 573 +++++++++++++++++++++++++++
> drivers/devfreq/scmi-qcom-memlat-devfreq.c | 616 +++++++++++++++++++++++++++++
> 4 files changed, 1203 insertions(+)
>
> diff --git a/drivers/devfreq/Kconfig b/drivers/devfreq/Kconfig
> index 2caa87554914..98b5a50d3189 100644
> --- a/drivers/devfreq/Kconfig
> +++ b/drivers/devfreq/Kconfig
> @@ -169,6 +169,19 @@ config ARM_SUN8I_A33_MBUS_DEVFREQ
> This adds the DEVFREQ driver for the MBUS controller in some
> Allwinner sun8i (A33 through H3) and sun50i (A64 and H5) SoCs.
>
> +config SCMI_QCOM_MEMLAT_DEVFREQ
> + tristate "Qualcomm Technologies Inc. SCMI client driver"
> + depends on QCOM_SCMI_GENERIC_EXT || COMPILE_TEST
> + select DEVFREQ_GOV_REMOTE
> + help
> + This driver uses the MEMLAT (memory latency) algorithm string
Is "driver uses X algorithm string" idiomatic SCMI terms?
> + hosted on QCOM SCMI Vendor Protocol to detect memory latency
> + workloads and control frequency/level of the various memory
> + buses (DDR/LLCC/DDR_QOS).
> +
> + This driver defines/documents the parameter IDs used while configuring
> + the memory buses.
Imagine an person outside your team, sitting there in menuconfig
wondering if they should enable this driver or not.
There's a sentence in the middle ("control frequency/level of various
memory buses" - that sounds like something I want. But "detect memory
latency", is it just monitoring or does that part relate to the
controlling part? "This driver defines" so what are those parameters
used for, do I need some other driver for the control part? Is this last
paragraph adding value to my understanding for that
CONFIG_SCMI_QCOM_MEMLAT_DEVFREQ does?
> +
> source "drivers/devfreq/event/Kconfig"
>
> endif # PM_DEVFREQ
> diff --git a/drivers/devfreq/Makefile b/drivers/devfreq/Makefile
> index cde57c8cda76..b11f94e2f485 100644
> --- a/drivers/devfreq/Makefile
> +++ b/drivers/devfreq/Makefile
> @@ -17,6 +17,7 @@ obj-$(CONFIG_ARM_MEDIATEK_CCI_DEVFREQ) += mtk-cci-devfreq.o
> obj-$(CONFIG_ARM_RK3399_DMC_DEVFREQ) += rk3399_dmc.o
> obj-$(CONFIG_ARM_SUN8I_A33_MBUS_DEVFREQ) += sun8i-a33-mbus.o
> obj-$(CONFIG_ARM_TEGRA_DEVFREQ) += tegra30-devfreq.o
> +obj-$(CONFIG_SCMI_QCOM_MEMLAT_DEVFREQ) += scmi-qcom-memlat-devfreq.o
>
> # DEVFREQ Event Drivers
> obj-$(CONFIG_PM_DEVFREQ_EVENT) += event/
> diff --git a/drivers/devfreq/scmi-qcom-memlat-cfg.h b/drivers/devfreq/scmi-qcom-memlat-cfg.h
> new file mode 100644
> index 000000000000..1ab8b61ea271
> --- /dev/null
> +++ b/drivers/devfreq/scmi-qcom-memlat-cfg.h
Are the entities declared in this header file used by anything other
than scmi-qcom-memlat-devfreq.c? If not why is it a separate header file?
> @@ -0,0 +1,573 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#ifndef __DRIVERS_DEVFREQ_SCMI_QCOM_MEMLAT_CONFIG_H__
> +#define __DRIVERS_DEVFREQ_SCMI_QCOM_MEMLAT_CONFIG_H__
> +
> +/*
> + * Memlat Effective Frequency Calculation Method
> + * CPUCP_EFFECTIVE_FREQ_METHOD_0 - Uses CPU Cycles and CONST Cycles to calculate
> + * CPUCP_EFFECTIVE_FREQ_METHOD_1 - Uses CPU Cycles and time period
> + */
> +#define CPUCP_EFFECTIVE_FREQ_CALC_METHOD_0 0
> +#define CPUCP_EFFECTIVE_FREQ_CALC_METHOD_1 1
#define ZERO 0
#define ONE 1
Can these be given real names, or could the struct member even be a
boolean?
That said, "ZERO" isn't used in your patch...
> +
> +#define EV_CPU_CYCLES 0
> +#define EV_CNT_CYCLES 1
> +#define EV_INST_RETIRED 2
> +#define EV_STALL_BACKEND_MEM 3
> +#define EV_L2_D_RFILL 5
> +#define INVALID_IDX 0xff
The names of these defines aren't awesome names to put in include
files...
> +
> +#define MEMLAT_ALGO_STR 0x4D454D4C4154ULL /* MEMLAT */
As you've seen a thousand times on LKML already, we want lower-case hex
digits...
> +
> +struct scmi_qcom_map_table {
> + unsigned int cpu_freq;
> + unsigned int mem_freq;
Unit soup
> +};
> +
> +struct scmi_qcom_opp_data {
> + unsigned long freq;
> + unsigned int level;
The arrays of opp_data is all static const, and you seem to only define
"level" for ddr_qos, which if I read the code correctly you later
treat in some special way anyways.
> +};
> +
> +struct scmi_qcom_memory_range {
> + unsigned int min_freq;
> + unsigned int max_freq;
> +};
> +
> +enum common_ev_idx {
> + INST_IDX,
> + CYC_IDX,
> + CONST_CYC_IDX,
> + FE_STALL_IDX,
> + BE_STALL_IDX,
> + NUM_COMMON_EVS
> +};
> +
> +enum grp_ev_idx {
> + MISS_IDX,
> + WB_IDX,
> + ACC_IDX,
> + NUM_GRP_EVS
> +};
> +
> +/*
> + * CPUCP firmware identifies memory groups by a small integer (the hw_type
> + * carried in node_msg / scalar_param_msg / map_param_msg / ev_map_msg).
Ok, sounds reasonable.
> The
> + * encoding is shared between the cfg tables below and scmi_qcom_devfreq_get_cur_freq()
> + * which special-cases DDR_QOS as a level-based bus rather than a frequency-scaled one.
The constants are shared between A and B, both being described as
in-driver users. So is this constants shared between the first (as you
said above) or an enumeration within the driver?
> + */
> +enum scmi_qcom_memlat_hw_type {
> + MEMLAT_HW_DDR = 0,
> + MEMLAT_HW_LLCC = 1,
> + MEMLAT_HW_DDR_QOS_COMPUTE = 2,
Please use "enum" for enumerations and #define for constants. (These
aren't enumerations, as they are defined constants)
> +};
> +
> +struct scmi_qcom_monitor_cfg {
> + const struct scmi_qcom_map_table *table;
> + const char *name;
> + u32 be_stall_floor;
What is a "be stall floor"? Also, it seems to be 1 in all your cases. Is
it boolean? Is it constant?
> + u32 cpu_mask;
> + u32 ipm_ceil;
> + int table_len;
Why is this signed?
> +};
> +
> +struct scmi_qcom_memory_cfg {
> + const struct scmi_qcom_monitor_cfg *monitor_cfg;
> + const struct scmi_qcom_opp_data *mem_table;
> + struct scmi_qcom_memory_range memory_range;
> + const u32 *grp_ev;
> + const char *name;
> + u32 memory_type;
As the memory type isn't an enumeration, I asked you above to make it
constants. But in the form you wrote it, there's no reason for this not
to have its enum type.
> + int monitor_cnt;
> + int num_opps;
Aren't these unsigned?
> +};
> +
> +struct scmi_qcom_memlat_cfg_data {
> + const struct scmi_qcom_memory_cfg *memory_cfg;
> + const u32 *common_ev;
> + u32 cpucp_freq_method;
> + u32 cpucp_sample_ms;
> + int memory_cnt;
Unsigned...
> +};
> +
> +static const u32 glymur_common_ev[NUM_COMMON_EVS] = {
> + [INST_IDX] = EV_INST_RETIRED,
> + [CYC_IDX] = EV_CPU_CYCLES,
> + [CONST_CYC_IDX] = EV_CNT_CYCLES,
> + [FE_STALL_IDX] = INVALID_IDX,
> + [BE_STALL_IDX] = EV_STALL_BACKEND_MEM,
> +};
> +
> +static const u32 glymur_ddr_grp_ev[NUM_GRP_EVS] = {
> + [MISS_IDX] = EV_L2_D_RFILL,
> + [WB_IDX] = INVALID_IDX,
> + [ACC_IDX] = INVALID_IDX,
> +};
> +
> +static const u32 glymur_llcc_grp_ev[NUM_GRP_EVS] = {
> + [MISS_IDX] = EV_L2_D_RFILL,
> + [WB_IDX] = INVALID_IDX,
> + [ACC_IDX] = INVALID_IDX,
> +};
> +
> +static const u32 glymur_ddr_qos_grp_ev[NUM_GRP_EVS] = {
> + [MISS_IDX] = EV_L2_D_RFILL,
> + [WB_IDX] = INVALID_IDX,
> + [ACC_IDX] = INVALID_IDX,
> +};
> +
> +static const u32 hamoa_common_ev[NUM_COMMON_EVS] = {
> + [INST_IDX] = EV_INST_RETIRED,
> + [CYC_IDX] = EV_CPU_CYCLES,
> + [CONST_CYC_IDX] = EV_CNT_CYCLES,
> + [FE_STALL_IDX] = INVALID_IDX,
> + [BE_STALL_IDX] = EV_STALL_BACKEND_MEM,
> +};
> +
> +static const u32 hamoa_ddr_grp_ev[NUM_GRP_EVS] = {
> + [MISS_IDX] = EV_L2_D_RFILL,
> + [WB_IDX] = INVALID_IDX,
> + [ACC_IDX] = INVALID_IDX,
> +};
> +
> +static const u32 hamoa_llcc_grp_ev[NUM_GRP_EVS] = {
> + [MISS_IDX] = EV_L2_D_RFILL,
> + [WB_IDX] = INVALID_IDX,
> + [ACC_IDX] = INVALID_IDX,
> +};
> +
> +static const u32 hamoa_ddr_qos_grp_ev[NUM_GRP_EVS] = {
> + [MISS_IDX] = EV_L2_D_RFILL,
> + [WB_IDX] = INVALID_IDX,
> + [ACC_IDX] = INVALID_IDX,
> +};
> +
> +static const struct scmi_qcom_opp_data glymur_llcc_table[] = {
> + { .freq = 315000000 },
> + { .freq = 479000000 },
> + { .freq = 545000000 },
> + { .freq = 725000000 },
> + { .freq = 840000000 },
> + { .freq = 959000000 },
> + { .freq = 1090000000 },
> + { .freq = 1211000000 },
> +};
> +
> +static const struct scmi_qcom_opp_data hamoa_llcc_table[] = {
> + { .freq = 300000000 },
> + { .freq = 466000000 },
> + { .freq = 600000000 },
> + { .freq = 806000000 },
> + { .freq = 933000000 },
> + { .freq = 1066000000 },
> +};
> +
> +static const struct scmi_qcom_opp_data glymur_ddr_table[] = {
> + { .freq = 200000000 },
> + { .freq = 547000000 },
> + { .freq = 1353000000 },
> + { .freq = 1555000000 },
> + { .freq = 1708000000 },
> + { .freq = 2092000000 },
> + { .freq = 2736000000 },
> + { .freq = 3187000000 },
> + { .freq = 3686000000 },
> + { .freq = 4224000000 },
> + { .freq = 4761000000 },
> +};
> +
> +static const struct scmi_qcom_opp_data hamoa_ddr_table[] = {
> + { .freq = 200000000 },
> + { .freq = 547000000 },
> + { .freq = 768000000 },
> + { .freq = 1555000000 },
> + { .freq = 1708000000 },
> + { .freq = 2092000000 },
> + { .freq = 2736000000 },
> + { .freq = 3187000000 },
> + { .freq = 3686000000 },
> + { .freq = 4224000000 },
> +};
> +
> +/*
> + * DDR_QOS is a level-based bus (0 = nominal, 1 = boost), not a
> + * frequency-scaled one.
"level-based"? Looks more like "boolean"?
So I presume "not a frequency-scaled one" should be interpreted as the
.freq is bogus?
> The OPP entries below use synthetic frequencies
> + * (1 / 100) purely as distinct devfreq keys so trans_stat can show
> + * level transitions.
1/100? 0.01?
Why are the frequencies 1 and 100, why not 0 and 1, or 0 and 100?
> scmi_qcom_devfreq_get_cur_freq() maps the firmware
> + * level back to the matching key.
What is the "key" here?
> + */
> +static const struct scmi_qcom_opp_data glymur_ddr_qos_table[] = {
> + { .freq = 1, .level = 0 },
> + { .freq = 100, .level = 1 },
> +};
> +
> +static const struct scmi_qcom_memory_cfg glymur_memory_cfg[] = {
> + {
> + .memory_type = MEMLAT_HW_DDR,
> + .name = "ddr",
> + .mem_table = glymur_ddr_table,
> + .num_opps = ARRAY_SIZE(glymur_ddr_table),
> + .grp_ev = glymur_ddr_grp_ev,
> + .monitor_cnt = 4,
> + .memory_range = { .min_freq = 547000, .max_freq = 4761000},
> + .monitor_cfg = (const struct scmi_qcom_monitor_cfg[]) {
> + {
> + .name = "mon_0",
> + .cpu_mask = 0x3f,
> + .ipm_ceil = 60000000,
> + .be_stall_floor = 1,
> + .table_len = 8,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 960, .mem_freq = 547000 },
> + { .cpu_freq = 1133, .mem_freq = 1353000 },
> + { .cpu_freq = 1594, .mem_freq = 1555000 },
> + { .cpu_freq = 1920, .mem_freq = 1708000 },
> + { .cpu_freq = 2228, .mem_freq = 2736000 },
> + { .cpu_freq = 2362, .mem_freq = 3187000 },
> + { .cpu_freq = 2650, .mem_freq = 3686000 },
> + { .cpu_freq = 2938, .mem_freq = 4761000 },
Why are these tables hard coded in the driver? Are they constant?
> + }
> + },
> + {
> + .name = "mon_1",
> + .cpu_mask = 0xfc0,
> + .ipm_ceil = 60000000,
> + .be_stall_floor = 1,
> + .table_len = 8,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 356, .mem_freq = 547000 },
> + { .cpu_freq = 1018, .mem_freq = 1353000 },
> + { .cpu_freq = 1536, .mem_freq = 1555000 },
> + { .cpu_freq = 1748, .mem_freq = 1708000 },
> + { .cpu_freq = 2324, .mem_freq = 2736000 },
> + { .cpu_freq = 2496, .mem_freq = 3187000 },
> + { .cpu_freq = 2900, .mem_freq = 3686000 },
> + { .cpu_freq = 3514, .mem_freq = 4761000 },
> + }
> + },
> + {
> + .name = "mon_2",
> + .cpu_mask = 0x3f000,
> + .ipm_ceil = 60000000,
> + .be_stall_floor = 1,
> + .table_len = 8,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 356, .mem_freq = 547000 },
> + { .cpu_freq = 1018, .mem_freq = 1353000 },
> + { .cpu_freq = 1536, .mem_freq = 1555000 },
> + { .cpu_freq = 1748, .mem_freq = 1708000 },
> + { .cpu_freq = 2324, .mem_freq = 2736000 },
> + { .cpu_freq = 2496, .mem_freq = 3187000 },
> + { .cpu_freq = 2900, .mem_freq = 3686000 },
> + { .cpu_freq = 3514, .mem_freq = 4761000 },
> + }
> + },
> + {
> + .name = "mon_3",
> + .cpu_mask = 0x3ffff,
> + .table_len = 4,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 2823, .mem_freq = 547000 },
> + { .cpu_freq = 3034, .mem_freq = 1555000 },
> + { .cpu_freq = 3226, .mem_freq = 1708000 },
> + { .cpu_freq = 5012, .mem_freq = 2092000 },
> + }
> + },
> + },
> + },
> + {
> + .memory_type = MEMLAT_HW_LLCC,
> + .name = "llcc",
> + .mem_table = glymur_llcc_table,
> + .num_opps = ARRAY_SIZE(glymur_llcc_table),
> + .grp_ev = glymur_llcc_grp_ev,
> + .monitor_cnt = 3,
> + .memory_range = { .min_freq = 315000, .max_freq = 1211000},
> + .monitor_cfg = (const struct scmi_qcom_monitor_cfg[]) {
> + {
> + .name = "mon_0",
> + .cpu_mask = 0x3f,
> + .ipm_ceil = 60000000,
> + .be_stall_floor = 1,
> + .table_len = 7,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 960, .mem_freq = 315000 },
> + { .cpu_freq = 1113, .mem_freq = 479000 },
> + { .cpu_freq = 1594, .mem_freq = 545000 },
> + { .cpu_freq = 1920, .mem_freq = 725000 },
> + { .cpu_freq = 2362, .mem_freq = 840000 },
> + { .cpu_freq = 2650, .mem_freq = 959000 },
> + { .cpu_freq = 2938, .mem_freq = 1211000 },
> + }
> + },
> + {
> + .name = "mon_1",
> + .cpu_mask = 0xfc0,
> + .ipm_ceil = 60000000,
> + .be_stall_floor = 1,
> + .table_len = 7,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 356, .mem_freq = 315000 },
> + { .cpu_freq = 1018, .mem_freq = 479000 },
> + { .cpu_freq = 1536, .mem_freq = 545000 },
> + { .cpu_freq = 1748, .mem_freq = 725000 },
> + { .cpu_freq = 2496, .mem_freq = 840000 },
> + { .cpu_freq = 2900, .mem_freq = 959000 },
> + { .cpu_freq = 3514, .mem_freq = 1211000 },
> + }
> + },
> + {
> + .name = "mon_2",
> + .cpu_mask = 0x3f000,
> + .ipm_ceil = 60000000,
> + .be_stall_floor = 1,
> + .table_len = 7,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 356, .mem_freq = 315000 },
> + { .cpu_freq = 1018, .mem_freq = 479000 },
> + { .cpu_freq = 1536, .mem_freq = 545000 },
> + { .cpu_freq = 1748, .mem_freq = 725000 },
> + { .cpu_freq = 2496, .mem_freq = 840000 },
> + { .cpu_freq = 2900, .mem_freq = 959000 },
> + { .cpu_freq = 3514, .mem_freq = 1211000 },
> + }
> + },
> + },
> + },
> + {
> + .memory_type = MEMLAT_HW_DDR_QOS_COMPUTE,
> + .name = "ddr-qos",
> + .monitor_cnt = 3,
> + .mem_table = glymur_ddr_qos_table,
> + .num_opps = ARRAY_SIZE(glymur_ddr_qos_table),
> + .grp_ev = glymur_ddr_qos_grp_ev,
> + .memory_range = { .min_freq = 0, .max_freq = 1},
> + .monitor_cfg = (const struct scmi_qcom_monitor_cfg[]) {
> + {
> + .name = "mon_0",
> + .cpu_mask = 0x3f,
> + .ipm_ceil = 80000000,
> + .be_stall_floor = 1,
> + .table_len = 2,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 2362, .mem_freq = 0 },
> + { .cpu_freq = 2938, .mem_freq = 1 },
> + }
> + },
> + {
> + .name = "mon_1",
> + .cpu_mask = 0xfc0,
> + .ipm_ceil = 80000000,
> + .be_stall_floor = 1,
> + .table_len = 2,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 2496, .mem_freq = 0 },
> + { .cpu_freq = 3514, .mem_freq = 1 },
> + }
> + },
> + {
> + .name = "mon_2",
> + .cpu_mask = 0x3f000,
> + .ipm_ceil = 80000000,
> + .be_stall_floor = 1,
> + .table_len = 2,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 2496, .mem_freq = 0 },
> + { .cpu_freq = 3514, .mem_freq = 1 },
> + }
> + },
> + },
> + },
> +};
> +
> +static const struct scmi_qcom_memory_cfg hamoa_memory_cfg[] = {
> + {
> + .memory_type = MEMLAT_HW_DDR,
> + .name = "ddr",
> + .mem_table = hamoa_ddr_table,
> + .num_opps = ARRAY_SIZE(hamoa_ddr_table),
> + .grp_ev = hamoa_ddr_grp_ev,
> + .monitor_cnt = 4,
> + .memory_range = { .min_freq = 200000, .max_freq = 4224000},
> + .monitor_cfg = (const struct scmi_qcom_monitor_cfg[]) {
> + {
> + .name = "mon_0",
> + .cpu_mask = 0xf,
> + .ipm_ceil = 20000000,
> + .be_stall_floor = 1,
> + .table_len = 6,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 999, .mem_freq = 547000 },
> + { .cpu_freq = 1440, .mem_freq = 768000 },
> + { .cpu_freq = 1671, .mem_freq = 1555000 },
> + { .cpu_freq = 2189, .mem_freq = 2092000 },
> + { .cpu_freq = 2516, .mem_freq = 3187000 },
> + { .cpu_freq = 3860, .mem_freq = 4224000 },
> + }
> + },
> + {
> + .name = "mon_1",
> + .cpu_mask = 0xf0,
> + .ipm_ceil = 20000000,
> + .be_stall_floor = 1,
> + .table_len = 6,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 999, .mem_freq = 547000 },
> + { .cpu_freq = 1440, .mem_freq = 768000 },
> + { .cpu_freq = 1671, .mem_freq = 1555000 },
> + { .cpu_freq = 2189, .mem_freq = 2092000 },
> + { .cpu_freq = 2516, .mem_freq = 3187000 },
> + { .cpu_freq = 3860, .mem_freq = 4224000 },
> + }
> + },
> + {
> + .name = "mon_2",
> + .cpu_mask = 0xf00,
> + .ipm_ceil = 20000000,
> + .be_stall_floor = 1,
> + .table_len = 6,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 999, .mem_freq = 547000 },
> + { .cpu_freq = 1440, .mem_freq = 768000 },
> + { .cpu_freq = 1671, .mem_freq = 1555000 },
> + { .cpu_freq = 2189, .mem_freq = 2092000 },
> + { .cpu_freq = 2516, .mem_freq = 3187000 },
> + { .cpu_freq = 3860, .mem_freq = 4224000 },
> + }
> + },
> + {
> + .name = "mon_3",
> + .cpu_mask = 0xfff,
> + .table_len = 4,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 1440, .mem_freq = 547000 },
> + { .cpu_freq = 2189, .mem_freq = 768000 },
> + { .cpu_freq = 2516, .mem_freq = 1555000 },
> + { .cpu_freq = 3860, .mem_freq = 2092000 },
> + }
> + },
> + },
> + },
> + {
> + .memory_type = MEMLAT_HW_LLCC,
> + .name = "llcc",
> + .mem_table = hamoa_llcc_table,
> + .num_opps = ARRAY_SIZE(hamoa_llcc_table),
> + .grp_ev = hamoa_llcc_grp_ev,
> + .monitor_cnt = 3,
> + .memory_range = { .min_freq = 300000, .max_freq = 1066000},
> + .monitor_cfg = (const struct scmi_qcom_monitor_cfg[]) {
> + {
> + .name = "mon_0",
> + .cpu_mask = 0xf,
> + .ipm_ceil = 20000000,
> + .be_stall_floor = 1,
> + .table_len = 6,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 999, .mem_freq = 300000 },
> + { .cpu_freq = 1440, .mem_freq = 466000 },
> + { .cpu_freq = 1671, .mem_freq = 600000 },
> + { .cpu_freq = 2189, .mem_freq = 806000 },
> + { .cpu_freq = 2516, .mem_freq = 933000 },
> + { .cpu_freq = 3860, .mem_freq = 1066000 },
> + }
> + },
> + {
> + .name = "mon_1",
> + .cpu_mask = 0xf0,
> + .ipm_ceil = 20000000,
> + .be_stall_floor = 1,
> + .table_len = 6,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 999, .mem_freq = 300000 },
> + { .cpu_freq = 1440, .mem_freq = 466000 },
> + { .cpu_freq = 1671, .mem_freq = 600000 },
> + { .cpu_freq = 2189, .mem_freq = 806000 },
> + { .cpu_freq = 2516, .mem_freq = 933000 },
> + { .cpu_freq = 3860, .mem_freq = 1066000 },
> + }
> + },
> + {
> + .name = "mon_2",
> + .cpu_mask = 0xf00,
> + .ipm_ceil = 20000000,
> + .be_stall_floor = 1,
> + .table_len = 6,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 999, .mem_freq = 300000 },
> + { .cpu_freq = 1440, .mem_freq = 466000 },
> + { .cpu_freq = 1671, .mem_freq = 600000 },
> + { .cpu_freq = 2189, .mem_freq = 806000 },
> + { .cpu_freq = 2516, .mem_freq = 933000 },
> + { .cpu_freq = 3860, .mem_freq = 1066000 },
> + }
> + },
> + },
> + },
> + {
> + .memory_type = MEMLAT_HW_DDR_QOS_COMPUTE,
> + .name = "ddr-qos",
> + .monitor_cnt = 3,
> + .mem_table = glymur_ddr_qos_table,
> + .num_opps = ARRAY_SIZE(glymur_ddr_qos_table),
> + .grp_ev = hamoa_ddr_qos_grp_ev,
> + .memory_range = { .min_freq = 0, .max_freq = 1},
> + .monitor_cfg = (const struct scmi_qcom_monitor_cfg[]) {
> + {
> + .name = "mon_0",
> + .cpu_mask = 0xf,
> + .ipm_ceil = 20000000,
> + .be_stall_floor = 1,
> + .table_len = 2,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 2189, .mem_freq = 0 },
> + { .cpu_freq = 3860, .mem_freq = 1 },
> + }
> + },
> + {
> + .name = "mon_1",
> + .cpu_mask = 0xf0,
> + .ipm_ceil = 20000000,
> + .be_stall_floor = 1,
> + .table_len = 2,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 2189, .mem_freq = 0 },
> + { .cpu_freq = 3860, .mem_freq = 1 },
> + }
> + },
> + {
> + .name = "mon_2",
> + .cpu_mask = 0xf00,
> + .ipm_ceil = 20000000,
> + .be_stall_floor = 1,
> + .table_len = 2,
> + .table = (const struct scmi_qcom_map_table[]) {
> + { .cpu_freq = 2189, .mem_freq = 0 },
> + { .cpu_freq = 3860, .mem_freq = 1 },
> + }
> + },
> + },
> + },
> +};
> +
> +static const struct scmi_qcom_memlat_cfg_data glymur_memlat_data = {
> + .memory_cfg = glymur_memory_cfg,
> + .common_ev = glymur_common_ev,
> + .cpucp_freq_method = CPUCP_EFFECTIVE_FREQ_CALC_METHOD_1,
> + .cpucp_sample_ms = 4,
> + .memory_cnt = ARRAY_SIZE(glymur_memory_cfg),
> +};
> +
> +static const struct scmi_qcom_memlat_cfg_data hamoa_memlat_data = {
> + .memory_cfg = hamoa_memory_cfg,
> + .common_ev = hamoa_common_ev,
> + .cpucp_freq_method = CPUCP_EFFECTIVE_FREQ_CALC_METHOD_1,
> + .cpucp_sample_ms = 4,
> + .memory_cnt = ARRAY_SIZE(hamoa_memory_cfg),
> +};
It would be wonderful to declare scmi_qcom_memlat_configs[] in the
vicinity of these two definitions.
> +
> +#endif
> diff --git a/drivers/devfreq/scmi-qcom-memlat-devfreq.c b/drivers/devfreq/scmi-qcom-memlat-devfreq.c
> new file mode 100644
> index 000000000000..99ae229acfdc
> --- /dev/null
> +++ b/drivers/devfreq/scmi-qcom-memlat-devfreq.c
> @@ -0,0 +1,616 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries.
> + */
> +
> +#include <linux/cpu.h>
> +#include <linux/devfreq.h>
> +#include <linux/err.h>
> +#include <linux/errno.h>
> +#include <linux/init.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_opp.h>
> +#include <linux/scmi_protocol.h>
> +#include <linux/scmi_qcom_protocol.h>
> +#include <linux/units.h>
> +
> +#define MAX_NAME_LEN 20
> +#define MAX_MAP_ENTRIES 10
The names used for these defines are a bit generic...
> +
> +#include "scmi-qcom-memlat-cfg.h"
> +
> +/**
> + * enum scmi_memlat_protocol_cmd - parameter_ids supported by the "MEMLAT" algo_str hosted
> + * by the Qualcomm Generic Vendor Protocol on the SCMI controller.
This is supposed to be the short description, yet it's the same length
as the long description on the very next line.
> + *
> + * MEMLAT (Memory Latency) monitors the counters to detect memory latency bound workloads
> + * and scales the frequency/levels of the memory buses accordingly.
> + *
> + * @MEMLAT_SET_MEM_GROUP: initializes the frequency/level scaling functions for the memory bus.
> + * @MEMLAT_SET_MONITOR: configures the monitor to work on a specific memory bus.
> + * @MEMLAT_SET_COMMON_EV_MAP: set up common counters used to monitor the cpu frequency.
> + * @MEMLAT_SET_GRP_EV_MAP: set up any specific counters used to monitor the memory bus.
> + * @MEMLAT_IPM_CEIL: set the IPM (Instruction Per Misses) ceiling per monitor.
> + * @MEMLAT_BE_STALL_FLOOR: set the back-end stall floor per monitor.
> + * @MEMLAT_SAMPLE_MS: set the sampling period for all the monitors.
> + * @MEMLAT_MON_FREQ_MAP: setup the cpufreq to memfreq map.
> + * @MEMLAT_SET_MIN_FREQ: set the min frequency of the memory bus.
> + * @MEMLAT_SET_MAX_FREQ: set the max frequency of the memory bus.
> + * @MEMLAT_GET_CUR_FREQ: query the current frequency/level of the memory bus.
> + * @MEMLAT_START_TIMER: start all the monitors with the requested sampling period.
> + * @MEMLAT_STOP_TIMER: stop all the running monitors.
> + * @MEMLAT_SET_EFFECTIVE_FREQ_METHOD: set the method used to determine cpu frequency.
> + */
> +enum scmi_memlat_protocol_cmd {
Don't use "enum" when you don't mean "enumeration".
> + MEMLAT_SET_MEM_GROUP = 16,
> + MEMLAT_SET_MONITOR,
> + MEMLAT_SET_COMMON_EV_MAP,
> + MEMLAT_SET_GRP_EV_MAP,
> + MEMLAT_IPM_CEIL = 23,
> + MEMLAT_BE_STALL_FLOOR = 25,
> + MEMLAT_SAMPLE_MS = 31,
> + MEMLAT_MON_FREQ_MAP,
> + MEMLAT_SET_MIN_FREQ,
> + MEMLAT_SET_MAX_FREQ,
> + MEMLAT_GET_CUR_FREQ,
> + MEMLAT_START_TIMER = 36,
> + MEMLAT_STOP_TIMER,
> + MEMLAT_SET_EFFECTIVE_FREQ_METHOD = 39,
> +};
> +
> +struct cpucp_map_table {
> + __le16 v1;
> + __le16 v2;
v1 v2?
As far as I can tell these two numbers are cpu and memory frequency.
> +};
> +
> +struct map_param_msg {
> + __le32 hw_type;
> + __le32 mon_idx;
> + __le32 nr_rows;
> + struct cpucp_map_table tbl[MAX_MAP_ENTRIES];
> +} __packed;
Does this really need the __packed? And if so, why is the only message
that you explicitly mark as packed?
> +
> +struct node_msg {
> + __le32 cpumask;
> + __le32 hw_type;
> + __le32 mon_type;
> + __le32 mon_idx;
> + char mon_name[MAX_NAME_LEN];
> +};
> +
> +struct scalar_param_msg {
> + __le32 hw_type;
> + __le32 mon_idx;
> + __le32 val;
> +};
> +
> +struct ev_map_msg {
> + __le32 num_evs;
> + __le32 hw_type;
> + __le32 cid[NUM_COMMON_EVS];
> +};
> +
> +struct scmi_qcom_memlat_map {
> + unsigned int cpufreq_mhz;
> + unsigned int memfreq_khz;
unit soup
> +};
> +
> +struct scmi_qcom_monitor_info {
> + struct scmi_qcom_memlat_map *freq_map;
> + char name[MAX_NAME_LEN];
> + u32 mon_idx;
> + u32 mon_type;
> + u32 ipm_ceil;
> + u32 be_stall_floor;
> + u32 mask;
> + u32 freq_map_len;
Most of these values are merely copied from const tables to heap on
probe - verbatim - and then put in messages. Would it be possible to
just reference the static configuration, or are the any dynamic aspects
that I'm missing?
> +};
> +
> +struct scmi_qcom_memory_info {
> + struct scmi_qcom_monitor_info **monitor;
> + u32 hw_type;
> + int monitor_cnt;
Why signed?
> + u32 min_freq;
> + u32 max_freq;
Throughout the driver we see frequencies expressed in mhz, khz, and hz.
Suffix these to help the reader.
> + struct devfreq_dev_profile profile;
> + struct devfreq *devfreq;
> + struct platform_device *pdev;
> + struct scmi_protocol_handle *ph;
> + const struct qcom_generic_ext_ops *ops;
> +};
> +
> +struct scmi_qcom_memlat_info {
> + struct scmi_protocol_handle *ph;
> + const struct qcom_generic_ext_ops *ops;
> + const struct scmi_qcom_memlat_cfg_data *cfg_data;
> + struct scmi_qcom_memory_info **memory;
> + u32 cpucp_freq_method;
> + u32 cpucp_sample_ms;
> + int memory_cnt;
Sounds like an unsigned to me
> +};
> +
> +static int configure_cpucp_common_events(struct scmi_qcom_memlat_info *info,
> + const struct scmi_qcom_memlat_cfg_data *cfg_data)
> +{
> + const struct qcom_generic_ext_ops *ops = info->ops;
> + struct ev_map_msg msg = {};
> + int i;
> +
> + msg.num_evs = cpu_to_le32(NUM_COMMON_EVS);
> + /* Common events apply to all groups; INVALID_IDX flags "no specific group". */
Does hw_type of INVALID_IDX mean "all groups", or "any (no specific)
group"?
Is the protocol defined that you pass "invalid index" for both hw_type
and cid? Or do we just reuse the INVALID_IDX define because it happens
to have the right value?
Why not "#define HW_TYPE_ALL 0xff"? Then you can skip the second half of
the comment above, which only purpose is to clarify why the code looks
"wrong".
> + msg.hw_type = cpu_to_le32(INVALID_IDX);
> + for (i = 0; i < NUM_COMMON_EVS; i++)
> + msg.cid[i] = cpu_to_le32(cfg_data->common_ev[i]);
> +
> + return ops->set_param(info->ph, &msg, sizeof(msg), MEMLAT_ALGO_STR,
This is always info->ops->set_param(info->ph, ..., ..., MEMLAT_ALGO_STR,
...), why not create a helper unction that takes info, the command,
message and size instead?
Would make the code less noisy.
> + MEMLAT_SET_COMMON_EV_MAP);
> +}
> +
> +static int configure_cpucp_grp(struct device *dev, struct scmi_qcom_memlat_info *info,
> + const struct scmi_qcom_memlat_cfg_data *cfg_data,
> + int memory_index)
> +{
> + const u32 *grp_ev = cfg_data->memory_cfg[memory_index].grp_ev;
> + struct scmi_qcom_memory_info *memory = info->memory[memory_index];
> + const struct qcom_generic_ext_ops *ops = info->ops;
> + struct ev_map_msg ev_msg = {};
> + struct node_msg msg = {};
> + int ret;
> + int i;
> +
> + msg.cpumask = cpu_to_le32(*cpumask_bits(cpu_possible_mask));
> + msg.hw_type = cpu_to_le32(memory->hw_type);
> + msg.mon_type = 0;
> + msg.mon_idx = 0;
> + ret = ops->set_param(info->ph, &msg, sizeof(msg), MEMLAT_ALGO_STR, MEMLAT_SET_MEM_GROUP);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "failed to configure mem type %d\n",
> + memory->hw_type);
> +
> + ev_msg.num_evs = cpu_to_le32(NUM_GRP_EVS);
> + ev_msg.hw_type = cpu_to_le32(memory->hw_type);
> + for (i = 0; i < NUM_GRP_EVS; i++)
> + ev_msg.cid[i] = cpu_to_le32(grp_ev[i]);
> +
> + ret = ops->set_param(info->ph, &ev_msg, sizeof(ev_msg), MEMLAT_ALGO_STR,
> + MEMLAT_SET_GRP_EV_MAP);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "failed to configure event map for mem type %d\n",
> + memory->hw_type);
> +
> + return ret;
> +}
> +
> +static int configure_cpucp_mon(struct device *dev, struct scmi_qcom_memlat_info *info,
> + int memory_index, int monitor_index)
> +{
> + const struct qcom_generic_ext_ops *ops = info->ops;
> + struct scmi_qcom_memory_info *memory = info->memory[memory_index];
> + struct scmi_qcom_monitor_info *monitor = memory->monitor[monitor_index];
> + struct scalar_param_msg scalar_msg = {};
> + struct map_param_msg map_msg = {};
> + struct node_msg msg = {};
So you have scalar_msg, map_msg, and msg... Why is "msg" just "msg" and
not "node_msg", is it special?
If you made scalar_param_msg the "msg" instead, this function would be
significantly less characters :)
In fact, is it significant that these local variables are denoted to be
messages? If you name them "scalar", "map" and "node" you will reduce
the noise below quite a bit.
Same goes for all other functions that has multiple messages - where the
message type is the more significant fact, and you choose to name the
variable "this is a message".
> + int ret;
> + int i;
> +
> + msg.cpumask = cpu_to_le32(monitor->mask);
> + msg.hw_type = cpu_to_le32(memory->hw_type);
> + msg.mon_type = cpu_to_le32(monitor->mon_type);
> + msg.mon_idx = cpu_to_le32(monitor->mon_idx);
> + strscpy(msg.mon_name, monitor->name, sizeof(msg.mon_name));
> + ret = ops->set_param(info->ph, &msg, sizeof(msg), MEMLAT_ALGO_STR, MEMLAT_SET_MONITOR);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "failed to configure monitor %s\n",
> + monitor->name);
> +
> + scalar_msg.hw_type = cpu_to_le32(memory->hw_type);
> + scalar_msg.mon_idx = cpu_to_le32(monitor->mon_idx);
> + scalar_msg.val = cpu_to_le32(monitor->ipm_ceil);
> + ret = ops->set_param(info->ph, &scalar_msg, sizeof(scalar_msg), MEMLAT_ALGO_STR,
> + MEMLAT_IPM_CEIL);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "failed to set ipm ceil for %s\n",
> + monitor->name);
> +
> + scalar_msg.hw_type = cpu_to_le32(memory->hw_type);
> + scalar_msg.mon_idx = cpu_to_le32(monitor->mon_idx);
> + scalar_msg.val = cpu_to_le32(monitor->be_stall_floor);
> + ret = ops->set_param(info->ph, &scalar_msg, sizeof(scalar_msg), MEMLAT_ALGO_STR,
> + MEMLAT_BE_STALL_FLOOR);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "failed to set be_stall_floor for %s\n",
> + monitor->name);
> +
> + map_msg.hw_type = cpu_to_le32(memory->hw_type);
> + map_msg.mon_idx = cpu_to_le32(monitor->mon_idx);
> + map_msg.nr_rows = cpu_to_le32(monitor->freq_map_len);
> + for (i = 0; i < monitor->freq_map_len; i++) {
> + map_msg.tbl[i].v1 = cpu_to_le16(monitor->freq_map[i].cpufreq_mhz);
> +
> + /*
> + * Wire format v2 is u16 in MHz; convert from kHz.
Why is the freq_map cpufreq defined in MHz, but the memfreq in kHz - and
then we convert the memory frequency to MHz before we write it?
Why are they not both defined in MHz?
> For DDR_QOS
> + * the table holds level indices (0 / 1) rather than real
> + * frequencies, so pass them through unchanged.
"holds level indices", "pass them through unchanged"? What are you
trying to say?
> + */
> + if (monitor->freq_map[i].memfreq_khz > 1)
If you change memfreq_khz to memfreq_mhz you can remove the whole
comment, the condition, as well as the divide. There might be a reason
for you to do this, but the comment is poor, so I simply can't tell.
> + map_msg.tbl[i].v2 = cpu_to_le16(monitor->freq_map[i].memfreq_khz / 1000);
> + else
> + map_msg.tbl[i].v2 = cpu_to_le16(monitor->freq_map[i].memfreq_khz);
> + }
> + ret = ops->set_param(info->ph, &map_msg, sizeof(map_msg), MEMLAT_ALGO_STR,
> + MEMLAT_MON_FREQ_MAP);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "failed to configure freq_map for %s\n",
> + monitor->name);
> +
> + scalar_msg.hw_type = cpu_to_le32(memory->hw_type);
> + scalar_msg.mon_idx = cpu_to_le32(monitor->mon_idx);
> + scalar_msg.val = cpu_to_le32(memory->min_freq);
> + ret = ops->set_param(info->ph, &scalar_msg, sizeof(scalar_msg), MEMLAT_ALGO_STR,
> + MEMLAT_SET_MIN_FREQ);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "failed to set min_freq for %s\n",
> + monitor->name);
> +
> + scalar_msg.hw_type = cpu_to_le32(memory->hw_type);
> + scalar_msg.mon_idx = cpu_to_le32(monitor->mon_idx);
> + scalar_msg.val = cpu_to_le32(memory->max_freq);
> + ret = ops->set_param(info->ph, &scalar_msg, sizeof(scalar_msg), MEMLAT_ALGO_STR,
> + MEMLAT_SET_MAX_FREQ);
> + if (ret < 0)
> + return dev_err_probe(dev, ret, "failed to set max_freq for %s\n", monitor->name);
> +
> + return ret;
> +}
> +
> +static int scmi_qcom_devfreq_get_cur_freq(struct device *dev, unsigned long *freq)
> +{
> + struct scmi_qcom_memory_info *memory = dev_get_drvdata(dev);
> + const struct qcom_generic_ext_ops *ops = memory->ops;
> + struct scalar_param_msg scalar_msg = {};
> + u32 max_freq_khz = 0;
> + __le32 cur_freq;
> + int ret, i;
> +
> + /*
> + * MEMLAT_GET_CUR_FREQ returns target_freq for a single (hw_type,
> + * mon_idx) tuple. The bus's actual voted frequency is the max across
> + * all configured monitors in the group, so query each one and pick
> + * the highest vote.
You can say this with fewer words.
> + */
> + for (i = 0; i < memory->monitor_cnt; i++) {
> + scalar_msg.hw_type = cpu_to_le32(memory->hw_type);
> + scalar_msg.mon_idx = cpu_to_le32(memory->monitor[i]->mon_idx);
> + scalar_msg.val = 0;
> +
> + ret = ops->get_param(memory->ph, &scalar_msg, sizeof(scalar_msg),
> + MEMLAT_ALGO_STR, MEMLAT_GET_CUR_FREQ,
> + sizeof(cur_freq));
> + if (ret < 0) {
> + dev_err(dev, "failed to get current frequency for %s\n",
> + memory->monitor[i]->name);
> + return ret;
We're grabbing a bunch of these, is it really fatal if one fails?
> + }
> +
> + /* qcom_scmi_common_xfer() returns the response into the same tx buffer. */
What is qcom_scmi_common_xfer()?
You can express in C the fact that msg is a in/out - by declaring it as
a union.
That way you don't need a comment to describe why you're reinterpreting
the first 4 bytes of msg as the result of the query.
> + memcpy(&cur_freq, &scalar_msg, sizeof(cur_freq));
> + if (le32_to_cpu(cur_freq) > max_freq_khz)
> + max_freq_khz = le32_to_cpu(cur_freq);
And you could then le32_to_cpu() out of the union to a local variable
once, to avoid the double le32_to_cpu().
> + }
> +
> + /*
> + * Frequency-scaled buses (DDR/LLCC) report cur_freq in kHz; convert
> + * to Hz to match the devfreq OPP table units. Level-based buses
> + * (e.g. DDR_QOS_COMPUTE) configure max_freq == 1 because the firmware
> + * reports a 0/1 level rather than a frequency, and the matching
> + * synthetic OPP keys (1 / 100) live in glymur_ddr_qos_table.
You surely don't need 5 lines of solid text to express this.
> + */
> + if (memory->max_freq > 1)
> + *freq = max_freq_khz * 1000UL;
> + else
> + *freq = max_freq_khz ? 100 : 1;
> +
> + return 0;
> +}
> +
> +static void scmi_qcom_memlat_unwind(struct scmi_qcom_memlat_info *info, int count)
> +{
> + for (int i = 0; i < count; i++) {
> + struct scmi_qcom_memory_info *memory = info->memory[i];
> +
> + if (IS_ERR_OR_NULL(memory) || IS_ERR_OR_NULL(memory->pdev))
I don't get it, how can "memory" be IS_ERR()? Isn't it the result of the
devm_kcalloc() in scmi_qcom_memlat_parse_cfg()?
> + continue;
> +
> + dev_pm_opp_remove_all_dynamic(&memory->pdev->dev);
> + platform_device_unregister(memory->pdev);
> + }
> +}
> +
> +static int scmi_qcom_memlat_configure_events(struct scmi_device *sdev,
> + struct scmi_qcom_memlat_info *info)
> +{
> + const struct qcom_generic_ext_ops *ops = info->ops;
> + struct scmi_protocol_handle *ph = info->ph;
> + __le32 sample_ms, freq_method;
> + int i, j, ret;
> +
> + /* Configure common events ids */
> + ret = configure_cpucp_common_events(info, info->cfg_data);
> + if (ret < 0)
> + return dev_err_probe(&sdev->dev, ret, "failed to configure common events\n");
> +
> + for (i = 0; i < info->memory_cnt; i++) {
> + /* Configure per group parameters */
> + ret = configure_cpucp_grp(&sdev->dev, info, info->cfg_data, i);
> + if (ret < 0)
> + return ret;
> +
> + for (j = 0; j < info->memory[i]->monitor_cnt; j++) {
> + /* Configure per monitor parameters */
> + ret = configure_cpucp_mon(&sdev->dev, info, i, j);
> + if (ret < 0)
> + return ret;
> + }
> + }
> +
> + /* Set loop sampling time */
> + sample_ms = cpu_to_le32(info->cpucp_sample_ms);
> + ret = ops->set_param(ph, &sample_ms, sizeof(sample_ms),
> + MEMLAT_ALGO_STR, MEMLAT_SAMPLE_MS);
> + if (ret < 0)
> + return dev_err_probe(&sdev->dev, ret, "failed to set sample_ms\n");
> +
> + /* Set the effective cpu frequency calculation method */
> + freq_method = cpu_to_le32(info->cpucp_freq_method);
> + ret = ops->set_param(ph, &freq_method, sizeof(freq_method),
> + MEMLAT_ALGO_STR, MEMLAT_SET_EFFECTIVE_FREQ_METHOD);
> + if (ret < 0)
> + return dev_err_probe(&sdev->dev, ret,
> + "failed to set effective frequency calc method\n");
> +
> + /* Start sampling and voting timer */
> + ret = ops->start_activity(ph, NULL, 0, MEMLAT_ALGO_STR, MEMLAT_START_TIMER);
> + if (ret < 0)
> + return dev_err_probe(&sdev->dev, ret, "failed to start memory group timer\n");
> +
> + for (i = 0; i < info->memory_cnt; i++) {
> + struct scmi_qcom_memory_info *memory = info->memory[i];
> + struct platform_device *pdev = memory->pdev;
> + struct devfreq_dev_profile *profile = &memory->profile;
> +
> + /* sampling time should be double the devfreq observing time */
That's interesting, tell me more...
> + profile->polling_ms = max(1U, info->cpucp_sample_ms / 2);
> + profile->get_cur_freq = scmi_qcom_devfreq_get_cur_freq;
> + profile->initial_freq = memory->min_freq > 1 ?
> + (memory->min_freq * 1000UL) : memory->min_freq;
What is the purpose of this conditional?
You support memories with an actual frequency, and what you call
"level". But for any memory with a "level" resource other than a boolean
this logic fails.
For min_freq == 0 we get initial_freq of 0, both for frequency-based and
level-based memories.
So the only case where this actually does anything is if you have a
boolean resource with minimum value of 1 (i.e. a constant). Do we have
those?
> +
> + platform_set_drvdata(pdev, memory);
> +
> + memory->devfreq = devm_devfreq_add_device(&pdev->dev, profile,
> + DEVFREQ_GOV_REMOTE, NULL);
> + if (IS_ERR(memory->devfreq)) {
> + dev_err(&sdev->dev, "failed to add devfreq device\n");
I personally don't like all the dev_err_probe() that you have sprinkled
throughout the driver, but I know that many others do - and this one
isn't consistent.
> + /* Stop sampling and voting timer */
> + ret = ops->stop_activity(ph, NULL, 0, MEMLAT_ALGO_STR, MEMLAT_STOP_TIMER);
> + if (ret < 0)
> + dev_err_probe(&sdev->dev, ret,
> + "failed to stop memory group timer\n");
The significant event is that adding the devfreq device failed, but the
error message that you retain is "failed to stop memory group timer"...
> + return PTR_ERR(memory->devfreq);
> + }
> + }
> +
> + return 0;
> +}
> +
> +static struct scmi_qcom_memlat_map *
> +scmi_qcom_parse_memlat_map(struct device *dev, const struct scmi_qcom_monitor_cfg *mon_cfg)
> +{
> + struct scmi_qcom_memlat_map *map_table;
> + const struct scmi_qcom_map_table *table;
> +
> + if (mon_cfg->table_len > MAX_MAP_ENTRIES)
> + return ERR_PTR(-EINVAL);
> +
> + map_table = devm_kcalloc(dev, mon_cfg->table_len, sizeof(*map_table),
> + GFP_KERNEL);
> + if (!map_table)
> + return ERR_PTR(-ENOMEM);
> +
> + for (int i = 0; i < mon_cfg->table_len; i++) {
> + table = &mon_cfg->table[i];
> +
> + map_table[i].cpufreq_mhz = table->cpu_freq;
> + map_table[i].memfreq_khz = table->mem_freq;
> + }
> +
> + return map_table;
> +}
> +
> +static const struct of_device_id scmi_qcom_memlat_configs[] = {
> + { .compatible = "qcom,glymur", .data = &glymur_memlat_data},
> + { .compatible = "qcom,mahua", .data = &glymur_memlat_data},
> + { .compatible = "qcom,x1e80100", .data = &hamoa_memlat_data},
> + { .compatible = "qcom,x1p42100", .data = &hamoa_memlat_data},
> + { }
> +};
> +
> +static int scmi_qcom_memlat_parse_cfg(struct scmi_device *sdev, struct scmi_qcom_memlat_info *info)
> +{
> + const struct scmi_qcom_memlat_cfg_data *cfg_data;
> + struct scmi_qcom_monitor_info *monitor;
> + struct scmi_qcom_memory_info *memory;
> + int ret, i, j;
> +
> + cfg_data = of_machine_get_match_data(scmi_qcom_memlat_configs);
> + if (!cfg_data) {
> + /*
> + * The SCMI generic-ext protocol can bind on Qualcomm SoCs that
> + * do not ship CPUCP memlat. Bail out quietly in that case rather
> + * than printing an error on every such system.
> + */
> + dev_dbg(&sdev->dev, "no memlat config data for this platform\n");
> + return -ENODEV;
> + }
> +
> + info->memory = devm_kcalloc(&sdev->dev, cfg_data->memory_cnt,
> + sizeof(*info->memory), GFP_KERNEL);
> + if (!info->memory)
> + return -ENOMEM;
> +
> + for (i = 0; i < cfg_data->memory_cnt; i++) {
> + const struct scmi_qcom_memory_cfg *memory_cfg = &cfg_data->memory_cfg[i];
> + struct platform_device_info pdevinfo = { 0 };
> +
> + pdevinfo.parent = &sdev->dev;
> + pdevinfo.name = memory_cfg->name;
> + pdevinfo.id = PLATFORM_DEVID_NONE;
> +
> + memory = devm_kzalloc(&sdev->dev, sizeof(*memory), GFP_KERNEL);
> + if (!memory)
> + return -ENOMEM;
> +
> + memory->ops = info->ops;
> + memory->ph = info->ph;
> + memory->hw_type = memory_cfg->memory_type;
> + memory->monitor_cnt = memory_cfg->monitor_cnt;
> + memory->min_freq = memory_cfg->memory_range.min_freq;
> + memory->max_freq = memory_cfg->memory_range.max_freq;
> +
> + memory->pdev = platform_device_register_full(&pdevinfo);
Why are there platform_devices in here?
I'm guessing that you want the struct devices to hang the opp tables
off? But why a whole platform_device?
> + if (IS_ERR(memory->pdev))
> + return dev_err_probe(&sdev->dev, PTR_ERR(memory->pdev),
> + "failed to register platform device\n");
> +
> + info->memory[i] = memory;
> + /* Track progress so probe() can unwind on a later failure. */
Please clean up your own mess. If this function becomes too long for you
to be able to get things in order, that's just another indication that
you need to refactor this function.
> + info->memory_cnt = i + 1;
> +
> + for (j = 0; j < memory_cfg->num_opps; j++) {
> + const struct scmi_qcom_opp_data *table = &memory_cfg->mem_table[j];
> + struct platform_device *pdev = memory->pdev;
> + struct dev_pm_opp_data data = {};
> +
> + data.freq = table->freq;
> + data.level = table->level;
> +
> + ret = dev_pm_opp_add_dynamic(&pdev->dev, &data);
> + if (ret)
> + return dev_err_probe(&sdev->dev, ret, "failed to add OPP\n");
> + }
> +
> + memory->monitor = devm_kcalloc(&sdev->dev, memory_cfg->monitor_cnt,
> + sizeof(*memory->monitor), GFP_KERNEL);
> + if (!memory->monitor)
> + return -ENOMEM;
> +
> + for (j = 0; j < memory_cfg->monitor_cnt; j++) {
> + const struct scmi_qcom_monitor_cfg *mon_cfg = &memory_cfg->monitor_cfg[j];
> +
> + monitor = devm_kzalloc(&sdev->dev, sizeof(*monitor), GFP_KERNEL);
> + if (!monitor)
> + return -ENOMEM;
> +
> + monitor->ipm_ceil = mon_cfg->ipm_ceil;
> + /* mon_type 0 = IPM-based latency monitor; 1 = stall-only (compute) */
> + monitor->mon_type = monitor->ipm_ceil ? 0 : 1;
So the actual mon_type in the message is just a 0 or 1? Is this 0 the
same as the 0 in configure_cpucp_grp()? Should we have a couple of
defines for these - instead of the comment?
> + monitor->be_stall_floor = mon_cfg->be_stall_floor;
> + monitor->mask = mon_cfg->cpu_mask;
> + monitor->freq_map_len = mon_cfg->table_len;
> +
> + monitor->freq_map = scmi_qcom_parse_memlat_map(&sdev->dev, mon_cfg);
It might be a good idea to do this before you're starting to register
platform_devices and opps, as it would simplify the cleanup.
> + if (IS_ERR(monitor->freq_map))
> + return dev_err_probe(&sdev->dev, PTR_ERR(monitor->freq_map),
> + "failed to populate cpufreq-memfreq map\n");
There are two ways scmi_qcom_parse_memlat_map() can fail, either the
driver data is wrong and you get EINVAL (a developer BUG) or you get
ENOMEM, which is already printed...
> +
> + strscpy(monitor->name, mon_cfg->name, sizeof(monitor->name));
Do you really need a copy? As far as I can see the next access is
another copy out of this struct.
> + monitor->mon_idx = j;
> + memory->monitor[j] = monitor;
> + }
> + }
> +
> + info->cfg_data = cfg_data;
> + info->cpucp_freq_method = cfg_data->cpucp_freq_method;
> + info->cpucp_sample_ms = cfg_data->cpucp_sample_ms;
So much code just moving data between two representations...
> +
> + return 0;
> +}
> +
> +static int scmi_qcom_devfreq_memlat_probe(struct scmi_device *sdev)
> +{
> + const struct scmi_handle *handle = sdev->handle;
> + const struct qcom_generic_ext_ops *ops;
> + struct scmi_qcom_memlat_info *info;
> + struct scmi_protocol_handle *ph;
> + int ret;
> +
> + if (!handle)
> + return -ENODEV;
> +
> + info = devm_kzalloc(&sdev->dev, sizeof(*info), GFP_KERNEL);
> + if (!info)
> + return -ENOMEM;
> +
> + ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_QCOM_GENERIC, &ph);
> + if (IS_ERR(ops))
> + return PTR_ERR(ops);
> +
> + info->ops = ops;
> + info->ph = ph;
> +
> + ret = scmi_qcom_memlat_parse_cfg(sdev, info);
> + if (ret) {
> + scmi_qcom_memlat_unwind(info, info->memory_cnt);
scmi_qcom_memlat_unwind() unwinds the work done by
scmi_qcom_memlat_parse_cfg(), this isn't really idiomatic. Please have
scmi_qcom_memlat_parse_cfg() clean up its own mess on failure.
> + return ret;
> + }
> +
> + ret = scmi_qcom_memlat_configure_events(sdev, info);
> + if (ret) {
> + scmi_qcom_memlat_unwind(info, info->memory_cnt);
> + return ret;
> + }
> +
> + dev_set_drvdata(&sdev->dev, info);
> +
> + return ret;
> +}
> +
> +static void scmi_qcom_devfreq_memlat_remove(struct scmi_device *sdev)
> +{
> + struct scmi_qcom_memlat_info *info = dev_get_drvdata(&sdev->dev);
> + struct scmi_protocol_handle *ph;
> + const struct qcom_generic_ext_ops *ops;
> + int ret;
> +
> + if (!info)
How is that possible?
> + return;
> +
> + ph = info->ph;
> + ops = info->ops;
> +
> + ret = ops->stop_activity(ph, NULL, 0, MEMLAT_ALGO_STR, MEMLAT_STOP_TIMER);
> + if (ret < 0)
> + dev_err(&sdev->dev, "failed to stop memory group timer\n");
> +
> + scmi_qcom_memlat_unwind(info, info->memory_cnt);
> +}
> +
> +static const struct scmi_device_id scmi_id_table[] = {
> + { SCMI_PROTOCOL_QCOM_GENERIC, "qcom-generic-ext" },
> + { },
> +};
> +MODULE_DEVICE_TABLE(scmi, scmi_id_table);
> +
> +static struct scmi_driver scmi_qcom_devfreq_memlat_driver = {
> + .name = "scmi-qcom-devfreq-memlat",
> + .probe = scmi_qcom_devfreq_memlat_probe,
> + .remove = scmi_qcom_devfreq_memlat_remove,
> + .id_table = scmi_id_table,
> +};
> +module_scmi_driver(scmi_qcom_devfreq_memlat_driver);
> +
> +MODULE_AUTHOR("Pragnesh Papaniya <pragnesh.papaniya@oss.qualcomm.com>");
> +MODULE_DESCRIPTION("SCMI QCOM DEVFREQ MEMLAT driver");
Let go of the shift key!
s/QCOM/Qualcomm/ at least.
Regards,
Bjorn
> +MODULE_LICENSE("GPL");
>
> --
> 2.34.1
>
^ permalink raw reply
* [PATCH net v3] net: airoha: fix MIB stats collection to be lossless
From: Aniket Negi @ 2026-07-02 17:18 UTC (permalink / raw)
To: lorenzo, netdev
Cc: Aniket Negi, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, linux-arm-kernel, linux-mediatek,
linux-kernel
The current driver resets hardware MIB counters after every read via
REG_FE_GDM_MIB_CLEAR. This creates a race window: packets arriving
between the read and the clear are silently lost from statistics.
Fix this by removing the MIB clear and switching to a delta-based
software tracking approach:
- 64-bit H+L registers (tx/rx ok pkts, ok bytes, E64..L1023):
read the absolute hardware total directly each poll.
- 32-bit registers (drops, bc, mc, errors, runt, long, ...):
store the previous raw register value in mib_prev and accumulate
(u32)(curr - prev) into a 64-bit software counter. Unsigned
subtraction handles wrap-around transparently.
- tx_len[0]/rx_len[0] ({0,64} RMON bucket) combines RUNT_CNT
(32-bit, delta-tracked via mib_prev.tx_runt) and E64_CNT
(64-bit, absolute). A u64 accumulator tx_runt64 holds the
running RUNT delta sum so that each poll sets:
tx_len[0] = tx_runt64 + E64_abs
without double-counting the E64 value.
Merge airoha_dev_get_hw_stats() into airoha_update_hw_stats(),
moving the port spin_lock inside so callers do not need a separate
wrapper.
Acked-by: Lorenzo Bianconi <lorenzo@kernel.org>
Signed-off-by: Aniket Negi <aniket.negi03@gmail.com>
---
Changes in v3:
- Link to V2: https://lore.kernel.org/20260701173941.314795-1-aniket.negi03@gmail.com/
- Add Acked-by tag from Lorenzo
- Rename from tx_runt_cnt to tx_runt, tx_long_cnt to tx_long,
tx_runt_accum64 to tx_runt64
- Rename from rx_runt_cnt to rx_runt, rx_long_cnt to rx_long,
rx_runt_accum64 to rx_runt64
- Condense the marked comments in V2, remove new line after comment
Changes in v2:
- Store _CNT_L register reads in val before adding to stats, improving
readability (suggested by Lorenzo Bianconi)
- Fix double-counting bug in the RUNT+E64 combined bucket: previously
"+=" for E64 re-added the full absolute counter each poll; now a
dedicated tx_runt_accum64/rx_runt_accum64 accumulator holds the
running RUNT delta, and tx_len[0] is assigned (not accumulated) each
poll as runt_accum64 + E64_abs
- Replace 7-element tx_len[]/rx_len[] shadow arrays in mib_prev with
focused tx_runt_cnt/tx_long_cnt and rx_runt_cnt/rx_long_cnt fields;
only RUNT and LONG are 32-bit and need wrap-around tracking
- Rename inner struct hw_prev_stats to mib_prev; rename accumulator
fields to tx_runt_accum64/rx_runt_accum64 for clarity
- Fix comment alignment in mib_prev struct block
- Rename airoha_dev_get_hw_stats() to airoha_update_hw_stats() and
move the port spin_lock inside, removing the separate wrapper
drivers/net/ethernet/airoha/airoha_eth.c | 107 ++++++++++++-----------
drivers/net/ethernet/airoha/airoha_eth.h | 27 ++++++
2 files changed, 84 insertions(+), 50 deletions(-)
diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
index 59001fd4b6f7..e009266fd268 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.c
+++ b/drivers/net/ethernet/airoha/airoha_eth.c
@@ -1686,12 +1686,14 @@ static void airoha_qdma_stop_napi(struct airoha_qdma *qdma)
}
}
-static void airoha_dev_get_hw_stats(struct airoha_gdm_dev *dev)
+static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
{
struct airoha_gdm_port *port = dev->port;
struct airoha_eth *eth = dev->eth;
u32 val, i = 0;
+ spin_lock(&port->stats_lock);
+
/* Read relevant MIB for GDM with multiple port attached */
if (port->id == AIROHA_GDM3_IDX || port->id == AIROHA_GDM4_IDX)
airoha_fe_rmw(eth, REG_FE_GDM_MIB_CFG(port->id),
@@ -1701,152 +1703,157 @@ static void airoha_dev_get_hw_stats(struct airoha_gdm_dev *dev)
u64_stats_update_begin(&dev->stats.syncp);
- /* TX */
+ /* TX - 64-bit H+L registers: hw accumulates the total, read directly. */
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_H(port->id));
- dev->stats.tx_ok_pkts += ((u64)val << 32);
+ dev->stats.tx_ok_pkts = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_PKT_CNT_L(port->id));
dev->stats.tx_ok_pkts += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_H(port->id));
- dev->stats.tx_ok_bytes += ((u64)val << 32);
+ dev->stats.tx_ok_bytes = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_OK_BYTE_CNT_L(port->id));
dev->stats.tx_ok_bytes += val;
+ /* TX - 32-bit registers: accumulate delta to handle wrap-around. */
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_DROP_CNT(port->id));
- dev->stats.tx_drops += val;
+ dev->stats.tx_drops += (u32)(val - dev->stats.mib_prev.tx_drops);
+ dev->stats.mib_prev.tx_drops = val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_BC_CNT(port->id));
- dev->stats.tx_broadcast += val;
+ dev->stats.tx_broadcast += (u32)(val - dev->stats.mib_prev.tx_broadcast);
+ dev->stats.mib_prev.tx_broadcast = val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_MC_CNT(port->id));
- dev->stats.tx_multicast += val;
+ dev->stats.tx_multicast += (u32)(val - dev->stats.mib_prev.tx_multicast);
+ dev->stats.mib_prev.tx_multicast = val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_RUNT_CNT(port->id));
- dev->stats.tx_len[i] += val;
+ dev->stats.mib_prev.tx_runt64 +=
+ (u32)(val - dev->stats.mib_prev.tx_runt);
+ dev->stats.mib_prev.tx_runt = val;
+ /* tx_len[0]: RUNT (32-bit, delta) + E64 (64-bit, absolute). */
+ dev->stats.tx_len[i] = dev->stats.mib_prev.tx_runt64;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_H(port->id));
- dev->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] += (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_E64_CNT_L(port->id));
dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_H(port->id));
- dev->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L64_CNT_L(port->id));
dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_H(port->id));
- dev->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L127_CNT_L(port->id));
dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_H(port->id));
- dev->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L255_CNT_L(port->id));
dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_H(port->id));
- dev->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L511_CNT_L(port->id));
dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_H(port->id));
- dev->stats.tx_len[i] += ((u64)val << 32);
+ dev->stats.tx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_L1023_CNT_L(port->id));
dev->stats.tx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_TX_ETH_LONG_CNT(port->id));
- dev->stats.tx_len[i++] += val;
+ dev->stats.tx_len[i++] += (u32)(val - dev->stats.mib_prev.tx_long);
+ dev->stats.mib_prev.tx_long = val;
/* RX */
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_H(port->id));
- dev->stats.rx_ok_pkts += ((u64)val << 32);
+ dev->stats.rx_ok_pkts = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_PKT_CNT_L(port->id));
dev->stats.rx_ok_pkts += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_H(port->id));
- dev->stats.rx_ok_bytes += ((u64)val << 32);
+ dev->stats.rx_ok_bytes = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OK_BYTE_CNT_L(port->id));
dev->stats.rx_ok_bytes += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_DROP_CNT(port->id));
- dev->stats.rx_drops += val;
+ dev->stats.rx_drops += (u32)(val - dev->stats.mib_prev.rx_drops);
+ dev->stats.mib_prev.rx_drops = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_BC_CNT(port->id));
- dev->stats.rx_broadcast += val;
+ dev->stats.rx_broadcast += (u32)(val - dev->stats.mib_prev.rx_broadcast);
+ dev->stats.mib_prev.rx_broadcast = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_MC_CNT(port->id));
- dev->stats.rx_multicast += val;
+ dev->stats.rx_multicast += (u32)(val - dev->stats.mib_prev.rx_multicast);
+ dev->stats.mib_prev.rx_multicast = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ERROR_DROP_CNT(port->id));
- dev->stats.rx_errors += val;
+ dev->stats.rx_errors += (u32)(val - dev->stats.mib_prev.rx_errors);
+ dev->stats.mib_prev.rx_errors = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_CRC_ERR_CNT(port->id));
- dev->stats.rx_crc_error += val;
+ dev->stats.rx_crc_error += (u32)(val - dev->stats.mib_prev.rx_crc_error);
+ dev->stats.mib_prev.rx_crc_error = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_OVERFLOW_DROP_CNT(port->id));
- dev->stats.rx_over_errors += val;
+ dev->stats.rx_over_errors += (u32)(val - dev->stats.mib_prev.rx_over_errors);
+ dev->stats.mib_prev.rx_over_errors = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_FRAG_CNT(port->id));
- dev->stats.rx_fragment += val;
+ dev->stats.rx_fragment += (u32)(val - dev->stats.mib_prev.rx_fragment);
+ dev->stats.mib_prev.rx_fragment = val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_JABBER_CNT(port->id));
- dev->stats.rx_jabber += val;
+ dev->stats.rx_jabber += (u32)(val - dev->stats.mib_prev.rx_jabber);
+ dev->stats.mib_prev.rx_jabber = val;
i = 0;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_RUNT_CNT(port->id));
- dev->stats.rx_len[i] += val;
+ dev->stats.mib_prev.rx_runt64 +=
+ (u32)(val - dev->stats.mib_prev.rx_runt);
+ dev->stats.mib_prev.rx_runt = val;
+ /* rx_len[0]: RUNT (32-bit, delta) + E64 (64-bit, absolute). */
+ dev->stats.rx_len[i] = dev->stats.mib_prev.rx_runt64;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_H(port->id));
- dev->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] += (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_E64_CNT_L(port->id));
dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_H(port->id));
- dev->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L64_CNT_L(port->id));
dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_H(port->id));
- dev->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L127_CNT_L(port->id));
dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_H(port->id));
- dev->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L255_CNT_L(port->id));
dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_H(port->id));
- dev->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L511_CNT_L(port->id));
dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_H(port->id));
- dev->stats.rx_len[i] += ((u64)val << 32);
+ dev->stats.rx_len[i] = (u64)val << 32;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_L1023_CNT_L(port->id));
dev->stats.rx_len[i++] += val;
val = airoha_fe_rr(eth, REG_FE_GDM_RX_ETH_LONG_CNT(port->id));
- dev->stats.rx_len[i++] += val;
+ dev->stats.rx_len[i] += (u32)(val - dev->stats.mib_prev.rx_long);
+ dev->stats.mib_prev.rx_long = val;
u64_stats_update_end(&dev->stats.syncp);
-}
-
-static void airoha_update_hw_stats(struct airoha_gdm_dev *dev)
-{
- struct airoha_gdm_port *port = dev->port;
- int i;
-
- spin_lock(&port->stats_lock);
-
- for (i = 0; i < ARRAY_SIZE(port->devs); i++) {
- if (port->devs[i])
- airoha_dev_get_hw_stats(port->devs[i]);
- }
-
- /* Reset MIB counters */
- airoha_fe_set(dev->eth, REG_FE_GDM_MIB_CLEAR(port->id),
- FE_GDM_MIB_RX_CLEAR_MASK | FE_GDM_MIB_TX_CLEAR_MASK);
spin_unlock(&port->stats_lock);
}
diff --git a/drivers/net/ethernet/airoha/airoha_eth.h b/drivers/net/ethernet/airoha/airoha_eth.h
index f6d01a8e8da1..dae9cfa6cc06 100644
--- a/drivers/net/ethernet/airoha/airoha_eth.h
+++ b/drivers/net/ethernet/airoha/airoha_eth.h
@@ -245,6 +245,33 @@ struct airoha_hw_stats {
u64 rx_fragment;
u64 rx_jabber;
u64 rx_len[7];
+
+ struct {
+ /* Previous HW register values for 32-bit counter delta
+ * tracking. Storing the last seen value and accumulating
+ * (u32)(curr - prev) into the 64-bit software counter
+ * handles wrap-around transparently via unsigned arithmetic.
+ * tx_runt64/rx_runt64 hold the running sum of runt deltas.
+ * These fields are never reported to userspace.
+ */
+ u32 tx_drops;
+ u32 tx_broadcast;
+ u32 tx_multicast;
+ u32 tx_runt;
+ u32 tx_long;
+ u64 tx_runt64;
+ u32 rx_drops;
+ u32 rx_broadcast;
+ u32 rx_multicast;
+ u32 rx_errors;
+ u32 rx_crc_error;
+ u32 rx_over_errors;
+ u32 rx_fragment;
+ u32 rx_jabber;
+ u32 rx_runt;
+ u32 rx_long;
+ u64 rx_runt64;
+ } mib_prev;
};
enum {
base-commit: a225f8c20712713406ae47024b8df42deacddd4a
--
2.43.0
^ permalink raw reply related
* Re: [PATCH 2/2] ARM: dts: aspeed: Add NVIDIA VR-NVL BMC
From: Andrew Lunn @ 2026-07-02 17:16 UTC (permalink / raw)
To: Jacky Huang
Cc: Andrew Jeffery, Joel Stanley, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, devicetree, linux-aspeed, linux-arm-kernel,
linux-kernel
In-Reply-To: <20260702165524.2168091-3-jackyhuang@nvidia.com>
On Fri, Jul 03, 2026 at 01:55:24AM +0900, Jacky Huang wrote:
> Add the device tree for the Aspeed AST2600 BMC for NVIDIA's Vera
> Rubin NVL compute platform.
>
> MAC0 uses phy-mode = "rgmii-id" because the on-board PHY supplies
> both RGMII internal delays. The matching U-Boot device tree does not
> enable SoC-side MAC clock delays, leaving the MAC internal delay at
> zero so the PHY-provided delay is not doubled by the MAC controller.
> +&mac0 {
> + pinctrl-names = "default";
> + pinctrl-0 = <&pinctrl_rgmii1_default>;
> + /*
> + * The on-board PHY is strapped to add both RX and TX RGMII
> + * internal delays;
No change required, just a comment. The strapping should not
matter. All Linux PHY drivers should configure the PHY based on
phy-mode, replacing the strapping settings. There have been cases
where the strapping is wrong...
> declare "rgmii-id" so the MAC does not add
> + * additional delay. The same setting is applied in the
> + * matching u-boot DTS to keep early-boot networking working.
> + */
> + phy-mode = "rgmii-id";
> + phy-handle = <ðphy0>;
> + status = "okay";
> +};
> +
> +&mdio0 {
> + status = "okay";
> +
> + ethphy0: ethernet-phy@0 {
> + compatible = "ethernet-phy-ieee802.3-c22";
> + reg = <0>;
> + };
> +};
For these nodes only:
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Andrew
^ permalink raw reply
* Re: [PATCH RFC 4/8] clk: sunxi-ng: a733: Add PLL clocks support
From: Enzo Adriano @ 2026-07-02 17:10 UTC (permalink / raw)
To: Junhui Liu
Cc: Andre Przywara, Brian Masney, Michael Turquette, Stephen Boyd,
Chen-Yu Tsai, Jernej Skrabec, Samuel Holland, Philipp Zabel,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Richard Cochran,
linux-clk, linux-arm-kernel, linux-sunxi, linux-riscv, devicetree,
netdev, linux-kernel
In-Reply-To: <20260310-a733-clk-v1-4-36b4e9b24457@pigmoral.tech>
Hi Junhui,
Register check for the PLLs against the public A733 User Manual V0.92:
13 of the 14 PLL control registers match the manual's offsets
(PLL_DDR 0x0020 through PLL_DE 0x02E0, section 4.1.6.1 onwards).
The one exception is pll-ref at 0x0000: the manual's CCU register list
starts at 0x0020 (PLL_DDR), so the PLL_REF control register is not in
the public V0.92 document. It does match the vendor kernel's CCU
(SUN60IW2_PLL_REF_CTRL_REG 0x0000), so a short provenance note near the
definition might help future readers, same as for the other
vendor-sourced entries discussed in this thread. For what it's worth,
on a Radxa Cubie A7S (26 MHz DCXO) we can confirm at runtime that
pll-ref produces the normalized 24 MHz reference with the hosc-side
clocks reading 26 MHz, so the modeling demonstrably works on hardware.
Thanks,
Enzo
^ permalink raw reply
* [PATCH] perf: arm_spe: Make wakeup range check overflow safe
From: Leo Yan @ 2026-07-02 17:05 UTC (permalink / raw)
To: Will Deacon, Mark Rutland, linux-arm-kernel, linux-perf-users,
linux-kernel
Cc: Leo Yan
The current code checks whether the wakeup point is in the current
writable range by comparing it with handle->head + handle->size.
The perf AUX head is a monotonically increasing index, so that addition
can overflow when head is close to ULONG_MAX. In that case, a wakeup
point which is still inside the free space range can be missed.
Use unsigned subtraction to compare the distance from head to wakeup
against the handle->size. This can dismiss the issue when addition
overflow.
This is unlikely to happen in practice, but the change makes the
watermark check logically correct.
Fixes: d5d9696b0380 ("drivers/perf: Add support for ARMv8.2 Statistical Profiling Extension")
Signed-off-by: Leo Yan <leo.yan@arm.com>
---
drivers/perf/arm_spe_pmu.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/perf/arm_spe_pmu.c b/drivers/perf/arm_spe_pmu.c
index dbd0da111639..b64cf2313a20 100644
--- a/drivers/perf/arm_spe_pmu.c
+++ b/drivers/perf/arm_spe_pmu.c
@@ -577,7 +577,7 @@ static u64 __arm_spe_pmu_next_off(struct perf_output_handle *handle)
* the page boundary following it. Keep the tail boundary if
* that's lower.
*/
- if (handle->wakeup < (handle->head + handle->size) && head <= wakeup)
+ if ((handle->wakeup - handle->head) < handle->size && head <= wakeup)
limit = min(limit, round_up(wakeup, PAGE_SIZE));
if (limit > head)
--
2.34.1
^ permalink raw reply related
* Re: [PATCH v2 05/19] ARM: turn CONFIG_ATAGS off by default
From: Aaro Koskinen @ 2026-07-02 17:00 UTC (permalink / raw)
To: Arnd Bergmann
Cc: linux-arm-kernel, soc, linux-kernel, Arnd Bergmann,
Alexander Sverdlin, Alexandre Belloni, Alexandre Torgue,
Andrew Lunn, Ard Biesheuvel, Claudiu Beznea, Daniel Mack,
Ethan Nelson-Moore, Frank Li, Gregory Clement, Haojian Zhuang,
Jeremy J. Peper, Kristoffer Ericson, Krzysztof Kozlowski,
Linus Walleij, Mark Brown, Marc Zyngier, Mike Rapoport,
Nicolas Ferre, Patrice Chotard, Ralph Siemsen, Robert Jarzmik,
Russell King, Sascha Hauer, Sebastian Hesselbarth, Stefan Agner,
Stefan Wiehler, Tony Lindgren, Vladimir Zapolskiy, Will Deacon,
Linus Walleij
In-Reply-To: <20260701212353.2196041-6-arnd@kernel.org>
Hi,
On Wed, Jul 01, 2026 at 11:23:39PM +0200, Arnd Bergmann wrote:
> From: Arnd Bergmann <arnd@arndb.de>
>
> Only a very small number of machines remain that rely on
> traditional board files with the ATAGS boot data instead
> of devicetree, and we have not accepted new ones for
> a decade.
>
> Most of the remaining ones are on their way out of the
> kernel, but the Kconfig option that controls ATAGS
> is still enabled by default.
>
> To reflect the actual status of ATAGS support and alert
> the remaining users of upstream and out-of-tree platforms
> with this, chanyge it to being default-disabled and
> add a dependency on CONFIG_EXPERT.
>
> The board specific defconfig files that rely on ATAGS
> are changed to turn on both EXPERT and ATAGS if they
> were not doing thath already, and the ones that explicitly
> turned it off before no longer need to.
>
> Acked-by: Linus Walleij <linus.walleij@linaro.org>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: Aaro Koskinen <aaro.koskinen@iki.fi> # OMAP
A.
> ---
> arch/arm/Kconfig | 13 ++++++++-----
> arch/arm/configs/am200epdkit_defconfig | 1 +
> arch/arm/configs/aspeed_g4_defconfig | 1 -
> arch/arm/configs/aspeed_g5_defconfig | 1 -
> arch/arm/configs/assabet_defconfig | 2 ++
> arch/arm/configs/at91_dt_defconfig | 1 -
> arch/arm/configs/collie_defconfig | 1 +
> arch/arm/configs/dove_defconfig | 1 +
> arch/arm/configs/footbridge_defconfig | 1 +
> arch/arm/configs/h3600_defconfig | 2 ++
> arch/arm/configs/integrator_defconfig | 1 -
> arch/arm/configs/jornada720_defconfig | 2 ++
> arch/arm/configs/moxart_defconfig | 1 -
> arch/arm/configs/mps2_defconfig | 1 -
> arch/arm/configs/multi_v4t_defconfig | 1 -
> arch/arm/configs/mv78xx0_defconfig | 1 +
> arch/arm/configs/neponset_defconfig | 2 ++
> arch/arm/configs/netwinder_defconfig | 2 ++
> arch/arm/configs/omap1_defconfig | 1 +
> arch/arm/configs/rpc_defconfig | 2 ++
> arch/arm/configs/s3c6400_defconfig | 2 ++
> arch/arm/configs/sama5_defconfig | 1 -
> arch/arm/configs/sama7_defconfig | 1 -
> arch/arm/configs/spitz_defconfig | 1 +
> arch/arm/configs/stm32_defconfig | 3 ++-
> arch/arm/configs/wpcm450_defconfig | 1 -
> 26 files changed, 31 insertions(+), 16 deletions(-)
>
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 3b2316dc9d13..0033d861e378 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -1390,12 +1390,15 @@ config ARCH_WANT_FLAT_DTB_INSTALL
>
> config ATAGS
> bool "Support for the traditional ATAGS boot data passing"
> - default y
> + depends on EXPERT
> help
> - This is the traditional way of passing data to the kernel at boot
> - time. If you are solely relying on the flattened device tree (or
> - the ARM_ATAG_DTB_COMPAT option) then you may unselect this option
> - to remove ATAGS support from your kernel binary.
> + This is the traditional way of passing data to the kernel at
> + boot but is now only used by a few of the oldest ARMv4 and
> + ARMv5 machines. Users that still rely on ATAGS should plan
> + on migrating to devicetree based booting.
> +
> + Support for ATAGS will be removed when the last machine using
> + it is either converted to DT or removed.
>
> config DEPRECATED_PARAM_STRUCT
> bool "Provide old way to pass kernel parameters"
> diff --git a/arch/arm/configs/am200epdkit_defconfig b/arch/arm/configs/am200epdkit_defconfig
> index d4745b0f3dcb..61beac88e863 100644
> --- a/arch/arm/configs/am200epdkit_defconfig
> +++ b/arch/arm/configs/am200epdkit_defconfig
> @@ -7,6 +7,7 @@ CONFIG_EXPERT=y
> # CONFIG_ARCH_MULTI_V7 is not set
> CONFIG_ARCH_PXA=y
> CONFIG_ARCH_GUMSTIX=y
> +CONFIG_ATAGS=y
> CONFIG_CMDLINE="console=ttyS0,115200n8 root=1f01 rootfstype=jffs2"
> CONFIG_MODULES=y
> CONFIG_MODULE_UNLOAD=y
> diff --git a/arch/arm/configs/aspeed_g4_defconfig b/arch/arm/configs/aspeed_g4_defconfig
> index 3dcb80157f77..f86dd4ce7d0d 100644
> --- a/arch/arm/configs/aspeed_g4_defconfig
> +++ b/arch/arm/configs/aspeed_g4_defconfig
> @@ -23,7 +23,6 @@ CONFIG_ARCH_ASPEED=y
> CONFIG_MACH_ASPEED_G4=y
> CONFIG_VMSPLIT_2G=y
> CONFIG_UACCESS_WITH_MEMCPY=y
> -# CONFIG_ATAGS is not set
> CONFIG_JUMP_LABEL=y
> CONFIG_STRICT_KERNEL_RWX=y
> # CONFIG_BLK_DEBUG_FS is not set
> diff --git a/arch/arm/configs/aspeed_g5_defconfig b/arch/arm/configs/aspeed_g5_defconfig
> index ec558e57d081..45b937419dbd 100644
> --- a/arch/arm/configs/aspeed_g5_defconfig
> +++ b/arch/arm/configs/aspeed_g5_defconfig
> @@ -29,7 +29,6 @@ CONFIG_VMSPLIT_2G=y
> CONFIG_NR_CPUS=2
> CONFIG_HIGHMEM=y
> CONFIG_UACCESS_WITH_MEMCPY=y
> -# CONFIG_ATAGS is not set
> CONFIG_VFP=y
> CONFIG_NEON=y
> CONFIG_KERNEL_MODE_NEON=y
> diff --git a/arch/arm/configs/assabet_defconfig b/arch/arm/configs/assabet_defconfig
> index df63889b0c4c..e4264fdac2e5 100644
> --- a/arch/arm/configs/assabet_defconfig
> +++ b/arch/arm/configs/assabet_defconfig
> @@ -1,11 +1,13 @@
> CONFIG_SYSVIPC=y
> CONFIG_LOG_BUF_SHIFT=14
> CONFIG_BLK_DEV_INITRD=y
> +CONFIG_EXPERT=y
> CONFIG_ARCH_MULTI_V4=y
> # CONFIG_ARCH_MULTI_V7 is not set
> CONFIG_ARCH_SA1100=y
> CONFIG_SA1100_ASSABET=y
> # CONFIG_AEABI is not set
> +CONFIG_ATAGS=y
> CONFIG_CMDLINE="mem=32M console=ttySA0,38400n8 initrd=0xc0800000,3M root=/dev/ram"
> CONFIG_FPE_NWFPE=y
> CONFIG_PM=y
> diff --git a/arch/arm/configs/at91_dt_defconfig b/arch/arm/configs/at91_dt_defconfig
> index 320eb27a6a2e..830c6c0dbdaf 100644
> --- a/arch/arm/configs/at91_dt_defconfig
> +++ b/arch/arm/configs/at91_dt_defconfig
> @@ -19,7 +19,6 @@ CONFIG_SOC_SAM9X60=y
> CONFIG_SOC_SAM9X7=y
> # CONFIG_ATMEL_CLOCKSOURCE_PIT is not set
> CONFIG_UACCESS_WITH_MEMCPY=y
> -# CONFIG_ATAGS is not set
> CONFIG_ARM_APPENDED_DTB=y
> CONFIG_ARM_ATAG_DTB_COMPAT=y
> CONFIG_CMDLINE="console=ttyS0,115200 initrd=0x21100000,25165824 root=/dev/ram0 rw"
> diff --git a/arch/arm/configs/collie_defconfig b/arch/arm/configs/collie_defconfig
> index 165202960438..570a5f8bc57b 100644
> --- a/arch/arm/configs/collie_defconfig
> +++ b/arch/arm/configs/collie_defconfig
> @@ -9,6 +9,7 @@ CONFIG_ARCH_MULTI_V4=y
> # CONFIG_ARCH_MULTI_V7 is not set
> CONFIG_ARCH_SA1100=y
> CONFIG_SA1100_COLLIE=y
> +CONFIG_ATAGS=y
> CONFIG_CMDLINE="noinitrd root=/dev/mtdblock2 rootfstype=jffs2 fbcon=rotate:1"
> CONFIG_PM=y
> # CONFIG_SWAP is not set
> diff --git a/arch/arm/configs/dove_defconfig b/arch/arm/configs/dove_defconfig
> index 08271e6719d7..917a864fec74 100644
> --- a/arch/arm/configs/dove_defconfig
> +++ b/arch/arm/configs/dove_defconfig
> @@ -8,6 +8,7 @@ CONFIG_ARCH_MULTI_V7=y
> CONFIG_ARCH_DOVE=y
> CONFIG_MACH_CM_A510=y
> CONFIG_HIGHMEM=y
> +CONFIG_ATAGS=y
> CONFIG_ARM_APPENDED_DTB=y
> CONFIG_ARM_ATAG_DTB_COMPAT=y
> CONFIG_VFP=y
> diff --git a/arch/arm/configs/footbridge_defconfig b/arch/arm/configs/footbridge_defconfig
> index e8bf9847d35d..0872dba5de79 100644
> --- a/arch/arm/configs/footbridge_defconfig
> +++ b/arch/arm/configs/footbridge_defconfig
> @@ -10,6 +10,7 @@ CONFIG_ARCH_FOOTBRIDGE=y
> CONFIG_ARCH_EBSA285_HOST=y
> CONFIG_ARCH_NETWINDER=y
> # CONFIG_AEABI is not set
> +CONFIG_ATAGS=y
> CONFIG_FPE_NWFPE=y
> CONFIG_FPE_NWFPE_XP=y
> CONFIG_MODULES=y
> diff --git a/arch/arm/configs/h3600_defconfig b/arch/arm/configs/h3600_defconfig
> index 0923d331190a..3e8da62d959d 100644
> --- a/arch/arm/configs/h3600_defconfig
> +++ b/arch/arm/configs/h3600_defconfig
> @@ -4,11 +4,13 @@ CONFIG_HIGH_RES_TIMERS=y
> CONFIG_PREEMPT=y
> CONFIG_LOG_BUF_SHIFT=14
> CONFIG_BLK_DEV_INITRD=y
> +CONFIG_EXPERT=y
> CONFIG_ARCH_MULTI_V4=y
> # CONFIG_ARCH_MULTI_V7 is not set
> CONFIG_ARCH_SA1100=y
> CONFIG_SA1100_H3600=y
> # CONFIG_CPU_FREQ_STAT is not set
> +CONFIG_ATAGS=y
> CONFIG_MODULES=y
> CONFIG_NET=y
> CONFIG_UNIX=y
> diff --git a/arch/arm/configs/integrator_defconfig b/arch/arm/configs/integrator_defconfig
> index ba38ec810a61..82180381bf3c 100644
> --- a/arch/arm/configs/integrator_defconfig
> +++ b/arch/arm/configs/integrator_defconfig
> @@ -13,7 +13,6 @@ CONFIG_ARCH_INTEGRATOR=y
> CONFIG_ARCH_INTEGRATOR_AP=y
> CONFIG_INTEGRATOR_IMPD1=y
> CONFIG_ARCH_INTEGRATOR_CP=y
> -# CONFIG_ATAGS is not set
> CONFIG_CMDLINE="console=ttyAM0,38400n8 root=/dev/nfs ip=bootp"
> CONFIG_CPU_FREQ=y
> CONFIG_CPU_FREQ_GOV_POWERSAVE=y
> diff --git a/arch/arm/configs/jornada720_defconfig b/arch/arm/configs/jornada720_defconfig
> index 0ebddd083b36..a96cfb81aba5 100644
> --- a/arch/arm/configs/jornada720_defconfig
> +++ b/arch/arm/configs/jornada720_defconfig
> @@ -1,11 +1,13 @@
> CONFIG_SYSVIPC=y
> CONFIG_LOG_BUF_SHIFT=14
> +CONFIG_EXPERT=y
> CONFIG_ARCH_MULTI_V4=y
> # CONFIG_ARCH_MULTI_V7 is not set
> CONFIG_ARCH_SA1100=y
> CONFIG_SA1100_JORNADA720=y
> CONFIG_SA1100_JORNADA720_SSP=y
> CONFIG_PM=y
> +CONFIG_ATAGS=y
> CONFIG_MODULES=y
> CONFIG_NET=y
> CONFIG_PACKET=y
> diff --git a/arch/arm/configs/moxart_defconfig b/arch/arm/configs/moxart_defconfig
> index d97d9ea5c34d..699d7ad98dd2 100644
> --- a/arch/arm/configs/moxart_defconfig
> +++ b/arch/arm/configs/moxart_defconfig
> @@ -16,7 +16,6 @@ CONFIG_ARCH_MULTI_V4=y
> # CONFIG_ARCH_MULTI_V7 is not set
> CONFIG_ARCH_MOXART=y
> CONFIG_MACH_UC7112LX=y
> -# CONFIG_ATAGS is not set
> CONFIG_ARM_APPENDED_DTB=y
> # CONFIG_SWAP is not set
> # CONFIG_COMPAT_BRK is not set
> diff --git a/arch/arm/configs/mps2_defconfig b/arch/arm/configs/mps2_defconfig
> index e995e50537ef..cacf3876f6e4 100644
> --- a/arch/arm/configs/mps2_defconfig
> +++ b/arch/arm/configs/mps2_defconfig
> @@ -16,7 +16,6 @@ CONFIG_ARCH_MPS2=y
> CONFIG_SET_MEM_PARAM=y
> CONFIG_DRAM_BASE=0x21000000
> CONFIG_DRAM_SIZE=0x1000000
> -# CONFIG_ATAGS is not set
> # CONFIG_SUSPEND is not set
> # CONFIG_BLOCK is not set
> CONFIG_BINFMT_FLAT=y
> diff --git a/arch/arm/configs/multi_v4t_defconfig b/arch/arm/configs/multi_v4t_defconfig
> index 14c93bb59ad6..7b592345db21 100644
> --- a/arch/arm/configs/multi_v4t_defconfig
> +++ b/arch/arm/configs/multi_v4t_defconfig
> @@ -17,7 +17,6 @@ CONFIG_INTEGRATOR_IMPD1=y
> CONFIG_INTEGRATOR_CM720T=y
> CONFIG_INTEGRATOR_CM920T=y
> CONFIG_INTEGRATOR_CM922T_XA10=y
> -# CONFIG_ATAGS is not set
> CONFIG_CPU_IDLE=y
> CONFIG_ARM_CPUIDLE=y
> CONFIG_ARM_CLPS711X_CPUIDLE=y
> diff --git a/arch/arm/configs/mv78xx0_defconfig b/arch/arm/configs/mv78xx0_defconfig
> index 823f7963b8b3..e3cc8de7c1c2 100644
> --- a/arch/arm/configs/mv78xx0_defconfig
> +++ b/arch/arm/configs/mv78xx0_defconfig
> @@ -12,6 +12,7 @@ CONFIG_ARCH_MULTI_V5=y
> CONFIG_ARCH_MV78XX0=y
> CONFIG_MACH_TERASTATION_WXL=y
> CONFIG_HIGHMEM=y
> +CONFIG_ATAGS=y
> CONFIG_VFP=y
> CONFIG_KPROBES=y
> CONFIG_MODULES=y
> diff --git a/arch/arm/configs/neponset_defconfig b/arch/arm/configs/neponset_defconfig
> index 16f0afd770c1..d5d6d57f8fea 100644
> --- a/arch/arm/configs/neponset_defconfig
> +++ b/arch/arm/configs/neponset_defconfig
> @@ -1,12 +1,14 @@
> CONFIG_SYSVIPC=y
> CONFIG_LOG_BUF_SHIFT=14
> CONFIG_BLK_DEV_INITRD=y
> +CONFIG_EXPERT=y
> CONFIG_ARCH_MULTI_V4=y
> # CONFIG_ARCH_MULTI_V7 is not set
> CONFIG_ARCH_SA1100=y
> CONFIG_SA1100_ASSABET=y
> CONFIG_ASSABET_NEPONSET=y
> # CONFIG_AEABI is not set
> +CONFIG_ATAGS=y
> CONFIG_ZBOOT_ROM_TEXT=0x80000
> CONFIG_ZBOOT_ROM_BSS=0xc1000000
> CONFIG_ZBOOT_ROM=y
> diff --git a/arch/arm/configs/netwinder_defconfig b/arch/arm/configs/netwinder_defconfig
> index 7ff70439458d..77d4a2b5916b 100644
> --- a/arch/arm/configs/netwinder_defconfig
> +++ b/arch/arm/configs/netwinder_defconfig
> @@ -1,10 +1,12 @@
> CONFIG_SYSVIPC=y
> CONFIG_LOG_BUF_SHIFT=14
> +CONFIG_EXPERT=y
> CONFIG_ARCH_MULTI_V4=y
> # CONFIG_ARCH_MULTI_V7 is not set
> CONFIG_ARCH_FOOTBRIDGE=y
> CONFIG_ARCH_NETWINDER=y
> # CONFIG_AEABI is not set
> +CONFIG_ATAGS=y
> CONFIG_DEPRECATED_PARAM_STRUCT=y
> CONFIG_CMDLINE="root=0x801"
> CONFIG_FPE_NWFPE=y
> diff --git a/arch/arm/configs/omap1_defconfig b/arch/arm/configs/omap1_defconfig
> index 8d11ba724204..fcbaaf37bc9b 100644
> --- a/arch/arm/configs/omap1_defconfig
> +++ b/arch/arm/configs/omap1_defconfig
> @@ -28,6 +28,7 @@ CONFIG_MACH_OMAP_PALMTE=y
> CONFIG_MACH_SX1=y
> CONFIG_MACH_NOKIA770=y
> CONFIG_MACH_AMS_DELTA=y
> +CONFIG_ATAGS=y
> CONFIG_CMDLINE="root=1f03 rootfstype=jffs2"
> # CONFIG_SUSPEND is not set
> CONFIG_PM=y
> diff --git a/arch/arm/configs/rpc_defconfig b/arch/arm/configs/rpc_defconfig
> index 8d73ec43cb41..edb52d56f679 100644
> --- a/arch/arm/configs/rpc_defconfig
> +++ b/arch/arm/configs/rpc_defconfig
> @@ -2,12 +2,14 @@
> CONFIG_SYSVIPC=y
> CONFIG_LOG_BUF_SHIFT=14
> CONFIG_BLK_DEV_INITRD=y
> +CONFIG_EXPERT=y
> CONFIG_ARCH_MULTI_V4=y
> # CONFIG_ARCH_MULTI_V7 is not set
> CONFIG_ARCH_RPC=y
> CONFIG_CPU_SA110=y
> CONFIG_FPE_NWFPE=y
> # CONFIG_AEABI is not set
> +CONFIG_ATAGS=y
> CONFIG_MODULES=y
> CONFIG_MODULE_UNLOAD=y
> CONFIG_PARTITION_ADVANCED=y
> diff --git a/arch/arm/configs/s3c6400_defconfig b/arch/arm/configs/s3c6400_defconfig
> index 7bf28a83946a..a0707707d960 100644
> --- a/arch/arm/configs/s3c6400_defconfig
> +++ b/arch/arm/configs/s3c6400_defconfig
> @@ -2,8 +2,10 @@ CONFIG_BLK_DEV_INITRD=y
> CONFIG_KALLSYMS_ALL=y
> CONFIG_ARCH_MULTI_V6=y
> # CONFIG_ARCH_MULTI_V7 is not set
> +CONFIG_EXPERT=y
> CONFIG_ARCH_S3C64XX=y
> CONFIG_MACH_WLF_CRAGG_6410=y
> +CONFIG_ATAGS=y
> CONFIG_CMDLINE="console=ttySAC0,115200 root=/dev/ram init=/linuxrc initrd=0x51000000,6M ramdisk_size=6144"
> CONFIG_VFP=y
> CONFIG_MODULES=y
> diff --git a/arch/arm/configs/sama5_defconfig b/arch/arm/configs/sama5_defconfig
> index bd7f0b5f7d66..1eafad9139f1 100644
> --- a/arch/arm/configs/sama5_defconfig
> +++ b/arch/arm/configs/sama5_defconfig
> @@ -13,7 +13,6 @@ CONFIG_SOC_SAMA5D3=y
> CONFIG_SOC_SAMA5D4=y
> # CONFIG_ATMEL_CLOCKSOURCE_PIT is not set
> CONFIG_UACCESS_WITH_MEMCPY=y
> -# CONFIG_ATAGS is not set
> CONFIG_CMDLINE="console=ttyS0,115200 initrd=0x21100000,25165824 root=/dev/ram0 rw"
> CONFIG_VFP=y
> CONFIG_NEON=y
> diff --git a/arch/arm/configs/sama7_defconfig b/arch/arm/configs/sama7_defconfig
> index cc7f6daf9fc7..4af87c57483d 100644
> --- a/arch/arm/configs/sama7_defconfig
> +++ b/arch/arm/configs/sama7_defconfig
> @@ -20,7 +20,6 @@ CONFIG_ATMEL_CLOCKSOURCE_TCB=y
> # CONFIG_CPU_SW_DOMAIN_PAN is not set
> CONFIG_ARCH_FORCE_MAX_ORDER=14
> CONFIG_UACCESS_WITH_MEMCPY=y
> -# CONFIG_ATAGS is not set
> CONFIG_CMDLINE="console=ttyS0,115200 earlyprintk ignore_loglevel"
> CONFIG_CPU_FREQ=y
> CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE=y
> diff --git a/arch/arm/configs/spitz_defconfig b/arch/arm/configs/spitz_defconfig
> index d68a8f9cdad4..c7cf089001bf 100644
> --- a/arch/arm/configs/spitz_defconfig
> +++ b/arch/arm/configs/spitz_defconfig
> @@ -10,6 +10,7 @@ CONFIG_ARCH_PXA=y
> CONFIG_PXA_SHARPSL=y
> CONFIG_MACH_AKITA=y
> CONFIG_MACH_BORZOI=y
> +CONFIG_ATAGS=y
> CONFIG_CMDLINE="console=ttyS0,115200n8 console=tty1 noinitrd root=/dev/mtdblock2 rootfstype=jffs2 debug"
> CONFIG_MODULES=y
> CONFIG_MODULE_UNLOAD=y
> diff --git a/arch/arm/configs/stm32_defconfig b/arch/arm/configs/stm32_defconfig
> index 82190b155b14..bdf3bb9e03a8 100644
> --- a/arch/arm/configs/stm32_defconfig
> +++ b/arch/arm/configs/stm32_defconfig
> @@ -20,7 +20,8 @@ CONFIG_ARCH_STM32=y
> CONFIG_CPU_V7M_NUM_IRQ=240
> CONFIG_SET_MEM_PARAM=y
> CONFIG_DRAM_BASE=0x90000000
> -# CONFIG_ATAGS is not set
> +CONFIG_FLASH_MEM_BASE=0x08000000
> +CONFIG_FLASH_SIZE=0x00200000
> CONFIG_XIP_KERNEL=y
> CONFIG_XIP_PHYS_ADDR=0x08008000
> # CONFIG_SUSPEND is not set
> diff --git a/arch/arm/configs/wpcm450_defconfig b/arch/arm/configs/wpcm450_defconfig
> index 9cb379077d70..7512ec4292a0 100644
> --- a/arch/arm/configs/wpcm450_defconfig
> +++ b/arch/arm/configs/wpcm450_defconfig
> @@ -14,7 +14,6 @@ CONFIG_ARCH_NPCM=y
> CONFIG_ARCH_WPCM450=y
> CONFIG_CPU_DCACHE_WRITETHROUGH=y
> CONFIG_UACCESS_WITH_MEMCPY=y
> -# CONFIG_ATAGS is not set
> CONFIG_ARM_APPENDED_DTB=y
> CONFIG_CPU_IDLE=y
> CONFIG_KPROBES=y
> --
> 2.39.5
>
^ permalink raw reply
* Re: [PATCH RFC 3/8] clk: sunxi-ng: a733: Add PRCM CCU
From: Enzo Adriano @ 2026-07-02 16:59 UTC (permalink / raw)
To: Junhui Liu
Cc: Andre Przywara, Brian Masney, Michael Turquette, Stephen Boyd,
Chen-Yu Tsai, Jernej Skrabec, Samuel Holland, Philipp Zabel,
Rob Herring, Krzysztof Kozlowski, Conor Dooley, Paul Walmsley,
Palmer Dabbelt, Albert Ou, Alexandre Ghiti, Richard Cochran,
linux-clk, linux-arm-kernel, linux-sunxi, linux-riscv, devicetree,
netdev, linux-kernel
In-Reply-To: <20260310-a733-clk-v1-3-36b4e9b24457@pigmoral.tech>
Hi Junhui,
Register check for the PRCM driver against the public A733 User Manual
V0.92, chapter 4.2.5: all 41 entries I could extract (13 reset-map
entries, 18 bus gates, 10 mod/mux clocks) match the manual's register
offsets and bit positions. No discrepancies found in this patch.
For what it's worth, we have also exercised the R-domain at runtime on a
Radxa Cubie A7S (r-ahb/r-apb0 rates with live consumers on r-pinctrl,
r-rtc and the PPU, bus-r-cpucfg via its critical flag), with no
misbehavior attributable to the PRCM model.
Thanks,
Enzo
^ permalink raw reply
* Re: [PATCH v2 04/19] ARM: deprecate support for ARM1136r0
From: Aaro Koskinen @ 2026-07-02 16:59 UTC (permalink / raw)
To: Arnd Bergmann
Cc: linux-arm-kernel, soc, linux-kernel, Arnd Bergmann,
Alexander Sverdlin, Alexandre Belloni, Alexandre Torgue,
Andrew Lunn, Ard Biesheuvel, Claudiu Beznea, Daniel Mack,
Ethan Nelson-Moore, Frank Li, Gregory Clement, Haojian Zhuang,
Jeremy J. Peper, Kristoffer Ericson, Krzysztof Kozlowski,
Linus Walleij, Mark Brown, Marc Zyngier, Mike Rapoport,
Nicolas Ferre, Patrice Chotard, Ralph Siemsen, Robert Jarzmik,
Russell King, Sascha Hauer, Sebastian Hesselbarth, Stefan Agner,
Stefan Wiehler, Tony Lindgren, Vladimir Zapolskiy, Will Deacon,
Linus Walleij
In-Reply-To: <20260701212353.2196041-5-arnd@kernel.org>
Hi,
On Wed, Jul 01, 2026 at 11:23:38PM +0200, Arnd Bergmann wrote:
> From: Arnd Bergmann <arnd@arndb.de>
>
> This CPU revision remains a special case as it is now incompatible
> with CONFIG_SMP. Only the Nokia N8x0 is used in practice, and even
> that one is fairly rare these days, so let's plan to remove all of
> them after the 2026 LTS kernel release.
>
> Acked-by: Linus Walleij <linus.walleij@linaro.org>
> Signed-off-by: Arnd Bergmann <arnd@arndb.de>
Acked-by: Aaro Koskinen <aaro.koskinen@iki.fi> # OMAP
A.
> ---
> arch/arm/mach-imx/Kconfig | 5 ++++-
> arch/arm/mach-omap2/Kconfig | 15 ++++++++++++---
> arch/arm/mach-versatile/Kconfig | 5 ++++-
> arch/arm/mm/Kconfig | 2 ++
> 4 files changed, 22 insertions(+), 5 deletions(-)
>
> diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
> index 041e73ad203a..f85a5f8c0a58 100644
> --- a/arch/arm/mach-imx/Kconfig
> +++ b/arch/arm/mach-imx/Kconfig
> @@ -40,13 +40,16 @@ if ARCH_MULTI_V6
> comment "ARM1136 platforms"
>
> config SOC_IMX31
> - bool "i.MX31 support"
> + bool "i.MX31 support (DEPRECATED)"
> depends on !SMP
> select CPU_ARM1136R0
> select MXC_AVIC
> help
> This enables support for Freescale i.MX31 processor
>
> + This SoC is scheduled for removal in early 2027,
> + since it uses the ARM1136r0 CPU revision.
> +
> config SOC_IMX35
> bool "i.MX35 support"
> select CPU_ARM1136R1
> diff --git a/arch/arm/mach-omap2/Kconfig b/arch/arm/mach-omap2/Kconfig
> index 13987ffbba00..79f8afe3a6bf 100644
> --- a/arch/arm/mach-omap2/Kconfig
> +++ b/arch/arm/mach-omap2/Kconfig
> @@ -207,18 +207,24 @@ comment "OMAP Core Type"
> depends on ARCH_OMAP2
>
> config SOC_OMAP2420
> - bool "OMAP2420 support"
> + bool "OMAP2420 support (DEPRECATED)"
> depends on ARCH_OMAP2
> default y
> select OMAP_DM_SYSTIMER
> select OMAP_DM_TIMER
> select SOC_HAS_OMAP2_SDRC
> + help
> + This SoC is scheduled for removal in early 2027,
> + since it uses the ARM1136r0 CPU revision.
>
> config SOC_OMAP2430
> - bool "OMAP2430 support"
> + bool "OMAP2430 support (DEPRECATED)"
> depends on ARCH_OMAP2
> default y
> select SOC_HAS_OMAP2_SDRC
> + help
> + This SoC is scheduled for removal in early 2027,
> + since it uses the ARM1136r0 CPU revision.
>
> config SOC_OMAP3430
> bool "OMAP3430 support"
> @@ -249,11 +255,14 @@ config MACH_NOKIA_N810_WIMAX
> bool
>
> config MACH_NOKIA_N8X0
> - bool "Nokia N800/N810"
> + bool "Nokia N800/N810 (DEPRECATED)"
> depends on SOC_OMAP2420
> default y
> select MACH_NOKIA_N810
> select MACH_NOKIA_N810_WIMAX
> + help
> + This machine is scheduled for removal in early 2027,
> + since it uses the ARM1136r0 CPU revision.
>
> endmenu
>
> diff --git a/arch/arm/mach-versatile/Kconfig b/arch/arm/mach-versatile/Kconfig
> index de42da7de8c8..1ee9b6608d71 100644
> --- a/arch/arm/mach-versatile/Kconfig
> +++ b/arch/arm/mach-versatile/Kconfig
> @@ -110,11 +110,14 @@ config INTEGRATOR_CM1026EJS
> select CPU_ARM1026
>
> config INTEGRATOR_CM1136JFS
> - bool "Integrator/CM1136JF-S core module"
> + bool "Integrator/CM1136JF-S core module (DEPRECATED)"
> depends on ARCH_INTEGRATOR_AP
> depends on ARCH_MULTI_V6
> depends on !SMP
> select CPU_ARM1136R0
> + help
> + This machine is scheduled for removal in early 2027,
> + since it uses the ARM1136r0 CPU revision.
>
> config ARCH_INTEGRATOR_CP
> bool "Support Integrator/CP platform"
> diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
> index f3d71e89a31f..27010b9389ad 100644
> --- a/arch/arm/mm/Kconfig
> +++ b/arch/arm/mm/Kconfig
> @@ -410,6 +410,8 @@ config CPU_ARM1136R0
> help
> These early revisions of ARM1136 lack support for the
> ARMv6k extensions for multiprocessing.
> + Support for this revision is scheduled for removal
> + from the kernel in early 2027.
>
> config CPU_ARM1136R1
> bool
> --
> 2.39.5
>
^ permalink raw reply
* [PATCH 2/2] ARM: dts: aspeed: Add NVIDIA VR-NVL BMC
From: Jacky Huang @ 2026-07-02 16:55 UTC (permalink / raw)
To: Andrew Jeffery, Joel Stanley, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: devicetree, linux-aspeed, linux-arm-kernel, linux-kernel,
Jacky Huang
In-Reply-To: <20260702165524.2168091-1-jackyhuang@nvidia.com>
Add the device tree for the Aspeed AST2600 BMC for NVIDIA's Vera
Rubin NVL compute platform.
MAC0 uses phy-mode = "rgmii-id" because the on-board PHY supplies
both RGMII internal delays. The matching U-Boot device tree does not
enable SoC-side MAC clock delays, leaving the MAC internal delay at
zero so the PHY-provided delay is not doubled by the MAC controller.
The corresponding U-Boot change has been submitted to openbmc/u-boot:
Link: https://lore.kernel.org/all/20260629105202.1417679-1-jackyhuang@nvidia.com/
Signed-off-by: Jacky Huang <jackyhuang@nvidia.com>
---
arch/arm/boot/dts/aspeed/Makefile | 1 +
.../aspeed/aspeed-bmc-nvidia-vr-nvl-bmc.dts | 901 ++++++++++++++++++
2 files changed, 902 insertions(+)
create mode 100644 arch/arm/boot/dts/aspeed/aspeed-bmc-nvidia-vr-nvl-bmc.dts
diff --git a/arch/arm/boot/dts/aspeed/Makefile b/arch/arm/boot/dts/aspeed/Makefile
index 6b68671f9379..07e10083a8bd 100644
--- a/arch/arm/boot/dts/aspeed/Makefile
+++ b/arch/arm/boot/dts/aspeed/Makefile
@@ -65,6 +65,7 @@ dtb-$(CONFIG_ARCH_ASPEED) += \
aspeed-bmc-microsoft-olympus.dtb \
aspeed-bmc-nvidia-gb200nvl-bmc.dtb \
aspeed-bmc-nvidia-msx4-bmc.dtb \
+ aspeed-bmc-nvidia-vr-nvl-bmc.dtb \
aspeed-bmc-opp-lanyang.dtb \
aspeed-bmc-opp-mowgli.dtb \
aspeed-bmc-opp-nicole.dtb \
diff --git a/arch/arm/boot/dts/aspeed/aspeed-bmc-nvidia-vr-nvl-bmc.dts b/arch/arm/boot/dts/aspeed/aspeed-bmc-nvidia-vr-nvl-bmc.dts
new file mode 100644
index 000000000000..f035e9b28f64
--- /dev/null
+++ b/arch/arm/boot/dts/aspeed/aspeed-bmc-nvidia-vr-nvl-bmc.dts
@@ -0,0 +1,901 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2026 NVIDIA Corporation
+
+/dts-v1/;
+
+#include "aspeed-g6.dtsi"
+#include <dt-bindings/gpio/aspeed-gpio.h>
+#include <dt-bindings/i2c/i2c.h>
+#include <dt-bindings/interrupt-controller/irq.h>
+#include <dt-bindings/leds/common.h>
+
+/ {
+ model = "AST2600 VR NVL BMC";
+ compatible = "nvidia,vr-nvl-bmc", "aspeed,ast2600";
+
+ aliases {
+ i2c16 = &i2c_usbhub;
+ i2c17 = &i2c_tpm;
+ i2c18 = &i2c_dp;
+ serial2 = &uart3;
+ serial4 = &uart5;
+ };
+
+ chosen {
+ stdout-path = &uart5;
+ };
+
+ memory@80000000 {
+ device_type = "memory";
+ reg = <0x80000000 0x80000000>;
+ };
+
+ reserved-memory {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ vga_memory: framebuffer@9f000000 {
+ no-map;
+ reg = <0x9f000000 0x01000000>; /* 16 MiB */
+ };
+
+ ramoops@a0000000 {
+ compatible = "ramoops";
+ reg = <0xa0000000 0x100000>; /* 1 MiB */
+ record-size = <0x10000>; /* 64 KiB */
+ max-reason = <2>; /* KMSG_DUMP_OOPS */
+ };
+
+ gfx_memory: framebuffer {
+ compatible = "shared-dma-pool";
+ size = <0x01000000>;
+ alignment = <0x01000000>;
+ reusable;
+ };
+
+ video_engine_memory: jpegbuffer {
+ compatible = "shared-dma-pool";
+ size = <0x02000000>; /* 32 MiB */
+ alignment = <0x01000000>;
+ reusable;
+ };
+ };
+
+ standby_power_regulator: regulator-standby-power {
+ compatible = "regulator-fixed";
+ regulator-name = "standby_power";
+ regulator-min-microvolt = <1800000>;
+ regulator-max-microvolt = <1800000>;
+ gpios = <&gpio0 ASPEED_GPIO(M, 3) GPIO_ACTIVE_HIGH>;
+ enable-active-high;
+ regulator-always-on;
+ regulator-boot-on;
+ };
+
+ leds {
+ compatible = "gpio-leds";
+
+ led-0 {
+ label = "uid_led";
+ gpios = <&exp4 10 GPIO_ACTIVE_LOW>;
+ };
+
+ led-1 {
+ label = "fault_led";
+ gpios = <&exp4 12 GPIO_ACTIVE_LOW>;
+ };
+
+ led-2 {
+ label = "power_led";
+ gpios = <&exp4 8 GPIO_ACTIVE_LOW>;
+ };
+ };
+};
+
+&ehci1 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ status = "okay";
+
+ hub@1 {
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ hub@1 {
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ hub@4 {
+ reg = <4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ /* port 1: BlueField-4 SMA management interface */
+ device@1 {
+ reg = <1>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ interface@1 {
+ reg = <1 1>;
+ };
+ };
+
+ cp2112g: device@2 {
+ reg = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-line-names =
+ "BF4_USB_MCU_RECOVERY_L",
+ "BF4_USB_MCU_RST_L",
+ "BF4_USB_FORCE_FNP_L",
+ "BF4_GLOBAL_WP",
+ "BF4_FORCE_POR_L",
+ "BF4_BMC_EROT_RST_L",
+ "BF4_BMC_EROT_RECOVERY_L",
+ "BF4_THERM_SHUTDOWN";
+ };
+ };
+ };
+
+ hub@2 {
+ reg = <2>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ hub@1 {
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ hub@3 {
+ reg = <3>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ device@1 {
+ reg = <1>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ iob1_sma: interface@1 {
+ reg = <1 1>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+ };
+
+ cp2112c: device@2 {
+ reg = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-line-names =
+ "IOB1_MCP_P0_2-B",
+ "IOB1_MCU_RST_L-O",
+ "IOB1_MCU_RECOVERY_L-O",
+ "IOB1_GLOBAL_WP-O",
+ "IOB1_GLOBAL_ADDR_L_R-O",
+ "IOB1_GLOBAL_ADDR_U_D-O",
+ "IOB1_PWR_EN-O",
+ "IOB1_MCU_READY_STATUS-I";
+ };
+ };
+
+ hub@4 {
+ reg = <4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ device@1 {
+ reg = <1>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ iob0_sma: interface@1 {
+ reg = <1 1>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+ };
+
+ cp2112d: device@2 {
+ reg = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-line-names =
+ "IOB0_MCP_P0_2-B",
+ "IOB0_MCU_RST_L-O",
+ "IOB0_MCU_RECOVERY_L-O",
+ "IOB0_GLOBAL_WP-O",
+ "IOB0_GLOBAL_ADDR_L_R-O",
+ "IOB0_GLOBAL_ADDR_U_D-O",
+ "IOB0_PWR_EN-O",
+ "IOB0_MCU_READY_STATUS-I";
+ };
+ };
+
+ device@1 {
+ reg = <1>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ cp2112a: interface@1 {
+ reg = <1 1>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+ };
+ };
+
+ hub@2 {
+ reg = <2>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ hub@3 {
+ reg = <3>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ device@1 {
+ reg = <1>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ iob2_sma: interface@1 {
+ reg = <1 1>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+ };
+
+ cp2112e: device@2 {
+ reg = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-line-names =
+ "IOB2_MCP_P0_2-B",
+ "IOB2_MCU_RST_L-O",
+ "IOB2_MCU_RECOVERY_L-O",
+ "IOB2_GLOBAL_WP-O",
+ "IOB2_GLOBAL_ADDR_L_R-O",
+ "IOB2_GLOBAL_ADDR_U_D-O",
+ "IOB2_PWR_EN-O",
+ "IOB2_MCU_READY_STATUS-I";
+ };
+ };
+
+ hub@4 {
+ reg = <4>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ device@1 {
+ reg = <1>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ iob3_sma: interface@1 {
+ reg = <1 1>;
+
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+ };
+
+ cp2112f: device@2 {
+ reg = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-line-names =
+ "IOB3_MCP_P0_2-B",
+ "IOB3_MCU_RST_L-O",
+ "IOB3_MCU_RECOVERY_L-O",
+ "IOB3_GLOBAL_WP-O",
+ "IOB3_GLOBAL_ADDR_L_R-O",
+ "IOB3_GLOBAL_ADDR_U_D-O",
+ "IOB3_PWR_EN-O",
+ "IOB3_MCU_READY_STATUS-I";
+ };
+ };
+
+ device@1 {
+ reg = <1>;
+ #address-cells = <2>;
+ #size-cells = <0>;
+
+ cp2112b: interface@1 {
+ reg = <1 1>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+ };
+ };
+ };
+ };
+};
+
+&fmc {
+ status = "okay";
+
+ flash@0 {
+ label = "bmc";
+ status = "okay";
+
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ partition@0 {
+ reg = <0x0 0xe0000>; /* 896 KiB */
+ label = "u-boot";
+ };
+
+ partition@100000 {
+ reg = <0x100000 0x900000>; /* 9 MiB */
+ label = "kernel";
+ };
+
+ partition@a00000 {
+ /*
+ * extends to end of 64 MiB - 4 KiB; last
+ * 4 KiB reserved for EROT image
+ */
+ reg = <0xa00000 0x35ff000>;
+ label = "rofs";
+ };
+ };
+ };
+};
+
+&gpio0 {
+ gpio-line-names =
+ /* A0-A7 */ "", "", "", "", "", "", "", "",
+ /* B0-B7 */ "", "", "", "", "", "", "", "",
+ /* C0-C7 */ "", "BF4_USB_RST_L-O", "", "", "", "", "", "",
+ /* D0-D7 */ "", "", "", "", "", "FPGA_PEX_RST_L", "", "",
+ /* E0-E7 */ "RTL8221_PHY_RST_L-O", "RTL8211_PHY_INT_L-I",
+ "", "", "", "", "", "MUX_SGPIO_SEL-O",
+ /* F0-F7 */ "", "", "", "", "", "", "", "",
+ /* G0-G7 */ "", "", "", "", "", "", "", "",
+ /* H0-H7 */ "", "", "", "", "", "", "", "",
+ /* I0-I7 */ "", "", "", "", "", "QSPI2_RST_L-O",
+ "GLOBAL_WP_BMC-I", "BMC_DDR4_TEN-O",
+ /* J0-J7 */ "", "", "", "", "", "", "", "",
+ /* K0-K7 */ "", "", "", "", "", "", "", "",
+ /* L0-L7 */ "", "", "", "", "", "", "", "",
+ /* M0-M7 */ "USB_HUB_RST_N-O", "BMC_FRU_WP-O",
+ "", "HMC_STBY_POWER_EN-O", "STBY_POWER_PG-I",
+ "PCIE_EP_RST_L-O", "", "",
+ /* N0-N7 */ "", "", "", "", "", "", "", "",
+ /* O0-O7 */ "", "", "", "", "", "", "", "",
+ /* P0-P7 */ "", "", "", "", "", "", "", "",
+ /* Q0-Q7 */ "", "", "", "", "", "", "", "",
+ /* R0-R7 */ "", "SP0_AP_INTR_N-I", "", "", "", "", "", "",
+ /* S0-S7 */ "", "", "", "", "", "", "", "",
+ /* T0-T7 */ "", "", "", "", "", "", "", "",
+ /* U0-U7 */ "", "", "", "", "", "", "", "",
+ /* V0-V7 */ "", "", "", "", "",
+ "PCB_TEMP_ALERT-I", "", "",
+ /* W0-W7 */ "", "", "", "", "", "", "", "CPU_RST_L-I",
+ /* X0-X7 */ "", "", "", "", "", "", "", "",
+ /* Y0-Y7 */ "", "", "", "EMMC_RST-O", "", "", "", "",
+ /* Z0-Z7 */ "HMC_EROT_SPI_INT_L-I",
+ "", "", "", "", "", "", "";
+};
+
+&gpio1 {
+ gpio-line-names =
+ /* A0-A7 */ "", "", "", "", "", "", "", "",
+ /* B0-B7 */ "", "", "", "", "AP_EROT_REQ-O",
+ "EROT_AP_GNT-I", "IO_EXPANDER_INT_L-I", "",
+ /* C0-C7 */ "", "", "", "", "", "", "", "",
+ /* D0-D7 */ "", "", "", "", "", "", "",
+ "I2C_SSIF_ALERT_L-I",
+ /* E0-E7 */ "", "", "", "", "", "", "", "";
+};
+
+/* SSIF (CPU0 host IPMI) */
+&i2c0 {
+ clock-frequency = <400000>;
+ status = "okay";
+};
+
+/* BMC <-> HMC management (FRU proxy) */
+&i2c1 {
+ clock-frequency = <400000>;
+ status = "okay";
+};
+
+&i2c2 {
+ clock-frequency = <400000>;
+ status = "okay";
+
+ exp0: gpio@20 {
+ compatible = "nxp,pca9555";
+ reg = <0x20>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <ASPEED_GPIO(B, 6) IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ vcc-supply = <&standby_power_regulator>;
+ gpio-line-names =
+ "B0_M0_SHDN_FORCE_L-O",
+ "B0_M0_STBY_POWER_PG-I",
+ "B0_M0_THERM_OVERT_L-I",
+ "B0_M0_THERM_WARN_L-I",
+ "B0_M0_GLOBAL_WP-O",
+ "B0_M0_USB_HUB0_RST_L-O",
+ "B0_M0_PRE_SYS_RST_L-O",
+ "B0_M0_LEAK_DETECT_ALERT_L-I",
+ "B0_M0_RUN_POWER_EN-O",
+ "B0_M0_RUN_POWER_PG-I",
+ "B0_M0_CPU_CHIPTHROT_L-O",
+ "B0_M0_SHDN_REQ_L-O",
+ "B0_M0_CPU_SHDN_OK_L-I",
+ "B0_M0_CPLD_READY-I",
+ "B0_M0_PWR_BRAKE_L-O",
+ "B0_M0_PWR_BRAKE_STATUS_L-I";
+ };
+
+ exp1: gpio@21 {
+ compatible = "nxp,pca9555";
+ reg = <0x21>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <ASPEED_GPIO(B, 6) IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ vcc-supply = <&standby_power_regulator>;
+ gpio-line-names =
+ "B0_M0_I2C_BUS_MUX_RESET_L-O",
+ "B0_M0_HPM_MCU_OK-I",
+ "B0_M0_CPU_L0_RST_IND_L-O",
+ "B0_M0_C0_SOCAMM_DAC_SEL0-O",
+ "B0_M0_C1_SOCAMM_DAC_SEL1-O",
+ "B0_M0_C0_SOCAMM_I2C_SEL_R-O",
+ "B0_M0_C1_SOCAMM_I2C_SEL_R-O",
+ "B0_M0_EEPROM_POWER_DISABLE-O",
+ "B0_M0_L0L1_RST_L-I",
+ "B0_M0_L2_RST_L-I",
+ "B0_M0_BRD_ID_0-I",
+ "B0_M0_BRD_ID_1-I",
+ "B0_M0_BMC_LEAK_TEST_L-O",
+ "B0_M0_MCU_BMC_ALERT_L-I",
+ "B0_M0_CPU_BOOT_COMPLETE_3V3-I",
+ "B0_M0_BMC_TO_GPU_MCU_I2C_EN-O";
+ };
+};
+
+/* HMC virtual EEPROM / sensor proxy */
+&i2c3 {
+ clock-frequency = <400000>;
+ status = "okay";
+};
+
+/* MCIO expansion connector (HPM board 0) */
+&i2c4 {
+ clock-frequency = <400000>;
+ status = "okay";
+};
+
+&i2c5 {
+ clock-frequency = <400000>;
+ status = "okay";
+
+ exp4: gpio@20 {
+ compatible = "nxp,pca9555";
+ reg = <0x20>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <ASPEED_GPIO(B, 6) IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-line-names =
+ "HMC_RST_R_L-O",
+ "HMC_RECOVERY_R-O",
+ "HMC_SPI_MUX_R_SEL-O",
+ "GLOBAL_WP-O",
+ "HMC_READY-I",
+ "HMC_PRSNT_R-I",
+ "BMC_SELF_PWR_CYCLE-O",
+ "EEDO_LED2-O",
+ "PWR_LED_L-O",
+ "PWR_BTN_L-I",
+ "UID_LED_L-O",
+ "UID_BTN_L-I",
+ "FAULT_LED_L-O",
+ "USB2_HUB_RST_L-O",
+ "IOX_GPIO_P16_TP",
+ "WARN_LED_L-O";
+ };
+
+ exp5: gpio@21 {
+ compatible = "nxp,pca9555";
+ reg = <0x21>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <ASPEED_GPIO(B, 6) IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-line-names =
+ "BMC_HMC_MUX_SEL-O",
+ "BMC_TPM_MUX_SEL-O",
+ "BMC_USB2_MUX_SEL-O",
+ "HMC_PGOOD_3V3-I",
+ "BF3_AOC_NCSI_PRSNT_L-I",
+ "HDR_SPI_PRSNT_L-I",
+ "SW_NRESET_R_L-O",
+ "MUX_I2C_ESPI_SEL-O",
+ "I2C_BUS_MUX_RESET_L-O",
+ "USB_MUX_EN-O",
+ "PDB_TRAY_RST-O",
+ "USB_PWR_EN-O",
+ "RTC_CLR_L-O",
+ "I2C_RTC_ALERT_L-I",
+ "X86_TPM_RST_SEL_L-O",
+ "BMC_MUX_PI3DP_SEL-O";
+ };
+};
+
+&i2c6 {
+ clock-frequency = <400000>;
+ status = "okay";
+
+ exp19: gpio@20 {
+ compatible = "ti,tca6408";
+ reg = <0x20>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <ASPEED_GPIO(B, 6) IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-line-names =
+ "", "",
+ "HMC_EROT_FATAL_ERROR_L-I", "",
+ "", "HMC_EROT_RECOVERY_L-O",
+ "HMC_EROT_RESET_L-O", "";
+ };
+
+ i2c-mux@70 {
+ compatible = "nxp,pca9546";
+ reg = <0x70>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ i2c-mux-idle-disconnect;
+
+ /* USB hub sideband */
+ i2c_usbhub: i2c@0 {
+ reg = <0>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ /* optional TPM mux (unused; primary TPM is SPI) */
+ i2c_tpm: i2c@1 {
+ reg = <1>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ /* DisplayPort/USB-C crossbar */
+ i2c_dp: i2c@2 {
+ reg = <2>;
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+ };
+};
+
+&i2c7 {
+ clock-frequency = <400000>;
+ status = "okay";
+
+ exp2: gpio@20 {
+ compatible = "nxp,pca9555";
+ reg = <0x20>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <ASPEED_GPIO(B, 6) IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ vcc-supply = <&standby_power_regulator>;
+ gpio-line-names =
+ "B1_M0_SHDN_FORCE_L-O",
+ "B1_M0_STBY_POWER_PG-I",
+ "B1_M0_THERM_OVERT_L-I",
+ "B1_M0_THERM_WARN_L-I",
+ "B1_M0_GLOBAL_WP-O",
+ "B1_M0_USB_HUB0_RST_L-O",
+ "B1_M0_PRE_SYS_RST_L-O",
+ "B1_M0_LEAK_DETECT_ALERT_L-I",
+ "B1_M0_RUN_POWER_EN-O",
+ "B1_M0_RUN_POWER_PG-I",
+ "B1_M0_CPU_CHIPTHROT_L-O",
+ "B1_M0_SHDN_REQ_L-O",
+ "B1_M0_CPU_SHDN_OK_L-I",
+ "B1_M0_CPLD_READY-I",
+ "B1_M0_PWR_BRAKE_L-O",
+ "B1_M0_PWR_BRAKE_STATUS_L-I";
+ };
+
+ exp3: gpio@21 {
+ compatible = "nxp,pca9555";
+ reg = <0x21>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <ASPEED_GPIO(B, 6) IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ vcc-supply = <&standby_power_regulator>;
+ gpio-line-names =
+ "B1_M0_I2C_BUS_MUX_RESET_L-O",
+ "B1_M0_HPM_MCU_OK-I",
+ "B1_M0_CPU_L0_RST_IND_L-O",
+ "B1_M0_C0_SOCAMM_DAC_SEL0-O",
+ "B1_M0_C1_SOCAMM_DAC_SEL1-O",
+ "B1_M0_C0_SOCAMM_I2C_SEL_R-O",
+ "B1_M0_C1_SOCAMM_I2C_SEL_R-O",
+ "B1_M0_EEPROM_POWER_DISABLE-O",
+ "B1_M0_L0L1_RST_L-I",
+ "B1_M0_L2_RST_L-I",
+ "B1_M0_BRD_ID_0-I",
+ "B1_M0_BRD_ID_1-I",
+ "B1_M0_BMC_LEAK_TEST_L-O",
+ "B1_M0_MCU_BMC_ALERT_L-I",
+ "B1_M0_CPU_BOOT_COMPLETE_3V3-I",
+ "B1_M0_BMC_TO_GPU_MCU_I2C_EN-O";
+ };
+};
+
+/* SSIF (CPU1 host IPMI) */
+&i2c8 {
+ clock-frequency = <400000>;
+ status = "okay";
+};
+
+&i2c9 {
+ clock-frequency = <400000>;
+ status = "okay";
+
+ exp6: gpio@20 {
+ compatible = "nxp,pca9555";
+ reg = <0x20>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <ASPEED_GPIO(B, 6) IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-line-names =
+ "PDB_STRAP-I",
+ "STBY_POWER_EN-O",
+ "STBY_PWR_OK-I",
+ "", "",
+ "MAIN_PWR_EN-O",
+ "MAIN_PWR_OK-I",
+ "PDB_GLB_WP-O",
+ "PDB_PWR_BRK_L-I",
+ "FAN_PRSNT-I",
+ "", "",
+ "HSC4_S5_OR_S0_N-O",
+ "", "", "";
+ };
+
+ eeprom@50 {
+ compatible = "atmel,24c02";
+ reg = <0x50>;
+ pagesize = <8>;
+ };
+
+ exp7: gpio@75 {
+ compatible = "nxp,pca9555";
+ reg = <0x75>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <ASPEED_GPIO(B, 6) IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-line-names =
+ "SNN_PDB_RUN_IO0_A_R_PG-I",
+ "RST_STBY_R_L-O",
+ "SNN_PDB_RUN_IO0_B_R_EN-O",
+ "PSU1_PRSNT_R_N-O",
+ "PSU2_PRSNT_R_N-O",
+ "PSU3_PRSNT_R_N-O",
+ "PSU4_PRSNT_R_N-O",
+ "PDB_12V_PG_R-I",
+ "PDB_12V_2_PG_R-I",
+ "PDB_12V_2_EN_R-O",
+ "PDB_12V_EN_N_R-O",
+ "",
+ "HSC_PG_VIN_PG_R-I",
+ "PDB_PSU_SMB_ALERT_L_R-O",
+ "SNN_JSB2_17_R-I",
+ "TRAY_RST_L_R-I";
+ };
+};
+
+&i2c10 {
+ clock-frequency = <400000>;
+ status = "okay";
+
+ eeprom@50 {
+ compatible = "atmel,24c02";
+ reg = <0x50>;
+ pagesize = <8>;
+ };
+};
+
+/* Realtek Ethernet switch config EEPROM */
+&i2c11 {
+ clock-frequency = <400000>;
+ status = "okay";
+};
+
+/* BMC EROT */
+&i2c12 {
+ clock-frequency = <400000>;
+ status = "okay";
+};
+
+&i2c13 {
+ clock-frequency = <400000>;
+ multi-master;
+ mctp-controller;
+ status = "okay";
+
+ mctp-controller@18 {
+ compatible = "mctp-i2c-controller";
+ reg = <(0x18 | I2C_OWN_SLAVE_ADDRESS)>;
+ };
+
+ exp18: gpio@38 {
+ compatible = "nxp,pca9554";
+ reg = <0x38>;
+ interrupt-parent = <&gpio1>;
+ interrupts = <ASPEED_GPIO(B, 6) IRQ_TYPE_LEVEL_LOW>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ gpio-line-names =
+ "SSD0_PRSNT_L-I",
+ "E1S_PWR_EN-O",
+ "SSD0_PWRDIS-O",
+ "I2C_PDB_ALERT_L-I",
+ "BMC_SSD0_RST_L-O",
+ "GLOBAL_WP_E1S-O",
+ "12V_SSD0_PGD-I",
+ "SSD0_LED-O";
+ };
+};
+
+/* MCIO expansion connector (HPM board 1) */
+&i2c14 {
+ clock-frequency = <400000>;
+ status = "okay";
+};
+
+/* USB-C PD controller */
+&i2c15 {
+ clock-frequency = <400000>;
+ status = "okay";
+};
+
+&mac0 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_rgmii1_default>;
+ /*
+ * The on-board PHY is strapped to add both RX and TX RGMII
+ * internal delays; declare "rgmii-id" so the MAC does not add
+ * additional delay. The same setting is applied in the
+ * matching u-boot DTS to keep early-boot networking working.
+ */
+ phy-mode = "rgmii-id";
+ phy-handle = <ðphy0>;
+ status = "okay";
+};
+
+&mdio0 {
+ status = "okay";
+
+ ethphy0: ethernet-phy@0 {
+ compatible = "ethernet-phy-ieee802.3-c22";
+ reg = <0>;
+ };
+};
+
+&spi2 {
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_spi2_default>;
+ status = "okay";
+
+ flash@0 {
+ label = "config";
+ status = "okay";
+
+ partitions {
+ compatible = "fixed-partitions";
+ #address-cells = <1>;
+ #size-cells = <1>;
+
+ partition@0 {
+ reg = <0x0 0x40000>; /* 256 KiB */
+ label = "u-boot-env";
+ };
+
+ partition@40000 {
+ reg = <0x40000 0x1000000>; /* 16 MiB */
+ label = "rwfs";
+ };
+
+ partition@1040000 {
+ reg = <0x1040000 0x2800000>; /* 40 MiB */
+ label = "log";
+ };
+ };
+ };
+};
+
+/* BMC <-> host CPU UART */
+&uart1 {
+ status = "okay";
+};
+
+/* Serial-over-LAN */
+&uart3 {
+ status = "okay";
+};
+
+/* BMC debug console */
+&uart5 {
+ status = "okay";
+};
+
+&uart_routing {
+ status = "okay";
+};
+
+&vhub {
+ status = "okay";
+};
+
+&video {
+ memory-region = <&video_engine_memory>;
+ status = "okay";
+};
--
2.43.0
^ permalink raw reply related
* [PATCH 1/2] dt-bindings: arm: aspeed: Add NVIDIA VR-NVL BMC
From: Jacky Huang @ 2026-07-02 16:55 UTC (permalink / raw)
To: Andrew Jeffery, Joel Stanley, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: devicetree, linux-aspeed, linux-arm-kernel, linux-kernel,
Jacky Huang
In-Reply-To: <20260702165524.2168091-1-jackyhuang@nvidia.com>
Add NVIDIA's VR-NVL BMC board compatible.
Signed-off-by: Jacky Huang <jackyhuang@nvidia.com>
---
Documentation/devicetree/bindings/arm/aspeed/aspeed.yaml | 1 +
1 file changed, 1 insertion(+)
diff --git a/Documentation/devicetree/bindings/arm/aspeed/aspeed.yaml b/Documentation/devicetree/bindings/arm/aspeed/aspeed.yaml
index dd7996960de3..ce893553cfc3 100644
--- a/Documentation/devicetree/bindings/arm/aspeed/aspeed.yaml
+++ b/Documentation/devicetree/bindings/arm/aspeed/aspeed.yaml
@@ -114,6 +114,7 @@ properties:
- jabil,rbp-bmc
- nvidia,gb200nvl-bmc
- nvidia,msx4-bmc
+ - nvidia,vr-nvl-bmc
- qcom,dc-scm-v1-bmc
- quanta,s6q-bmc
- ufispace,ncplite-bmc
--
2.43.0
^ permalink raw reply related
* [PATCH 0/2] Add device tree for NVIDIA VR-NVL BMC
From: Jacky Huang @ 2026-07-02 16:55 UTC (permalink / raw)
To: Andrew Jeffery, Joel Stanley, Rob Herring, Krzysztof Kozlowski,
Conor Dooley
Cc: devicetree, linux-aspeed, linux-arm-kernel, linux-kernel,
Jacky Huang
This series adds the DT binding compatible and the device tree for
the NVIDIA VR-NVL BMC, an Aspeed AST2600-based BMC for the Vera
Rubin NVL compute platform.
Jacky Huang (2):
dt-bindings: arm: aspeed: Add NVIDIA VR-NVL BMC
ARM: dts: aspeed: Add NVIDIA VR-NVL BMC
.../bindings/arm/aspeed/aspeed.yaml | 1 +
arch/arm/boot/dts/aspeed/Makefile | 1 +
.../aspeed/aspeed-bmc-nvidia-vr-nvl-bmc.dts | 901 ++++++++++++++++++
3 files changed, 903 insertions(+)
create mode 100644 arch/arm/boot/dts/aspeed/aspeed-bmc-nvidia-vr-nvl-bmc.dts
--
2.43.0
^ permalink raw reply
* Re: [PATCH v2] iommu/arm-smmu-v3: Shrink command/event/PRI queues in kdump kernel
From: Pranjal Shrivastava @ 2026-07-02 15:05 UTC (permalink / raw)
To: Kiryl Shutsemau (Meta)
Cc: Will Deacon, Robin Murphy, Joerg Roedel, Jason Gunthorpe,
Nicolin Chen, Kyle McMartin, Breno Leitao, Usama Arif,
linux-arm-kernel, iommu, linux-kernel
In-Reply-To: <20260702112825.781750-1-kas@kernel.org>
On Thu, Jul 02, 2026 at 12:28:25PM +0100, Kiryl Shutsemau (Meta) wrote:
> The command, event and PRI queues are sized from the maxima the hardware
A minor note here is PRI & EVT queues are disabled for the kdump kernel
(see arm_smmu_device_reset). We could just mention all SMMU queues are
sized [...] in the commit message.
> advertises in IDR1, which can be several megabytes each. On systems with
> many SMMUv3 instances that cost is paid per instance and adds up to tens
> of megabytes of coherent DMA in the capture kernel.
>
> A kdump capture kernel runs from a small crashkernel reservation and only
> has to drive the few devices used to save the dump, so deep queues serve
> no purpose. The queues carry invalidation commands and fault records, not
> DMA data, so dump throughput is unaffected; a shallower queue only bounds
> how many commands may be in flight before a sync, which does not matter for
> the capture kernel's small device count and modest I/O.
>
> Clamp every queue to a single page when is_kdump_kernel() is true. Doing
> it in arm_smmu_init_one_queue() covers the command, event and PRI queues
> in one place. The command queue still holds at least one batch plus a sync
> (256 entries on a 4K-page kernel, well above CMDQ_BATCH_ENTRIES), so
> command batching keeps working.
>
> Suggested-by: Kyle McMartin <jkkm@meta.com>
> Signed-off-by: Kiryl Shutsemau (Meta) <kas@kernel.org>
> Reviewed-by: Breno Leitao <leitao@debian.org>
> ---
Apart from that.
Reviewed-by: Pranjal Shrivastava <praan@google.com>
Thanks,
Praan
^ permalink raw reply
* [PATCH v8 10/39] drm/display: hdmi-state-helper: Sync SCDC state on hotplug
From: Cristian Ciocaltea @ 2026-07-02 14:46 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Sandy Huang, Heiko Stübner, Andy Yan, Daniel Stone,
Dave Stevenson, Maíra Canal, Raspberry Pi Kernel Maintenance
Cc: kernel, dri-devel, linux-kernel, linux-arm-kernel, linux-rockchip
In-Reply-To: <20260702-dw-hdmi-qp-scramb-v8-0-d79890d00b6a@collabora.com>
drm_atomic_helper_connector_hdmi_hotplug() does not currently
synchronize SCDC status on hotplug events, leaving the scrambler state
potentially inconsistent after (re)connect.
Hook drm_connector_hdmi_sync_scdc() into both the connect and disconnect
paths, replacing the existing TODOs around missing scrambler handling.
SCDC synchronization may require a CRTC reset, which in turn needs a
modeset acquire context for correct locking. Therefore, extend
drm_atomic_helper_connector_hdmi_hotplug() and
drm_atomic_helper_connector_hdmi_update() to take a
drm_modeset_acquire_ctx argument. Additionally, change their return
type from void to int, allowing propagation of errors, such as -EDEADLK
from lock contention.
Update existing callers to pass NULL for the acquire context, preserving
current behavior.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/display/drm_bridge_connector.c | 3 ++-
drivers/gpu/drm/display/drm_hdmi_state_helper.c | 28 +++++++++++++++++--------
drivers/gpu/drm/vc4/vc4_hdmi.c | 2 +-
include/drm/display/drm_hdmi_state_helper.h | 6 ++++--
4 files changed, 26 insertions(+), 13 deletions(-)
diff --git a/drivers/gpu/drm/display/drm_bridge_connector.c b/drivers/gpu/drm/display/drm_bridge_connector.c
index 796069dbf1a1..46104506fe32 100644
--- a/drivers/gpu/drm/display/drm_bridge_connector.c
+++ b/drivers/gpu/drm/display/drm_bridge_connector.c
@@ -221,7 +221,8 @@ drm_bridge_connector_detect(struct drm_connector *connector, bool force)
status = detect->funcs->detect(detect, connector);
if (hdmi)
- drm_atomic_helper_connector_hdmi_hotplug(connector, status);
+ drm_atomic_helper_connector_hdmi_hotplug(connector, status,
+ NULL);
drm_bridge_connector_hpd_notify(connector, status);
} else {
diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
index db76699093e8..69ccfbf123fe 100644
--- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c
+++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
@@ -1198,18 +1198,20 @@ drm_atomic_helper_connector_hdmi_clear_audio_infoframe(struct drm_connector *con
}
EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_clear_audio_infoframe);
-static void
+static int
drm_atomic_helper_connector_hdmi_update(struct drm_connector *connector,
- enum drm_connector_status status)
+ enum drm_connector_status status,
+ struct drm_modeset_acquire_ctx *ctx)
{
const struct drm_edid *drm_edid;
+ int ret = 0;
if (status == connector_status_disconnected) {
- // TODO: also handle scramber, HDMI sink disconnected.
+ ret = drm_connector_hdmi_sync_scdc(connector, false, ctx);
drm_connector_hdmi_audio_plugged_notify(connector, false);
drm_edid_connector_update(connector, NULL);
drm_connector_cec_phys_addr_invalidate(connector);
- return;
+ return ret;
}
if (connector->hdmi.funcs->read_edid)
@@ -1222,24 +1224,32 @@ drm_atomic_helper_connector_hdmi_update(struct drm_connector *connector,
drm_edid_free(drm_edid);
if (status == connector_status_connected) {
- // TODO: also handle scramber, HDMI sink is now connected.
+ ret = drm_connector_hdmi_sync_scdc(connector, true, ctx);
drm_connector_hdmi_audio_plugged_notify(connector, true);
drm_connector_cec_phys_addr_set(connector);
}
+
+ return ret;
}
/**
* drm_atomic_helper_connector_hdmi_hotplug - Handle the hotplug event for the HDMI connector
* @connector: A pointer to the HDMI connector
* @status: Connection status
+ * @ctx: Lock acquisition context to be used for resetting CRTC
*
* This function should be called as a part of the .detect() / .detect_ctx()
* callbacks for all status changes.
+ *
+ * Returns:
+ * Zero on success, error code on failure.
+ * If @ctx is set, it might also return -EDEADLK.
*/
-void drm_atomic_helper_connector_hdmi_hotplug(struct drm_connector *connector,
- enum drm_connector_status status)
+int drm_atomic_helper_connector_hdmi_hotplug(struct drm_connector *connector,
+ enum drm_connector_status status,
+ struct drm_modeset_acquire_ctx *ctx)
{
- drm_atomic_helper_connector_hdmi_update(connector, status);
+ return drm_atomic_helper_connector_hdmi_update(connector, status, ctx);
}
EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_hotplug);
@@ -1254,6 +1264,6 @@ EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_hotplug);
*/
void drm_atomic_helper_connector_hdmi_force(struct drm_connector *connector)
{
- drm_atomic_helper_connector_hdmi_update(connector, connector->status);
+ drm_atomic_helper_connector_hdmi_update(connector, connector->status, NULL);
}
EXPORT_SYMBOL(drm_atomic_helper_connector_hdmi_force);
diff --git a/drivers/gpu/drm/vc4/vc4_hdmi.c b/drivers/gpu/drm/vc4/vc4_hdmi.c
index 74dce4be0c00..e165f604939b 100644
--- a/drivers/gpu/drm/vc4/vc4_hdmi.c
+++ b/drivers/gpu/drm/vc4/vc4_hdmi.c
@@ -375,7 +375,7 @@ static void vc4_hdmi_handle_hotplug(struct vc4_hdmi *vc4_hdmi,
* the lock for now.
*/
- drm_atomic_helper_connector_hdmi_hotplug(connector, status);
+ drm_atomic_helper_connector_hdmi_hotplug(connector, status, NULL);
if (status != connector_status_connected)
return;
diff --git a/include/drm/display/drm_hdmi_state_helper.h b/include/drm/display/drm_hdmi_state_helper.h
index 13375bd0f4ae..0619cd7c435e 100644
--- a/include/drm/display/drm_hdmi_state_helper.h
+++ b/include/drm/display/drm_hdmi_state_helper.h
@@ -7,6 +7,7 @@ struct drm_atomic_commit;
struct drm_connector;
struct drm_connector_state;
struct drm_display_mode;
+struct drm_modeset_acquire_ctx;
struct hdmi_audio_infoframe;
enum drm_connector_status;
@@ -22,8 +23,9 @@ int drm_atomic_helper_connector_hdmi_update_audio_infoframe(struct drm_connector
int drm_atomic_helper_connector_hdmi_clear_audio_infoframe(struct drm_connector *connector);
int drm_atomic_helper_connector_hdmi_update_infoframes(struct drm_connector *connector,
struct drm_atomic_commit *state);
-void drm_atomic_helper_connector_hdmi_hotplug(struct drm_connector *connector,
- enum drm_connector_status status);
+int drm_atomic_helper_connector_hdmi_hotplug(struct drm_connector *connector,
+ enum drm_connector_status status,
+ struct drm_modeset_acquire_ctx *ctx);
void drm_atomic_helper_connector_hdmi_force(struct drm_connector *connector);
enum drm_mode_status
--
2.54.0
^ permalink raw reply related
* [PATCH v8 12/39] drm/bridge: Remove redundant error check in drm_bridge_helper_reset_crtc()
From: Cristian Ciocaltea @ 2026-07-02 14:46 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Sandy Huang, Heiko Stübner, Andy Yan, Daniel Stone,
Dave Stevenson, Maíra Canal, Raspberry Pi Kernel Maintenance
Cc: kernel, dri-devel, linux-kernel, linux-arm-kernel, linux-rockchip,
Dmitry Baryshkov
In-Reply-To: <20260702-dw-hdmi-qp-scramb-v8-0-d79890d00b6a@collabora.com>
Remove the no-op error check after drm_atomic_helper_reset_crtc() since
the goto target is the immediately following label and the return value
is already propagated correctly without it.
Reviewed-by: Dmitry Baryshkov <dmitry.baryshkov@oss.qualcomm.com>
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/drm_bridge_helper.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/drivers/gpu/drm/drm_bridge_helper.c b/drivers/gpu/drm/drm_bridge_helper.c
index 420f29cf3e54..0a3c8fee66b3 100644
--- a/drivers/gpu/drm/drm_bridge_helper.c
+++ b/drivers/gpu/drm/drm_bridge_helper.c
@@ -50,8 +50,6 @@ int drm_bridge_helper_reset_crtc(struct drm_bridge *bridge,
crtc = connector->state->crtc;
ret = drm_atomic_helper_reset_crtc(crtc, ctx);
- if (ret)
- goto out;
out:
drm_modeset_unlock(&dev->mode_config.connection_mutex);
--
2.54.0
^ permalink raw reply related
* Re: [PATCH v2 1/4] dt-bindings: can: rockchip: add rk3588 CAN-FD compatible
From: Heiko Stübner @ 2026-07-02 16:42 UTC (permalink / raw)
To: Marc Kleine-Budde, linux-can, 1579567540, Krzysztof Kozlowski
Cc: Vincent Mailhol, Rob Herring, Krzysztof Kozlowski, kernel,
Conor Dooley, Dmitry Torokhov, Shengjiu Wang, Pengpeng Hou,
Russell King, Eric Biggers, Mario Limonciello, Karl Mehltretter,
Yixun Lan, Stephen Boyd, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
In-Reply-To: <636d10c0-670d-4586-b604-7559d2a24185@kernel.org>
Am Donnerstag, 2. Juli 2026, 18:39:53 Mitteleuropäische Sommerzeit schrieb Krzysztof Kozlowski:
> On 02/07/2026 18:01, Heiko Stübner wrote:
> > Am Donnerstag, 2. Juli 2026, 16:06:51 Mitteleuropäische Sommerzeit schrieb 1579567540@qq.com:
> >> From: Cunhao Lu <1579567540@qq.com>
> >>
> >> RK3588 integrates a Rockchip CAN-FD controller variant that is not
> >> fully compatible with RK3568v2. The RX FIFO count register field is
> >> encoded in bits 7:5 on RK3588, while RK3568v2 uses bits 6:4.
> >>
> >> Add a dedicated rockchip,rk3588-canfd compatible to describe this
> >> variant. Do not use rockchip,rk3568v2-canfd as a fallback, because that
> >> would describe a register layout that does not match the hardware.
> >>
> >> Changes in v2:
> >> - Use enum for the single-compatible entries, as suggested by Krzysztof.
> >> - Reword the commit message to explain the hardware difference instead
> >> of referring to Linux driver match data.
> >>
> >> Signed-off-by: Cunhao Lu <1579567540@qq.com>
> >
> > after fixing Krzysztof's comment:
> > Reviewed-by: Heiko Stuebner <heiko@sntech.de>
> >
> > and doing dtbscheck of the binding against the dt-patches:
> > Tested-by: Heiko Stuebner <heiko@sntech.de>
>
> Makefile/build level steps are not testing. Please don't provide such
> tags for bindings. Otherwise all bindings would carry Rob's tag, because
> he wrote the bot which does such testing.
ok ... will keep that in mind for the future
Heiko
^ permalink raw reply
* Re: [PATCH v2 1/4] dt-bindings: can: rockchip: add rk3588 CAN-FD compatible
From: Krzysztof Kozlowski @ 2026-07-02 16:39 UTC (permalink / raw)
To: Heiko Stübner, Marc Kleine-Budde, linux-can, 1579567540
Cc: Vincent Mailhol, Rob Herring, Krzysztof Kozlowski, kernel,
Conor Dooley, Dmitry Torokhov, Shengjiu Wang, Pengpeng Hou,
Russell King, Eric Biggers, Mario Limonciello, Karl Mehltretter,
Yixun Lan, Stephen Boyd, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
In-Reply-To: <2459655.BjyWNHgNrj@diego>
On 02/07/2026 18:01, Heiko Stübner wrote:
> Am Donnerstag, 2. Juli 2026, 16:06:51 Mitteleuropäische Sommerzeit schrieb 1579567540@qq.com:
>> From: Cunhao Lu <1579567540@qq.com>
>>
>> RK3588 integrates a Rockchip CAN-FD controller variant that is not
>> fully compatible with RK3568v2. The RX FIFO count register field is
>> encoded in bits 7:5 on RK3588, while RK3568v2 uses bits 6:4.
>>
>> Add a dedicated rockchip,rk3588-canfd compatible to describe this
>> variant. Do not use rockchip,rk3568v2-canfd as a fallback, because that
>> would describe a register layout that does not match the hardware.
>>
>> Changes in v2:
>> - Use enum for the single-compatible entries, as suggested by Krzysztof.
>> - Reword the commit message to explain the hardware difference instead
>> of referring to Linux driver match data.
>>
>> Signed-off-by: Cunhao Lu <1579567540@qq.com>
>
> after fixing Krzysztof's comment:
> Reviewed-by: Heiko Stuebner <heiko@sntech.de>
>
> and doing dtbscheck of the binding against the dt-patches:
> Tested-by: Heiko Stuebner <heiko@sntech.de>
Makefile/build level steps are not testing. Please don't provide such
tags for bindings. Otherwise all bindings would carry Rob's tag, because
he wrote the bot which does such testing.
Best regards,
Krzysztof
^ permalink raw reply
* Re: [PATCH v4 1/7] dt-bindings: mtd: jedec,spi-nor: allow the SFDP to be exposed via NVMEM
From: Rob Herring @ 2026-07-02 16:37 UTC (permalink / raw)
To: Manikandan.M
Cc: krzk, pratyush, mwalle, takahiro.kuwano, miquel.raynal, richard,
vigneshr, krzk+dt, conor+dt, srini, Nicolas.Ferre,
alexandre.belloni, claudiu.beznea, linux, richardcochran, linusw,
arnd, michael, linux-mtd, devicetree, linux-kernel,
linux-arm-kernel, netdev
In-Reply-To: <860213fd-6a4b-42d4-a8f7-7308e070f09e@microchip.com>
On Thu, Jul 02, 2026 at 06:55:15AM +0000, Manikandan.M@microchip.com wrote:
> Hi Krzysztof,
>
> On 7/2/26 11:46 AM, Krzysztof Kozlowski wrote:
> > EXTERNAL EMAIL: Do not click links or open attachments unless you know the content is safe
> >
> > On Tue, Jun 30, 2026 at 02:54:00PM +0530, Manikandan Muralidharan wrote:
> >> Add an optional "sfdp" child node (compatible "jedec,sfdp") that
> >> describes the SFDP as a read-only NVMEM provider via nvmem.yaml, so its
> >
> > What is SFDP?
> >
> SFDP is the Serial Flash Discoverable Parameters -- a JEDEC-standardised
> (JESD216) read-only parameter table present in most SPI NOR flashes, the
> table contents provide basic information about the flash. There are
> standard tables which are specified by the JEDEC standard and there are
> vendor tables.
Is SFDP present or not discoverable? Or we have a table of discoverable
parameters that itself is not discoverable.
If it is discoverable, why does this need to be in DT?
Rob
^ permalink raw reply
* Re: [PATCH v2 2/4] can: rockchip: add RK3588 CAN support
From: Heiko Stübner @ 2026-07-02 16:36 UTC (permalink / raw)
To: Marc Kleine-Budde, linux-can, Cunhao Lu
Cc: Vincent Mailhol, Rob Herring, Krzysztof Kozlowski, kernel,
Conor Dooley, Dmitry Torokhov, Shengjiu Wang, Pengpeng Hou,
Russell King, Eric Biggers, Mario Limonciello, Karl Mehltretter,
Yixun Lan, Stephen Boyd, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel, Heiko Stuebner
In-Reply-To: <tencent_8BE421601F31C26FB9E99FA6440629016D05@qq.com>
Am Donnerstag, 2. Juli 2026, 18:34:04 Mitteleuropäische Sommerzeit schrieb Cunhao Lu:
> > I think you might want to drop that above. If anything a Co-developed-by
> > would be applicable, but from the (small) size of the change, that also
> > isn't really necessary for me :-)
> >
> >
> > > Signed-off-by: Cunhao Lu <1579567540@qq.com>
> >
> > Tested-by: Heiko Stuebner <heiko@sntech.de>
> > Reviewed-by: Heiko Stuebner <heiko@sntech.de>
>
> Sorry about that. This is my first contribution to the Linux kernel, and
> I have not fully understood yet which tags are appropriate in which
> situations. Please bear with me.
no worries, I think you're doing really great for your first contribution
Heiko
> I will add your Co-developed-by tag in v3, together with your
> Signed-off-by as required for Co-developed-by.
>
> Best regards,
> Cunhao
>
>
>
>
>
^ permalink raw reply
* Re: [PATCH v5 0/5] KVM: arm64: Add KVM_PRE_FAULT_MEMORY support
From: Alexandru Elisei @ 2026-07-02 16:36 UTC (permalink / raw)
To: Jack Thomson
Cc: maz, oupton, pbonzini, joey.gouly, seiden, suzuki.poulose,
yuzenghui, catalin.marinas, will, shuah, corbet, vladimir.murzin,
linux-arm-kernel, kvmarm, kvm, linux-kernel, linux-kselftest,
linux-doc, isaku.yamahata, Jack Thomson
In-Reply-To: <20260612162354.73378-1-jackabt.amazon@gmail.com_quarantine>
Hi,
On Fri, Jun 12, 2026 at 05:23:48PM +0100, Jack Thomson wrote:
> From: Jack Thomson <jackabt@amazon.com>
>
> Hi,
>
> This series adds arm64 support for KVM_PRE_FAULT_MEMORY, which was added
> for x86 in [1]. The ioctl allows userspace to populate stage-2 mappings
> before running a vCPU, reducing the number of stage-2 faults taken in
> the run path. This is useful for post-copy migration, where stage-2
> fault latency shows up directly in memory-intensive workloads.
>
> ..
>
> base-commit: 98f826f3c500fda08d51fca434b7aefa6a2f7076
I want to review and test the series, but I cannot seem to find the base
commit in the master branch:
$ git show --oneline
87320be9f0d2 (HEAD -> master, origin/master, origin/HEAD) Merge tag 'net-7.2-rc2' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
$ git show 98f826f3c500fda08d51fca434b7aefa6a2
fatal: ambiguous argument '98f826f3c500fda08d51fca434b7aefa6a2': unknown revision or path not in the working tree.
Thanks,
Alex
^ permalink raw reply
* Re: [PATCH v2 2/4] can: rockchip: add RK3588 CAN support
From: Cunhao Lu @ 2026-07-02 16:34 UTC (permalink / raw)
To: Heiko Stübner, Marc Kleine-Budde, linux-can
Cc: Vincent Mailhol, Rob Herring, Krzysztof Kozlowski, kernel,
Conor Dooley, Dmitry Torokhov, Shengjiu Wang, Pengpeng Hou,
Russell King, Eric Biggers, Mario Limonciello, Karl Mehltretter,
Yixun Lan, Stephen Boyd, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel, Heiko Stuebner
In-Reply-To: <3527453.usfYGdeWWP@diego>
> I think you might want to drop that above. If anything a Co-developed-by
> would be applicable, but from the (small) size of the change, that also
> isn't really necessary for me :-)
>
>
> > Signed-off-by: Cunhao Lu <1579567540@qq.com>
>
> Tested-by: Heiko Stuebner <heiko@sntech.de>
> Reviewed-by: Heiko Stuebner <heiko@sntech.de>
Hi Heiko,
Sorry about that. This is my first contribution to the Linux kernel, and
I have not fully understood yet which tags are appropriate in which
situations. Please bear with me.
I will add your Co-developed-by tag in v3, together with your
Signed-off-by as required for Co-developed-by.
Best regards,
Cunhao
^ permalink raw reply
* Re: [PATCH v2 1/4] dt-bindings: can: rockchip: add rk3588 CAN-FD compatible
From: Cunhao Lu @ 2026-07-02 16:32 UTC (permalink / raw)
To: Heiko Stübner, Marc Kleine-Budde, linux-can
Cc: Vincent Mailhol, Rob Herring, Krzysztof Kozlowski, kernel,
Conor Dooley, Dmitry Torokhov, Shengjiu Wang, Pengpeng Hou,
Russell King, Eric Biggers, Mario Limonciello, Karl Mehltretter,
Yixun Lan, Stephen Boyd, devicetree, linux-arm-kernel,
linux-rockchip, linux-kernel
In-Reply-To: <2459655.BjyWNHgNrj@diego>
> after fixing Krzysztof's comment:
> Reviewed-by: Heiko Stuebner <heiko@sntech.de>
>
> and doing dtbscheck of the binding against the dt-patches:
> Tested-by: Heiko Stuebner <heiko@sntech.de>
Hi Heiko,
Thanks for the review and testing.
I will add your Reviewed-by and Tested-by tags in v3.
Best regards,
Cunhao
^ permalink raw reply
* Re: [PATCH v4 1/5] arm_mpam: resctrl: Pick classes for use as MBM counters
From: Fenghua Yu @ 2026-07-02 16:30 UTC (permalink / raw)
To: Ben Horgan
Cc: amitsinght, baisheng.gao, baolin.wang, carl, dave.martin, david,
dfustini, gshan, james.morse, jic23, kobak, lcherian,
linux-arm-kernel, linux-kernel, peternewman, punit.agrawal,
quic_jiles, reinette.chatre, rohit.mathew, scott, sdonthineni,
tan.shaopeng, xhao, zengheng4, x86, Shaopeng Tan,
Jonathan Cameron
In-Reply-To: <20260520212458.1797221-2-ben.horgan@arm.com>
Hi, Ben,
On 5/20/26 14:24, Ben Horgan wrote:
> From: James Morse <james.morse@arm.com>
>
> resctrl has two types of bandwidth counters, NUMA-local and global. MPAM
> can only count globally; either using MSC at the L3 cache or in the memory
> controllers. When global and local equate to the same thing continue just
> to call it global.
>
> Pick the corresponding MPAM classes to back the MBM counters. As resctrl
> requires all monitors to be at the L3 cache, we can only use the counters
> at the memory controllers when they have the same topology as the L3 cache
> and the traffic they see if the same. In particular, for the bandwidth
> counters at the memory controllers to be exposed to resctrl it is required
> there is a single L3 cache and a single NUMA node as otherwise cross NUMA
> traffic will be counted at the wrong instance.
>
> Tested-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Tested-by: Zeng Heng <zengheng4@huawei.com>
> Reviewed-by: Shaopeng Tan <tan.shaopeng@jp.fujitsu.com>
> Reviewed-by: Jonathan Cameron <jonathan.cameron@huawei.com>
> Signed-off-by: James Morse <james.morse@arm.com>
> Signed-off-by: Ben Horgan <ben.horgan@arm.com>
> ---
> Changes since rfc v1:
> Move finding any_mon_comp into monitor boilerplate patch
> Move mpam_resctrl_get_domain_from_cpu() into monitor boilerplate
> Remove free running check
> Trim commit message
>
> Changes since v3:
> Extra paragraph in commit message
> ---
> drivers/resctrl/mpam_resctrl.c | 26 ++++++++++++++++++++++++++
> 1 file changed, 26 insertions(+)
>
> diff --git a/drivers/resctrl/mpam_resctrl.c b/drivers/resctrl/mpam_resctrl.c
> index 226ff6f532fa..f70fa65d39e4 100644
> --- a/drivers/resctrl/mpam_resctrl.c
> +++ b/drivers/resctrl/mpam_resctrl.c
> @@ -606,6 +606,16 @@ static bool cache_has_usable_csu(struct mpam_class *class)
> return true;
> }
>
> +static bool class_has_usable_mbwu(struct mpam_class *class)
> +{
> + struct mpam_props *cprops = &class->props;
> +
> + if (!mpam_has_feature(mpam_feat_msmon_mbwu, cprops))
> + return false;
> +
> + return true;
> +}
> +
> /*
> * Calculate the worst-case percentage change from each implemented step
> * in the control.
> @@ -983,6 +993,22 @@ static void mpam_resctrl_pick_counters(void)
> break;
> }
> }
> +
> + if (class_has_usable_mbwu(class) &&
> + topology_matches_l3(class) &&
> + traffic_matches_l3(class)) {
> + pr_debug("class %u has usable MBWU, and matches L3 topology and traffic\n",
> + class->level);
> +
> + /*
> + * We can't distinguish traffic by destination so
> + * we don't know if it's staying on the same NUMA
This sentence makes me think counting only on mbm_total is a "software"
limitation. In fact, it's a MPAM hardware feature (or limitation).
Maybe add a sentence like this to make it clear why mbm_total only comes
from originally?
MPAM only provides one memory bandwidth usage value for each MSC that
supports memory bandwidth usage . We can't distinguish traffic .."
> + * node. Hence, we can't calculate mbm_local except
> + * when we only have one L3 and it's equivalent to
> + * mbm_total and so always use mbm_total.
> + */
> + counter_update_class(QOS_L3_MBM_TOTAL_EVENT_ID, class);
> + }
> }
> }
>
Thanks.
-Fenghua
^ permalink raw reply
* [PATCH v8 11/39] drm/display: hdmi-state-helper: Set HDMI scrambling requirement
From: Cristian Ciocaltea @ 2026-07-02 14:46 UTC (permalink / raw)
To: Maarten Lankhorst, Maxime Ripard, Thomas Zimmermann, David Airlie,
Simona Vetter, Andrzej Hajda, Neil Armstrong, Robert Foss,
Laurent Pinchart, Jonas Karlman, Jernej Skrabec, Luca Ceresoli,
Sandy Huang, Heiko Stübner, Andy Yan, Daniel Stone,
Dave Stevenson, Maíra Canal, Raspberry Pi Kernel Maintenance
Cc: kernel, dri-devel, linux-kernel, linux-arm-kernel, linux-rockchip
In-Reply-To: <20260702-dw-hdmi-qp-scramb-v8-0-d79890d00b6a@collabora.com>
Set drm_connector_hdmi_state.scrambler_needed when the computed TMDS
character rate exceeds the HDMI 1.3 maximum TMDS character rate.
HDMI 2.0 requires scrambling above 340 MHz. Centralize that clock-based
requirement in the HDMI state helper so drivers do not need to open-code
the threshold check.
Signed-off-by: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
---
drivers/gpu/drm/display/drm_hdmi_state_helper.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/drivers/gpu/drm/display/drm_hdmi_state_helper.c b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
index 69ccfbf123fe..34e48e04d971 100644
--- a/drivers/gpu/drm/display/drm_hdmi_state_helper.c
+++ b/drivers/gpu/drm/display/drm_hdmi_state_helper.c
@@ -592,6 +592,7 @@ hdmi_compute_clock(const struct drm_connector *connector,
return -EINVAL;
conn_state->hdmi.tmds_char_rate = clock;
+ conn_state->hdmi.scrambler_needed = (clock > HDMI_1_3_TMDS_CHAR_RATE_MAX_HZ);
return 0;
}
--
2.54.0
^ permalink raw reply related
* [PATCH v2 15/15] arm_mpam: detect and enable MPAM-Fb PCC support
From: Andre Przywara @ 2026-07-02 16:22 UTC (permalink / raw)
To: Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Will Deacon, Rafael J . Wysocki, Len Brown, James Morse,
Ben Horgan, Reinette Chatre, Fenghua Yu
Cc: Jonathan Cameron, Srivathsa L Rao, Ganapatrao Kulkarni,
Trilok Soni, Srinivas Ramana, Niyas Sait, linux-acpi,
linux-arm-kernel, linux-kernel
In-Reply-To: <20260702162229.4008659-1-andre.przywara@arm.com>
The Arm MPAM-Fb specification [1] describes a protocol to access MSC
registers through a firmware interface. This requires a shared memory
region to hold the message, and a mailbox to trigger the access.
For ACPI this is wrapped as a PCC channel, described using existing
ACPI abstractions.
Add code to parse those PCC table descriptions associated with an MSC,
and store the parsed information in the MSC struct.
There can be multiple PCC channels, and each channel can serve multiple
MSCs, so we need to keep track of the channel usage, using a list and
a refcount.
This will be used by the MPAM-Fb access wrapper code.
[1] https://developer.arm.com/documentation/den0144/latest
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
drivers/acpi/arm64/mpam.c | 2 +
drivers/resctrl/mpam_devices.c | 113 ++++++++++++++++++++++++++++++++-
2 files changed, 113 insertions(+), 2 deletions(-)
diff --git a/drivers/acpi/arm64/mpam.c b/drivers/acpi/arm64/mpam.c
index 84963a20c3e7..64bc84bb2029 100644
--- a/drivers/acpi/arm64/mpam.c
+++ b/drivers/acpi/arm64/mpam.c
@@ -256,6 +256,8 @@ static struct platform_device * __init acpi_mpam_parse_msc(struct acpi_mpam_msc_
} else if (iface == MPAM_IFACE_PCC) {
props[next_prop++] = PROPERTY_ENTRY_U32("pcc-channel",
tbl_msc->base_address);
+ props[next_prop++] = PROPERTY_ENTRY_U32("msc-id",
+ tbl_msc->identifier);
}
acpi_mpam_parse_irqs(pdev, tbl_msc, res, &next_res);
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index 4a088e6cd235..35214520bfd4 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -19,6 +19,7 @@
#include <linux/irqdesc.h>
#include <linux/list.h>
#include <linux/lockdep.h>
+#include <linux/mailbox_client.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
@@ -27,6 +28,9 @@
#include <linux/types.h>
#include <linux/workqueue.h>
+#include <acpi/pcc.h>
+#include <acpi/acpi_io.h>
+
#include "mpam_internal.h"
#include "mpam_fb.h"
@@ -50,6 +54,88 @@ static LIST_HEAD(mpam_all_msc);
struct srcu_struct mpam_srcu;
+/* PCC channels might be serving multiple MSCs, so keep a refcounted list. */
+static DEFINE_MUTEX(pcc_chan_list_lock);
+static LIST_HEAD(pcc_chan_list);
+
+static void mpam_pcc_rx_callback(struct mbox_client *cl, void *msg)
+{
+ /* TODO: wake up tasks blocked on this MSC's PCC channel */
+}
+
+static struct mpam_pcc_chan *mpam_pcc_chan_get(struct device *dev,
+ int subspace_id)
+{
+ struct mpam_pcc_chan *cur;
+
+ mutex_lock(&pcc_chan_list_lock);
+
+ list_for_each_entry(cur, &pcc_chan_list, pcc_chans) {
+ if (cur->subspace_id == subspace_id) {
+ cur->refcount++;
+ mutex_unlock(&pcc_chan_list_lock);
+
+ return cur;
+ }
+ }
+
+ cur = kzalloc_obj(*cur);
+ if (!cur) {
+ mutex_unlock(&pcc_chan_list_lock);
+ return ERR_PTR(-ENOMEM);
+ }
+
+ cur->pcc_cl.dev = dev;
+ cur->pcc_cl.rx_callback = mpam_pcc_rx_callback;
+ cur->pcc_cl.tx_block = true;
+ cur->pcc_cl.tx_tout = 1000; /* 1s */
+
+ cur->pcc_chan = pcc_mbox_request_channel(&cur->pcc_cl, subspace_id);
+ if (IS_ERR(cur->pcc_chan)) {
+ long err = PTR_ERR(cur->pcc_chan);
+
+ kfree(cur);
+ mutex_unlock(&pcc_chan_list_lock);
+ return ERR_PTR(err);
+ }
+
+ mutex_init(&cur->pcc_chan_lock);
+ cur->subspace_id = subspace_id;
+ cur->refcount = 1;
+
+ list_add_tail(&cur->pcc_chans, &pcc_chan_list);
+
+ mutex_unlock(&pcc_chan_list_lock);
+
+ return cur;
+}
+
+static int mpam_pcc_chan_put(struct mpam_pcc_chan *pcc_chan)
+{
+ struct mpam_pcc_chan *cur, *tmp;
+
+ if (!pcc_chan)
+ return 0;
+
+ mutex_lock(&pcc_chan_list_lock);
+
+ list_for_each_entry_safe(cur, tmp, &pcc_chan_list, pcc_chans) {
+ if (cur == pcc_chan) {
+ if (!--cur->refcount) {
+ pcc_mbox_free_channel(cur->pcc_chan);
+ list_del(&pcc_chan->pcc_chans);
+ kfree(cur);
+ }
+ mutex_unlock(&pcc_chan_list_lock);
+ return 0;
+ }
+ }
+
+ mutex_unlock(&pcc_chan_list_lock);
+
+ return -ENOENT;
+}
+
/*
* Number of MSCs that have been probed. Once all MSCs have been probed MPAM
* can be enabled.
@@ -2202,6 +2288,8 @@ static void mpam_msc_drv_remove(struct platform_device *pdev)
{
struct mpam_msc *msc = platform_get_drvdata(pdev);
+ mpam_pcc_chan_put(msc->pcc_chan);
+
mutex_lock(&mpam_list_lock);
mpam_msc_destroy(msc);
mutex_unlock(&mpam_list_lock);
@@ -2212,7 +2300,7 @@ static void mpam_msc_drv_remove(struct platform_device *pdev)
static struct mpam_msc *do_mpam_msc_drv_probe(struct platform_device *pdev)
{
int err;
- u32 tmp;
+ u32 pcc_subspace_id;
struct mpam_msc *msc;
struct resource *msc_res;
struct device *dev = &pdev->dev;
@@ -2257,7 +2345,8 @@ static struct mpam_msc *do_mpam_msc_drv_probe(struct platform_device *pdev)
if (err)
return ERR_PTR(err);
- if (device_property_read_u32(&pdev->dev, "pcc-channel", &tmp))
+ if (device_property_read_u32(&pdev->dev, "pcc-channel",
+ &pcc_subspace_id))
msc->iface = MPAM_IFACE_MMIO;
else
msc->iface = MPAM_IFACE_PCC;
@@ -2273,6 +2362,26 @@ static struct mpam_msc *do_mpam_msc_drv_probe(struct platform_device *pdev)
}
msc->mapped_hwpage_sz = msc_res->end - msc_res->start;
msc->mapped_hwpage = io;
+ } else if (msc->iface == MPAM_IFACE_PCC) {
+ u32 msc_id;
+
+ if (device_property_read_u32(&pdev->dev, "msc-id", &msc_id)) {
+ pr_err("missing MPAM-Fb MSC identifier\n");
+ return ERR_PTR(-EINVAL);
+ }
+ msc->mpam_fb_msc_id = msc_id;
+
+ msc->pcc_chan = mpam_pcc_chan_get(&pdev->dev, pcc_subspace_id);
+ if (IS_ERR(msc->pcc_chan)) {
+ pr_err("Failed to request MSC PCC channel\n");
+ return (void *)msc->pcc_chan;
+ }
+
+ if (msc->pcc_chan->pcc_chan->shmem_size < MPAM_FB_MAX_MSG_SIZE) {
+ pr_err("MPAM-Fb PCC channel size too small.\n");
+ mpam_pcc_chan_put(msc->pcc_chan);
+ return ERR_PTR(-ENOMEM);
+ }
} else {
return ERR_PTR(-EINVAL);
}
--
2.43.0
^ permalink raw reply related
* [PATCH v2 14/15] arm_mpam: prevent MPAM-Fb accesses inside IRQ handler
From: Andre Przywara @ 2026-07-02 16:22 UTC (permalink / raw)
To: Lorenzo Pieralisi, Hanjun Guo, Sudeep Holla, Catalin Marinas,
Will Deacon, Rafael J . Wysocki, Len Brown, James Morse,
Ben Horgan, Reinette Chatre, Fenghua Yu
Cc: Jonathan Cameron, Srivathsa L Rao, Ganapatrao Kulkarni,
Trilok Soni, Srinivas Ramana, Niyas Sait, linux-acpi,
linux-arm-kernel, linux-kernel
In-Reply-To: <20260702162229.4008659-1-andre.przywara@arm.com>
When an MPAM MSC gets into an error condition, it can trigger an error
IRQ. We cannot really do much about those errors, but we at least query
and log the error, then disable MPAM functionality.
This error report relies on reading the MSC's error status register
(ESR) in the IRQ handler, which is not possible for MPAM-Fb based
MSC accesses, since they involve mailbox routines that might sleep.
The same is true for clearing the interrupt at the source, which
requires MSC access.
For simplicity just skip the ESR read when the MSC is not using direct
MMIO accesses, and just ignore the pending interrupts. We will wrap up
MPAM functionality regardless, knowing the exact error value will not
change that.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
drivers/resctrl/mpam_devices.c | 35 +++++++++++++++++++---------------
1 file changed, 20 insertions(+), 15 deletions(-)
diff --git a/drivers/resctrl/mpam_devices.c b/drivers/resctrl/mpam_devices.c
index b858ff389bff..4a088e6cd235 100644
--- a/drivers/resctrl/mpam_devices.c
+++ b/drivers/resctrl/mpam_devices.c
@@ -2639,7 +2639,7 @@ static int mpam_disable_msc_ecr(void *_msc)
static irqreturn_t __mpam_irq_handler(int irq, struct mpam_msc *msc)
{
- u64 reg;
+ u64 reg = 0;
u16 partid;
u8 errcode, pmg, ris;
@@ -2648,25 +2648,30 @@ static irqreturn_t __mpam_irq_handler(int irq, struct mpam_msc *msc)
&msc->accessibility)))
return IRQ_NONE;
- mpam_msc_read_esr(msc, ®);
+ /* MPAM-Fb MSC accesses cannot be done in atomic context. */
+ if (msc->iface == MPAM_IFACE_MMIO) {
+ mpam_msc_read_esr(msc, ®);
- errcode = FIELD_GET(MPAMF_ESR_ERRCODE, reg);
- if (!errcode)
- return IRQ_NONE;
+ errcode = FIELD_GET(MPAMF_ESR_ERRCODE, reg);
+ if (!errcode)
+ return IRQ_NONE;
- /* Clear level triggered irq */
- mpam_msc_clear_esr(msc);
+ /* Clear level triggered irq */
+ mpam_msc_clear_esr(msc);
- partid = FIELD_GET(MPAMF_ESR_PARTID_MON, reg);
- pmg = FIELD_GET(MPAMF_ESR_PMG, reg);
- ris = FIELD_GET(MPAMF_ESR_RIS, reg);
+ partid = FIELD_GET(MPAMF_ESR_PARTID_MON, reg);
+ pmg = FIELD_GET(MPAMF_ESR_PMG, reg);
+ ris = FIELD_GET(MPAMF_ESR_RIS, reg);
- pr_err_ratelimited("error irq from msc:%u '%s', partid:%u, pmg: %u, ris: %u\n",
- msc->id, mpam_errcode_names[errcode], partid, pmg,
- ris);
+ pr_err_ratelimited("error irq from msc:%u '%s', partid:%u, pmg: %u, ris: %u\n",
+ msc->id, mpam_errcode_names[errcode], partid,
+ pmg, ris);
- /* Disable this interrupt. */
- mpam_disable_msc_ecr(msc);
+ /* Disable this interrupt. */
+ mpam_disable_msc_ecr(msc);
+ } else {
+ pr_err_ratelimited("unknown error irq from msc:%u\n", msc->id);
+ }
/* Are we racing with the thread disabling MPAM? */
if (!mpam_is_enabled())
--
2.43.0
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox