Skip to content

Commit b6e9fb7

Browse files
konradybcioandersson
authored andcommitted
soc: qcom: icc-bwmon: Handle global registers correctly
The BWMON hardware has two sets of registers: one for the monitor itself and one called "global". It has what seems to be some kind of a head switch and an interrupt control register. It's usually 0x200 in size. On fairly recent SoCs (with the starting point seemingly being moving the OSM programming to the firmware) these two register sets are contiguous and overlapping, like this (on sm8450): /* notice how base.start == global_base.start+0x100 */ reg = <0x90b6400 0x300>, <0x90b6300 0x200>; reg-names = "base", "global_base"; Which led to some confusion and the assumption that since the "interesting" global registers begin right after global_base+0x100, there's no need to map two separate regions and one can simply subtract 0x100 from the offsets. This is however not the case for anything older than SDM845, as the global region can appear in seemingly random spots on the register map. Handle the case where the global registers are mapped separately to allow proper functioning of BWMONv4 on MSM8998 and older. Add specific compatibles for 845, 8280xp, 7280 and 8550 (all of which use the single reg space scheme) to keep backwards compatibility with old DTs. Signed-off-by: Konrad Dybcio <[email protected]> Reviewed-by: Krzysztof Kozlowski <[email protected]> Signed-off-by: Bjorn Andersson <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent 452c165 commit b6e9fb7

File tree

1 file changed

+209
-21
lines changed

1 file changed

+209
-21
lines changed

drivers/soc/qcom/icc-bwmon.c

Lines changed: 209 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,27 @@
3434
/* Internal sampling clock frequency */
3535
#define HW_TIMER_HZ 19200000
3636

37-
#define BWMON_V4_GLOBAL_IRQ_CLEAR 0x008
38-
#define BWMON_V4_GLOBAL_IRQ_ENABLE 0x00c
37+
#define BWMON_V4_GLOBAL_IRQ_CLEAR 0x108
38+
#define BWMON_V4_GLOBAL_IRQ_ENABLE 0x10c
3939
/*
4040
* All values here and further are matching regmap fields, so without absolute
4141
* register offsets.
4242
*/
4343
#define BWMON_V4_GLOBAL_IRQ_ENABLE_ENABLE BIT(0)
4444

45+
/*
46+
* Starting with SDM845, the BWMON4 register space has changed a bit:
47+
* the global registers were jammed into the beginning of the monitor region.
48+
* To keep the proper offsets, one would have to map <GLOBAL_BASE 0x200> and
49+
* <GLOBAL_BASE+0x100 0x300>, which is straight up wrong.
50+
* To facilitate for that, while allowing the older, arguably more proper
51+
* implementations to work, offset the global registers by -0x100 to avoid
52+
* having to map half of the global registers twice.
53+
*/
54+
#define BWMON_V4_845_OFFSET 0x100
55+
#define BWMON_V4_GLOBAL_IRQ_CLEAR_845 (BWMON_V4_GLOBAL_IRQ_CLEAR - BWMON_V4_845_OFFSET)
56+
#define BWMON_V4_GLOBAL_IRQ_ENABLE_845 (BWMON_V4_GLOBAL_IRQ_ENABLE - BWMON_V4_845_OFFSET)
57+
4558
#define BWMON_V4_IRQ_STATUS 0x100
4659
#define BWMON_V4_IRQ_CLEAR 0x108
4760

@@ -118,9 +131,13 @@
118131
#define BWMON_NEEDS_FORCE_CLEAR BIT(1)
119132

120133
enum bwmon_fields {
134+
/* Global region fields, keep them at the top */
121135
F_GLOBAL_IRQ_CLEAR,
122136
F_GLOBAL_IRQ_ENABLE,
123-
F_IRQ_STATUS,
137+
F_NUM_GLOBAL_FIELDS,
138+
139+
/* Monitor region fields */
140+
F_IRQ_STATUS = F_NUM_GLOBAL_FIELDS,
124141
F_IRQ_CLEAR,
125142
F_IRQ_ENABLE,
126143
F_ENABLE,
@@ -157,6 +174,9 @@ struct icc_bwmon_data {
157174

158175
const struct regmap_config *regmap_cfg;
159176
const struct reg_field *regmap_fields;
177+
178+
const struct regmap_config *global_regmap_cfg;
179+
const struct reg_field *global_regmap_fields;
160180
};
161181

162182
struct icc_bwmon {
@@ -165,6 +185,7 @@ struct icc_bwmon {
165185
int irq;
166186

167187
struct regmap_field *regs[F_NUM_FIELDS];
188+
struct regmap_field *global_regs[F_NUM_GLOBAL_FIELDS];
168189

169190
unsigned int max_bw_kbps;
170191
unsigned int min_bw_kbps;
@@ -174,8 +195,8 @@ struct icc_bwmon {
174195

175196
/* BWMON v4 */
176197
static const struct reg_field msm8998_bwmon_reg_fields[] = {
177-
[F_GLOBAL_IRQ_CLEAR] = REG_FIELD(BWMON_V4_GLOBAL_IRQ_CLEAR, 0, 0),
178-
[F_GLOBAL_IRQ_ENABLE] = REG_FIELD(BWMON_V4_GLOBAL_IRQ_ENABLE, 0, 0),
198+
[F_GLOBAL_IRQ_CLEAR] = {},
199+
[F_GLOBAL_IRQ_ENABLE] = {},
179200
[F_IRQ_STATUS] = REG_FIELD(BWMON_V4_IRQ_STATUS, 4, 7),
180201
[F_IRQ_CLEAR] = REG_FIELD(BWMON_V4_IRQ_CLEAR, 4, 7),
181202
[F_IRQ_ENABLE] = REG_FIELD(BWMON_V4_IRQ_ENABLE, 4, 7),
@@ -201,7 +222,6 @@ static const struct reg_field msm8998_bwmon_reg_fields[] = {
201222
};
202223

203224
static const struct regmap_range msm8998_bwmon_reg_noread_ranges[] = {
204-
regmap_reg_range(BWMON_V4_GLOBAL_IRQ_CLEAR, BWMON_V4_GLOBAL_IRQ_CLEAR),
205225
regmap_reg_range(BWMON_V4_IRQ_CLEAR, BWMON_V4_IRQ_CLEAR),
206226
regmap_reg_range(BWMON_V4_CLEAR, BWMON_V4_CLEAR),
207227
};
@@ -221,16 +241,33 @@ static const struct regmap_access_table msm8998_bwmon_reg_volatile_table = {
221241
.n_yes_ranges = ARRAY_SIZE(msm8998_bwmon_reg_volatile_ranges),
222242
};
223243

244+
static const struct reg_field msm8998_bwmon_global_reg_fields[] = {
245+
[F_GLOBAL_IRQ_CLEAR] = REG_FIELD(BWMON_V4_GLOBAL_IRQ_CLEAR, 0, 0),
246+
[F_GLOBAL_IRQ_ENABLE] = REG_FIELD(BWMON_V4_GLOBAL_IRQ_ENABLE, 0, 0),
247+
};
248+
249+
static const struct regmap_range msm8998_bwmon_global_reg_noread_ranges[] = {
250+
regmap_reg_range(BWMON_V4_GLOBAL_IRQ_CLEAR, BWMON_V4_GLOBAL_IRQ_CLEAR),
251+
};
252+
253+
static const struct regmap_access_table msm8998_bwmon_global_reg_read_table = {
254+
.no_ranges = msm8998_bwmon_global_reg_noread_ranges,
255+
.n_no_ranges = ARRAY_SIZE(msm8998_bwmon_global_reg_noread_ranges),
256+
};
257+
224258
/*
225259
* Fill the cache for non-readable registers only as rest does not really
226260
* matter and can be read from the device.
227261
*/
228262
static const struct reg_default msm8998_bwmon_reg_defaults[] = {
229-
{ BWMON_V4_GLOBAL_IRQ_CLEAR, 0x0 },
230263
{ BWMON_V4_IRQ_CLEAR, 0x0 },
231264
{ BWMON_V4_CLEAR, 0x0 },
232265
};
233266

267+
static const struct reg_default msm8998_bwmon_global_reg_defaults[] = {
268+
{ BWMON_V4_GLOBAL_IRQ_CLEAR, 0x0 },
269+
};
270+
234271
static const struct regmap_config msm8998_bwmon_regmap_cfg = {
235272
.reg_bits = 32,
236273
.reg_stride = 4,
@@ -251,6 +288,93 @@ static const struct regmap_config msm8998_bwmon_regmap_cfg = {
251288
.cache_type = REGCACHE_RBTREE,
252289
};
253290

291+
static const struct regmap_config msm8998_bwmon_global_regmap_cfg = {
292+
.reg_bits = 32,
293+
.reg_stride = 4,
294+
.val_bits = 32,
295+
/*
296+
* No concurrent access expected - driver has one interrupt handler,
297+
* regmap is not shared, no driver or user-space API.
298+
*/
299+
.disable_locking = true,
300+
.rd_table = &msm8998_bwmon_global_reg_read_table,
301+
.reg_defaults = msm8998_bwmon_global_reg_defaults,
302+
.num_reg_defaults = ARRAY_SIZE(msm8998_bwmon_global_reg_defaults),
303+
/*
304+
* Cache is necessary for using regmap fields with non-readable
305+
* registers.
306+
*/
307+
.cache_type = REGCACHE_RBTREE,
308+
};
309+
310+
static const struct reg_field sdm845_cpu_bwmon_reg_fields[] = {
311+
[F_GLOBAL_IRQ_CLEAR] = REG_FIELD(BWMON_V4_GLOBAL_IRQ_CLEAR_845, 0, 0),
312+
[F_GLOBAL_IRQ_ENABLE] = REG_FIELD(BWMON_V4_GLOBAL_IRQ_ENABLE_845, 0, 0),
313+
[F_IRQ_STATUS] = REG_FIELD(BWMON_V4_IRQ_STATUS, 4, 7),
314+
[F_IRQ_CLEAR] = REG_FIELD(BWMON_V4_IRQ_CLEAR, 4, 7),
315+
[F_IRQ_ENABLE] = REG_FIELD(BWMON_V4_IRQ_ENABLE, 4, 7),
316+
/* F_ENABLE covers entire register to disable other features */
317+
[F_ENABLE] = REG_FIELD(BWMON_V4_ENABLE, 0, 31),
318+
[F_CLEAR] = REG_FIELD(BWMON_V4_CLEAR, 0, 1),
319+
[F_SAMPLE_WINDOW] = REG_FIELD(BWMON_V4_SAMPLE_WINDOW, 0, 23),
320+
[F_THRESHOLD_HIGH] = REG_FIELD(BWMON_V4_THRESHOLD_HIGH, 0, 11),
321+
[F_THRESHOLD_MED] = REG_FIELD(BWMON_V4_THRESHOLD_MED, 0, 11),
322+
[F_THRESHOLD_LOW] = REG_FIELD(BWMON_V4_THRESHOLD_LOW, 0, 11),
323+
[F_ZONE_ACTIONS_ZONE0] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 0, 7),
324+
[F_ZONE_ACTIONS_ZONE1] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 8, 15),
325+
[F_ZONE_ACTIONS_ZONE2] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 16, 23),
326+
[F_ZONE_ACTIONS_ZONE3] = REG_FIELD(BWMON_V4_ZONE_ACTIONS, 24, 31),
327+
[F_THRESHOLD_COUNT_ZONE0] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 0, 7),
328+
[F_THRESHOLD_COUNT_ZONE1] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 8, 15),
329+
[F_THRESHOLD_COUNT_ZONE2] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 16, 23),
330+
[F_THRESHOLD_COUNT_ZONE3] = REG_FIELD(BWMON_V4_THRESHOLD_COUNT, 24, 31),
331+
[F_ZONE0_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(0), 0, 11),
332+
[F_ZONE1_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(1), 0, 11),
333+
[F_ZONE2_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(2), 0, 11),
334+
[F_ZONE3_MAX] = REG_FIELD(BWMON_V4_ZONE_MAX(3), 0, 11),
335+
};
336+
337+
static const struct regmap_range sdm845_cpu_bwmon_reg_noread_ranges[] = {
338+
regmap_reg_range(BWMON_V4_GLOBAL_IRQ_CLEAR_845, BWMON_V4_GLOBAL_IRQ_CLEAR_845),
339+
regmap_reg_range(BWMON_V4_IRQ_CLEAR, BWMON_V4_IRQ_CLEAR),
340+
regmap_reg_range(BWMON_V4_CLEAR, BWMON_V4_CLEAR),
341+
};
342+
343+
static const struct regmap_access_table sdm845_cpu_bwmon_reg_read_table = {
344+
.no_ranges = sdm845_cpu_bwmon_reg_noread_ranges,
345+
.n_no_ranges = ARRAY_SIZE(sdm845_cpu_bwmon_reg_noread_ranges),
346+
};
347+
348+
/*
349+
* Fill the cache for non-readable registers only as rest does not really
350+
* matter and can be read from the device.
351+
*/
352+
static const struct reg_default sdm845_cpu_bwmon_reg_defaults[] = {
353+
{ BWMON_V4_GLOBAL_IRQ_CLEAR_845, 0x0 },
354+
{ BWMON_V4_IRQ_CLEAR, 0x0 },
355+
{ BWMON_V4_CLEAR, 0x0 },
356+
};
357+
358+
static const struct regmap_config sdm845_cpu_bwmon_regmap_cfg = {
359+
.reg_bits = 32,
360+
.reg_stride = 4,
361+
.val_bits = 32,
362+
/*
363+
* No concurrent access expected - driver has one interrupt handler,
364+
* regmap is not shared, no driver or user-space API.
365+
*/
366+
.disable_locking = true,
367+
.rd_table = &sdm845_cpu_bwmon_reg_read_table,
368+
.volatile_table = &msm8998_bwmon_reg_volatile_table,
369+
.reg_defaults = sdm845_cpu_bwmon_reg_defaults,
370+
.num_reg_defaults = ARRAY_SIZE(sdm845_cpu_bwmon_reg_defaults),
371+
/*
372+
* Cache is necessary for using regmap fields with non-readable
373+
* registers.
374+
*/
375+
.cache_type = REGCACHE_RBTREE,
376+
};
377+
254378
/* BWMON v5 */
255379
static const struct reg_field sdm845_llcc_bwmon_reg_fields[] = {
256380
[F_GLOBAL_IRQ_CLEAR] = {},
@@ -349,6 +473,13 @@ static void bwmon_clear_counters(struct icc_bwmon *bwmon, bool clear_all)
349473

350474
static void bwmon_clear_irq(struct icc_bwmon *bwmon)
351475
{
476+
struct regmap_field *global_irq_clr;
477+
478+
if (bwmon->data->global_regmap_fields)
479+
global_irq_clr = bwmon->global_regs[F_GLOBAL_IRQ_CLEAR];
480+
else
481+
global_irq_clr = bwmon->regs[F_GLOBAL_IRQ_CLEAR];
482+
352483
/*
353484
* Clear zone and global interrupts. The order and barriers are
354485
* important. Quoting downstream Qualcomm msm-4.9 tree:
@@ -369,15 +500,22 @@ static void bwmon_clear_irq(struct icc_bwmon *bwmon)
369500
if (bwmon->data->quirks & BWMON_NEEDS_FORCE_CLEAR)
370501
regmap_field_force_write(bwmon->regs[F_IRQ_CLEAR], 0);
371502
if (bwmon->data->quirks & BWMON_HAS_GLOBAL_IRQ)
372-
regmap_field_force_write(bwmon->regs[F_GLOBAL_IRQ_CLEAR],
503+
regmap_field_force_write(global_irq_clr,
373504
BWMON_V4_GLOBAL_IRQ_ENABLE_ENABLE);
374505
}
375506

376507
static void bwmon_disable(struct icc_bwmon *bwmon)
377508
{
509+
struct regmap_field *global_irq_en;
510+
511+
if (bwmon->data->global_regmap_fields)
512+
global_irq_en = bwmon->global_regs[F_GLOBAL_IRQ_ENABLE];
513+
else
514+
global_irq_en = bwmon->regs[F_GLOBAL_IRQ_ENABLE];
515+
378516
/* Disable interrupts. Strict ordering, see bwmon_clear_irq(). */
379517
if (bwmon->data->quirks & BWMON_HAS_GLOBAL_IRQ)
380-
regmap_field_write(bwmon->regs[F_GLOBAL_IRQ_ENABLE], 0x0);
518+
regmap_field_write(global_irq_en, 0x0);
381519
regmap_field_write(bwmon->regs[F_IRQ_ENABLE], 0x0);
382520

383521
/*
@@ -389,10 +527,18 @@ static void bwmon_disable(struct icc_bwmon *bwmon)
389527

390528
static void bwmon_enable(struct icc_bwmon *bwmon, unsigned int irq_enable)
391529
{
530+
struct regmap_field *global_irq_en;
531+
532+
if (bwmon->data->global_regmap_fields)
533+
global_irq_en = bwmon->global_regs[F_GLOBAL_IRQ_ENABLE];
534+
else
535+
global_irq_en = bwmon->regs[F_GLOBAL_IRQ_ENABLE];
536+
392537
/* Enable interrupts */
393538
if (bwmon->data->quirks & BWMON_HAS_GLOBAL_IRQ)
394-
regmap_field_write(bwmon->regs[F_GLOBAL_IRQ_ENABLE],
539+
regmap_field_write(global_irq_en,
395540
BWMON_V4_GLOBAL_IRQ_ENABLE_ENABLE);
541+
396542
regmap_field_write(bwmon->regs[F_IRQ_ENABLE], irq_enable);
397543

398544
/* Enable bwmon */
@@ -555,7 +701,9 @@ static int bwmon_init_regmap(struct platform_device *pdev,
555701
struct device *dev = &pdev->dev;
556702
void __iomem *base;
557703
struct regmap *map;
704+
int ret;
558705

706+
/* Map the monitor base */
559707
base = devm_platform_ioremap_resource(pdev, 0);
560708
if (IS_ERR(base))
561709
return dev_err_probe(dev, PTR_ERR(base),
@@ -566,12 +714,35 @@ static int bwmon_init_regmap(struct platform_device *pdev,
566714
return dev_err_probe(dev, PTR_ERR(map),
567715
"failed to initialize regmap\n");
568716

717+
BUILD_BUG_ON(ARRAY_SIZE(msm8998_bwmon_global_reg_fields) != F_NUM_GLOBAL_FIELDS);
569718
BUILD_BUG_ON(ARRAY_SIZE(msm8998_bwmon_reg_fields) != F_NUM_FIELDS);
719+
BUILD_BUG_ON(ARRAY_SIZE(sdm845_cpu_bwmon_reg_fields) != F_NUM_FIELDS);
570720
BUILD_BUG_ON(ARRAY_SIZE(sdm845_llcc_bwmon_reg_fields) != F_NUM_FIELDS);
571721

572-
return devm_regmap_field_bulk_alloc(dev, map, bwmon->regs,
722+
ret = devm_regmap_field_bulk_alloc(dev, map, bwmon->regs,
573723
bwmon->data->regmap_fields,
574724
F_NUM_FIELDS);
725+
if (ret)
726+
return ret;
727+
728+
if (bwmon->data->global_regmap_cfg) {
729+
/* Map the global base, if separate */
730+
base = devm_platform_ioremap_resource(pdev, 1);
731+
if (IS_ERR(base))
732+
return dev_err_probe(dev, PTR_ERR(base),
733+
"failed to map bwmon global registers\n");
734+
735+
map = devm_regmap_init_mmio(dev, base, bwmon->data->global_regmap_cfg);
736+
if (IS_ERR(map))
737+
return dev_err_probe(dev, PTR_ERR(map),
738+
"failed to initialize global regmap\n");
739+
740+
ret = devm_regmap_field_bulk_alloc(dev, map, bwmon->global_regs,
741+
bwmon->data->global_regmap_fields,
742+
F_NUM_GLOBAL_FIELDS);
743+
}
744+
745+
return ret;
575746
}
576747

577748
static int bwmon_probe(struct platform_device *pdev)
@@ -644,6 +815,21 @@ static const struct icc_bwmon_data msm8998_bwmon_data = {
644815
.quirks = BWMON_HAS_GLOBAL_IRQ,
645816
.regmap_fields = msm8998_bwmon_reg_fields,
646817
.regmap_cfg = &msm8998_bwmon_regmap_cfg,
818+
.global_regmap_fields = msm8998_bwmon_global_reg_fields,
819+
.global_regmap_cfg = &msm8998_bwmon_global_regmap_cfg,
820+
};
821+
822+
static const struct icc_bwmon_data sdm845_cpu_bwmon_data = {
823+
.sample_ms = 4,
824+
.count_unit_kb = 64,
825+
.default_highbw_kbps = 4800 * 1024, /* 4.8 GBps */
826+
.default_medbw_kbps = 512 * 1024, /* 512 MBps */
827+
.default_lowbw_kbps = 0,
828+
.zone1_thres_count = 16,
829+
.zone3_thres_count = 1,
830+
.quirks = BWMON_HAS_GLOBAL_IRQ,
831+
.regmap_fields = sdm845_cpu_bwmon_reg_fields,
832+
.regmap_cfg = &sdm845_cpu_bwmon_regmap_cfg,
647833
};
648834

649835
static const struct icc_bwmon_data sdm845_llcc_bwmon_data = {
@@ -672,16 +858,18 @@ static const struct icc_bwmon_data sc7280_llcc_bwmon_data = {
672858
};
673859

674860
static const struct of_device_id bwmon_of_match[] = {
675-
{
676-
.compatible = "qcom,msm8998-bwmon",
677-
.data = &msm8998_bwmon_data
678-
}, {
679-
.compatible = "qcom,sdm845-llcc-bwmon",
680-
.data = &sdm845_llcc_bwmon_data
681-
}, {
682-
.compatible = "qcom,sc7280-llcc-bwmon",
683-
.data = &sc7280_llcc_bwmon_data
684-
},
861+
/* BWMONv4, separate monitor and global register spaces */
862+
{ .compatible = "qcom,msm8998-bwmon", .data = &msm8998_bwmon_data },
863+
/* BWMONv4, unified register space */
864+
{ .compatible = "qcom,sdm845-bwmon", .data = &sdm845_cpu_bwmon_data },
865+
/* BWMONv5 */
866+
{ .compatible = "qcom,sdm845-llcc-bwmon", .data = &sdm845_llcc_bwmon_data },
867+
{ .compatible = "qcom,sc7280-llcc-bwmon", .data = &sc7280_llcc_bwmon_data },
868+
869+
/* Compatibles kept for legacy reasons */
870+
{ .compatible = "qcom,sc7280-cpu-bwmon", .data = &sdm845_cpu_bwmon_data },
871+
{ .compatible = "qcom,sc8280xp-cpu-bwmon", .data = &sdm845_cpu_bwmon_data },
872+
{ .compatible = "qcom,sm8550-cpu-bwmon", .data = &sdm845_cpu_bwmon_data },
685873
{}
686874
};
687875
MODULE_DEVICE_TABLE(of, bwmon_of_match);

0 commit comments

Comments
 (0)