1
2
3
4
5
6
7#include <linux/bitmap.h>
8#include <linux/bitops.h>
9#include <linux/device.h>
10#include <linux/io.h>
11#include <linux/kernel.h>
12#include <linux/module.h>
13#include <linux/mutex.h>
14#include <linux/of.h>
15#include <linux/of_device.h>
16#include <linux/regmap.h>
17#include <linux/sizes.h>
18#include <linux/slab.h>
19#include <linux/soc/qcom/llcc-qcom.h>
20
21#define ACTIVATE BIT(0)
22#define DEACTIVATE BIT(1)
23#define ACT_CTRL_OPCODE_ACTIVATE BIT(0)
24#define ACT_CTRL_OPCODE_DEACTIVATE BIT(1)
25#define ACT_CTRL_ACT_TRIG BIT(0)
26#define ACT_CTRL_OPCODE_SHIFT 0x01
27#define ATTR1_PROBE_TARGET_WAYS_SHIFT 0x02
28#define ATTR1_FIXED_SIZE_SHIFT 0x03
29#define ATTR1_PRIORITY_SHIFT 0x04
30#define ATTR1_MAX_CAP_SHIFT 0x10
31#define ATTR0_RES_WAYS_MASK GENMASK(11, 0)
32#define ATTR0_BONUS_WAYS_MASK GENMASK(27, 16)
33#define ATTR0_BONUS_WAYS_SHIFT 0x10
34#define LLCC_STATUS_READ_DELAY 100
35
36#define CACHE_LINE_SIZE_SHIFT 6
37
38#define LLCC_COMMON_STATUS0 0x0003000c
39#define LLCC_LB_CNT_MASK GENMASK(31, 28)
40#define LLCC_LB_CNT_SHIFT 28
41
42#define MAX_CAP_TO_BYTES(n) (n * SZ_1K)
43#define LLCC_TRP_ACT_CTRLn(n) (n * SZ_4K)
44#define LLCC_TRP_STATUSn(n) (4 + n * SZ_4K)
45#define LLCC_TRP_ATTR0_CFGn(n) (0x21000 + SZ_8 * n)
46#define LLCC_TRP_ATTR1_CFGn(n) (0x21004 + SZ_8 * n)
47
48#define LLCC_TRP_SCID_DIS_CAP_ALLOC 0x21f00
49#define LLCC_TRP_PCB_ACT 0x21f04
50
51#define BANK_OFFSET_STRIDE 0x80000
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77struct llcc_slice_config {
78 u32 usecase_id;
79 u32 slice_id;
80 u32 max_cap;
81 u32 priority;
82 bool fixed_size;
83 u32 bonus_ways;
84 u32 res_ways;
85 u32 cache_mode;
86 u32 probe_target_ways;
87 bool dis_cap_alloc;
88 bool retain_on_pc;
89 bool activate_on_init;
90};
91
92struct qcom_llcc_config {
93 const struct llcc_slice_config *sct_data;
94 int size;
95 bool need_llcc_cfg;
96};
97
98static const struct llcc_slice_config sc7180_data[] = {
99 { LLCC_CPUSS, 1, 256, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 1 },
100 { LLCC_MDM, 8, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 },
101 { LLCC_GPUHTW, 11, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 },
102 { LLCC_GPU, 12, 128, 1, 0, 0xf, 0x0, 0, 0, 0, 1, 0 },
103};
104
105static const struct llcc_slice_config sdm845_data[] = {
106 { LLCC_CPUSS, 1, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 1 },
107 { LLCC_VIDSC0, 2, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 },
108 { LLCC_VIDSC1, 3, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 },
109 { LLCC_ROTATOR, 4, 563, 2, 1, 0x0, 0x00e, 2, 0, 1, 1, 0 },
110 { LLCC_VOICE, 5, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
111 { LLCC_AUDIO, 6, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
112 { LLCC_MDMHPGRW, 7, 1024, 2, 0, 0xfc, 0xf00, 0, 0, 1, 1, 0 },
113 { LLCC_MDM, 8, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
114 { LLCC_CMPT, 10, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
115 { LLCC_GPUHTW, 11, 512, 1, 1, 0xc, 0x0, 0, 0, 1, 1, 0 },
116 { LLCC_GPU, 12, 2304, 1, 0, 0xff0, 0x2, 0, 0, 1, 1, 0 },
117 { LLCC_MMUHWT, 13, 256, 2, 0, 0x0, 0x1, 0, 0, 1, 0, 1 },
118 { LLCC_CMPTDMA, 15, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
119 { LLCC_DISP, 16, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
120 { LLCC_VIDFW, 17, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
121 { LLCC_MDMHPFX, 20, 1024, 2, 1, 0x0, 0xf00, 0, 0, 1, 1, 0 },
122 { LLCC_MDMPNG, 21, 1024, 0, 1, 0x1e, 0x0, 0, 0, 1, 1, 0 },
123 { LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0 },
124};
125
126static const struct llcc_slice_config sm8150_data[] = {
127 { LLCC_CPUSS, 1, 3072, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 1 },
128 { LLCC_VIDSC0, 2, 512, 2, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 },
129 { LLCC_VIDSC1, 3, 512, 2, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 },
130 { LLCC_AUDIO, 6, 1024, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 },
131 { LLCC_MDMHPGRW, 7, 3072, 1, 0, 0xFF, 0xF00, 0, 0, 0, 1, 0 },
132 { LLCC_MDM, 8, 3072, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 },
133 { LLCC_MODHW, 9, 1024, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 },
134 { LLCC_CMPT, 10, 3072, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 },
135 { LLCC_GPUHTW , 11, 512, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 },
136 { LLCC_GPU, 12, 2560, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 },
137 { LLCC_MMUHWT, 13, 1024, 1, 1, 0xFFF, 0x0, 0, 0, 0, 0, 1 },
138 { LLCC_CMPTDMA, 15, 3072, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 },
139 { LLCC_DISP, 16, 3072, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 },
140 { LLCC_MDMHPFX, 20, 1024, 2, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 },
141 { LLCC_MDMHPFX, 21, 1024, 0, 1, 0xF, 0x0, 0, 0, 0, 1, 0 },
142 { LLCC_AUDHW, 22, 1024, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 },
143 { LLCC_NPU, 23, 3072, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 },
144 { LLCC_WLHW, 24, 3072, 1, 1, 0xFFF, 0x0, 0, 0, 0, 1, 0 },
145 { LLCC_MODPE, 29, 256, 1, 1, 0xF, 0x0, 0, 0, 0, 1, 0 },
146 { LLCC_APTCM, 30, 256, 3, 1, 0x0, 0x1, 1, 0, 0, 1, 0 },
147 { LLCC_WRCACHE, 31, 128, 1, 1, 0xFFF, 0x0, 0, 0, 0, 0, 0 },
148};
149
150static const struct qcom_llcc_config sc7180_cfg = {
151 .sct_data = sc7180_data,
152 .size = ARRAY_SIZE(sc7180_data),
153 .need_llcc_cfg = true,
154};
155
156static const struct qcom_llcc_config sdm845_cfg = {
157 .sct_data = sdm845_data,
158 .size = ARRAY_SIZE(sdm845_data),
159 .need_llcc_cfg = false,
160};
161
162static const struct qcom_llcc_config sm8150_cfg = {
163 .sct_data = sm8150_data,
164 .size = ARRAY_SIZE(sm8150_data),
165};
166
167static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER;
168
169
170
171
172
173
174
175
176struct llcc_slice_desc *llcc_slice_getd(u32 uid)
177{
178 const struct llcc_slice_config *cfg;
179 struct llcc_slice_desc *desc;
180 u32 sz, count;
181
182 if (IS_ERR(drv_data))
183 return ERR_CAST(drv_data);
184
185 cfg = drv_data->cfg;
186 sz = drv_data->cfg_size;
187
188 for (count = 0; cfg && count < sz; count++, cfg++)
189 if (cfg->usecase_id == uid)
190 break;
191
192 if (count == sz || !cfg)
193 return ERR_PTR(-ENODEV);
194
195 desc = kzalloc(sizeof(*desc), GFP_KERNEL);
196 if (!desc)
197 return ERR_PTR(-ENOMEM);
198
199 desc->slice_id = cfg->slice_id;
200 desc->slice_size = cfg->max_cap;
201
202 return desc;
203}
204EXPORT_SYMBOL_GPL(llcc_slice_getd);
205
206
207
208
209
210void llcc_slice_putd(struct llcc_slice_desc *desc)
211{
212 if (!IS_ERR_OR_NULL(desc))
213 kfree(desc);
214}
215EXPORT_SYMBOL_GPL(llcc_slice_putd);
216
217static int llcc_update_act_ctrl(u32 sid,
218 u32 act_ctrl_reg_val, u32 status)
219{
220 u32 act_ctrl_reg;
221 u32 status_reg;
222 u32 slice_status;
223 int ret;
224
225 if (IS_ERR(drv_data))
226 return PTR_ERR(drv_data);
227
228 act_ctrl_reg = LLCC_TRP_ACT_CTRLn(sid);
229 status_reg = LLCC_TRP_STATUSn(sid);
230
231
232 act_ctrl_reg_val |= ACT_CTRL_ACT_TRIG;
233 ret = regmap_write(drv_data->bcast_regmap, act_ctrl_reg,
234 act_ctrl_reg_val);
235 if (ret)
236 return ret;
237
238
239 act_ctrl_reg_val &= ~ACT_CTRL_ACT_TRIG;
240 ret = regmap_write(drv_data->bcast_regmap, act_ctrl_reg,
241 act_ctrl_reg_val);
242 if (ret)
243 return ret;
244
245 ret = regmap_read_poll_timeout(drv_data->bcast_regmap, status_reg,
246 slice_status, !(slice_status & status),
247 0, LLCC_STATUS_READ_DELAY);
248 return ret;
249}
250
251
252
253
254
255
256
257
258int llcc_slice_activate(struct llcc_slice_desc *desc)
259{
260 int ret;
261 u32 act_ctrl_val;
262
263 if (IS_ERR(drv_data))
264 return PTR_ERR(drv_data);
265
266 if (IS_ERR_OR_NULL(desc))
267 return -EINVAL;
268
269 mutex_lock(&drv_data->lock);
270 if (test_bit(desc->slice_id, drv_data->bitmap)) {
271 mutex_unlock(&drv_data->lock);
272 return 0;
273 }
274
275 act_ctrl_val = ACT_CTRL_OPCODE_ACTIVATE << ACT_CTRL_OPCODE_SHIFT;
276
277 ret = llcc_update_act_ctrl(desc->slice_id, act_ctrl_val,
278 DEACTIVATE);
279 if (ret) {
280 mutex_unlock(&drv_data->lock);
281 return ret;
282 }
283
284 __set_bit(desc->slice_id, drv_data->bitmap);
285 mutex_unlock(&drv_data->lock);
286
287 return ret;
288}
289EXPORT_SYMBOL_GPL(llcc_slice_activate);
290
291
292
293
294
295
296
297
298int llcc_slice_deactivate(struct llcc_slice_desc *desc)
299{
300 u32 act_ctrl_val;
301 int ret;
302
303 if (IS_ERR(drv_data))
304 return PTR_ERR(drv_data);
305
306 if (IS_ERR_OR_NULL(desc))
307 return -EINVAL;
308
309 mutex_lock(&drv_data->lock);
310 if (!test_bit(desc->slice_id, drv_data->bitmap)) {
311 mutex_unlock(&drv_data->lock);
312 return 0;
313 }
314 act_ctrl_val = ACT_CTRL_OPCODE_DEACTIVATE << ACT_CTRL_OPCODE_SHIFT;
315
316 ret = llcc_update_act_ctrl(desc->slice_id, act_ctrl_val,
317 ACTIVATE);
318 if (ret) {
319 mutex_unlock(&drv_data->lock);
320 return ret;
321 }
322
323 __clear_bit(desc->slice_id, drv_data->bitmap);
324 mutex_unlock(&drv_data->lock);
325
326 return ret;
327}
328EXPORT_SYMBOL_GPL(llcc_slice_deactivate);
329
330
331
332
333
334int llcc_get_slice_id(struct llcc_slice_desc *desc)
335{
336 if (IS_ERR_OR_NULL(desc))
337 return -EINVAL;
338
339 return desc->slice_id;
340}
341EXPORT_SYMBOL_GPL(llcc_get_slice_id);
342
343
344
345
346
347size_t llcc_get_slice_size(struct llcc_slice_desc *desc)
348{
349 if (IS_ERR_OR_NULL(desc))
350 return 0;
351
352 return desc->slice_size;
353}
354EXPORT_SYMBOL_GPL(llcc_get_slice_size);
355
356static int _qcom_llcc_cfg_program(const struct llcc_slice_config *config,
357 const struct qcom_llcc_config *cfg)
358{
359 int ret;
360 u32 attr1_cfg;
361 u32 attr0_cfg;
362 u32 attr1_val;
363 u32 attr0_val;
364 u32 max_cap_cacheline;
365 struct llcc_slice_desc desc;
366
367 attr1_val = config->cache_mode;
368 attr1_val |= config->probe_target_ways << ATTR1_PROBE_TARGET_WAYS_SHIFT;
369 attr1_val |= config->fixed_size << ATTR1_FIXED_SIZE_SHIFT;
370 attr1_val |= config->priority << ATTR1_PRIORITY_SHIFT;
371
372 max_cap_cacheline = MAX_CAP_TO_BYTES(config->max_cap);
373
374
375
376
377
378
379
380
381 max_cap_cacheline = max_cap_cacheline / drv_data->num_banks;
382 max_cap_cacheline >>= CACHE_LINE_SIZE_SHIFT;
383 attr1_val |= max_cap_cacheline << ATTR1_MAX_CAP_SHIFT;
384
385 attr1_cfg = LLCC_TRP_ATTR1_CFGn(config->slice_id);
386
387 ret = regmap_write(drv_data->bcast_regmap, attr1_cfg, attr1_val);
388 if (ret)
389 return ret;
390
391 attr0_val = config->res_ways & ATTR0_RES_WAYS_MASK;
392 attr0_val |= config->bonus_ways << ATTR0_BONUS_WAYS_SHIFT;
393
394 attr0_cfg = LLCC_TRP_ATTR0_CFGn(config->slice_id);
395
396 ret = regmap_write(drv_data->bcast_regmap, attr0_cfg, attr0_val);
397 if (ret)
398 return ret;
399
400 if (cfg->need_llcc_cfg) {
401 u32 disable_cap_alloc, retain_pc;
402
403 disable_cap_alloc = config->dis_cap_alloc << config->slice_id;
404 ret = regmap_write(drv_data->bcast_regmap,
405 LLCC_TRP_SCID_DIS_CAP_ALLOC, disable_cap_alloc);
406 if (ret)
407 return ret;
408
409 retain_pc = config->retain_on_pc << config->slice_id;
410 ret = regmap_write(drv_data->bcast_regmap,
411 LLCC_TRP_PCB_ACT, retain_pc);
412 if (ret)
413 return ret;
414 }
415
416 if (config->activate_on_init) {
417 desc.slice_id = config->slice_id;
418 ret = llcc_slice_activate(&desc);
419 }
420
421 return ret;
422}
423
424static int qcom_llcc_cfg_program(struct platform_device *pdev,
425 const struct qcom_llcc_config *cfg)
426{
427 int i;
428 u32 sz;
429 int ret = 0;
430 const struct llcc_slice_config *llcc_table;
431
432 sz = drv_data->cfg_size;
433 llcc_table = drv_data->cfg;
434
435 for (i = 0; i < sz; i++) {
436 ret = _qcom_llcc_cfg_program(&llcc_table[i], cfg);
437 if (ret)
438 return ret;
439 }
440
441 return ret;
442}
443
444static int qcom_llcc_remove(struct platform_device *pdev)
445{
446
447 drv_data = ERR_PTR(-ENODEV);
448 return 0;
449}
450
451static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev,
452 const char *name)
453{
454 void __iomem *base;
455 struct regmap_config llcc_regmap_config = {
456 .reg_bits = 32,
457 .reg_stride = 4,
458 .val_bits = 32,
459 .fast_io = true,
460 };
461
462 base = devm_platform_ioremap_resource_byname(pdev, name);
463 if (IS_ERR(base))
464 return ERR_CAST(base);
465
466 llcc_regmap_config.name = name;
467 return devm_regmap_init_mmio(&pdev->dev, base, &llcc_regmap_config);
468}
469
470static int qcom_llcc_probe(struct platform_device *pdev)
471{
472 u32 num_banks;
473 struct device *dev = &pdev->dev;
474 int ret, i;
475 struct platform_device *llcc_edac;
476 const struct qcom_llcc_config *cfg;
477 const struct llcc_slice_config *llcc_cfg;
478 u32 sz;
479
480 drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
481 if (!drv_data) {
482 ret = -ENOMEM;
483 goto err;
484 }
485
486 drv_data->regmap = qcom_llcc_init_mmio(pdev, "llcc_base");
487 if (IS_ERR(drv_data->regmap)) {
488 ret = PTR_ERR(drv_data->regmap);
489 goto err;
490 }
491
492 drv_data->bcast_regmap =
493 qcom_llcc_init_mmio(pdev, "llcc_broadcast_base");
494 if (IS_ERR(drv_data->bcast_regmap)) {
495 ret = PTR_ERR(drv_data->bcast_regmap);
496 goto err;
497 }
498
499 ret = regmap_read(drv_data->regmap, LLCC_COMMON_STATUS0,
500 &num_banks);
501 if (ret)
502 goto err;
503
504 num_banks &= LLCC_LB_CNT_MASK;
505 num_banks >>= LLCC_LB_CNT_SHIFT;
506 drv_data->num_banks = num_banks;
507
508 cfg = of_device_get_match_data(&pdev->dev);
509 llcc_cfg = cfg->sct_data;
510 sz = cfg->size;
511
512 for (i = 0; i < sz; i++)
513 if (llcc_cfg[i].slice_id > drv_data->max_slices)
514 drv_data->max_slices = llcc_cfg[i].slice_id;
515
516 drv_data->offsets = devm_kcalloc(dev, num_banks, sizeof(u32),
517 GFP_KERNEL);
518 if (!drv_data->offsets) {
519 ret = -ENOMEM;
520 goto err;
521 }
522
523 for (i = 0; i < num_banks; i++)
524 drv_data->offsets[i] = i * BANK_OFFSET_STRIDE;
525
526 drv_data->bitmap = devm_kcalloc(dev,
527 BITS_TO_LONGS(drv_data->max_slices), sizeof(unsigned long),
528 GFP_KERNEL);
529 if (!drv_data->bitmap) {
530 ret = -ENOMEM;
531 goto err;
532 }
533
534 drv_data->cfg = llcc_cfg;
535 drv_data->cfg_size = sz;
536 mutex_init(&drv_data->lock);
537 platform_set_drvdata(pdev, drv_data);
538
539 ret = qcom_llcc_cfg_program(pdev, cfg);
540 if (ret)
541 goto err;
542
543 drv_data->ecc_irq = platform_get_irq(pdev, 0);
544 if (drv_data->ecc_irq >= 0) {
545 llcc_edac = platform_device_register_data(&pdev->dev,
546 "qcom_llcc_edac", -1, drv_data,
547 sizeof(*drv_data));
548 if (IS_ERR(llcc_edac))
549 dev_err(dev, "Failed to register llcc edac driver\n");
550 }
551
552 return 0;
553err:
554 drv_data = ERR_PTR(-ENODEV);
555 return ret;
556}
557
558static const struct of_device_id qcom_llcc_of_match[] = {
559 { .compatible = "qcom,sc7180-llcc", .data = &sc7180_cfg },
560 { .compatible = "qcom,sdm845-llcc", .data = &sdm845_cfg },
561 { .compatible = "qcom,sm8150-llcc", .data = &sm8150_cfg },
562 { }
563};
564
565static struct platform_driver qcom_llcc_driver = {
566 .driver = {
567 .name = "qcom-llcc",
568 .of_match_table = qcom_llcc_of_match,
569 },
570 .probe = qcom_llcc_probe,
571 .remove = qcom_llcc_remove,
572};
573module_platform_driver(qcom_llcc_driver);
574
575MODULE_DESCRIPTION("Qualcomm Last Level Cache Controller");
576MODULE_LICENSE("GPL v2");
577