From e28f802a7f1d933e3e3b5d514576de71e06169fb Mon Sep 17 00:00:00 2001 From: Pushpendra Singh Date: Sat, 15 Aug 2026 17:56:41 +0530 Subject: [PATCH 1/3] dt-bindings: interconnect: qcom-bwmon: add sa8797p compatibles Document the qcom,sa8797p-cpu-bwmon and qcom,sa8797p-llcc-bwmon fallback compatibles used by the CPU and LLCC bandwidth monitors on Nord Embedded. Signed-off-by: Pushpendra Singh --- .../devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml index 8f6c937e44cef..83ec8052122e4 100644 --- a/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml +++ b/Documentation/devicetree/bindings/interconnect/qcom,msm8998-bwmon.yaml @@ -32,6 +32,7 @@ properties: - qcom,qcs615-cpu-bwmon - qcom,qcs8300-cpu-bwmon - qcom,sa8775p-cpu-bwmon + - qcom,sa8797p-cpu-bwmon - qcom,sc7180-cpu-bwmon - qcom,sc7280-cpu-bwmon - qcom,sc8280xp-cpu-bwmon @@ -50,6 +51,7 @@ properties: - qcom,qcs615-llcc-bwmon - qcom,qcs8300-llcc-bwmon - qcom,sa8775p-llcc-bwmon + - qcom,sa8797p-llcc-bwmon - qcom,sc7180-llcc-bwmon - qcom,sc8280xp-llcc-bwmon - qcom,shikra-cpu-bwmon From 56d4ee72a84d18b2166d0b56312911d5c45c7dc4 Mon Sep 17 00:00:00 2001 From: Pushpendra Singh Date: Sat, 15 Aug 2026 17:57:30 +0530 Subject: [PATCH 2/3] arm64: dts: qcom: nord-embedded: Add CPU and LLCC BWMONs Added the node for cpu and llcc bwmon for NORD. Signed-off-by: Pushpendra Singh --- arch/arm64/boot/dts/qcom/nord-embedded.dtsi | 104 ++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/arch/arm64/boot/dts/qcom/nord-embedded.dtsi b/arch/arm64/boot/dts/qcom/nord-embedded.dtsi index 0d1c561b1f9e0..6a1189dbe3d3d 100644 --- a/arch/arm64/boot/dts/qcom/nord-embedded.dtsi +++ b/arch/arm64/boot/dts/qcom/nord-embedded.dtsi @@ -1400,6 +1400,110 @@ #interconnect-cells = <2>; qcom,bcm-voters = <&apps_bcm_voter>; }; + + llcc_bwmon: pmu@21091000 { + compatible = "qcom,sa8797p-llcc-bwmon", "qcom,sc7280-llcc-bwmon"; + reg = <0x0 0x21091000 0x0 0x1000>; + interrupts = ; + interconnects = <&mc_virt MASTER_LLCC QCOM_ICC_TAG_ACTIVE_ONLY + &mc_virt SLAVE_EBI1 QCOM_ICC_TAG_ACTIVE_ONLY>; + + operating-points-v2 = <&llcc_bwmon_opp_table>; + + llcc_bwmon_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-0 { + opp-peak-kBps = <762000>; + }; + + opp-1 { + opp-peak-kBps = <1720000>; + }; + + opp-2 { + opp-peak-kBps = <2086000>; + }; + + opp-3 { + opp-peak-kBps = <2601000>; + }; + + opp-4 { + opp-peak-kBps = <2929000>; + }; + + opp-5 { + opp-peak-kBps = <5931000>; + }; + + opp-6 { + opp-peak-kBps = <6515000>; + }; + + opp-7 { + opp-peak-kBps = <7984000>; + }; + + opp-8 { + opp-peak-kBps = <10437000>; + }; + + opp-9 { + opp-peak-kBps = <12195000>; + }; + }; + }; + + cpu_bwmon0: pmu@1016300 { + compatible = "qcom,sa8797p-cpu-bwmon", "qcom,sdm845-bwmon"; + reg = <0x0 0x1016300 0x0 0x600>; + interrupts = ; + interconnects = <&hscnoc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &hscnoc SLAVE_LLCC QCOM_ICC_TAG_ACTIVE_ONLY>; + + operating-points-v2 = <&cpu_bwmon_opp_table>; + + cpu_bwmon_opp_table: opp-table { + compatible = "operating-points-v2"; + + opp-0 { + opp-peak-kBps = <9155000>; + }; + + opp-1 { + opp-peak-kBps = <12298000>; + }; + + opp-2 { + opp-peak-kBps = <14236000>; + }; + + opp-3 { + opp-peak-kBps = <16265000>; + }; + }; + }; + + cpu_bwmon1: pmu@1015300 { + compatible = "qcom,sa8797p-cpu-bwmon", "qcom,sdm845-bwmon"; + reg = <0x0 0x1015300 0x0 0x600>; + interrupts = ; + interconnects = <&hscnoc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &hscnoc SLAVE_LLCC QCOM_ICC_TAG_ACTIVE_ONLY>; + + operating-points-v2 = <&cpu_bwmon_opp_table>; + }; + + cpu_bwmon2: pmu@1014300 { + compatible = "qcom,sa8797p-cpu-bwmon", "qcom,sc7280-cpu-bwmon"; + reg = <0x0 0x1014300 0x0 0x600>; + interrupts = ; + interconnects = <&hscnoc MASTER_APPSS_PROC QCOM_ICC_TAG_ACTIVE_ONLY + &hscnoc SLAVE_LLCC QCOM_ICC_TAG_ACTIVE_ONLY>; + + operating-points-v2 = <&cpu_bwmon_opp_table>; + }; }; &tlmm { From c0e6f7a09b634915cb16e2eac4dce45a7dc1665b Mon Sep 17 00:00:00 2001 From: Pushpendra Singh Date: Sat, 15 Aug 2026 17:58:41 +0530 Subject: [PATCH 3/3] soc: qcom: icc-bwmon: Add SA8797P cpu-bwmon and llcc-bwmon support Add register field, regmap config and icc_bwmon_data definitions for the SA8797P cpu-bwmon (throttle_bwmon_wrapper_apss) and llcc-bwmon instances. cpu-bwmon uses a unified register space with a different layout than BWMON v4/v5 (global and local IRQ registers do not overlap, and threshold/zone-max fields are 16 bits wide instead of 12), so it needs its own register fields, access tables and regmap config, wired up via the new "qcom,sa8797p-cpu-bwmon" compatible. llcc-bwmon reuses the BWMON v5 register offsets, only the threshold and zone-max fields are wider (16 bits vs 12 bits), so it reuses the v5 read/volatile tables and reg_defaults, wired up via the new "qcom,sa8797p-llcc-bwmon" compatible. Signed-off-by: Pushpendra Singh --- drivers/soc/qcom/icc-bwmon.c | 185 +++++++++++++++++++++++++++++++++++ 1 file changed, 185 insertions(+) diff --git a/drivers/soc/qcom/icc-bwmon.c b/drivers/soc/qcom/icc-bwmon.c index 597f9025e4228..f6d5f926b9872 100644 --- a/drivers/soc/qcom/icc-bwmon.c +++ b/drivers/soc/qcom/icc-bwmon.c @@ -128,6 +128,29 @@ #define BWMON_V4_ZONE_MAX(zone) (0x2e0 + 4 * (zone)) #define BWMON_V5_ZONE_MAX(zone) (0x044 + 4 * (zone)) +/* + * SA8797P cpu-bwmon: global registers and monitor registers are both mapped + * in a single, unified region, but at different absolute offsets than + * BWMON v4 - no "845 offset" adjustment is needed. The zone mechanism + * (BWMON2 in the hardware docs) is used, same as v4/v5. + */ +#define SA8797P_CPU_BWMON_GLOBAL_IRQ_CLEAR 0x108 +#define SA8797P_CPU_BWMON_GLOBAL_IRQ_ENABLE 0x10c + +#define SA8797P_CPU_BWMON_IRQ_STATUS 0x200 +#define SA8797P_CPU_BWMON_IRQ_CLEAR 0x208 +#define SA8797P_CPU_BWMON_IRQ_ENABLE 0x20c + +#define SA8797P_CPU_BWMON_ENABLE 0x3a0 +#define SA8797P_CPU_BWMON_CLEAR 0x3a4 +#define SA8797P_CPU_BWMON_SAMPLE_WINDOW 0x3a8 +#define SA8797P_CPU_BWMON_THRESHOLD_HIGH 0x3ac +#define SA8797P_CPU_BWMON_THRESHOLD_MED 0x3b0 +#define SA8797P_CPU_BWMON_THRESHOLD_LOW 0x3b4 +#define SA8797P_CPU_BWMON_ZONE_ACTIONS 0x3b8 +#define SA8797P_CPU_BWMON_THRESHOLD_COUNT 0x3bc +#define SA8797P_CPU_BWMON_ZONE_MAX(zone) (0x3e0 + 4 * (zone)) + /* Quirks for specific BWMON types */ #define BWMON_HAS_GLOBAL_IRQ BIT(0) #define BWMON_NEEDS_FORCE_CLEAR BIT(1) @@ -374,6 +397,91 @@ static const struct regmap_config sdm845_cpu_bwmon_regmap_cfg = { .cache_type = REGCACHE_MAPLE, }; +/* + * SA8797P cpu-bwmon (throttle_bwmon_wrapper_apss). Unified register space, + * but global and local IRQ registers do not overlap so, unlike the sdm845 + * "unified register space" case, no offset shifting is needed for the + * global IRQ registers. Threshold and zone-max registers are 16-bit wide, + * versus 12-bit on BWMON v4/v5. + */ +static const struct reg_field sa8797p_cpu_bwmon_reg_fields[] = { + [F_GLOBAL_IRQ_CLEAR] = REG_FIELD(SA8797P_CPU_BWMON_GLOBAL_IRQ_CLEAR, 0, 0), + [F_GLOBAL_IRQ_ENABLE] = REG_FIELD(SA8797P_CPU_BWMON_GLOBAL_IRQ_ENABLE, 0, 0), + [F_IRQ_STATUS] = REG_FIELD(SA8797P_CPU_BWMON_IRQ_STATUS, 4, 7), + [F_IRQ_CLEAR] = REG_FIELD(SA8797P_CPU_BWMON_IRQ_CLEAR, 4, 7), + [F_IRQ_ENABLE] = REG_FIELD(SA8797P_CPU_BWMON_IRQ_ENABLE, 4, 7), + /* F_ENABLE covers entire register to disable other features */ + [F_ENABLE] = REG_FIELD(SA8797P_CPU_BWMON_ENABLE, 0, 31), + [F_CLEAR] = REG_FIELD(SA8797P_CPU_BWMON_CLEAR, 0, 1), + [F_SAMPLE_WINDOW] = REG_FIELD(SA8797P_CPU_BWMON_SAMPLE_WINDOW, 0, 23), + [F_THRESHOLD_HIGH] = REG_FIELD(SA8797P_CPU_BWMON_THRESHOLD_HIGH, 0, 15), + [F_THRESHOLD_MED] = REG_FIELD(SA8797P_CPU_BWMON_THRESHOLD_MED, 0, 15), + [F_THRESHOLD_LOW] = REG_FIELD(SA8797P_CPU_BWMON_THRESHOLD_LOW, 0, 15), + [F_ZONE_ACTIONS_ZONE0] = REG_FIELD(SA8797P_CPU_BWMON_ZONE_ACTIONS, 0, 7), + [F_ZONE_ACTIONS_ZONE1] = REG_FIELD(SA8797P_CPU_BWMON_ZONE_ACTIONS, 8, 15), + [F_ZONE_ACTIONS_ZONE2] = REG_FIELD(SA8797P_CPU_BWMON_ZONE_ACTIONS, 16, 23), + [F_ZONE_ACTIONS_ZONE3] = REG_FIELD(SA8797P_CPU_BWMON_ZONE_ACTIONS, 24, 31), + [F_THRESHOLD_COUNT_ZONE0] = REG_FIELD(SA8797P_CPU_BWMON_THRESHOLD_COUNT, 0, 7), + [F_THRESHOLD_COUNT_ZONE1] = REG_FIELD(SA8797P_CPU_BWMON_THRESHOLD_COUNT, 8, 15), + [F_THRESHOLD_COUNT_ZONE2] = REG_FIELD(SA8797P_CPU_BWMON_THRESHOLD_COUNT, 16, 23), + [F_THRESHOLD_COUNT_ZONE3] = REG_FIELD(SA8797P_CPU_BWMON_THRESHOLD_COUNT, 24, 31), + [F_ZONE0_MAX] = REG_FIELD(SA8797P_CPU_BWMON_ZONE_MAX(0), 0, 15), + [F_ZONE1_MAX] = REG_FIELD(SA8797P_CPU_BWMON_ZONE_MAX(1), 0, 15), + [F_ZONE2_MAX] = REG_FIELD(SA8797P_CPU_BWMON_ZONE_MAX(2), 0, 15), + [F_ZONE3_MAX] = REG_FIELD(SA8797P_CPU_BWMON_ZONE_MAX(3), 0, 15), +}; + +static const struct regmap_range sa8797p_cpu_bwmon_reg_noread_ranges[] = { + regmap_reg_range(SA8797P_CPU_BWMON_GLOBAL_IRQ_CLEAR, SA8797P_CPU_BWMON_GLOBAL_IRQ_CLEAR), + regmap_reg_range(SA8797P_CPU_BWMON_IRQ_CLEAR, SA8797P_CPU_BWMON_IRQ_CLEAR), + regmap_reg_range(SA8797P_CPU_BWMON_CLEAR, SA8797P_CPU_BWMON_CLEAR), +}; + +static const struct regmap_access_table sa8797p_cpu_bwmon_reg_read_table = { + .no_ranges = sa8797p_cpu_bwmon_reg_noread_ranges, + .n_no_ranges = ARRAY_SIZE(sa8797p_cpu_bwmon_reg_noread_ranges), +}; + +static const struct regmap_range sa8797p_cpu_bwmon_reg_volatile_ranges[] = { + regmap_reg_range(SA8797P_CPU_BWMON_IRQ_STATUS, SA8797P_CPU_BWMON_IRQ_STATUS), + regmap_reg_range(SA8797P_CPU_BWMON_ZONE_MAX(0), SA8797P_CPU_BWMON_ZONE_MAX(3)), +}; + +static const struct regmap_access_table sa8797p_cpu_bwmon_reg_volatile_table = { + .yes_ranges = sa8797p_cpu_bwmon_reg_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(sa8797p_cpu_bwmon_reg_volatile_ranges), +}; + +/* + * Fill the cache for non-readable registers only as rest does not really + * matter and can be read from the device. + */ +static const struct reg_default sa8797p_cpu_bwmon_reg_defaults[] = { + { SA8797P_CPU_BWMON_GLOBAL_IRQ_CLEAR, 0x0 }, + { SA8797P_CPU_BWMON_IRQ_CLEAR, 0x0 }, + { SA8797P_CPU_BWMON_CLEAR, 0x0 }, +}; + +static const struct regmap_config sa8797p_cpu_bwmon_regmap_cfg = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + /* + * No concurrent access expected - driver has one interrupt handler, + * regmap is not shared, no driver or user-space API. + */ + .disable_locking = true, + .rd_table = &sa8797p_cpu_bwmon_reg_read_table, + .volatile_table = &sa8797p_cpu_bwmon_reg_volatile_table, + .reg_defaults = sa8797p_cpu_bwmon_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(sa8797p_cpu_bwmon_reg_defaults), + /* + * Cache is necessary for using regmap fields with non-readable + * registers. + */ + .cache_type = REGCACHE_MAPLE, +}; + /* BWMON v5 */ static const struct reg_field sdm845_llcc_bwmon_reg_fields[] = { [F_GLOBAL_IRQ_CLEAR] = {}, @@ -451,6 +559,57 @@ static const struct regmap_config sdm845_llcc_bwmon_regmap_cfg = { .cache_type = REGCACHE_MAPLE, }; +/* + * SA8797P llcc-bwmon: same register offsets as BWMON v5, but the + * threshold and zone-max fields are 16 bits wide instead of 12. + */ +static const struct reg_field sa8797p_llcc_bwmon_reg_fields[] = { + [F_GLOBAL_IRQ_CLEAR] = {}, + [F_GLOBAL_IRQ_ENABLE] = {}, + [F_IRQ_STATUS] = REG_FIELD(BWMON_V5_IRQ_STATUS, 0, 3), + [F_IRQ_CLEAR] = REG_FIELD(BWMON_V5_IRQ_CLEAR, 0, 3), + [F_IRQ_ENABLE] = REG_FIELD(BWMON_V5_IRQ_ENABLE, 0, 3), + /* F_ENABLE covers entire register to disable other features */ + [F_ENABLE] = REG_FIELD(BWMON_V5_ENABLE, 0, 31), + [F_CLEAR] = REG_FIELD(BWMON_V5_CLEAR, 0, 1), + [F_SAMPLE_WINDOW] = REG_FIELD(BWMON_V5_SAMPLE_WINDOW, 0, 19), + [F_THRESHOLD_HIGH] = REG_FIELD(BWMON_V5_THRESHOLD_HIGH, 0, 15), + [F_THRESHOLD_MED] = REG_FIELD(BWMON_V5_THRESHOLD_MED, 0, 15), + [F_THRESHOLD_LOW] = REG_FIELD(BWMON_V5_THRESHOLD_LOW, 0, 15), + [F_ZONE_ACTIONS_ZONE0] = REG_FIELD(BWMON_V5_ZONE_ACTIONS, 0, 7), + [F_ZONE_ACTIONS_ZONE1] = REG_FIELD(BWMON_V5_ZONE_ACTIONS, 8, 15), + [F_ZONE_ACTIONS_ZONE2] = REG_FIELD(BWMON_V5_ZONE_ACTIONS, 16, 23), + [F_ZONE_ACTIONS_ZONE3] = REG_FIELD(BWMON_V5_ZONE_ACTIONS, 24, 31), + [F_THRESHOLD_COUNT_ZONE0] = REG_FIELD(BWMON_V5_THRESHOLD_COUNT, 0, 7), + [F_THRESHOLD_COUNT_ZONE1] = REG_FIELD(BWMON_V5_THRESHOLD_COUNT, 8, 15), + [F_THRESHOLD_COUNT_ZONE2] = REG_FIELD(BWMON_V5_THRESHOLD_COUNT, 16, 23), + [F_THRESHOLD_COUNT_ZONE3] = REG_FIELD(BWMON_V5_THRESHOLD_COUNT, 24, 31), + [F_ZONE0_MAX] = REG_FIELD(BWMON_V5_ZONE_MAX(0), 0, 15), + [F_ZONE1_MAX] = REG_FIELD(BWMON_V5_ZONE_MAX(1), 0, 15), + [F_ZONE2_MAX] = REG_FIELD(BWMON_V5_ZONE_MAX(2), 0, 15), + [F_ZONE3_MAX] = REG_FIELD(BWMON_V5_ZONE_MAX(3), 0, 15), +}; + +static const struct regmap_config sa8797p_llcc_bwmon_regmap_cfg = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + /* + * No concurrent access expected - driver has one interrupt handler, + * regmap is not shared, no driver or user-space API. + */ + .disable_locking = true, + .rd_table = &sdm845_llcc_bwmon_reg_read_table, + .volatile_table = &sdm845_llcc_bwmon_reg_volatile_table, + .reg_defaults = sdm845_llcc_bwmon_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(sdm845_llcc_bwmon_reg_defaults), + /* + * Cache is necessary for using regmap fields with non-readable + * registers. + */ + .cache_type = REGCACHE_MAPLE, +}; + static void bwmon_clear_counters(struct icc_bwmon *bwmon, bool clear_all) { unsigned int val = BWMON_CLEAR_CLEAR; @@ -722,7 +881,9 @@ static int bwmon_init_regmap(struct platform_device *pdev, BUILD_BUG_ON(ARRAY_SIZE(msm8998_bwmon_global_reg_fields) != F_NUM_GLOBAL_FIELDS); BUILD_BUG_ON(ARRAY_SIZE(msm8998_bwmon_reg_fields) != F_NUM_FIELDS); BUILD_BUG_ON(ARRAY_SIZE(sdm845_cpu_bwmon_reg_fields) != F_NUM_FIELDS); + BUILD_BUG_ON(ARRAY_SIZE(sa8797p_cpu_bwmon_reg_fields) != F_NUM_FIELDS); BUILD_BUG_ON(ARRAY_SIZE(sdm845_llcc_bwmon_reg_fields) != F_NUM_FIELDS); + BUILD_BUG_ON(ARRAY_SIZE(sa8797p_llcc_bwmon_reg_fields) != F_NUM_FIELDS); ret = devm_regmap_field_bulk_alloc(dev, map, bwmon->regs, bwmon->data->regmap_fields, @@ -837,6 +998,16 @@ static const struct icc_bwmon_data sdm845_cpu_bwmon_data = { .regmap_cfg = &sdm845_cpu_bwmon_regmap_cfg, }; +static const struct icc_bwmon_data sa8797p_cpu_bwmon_data = { + .sample_ms = 4, + .count_unit_kb = 64, + .zone1_thres_count = 16, + .zone3_thres_count = 1, + .quirks = BWMON_HAS_GLOBAL_IRQ, + .regmap_fields = sa8797p_cpu_bwmon_reg_fields, + .regmap_cfg = &sa8797p_cpu_bwmon_regmap_cfg, +}; + static const struct icc_bwmon_data sdm845_llcc_bwmon_data = { .sample_ms = 4, .count_unit_kb = 1024, @@ -856,14 +1027,28 @@ static const struct icc_bwmon_data sc7280_llcc_bwmon_data = { .regmap_cfg = &sdm845_llcc_bwmon_regmap_cfg, }; +static const struct icc_bwmon_data sa8797p_llcc_bwmon_data = { + .sample_ms = 4, + .count_unit_kb = 64, + .zone1_thres_count = 16, + .zone3_thres_count = 1, + .quirks = BWMON_NEEDS_FORCE_CLEAR, + .regmap_fields = sa8797p_llcc_bwmon_reg_fields, + .regmap_cfg = &sa8797p_llcc_bwmon_regmap_cfg, +}; + static const struct of_device_id bwmon_of_match[] = { /* BWMONv4, separate monitor and global register spaces */ { .compatible = "qcom,msm8998-bwmon", .data = &msm8998_bwmon_data }, /* BWMONv4, unified register space */ { .compatible = "qcom,sdm845-bwmon", .data = &sdm845_cpu_bwmon_data }, + /* SA8797P cpu-bwmon, unified register space, different layout than v4 */ + { .compatible = "qcom,sa8797p-cpu-bwmon", .data = &sa8797p_cpu_bwmon_data }, /* BWMONv5 */ { .compatible = "qcom,sdm845-llcc-bwmon", .data = &sdm845_llcc_bwmon_data }, { .compatible = "qcom,sc7280-llcc-bwmon", .data = &sc7280_llcc_bwmon_data }, + /* BWMONv5, same offsets but wider threshold/zone-max fields */ + { .compatible = "qcom,sa8797p-llcc-bwmon", .data = &sa8797p_llcc_bwmon_data }, /* Compatibles kept for legacy reasons */ { .compatible = "qcom,sc7280-cpu-bwmon", .data = &sdm845_cpu_bwmon_data },