blob: 574bb5bf20bc24ed75b53a1ad363d7507be73543 [file] [log] [blame]
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -07001// SPDX-License-Identifier: GPL-2.0
2/*
Vivek Gautama14b8202019-07-18 18:32:36 +05303 * Copyright (c) 2017-2019, The Linux Foundation. All rights reserved.
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -07004 *
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>
Niklas Cassel4da3b042018-06-29 17:44:47 +020012#include <linux/module.h>
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -070013#include <linux/mutex.h>
Vivek Gautama14b8202019-07-18 18:32:36 +053014#include <linux/of.h>
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -070015#include <linux/of_device.h>
16#include <linux/regmap.h>
Niklas Casselda8eaf92018-08-29 09:57:16 +020017#include <linux/sizes.h>
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -070018#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 BANK_OFFSET_STRIDE 0x80000
49
Vivek Gautama14b8202019-07-18 18:32:36 +053050static struct llcc_slice_config sdm845_data[] = {
51 { LLCC_CPUSS, 1, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 1 },
52 { LLCC_VIDSC0, 2, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 },
53 { LLCC_VIDSC1, 3, 512, 2, 1, 0x0, 0x0f0, 0, 0, 1, 1, 0 },
54 { LLCC_ROTATOR, 4, 563, 2, 1, 0x0, 0x00e, 2, 0, 1, 1, 0 },
55 { LLCC_VOICE, 5, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
56 { LLCC_AUDIO, 6, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
57 { LLCC_MDMHPGRW, 7, 1024, 2, 0, 0xfc, 0xf00, 0, 0, 1, 1, 0 },
58 { LLCC_MDM, 8, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
59 { LLCC_CMPT, 10, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
60 { LLCC_GPUHTW, 11, 512, 1, 1, 0xc, 0x0, 0, 0, 1, 1, 0 },
61 { LLCC_GPU, 12, 2304, 1, 0, 0xff0, 0x2, 0, 0, 1, 1, 0 },
62 { LLCC_MMUHWT, 13, 256, 2, 0, 0x0, 0x1, 0, 0, 1, 0, 1 },
63 { LLCC_CMPTDMA, 15, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
64 { LLCC_DISP, 16, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
65 { LLCC_VIDFW, 17, 2816, 1, 0, 0xffc, 0x2, 0, 0, 1, 1, 0 },
66 { LLCC_MDMHPFX, 20, 1024, 2, 1, 0x0, 0xf00, 0, 0, 1, 1, 0 },
67 { LLCC_MDMPNG, 21, 1024, 0, 1, 0x1e, 0x0, 0, 0, 1, 1, 0 },
68 { LLCC_AUDHW, 22, 1024, 1, 1, 0xffc, 0x2, 0, 0, 1, 1, 0 },
69};
70
Jordan Crouse72d1cd02018-12-11 13:07:45 -070071static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER;
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -070072
73static const struct regmap_config llcc_regmap_config = {
74 .reg_bits = 32,
75 .reg_stride = 4,
76 .val_bits = 32,
77 .fast_io = true,
78};
79
80/**
81 * llcc_slice_getd - get llcc slice descriptor
82 * @uid: usecase_id for the client
83 *
84 * A pointer to llcc slice descriptor will be returned on success and
85 * and error pointer is returned on failure
86 */
87struct llcc_slice_desc *llcc_slice_getd(u32 uid)
88{
89 const struct llcc_slice_config *cfg;
90 struct llcc_slice_desc *desc;
91 u32 sz, count;
92
Jordan Crouse72d1cd02018-12-11 13:07:45 -070093 if (IS_ERR(drv_data))
94 return ERR_CAST(drv_data);
95
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -070096 cfg = drv_data->cfg;
97 sz = drv_data->cfg_size;
98
99 for (count = 0; cfg && count < sz; count++, cfg++)
100 if (cfg->usecase_id == uid)
101 break;
102
103 if (count == sz || !cfg)
104 return ERR_PTR(-ENODEV);
105
106 desc = kzalloc(sizeof(*desc), GFP_KERNEL);
107 if (!desc)
108 return ERR_PTR(-ENOMEM);
109
110 desc->slice_id = cfg->slice_id;
111 desc->slice_size = cfg->max_cap;
112
113 return desc;
114}
115EXPORT_SYMBOL_GPL(llcc_slice_getd);
116
117/**
118 * llcc_slice_putd - llcc slice descritpor
119 * @desc: Pointer to llcc slice descriptor
120 */
121void llcc_slice_putd(struct llcc_slice_desc *desc)
122{
Jordan Crousee0f2cfe2018-10-05 18:38:29 +0530123 if (!IS_ERR_OR_NULL(desc))
124 kfree(desc);
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700125}
126EXPORT_SYMBOL_GPL(llcc_slice_putd);
127
128static int llcc_update_act_ctrl(u32 sid,
129 u32 act_ctrl_reg_val, u32 status)
130{
131 u32 act_ctrl_reg;
132 u32 status_reg;
133 u32 slice_status;
134 int ret;
135
Jordan Crouse72d1cd02018-12-11 13:07:45 -0700136 if (IS_ERR(drv_data))
137 return PTR_ERR(drv_data);
138
Venkata Narendra Kumar Gutta7f9c1362018-09-12 11:06:32 -0700139 act_ctrl_reg = LLCC_TRP_ACT_CTRLn(sid);
140 status_reg = LLCC_TRP_STATUSn(sid);
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700141
142 /* Set the ACTIVE trigger */
143 act_ctrl_reg_val |= ACT_CTRL_ACT_TRIG;
Venkata Narendra Kumar Gutta7f9c1362018-09-12 11:06:32 -0700144 ret = regmap_write(drv_data->bcast_regmap, act_ctrl_reg,
145 act_ctrl_reg_val);
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700146 if (ret)
147 return ret;
148
149 /* Clear the ACTIVE trigger */
150 act_ctrl_reg_val &= ~ACT_CTRL_ACT_TRIG;
Venkata Narendra Kumar Gutta7f9c1362018-09-12 11:06:32 -0700151 ret = regmap_write(drv_data->bcast_regmap, act_ctrl_reg,
152 act_ctrl_reg_val);
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700153 if (ret)
154 return ret;
155
Venkata Narendra Kumar Gutta7f9c1362018-09-12 11:06:32 -0700156 ret = regmap_read_poll_timeout(drv_data->bcast_regmap, status_reg,
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700157 slice_status, !(slice_status & status),
158 0, LLCC_STATUS_READ_DELAY);
159 return ret;
160}
161
162/**
163 * llcc_slice_activate - Activate the llcc slice
164 * @desc: Pointer to llcc slice descriptor
165 *
166 * A value of zero will be returned on success and a negative errno will
167 * be returned in error cases
168 */
169int llcc_slice_activate(struct llcc_slice_desc *desc)
170{
171 int ret;
172 u32 act_ctrl_val;
173
Andy Gross32616b22019-02-15 16:30:34 -0600174 if (IS_ERR(drv_data))
Jordan Crouse72d1cd02018-12-11 13:07:45 -0700175 return PTR_ERR(drv_data);
176
Jordan Crousee0f2cfe2018-10-05 18:38:29 +0530177 if (IS_ERR_OR_NULL(desc))
178 return -EINVAL;
179
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700180 mutex_lock(&drv_data->lock);
181 if (test_bit(desc->slice_id, drv_data->bitmap)) {
182 mutex_unlock(&drv_data->lock);
183 return 0;
184 }
185
186 act_ctrl_val = ACT_CTRL_OPCODE_ACTIVATE << ACT_CTRL_OPCODE_SHIFT;
187
188 ret = llcc_update_act_ctrl(desc->slice_id, act_ctrl_val,
189 DEACTIVATE);
190 if (ret) {
191 mutex_unlock(&drv_data->lock);
192 return ret;
193 }
194
195 __set_bit(desc->slice_id, drv_data->bitmap);
196 mutex_unlock(&drv_data->lock);
197
198 return ret;
199}
200EXPORT_SYMBOL_GPL(llcc_slice_activate);
201
202/**
203 * llcc_slice_deactivate - Deactivate the llcc slice
204 * @desc: Pointer to llcc slice descriptor
205 *
206 * A value of zero will be returned on success and a negative errno will
207 * be returned in error cases
208 */
209int llcc_slice_deactivate(struct llcc_slice_desc *desc)
210{
211 u32 act_ctrl_val;
212 int ret;
213
Andy Gross32616b22019-02-15 16:30:34 -0600214 if (IS_ERR(drv_data))
Jordan Crouse72d1cd02018-12-11 13:07:45 -0700215 return PTR_ERR(drv_data);
216
Jordan Crousee0f2cfe2018-10-05 18:38:29 +0530217 if (IS_ERR_OR_NULL(desc))
218 return -EINVAL;
219
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700220 mutex_lock(&drv_data->lock);
221 if (!test_bit(desc->slice_id, drv_data->bitmap)) {
222 mutex_unlock(&drv_data->lock);
223 return 0;
224 }
225 act_ctrl_val = ACT_CTRL_OPCODE_DEACTIVATE << ACT_CTRL_OPCODE_SHIFT;
226
227 ret = llcc_update_act_ctrl(desc->slice_id, act_ctrl_val,
228 ACTIVATE);
229 if (ret) {
230 mutex_unlock(&drv_data->lock);
231 return ret;
232 }
233
234 __clear_bit(desc->slice_id, drv_data->bitmap);
235 mutex_unlock(&drv_data->lock);
236
237 return ret;
238}
239EXPORT_SYMBOL_GPL(llcc_slice_deactivate);
240
241/**
242 * llcc_get_slice_id - return the slice id
243 * @desc: Pointer to llcc slice descriptor
244 */
245int llcc_get_slice_id(struct llcc_slice_desc *desc)
246{
Jordan Crousee0f2cfe2018-10-05 18:38:29 +0530247 if (IS_ERR_OR_NULL(desc))
248 return -EINVAL;
249
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700250 return desc->slice_id;
251}
252EXPORT_SYMBOL_GPL(llcc_get_slice_id);
253
254/**
255 * llcc_get_slice_size - return the slice id
256 * @desc: Pointer to llcc slice descriptor
257 */
258size_t llcc_get_slice_size(struct llcc_slice_desc *desc)
259{
Jordan Crousee0f2cfe2018-10-05 18:38:29 +0530260 if (IS_ERR_OR_NULL(desc))
261 return 0;
262
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700263 return desc->slice_size;
264}
265EXPORT_SYMBOL_GPL(llcc_get_slice_size);
266
267static int qcom_llcc_cfg_program(struct platform_device *pdev)
268{
269 int i;
270 u32 attr1_cfg;
271 u32 attr0_cfg;
272 u32 attr1_val;
273 u32 attr0_val;
274 u32 max_cap_cacheline;
275 u32 sz;
Venkata Narendra Kumar Guttac081f302018-09-12 11:06:33 -0700276 int ret = 0;
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700277 const struct llcc_slice_config *llcc_table;
278 struct llcc_slice_desc desc;
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700279
280 sz = drv_data->cfg_size;
281 llcc_table = drv_data->cfg;
282
283 for (i = 0; i < sz; i++) {
Venkata Narendra Kumar Gutta7f9c1362018-09-12 11:06:32 -0700284 attr1_cfg = LLCC_TRP_ATTR1_CFGn(llcc_table[i].slice_id);
285 attr0_cfg = LLCC_TRP_ATTR0_CFGn(llcc_table[i].slice_id);
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700286
287 attr1_val = llcc_table[i].cache_mode;
288 attr1_val |= llcc_table[i].probe_target_ways <<
289 ATTR1_PROBE_TARGET_WAYS_SHIFT;
290 attr1_val |= llcc_table[i].fixed_size <<
291 ATTR1_FIXED_SIZE_SHIFT;
292 attr1_val |= llcc_table[i].priority <<
293 ATTR1_PRIORITY_SHIFT;
294
295 max_cap_cacheline = MAX_CAP_TO_BYTES(llcc_table[i].max_cap);
296
297 /* LLCC instances can vary for each target.
298 * The SW writes to broadcast register which gets propagated
299 * to each llcc instace (llcc0,.. llccN).
300 * Since the size of the memory is divided equally amongst the
301 * llcc instances, we need to configure the max cap accordingly.
302 */
303 max_cap_cacheline = max_cap_cacheline / drv_data->num_banks;
304 max_cap_cacheline >>= CACHE_LINE_SIZE_SHIFT;
305 attr1_val |= max_cap_cacheline << ATTR1_MAX_CAP_SHIFT;
306
307 attr0_val = llcc_table[i].res_ways & ATTR0_RES_WAYS_MASK;
308 attr0_val |= llcc_table[i].bonus_ways << ATTR0_BONUS_WAYS_SHIFT;
309
Venkata Narendra Kumar Gutta7f9c1362018-09-12 11:06:32 -0700310 ret = regmap_write(drv_data->bcast_regmap, attr1_cfg,
311 attr1_val);
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700312 if (ret)
313 return ret;
Venkata Narendra Kumar Gutta7f9c1362018-09-12 11:06:32 -0700314 ret = regmap_write(drv_data->bcast_regmap, attr0_cfg,
315 attr0_val);
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700316 if (ret)
317 return ret;
318 if (llcc_table[i].activate_on_init) {
319 desc.slice_id = llcc_table[i].slice_id;
320 ret = llcc_slice_activate(&desc);
321 }
322 }
323 return ret;
324}
325
Vivek Gautama14b8202019-07-18 18:32:36 +0530326static int qcom_llcc_remove(struct platform_device *pdev)
Jordan Crouse72d1cd02018-12-11 13:07:45 -0700327{
328 /* Set the global pointer to a error code to avoid referencing it */
329 drv_data = ERR_PTR(-ENODEV);
330 return 0;
331}
Jordan Crouse72d1cd02018-12-11 13:07:45 -0700332
Jordan Crouseed10a252018-12-11 13:07:46 -0700333static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev,
334 const char *name)
335{
336 struct resource *res;
337 void __iomem *base;
338
339 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
340 if (!res)
341 return ERR_PTR(-ENODEV);
342
343 base = devm_ioremap_resource(&pdev->dev, res);
344 if (IS_ERR(base))
345 return ERR_CAST(base);
346
347 return devm_regmap_init_mmio(&pdev->dev, base, &llcc_regmap_config);
348}
349
Vivek Gautama14b8202019-07-18 18:32:36 +0530350static int qcom_llcc_probe(struct platform_device *pdev,
351 const struct llcc_slice_config *llcc_cfg, u32 sz)
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700352{
353 u32 num_banks;
354 struct device *dev = &pdev->dev;
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700355 int ret, i;
Venkata Narendra Kumar Guttac081f302018-09-12 11:06:33 -0700356 struct platform_device *llcc_edac;
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700357
358 drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
Jordan Crouse72d1cd02018-12-11 13:07:45 -0700359 if (!drv_data) {
360 ret = -ENOMEM;
361 goto err;
362 }
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700363
Jordan Crouseed10a252018-12-11 13:07:46 -0700364 drv_data->regmap = qcom_llcc_init_mmio(pdev, "llcc_base");
Jordan Crouse72d1cd02018-12-11 13:07:45 -0700365 if (IS_ERR(drv_data->regmap)) {
366 ret = PTR_ERR(drv_data->regmap);
367 goto err;
368 }
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700369
Jordan Crouseed10a252018-12-11 13:07:46 -0700370 drv_data->bcast_regmap =
371 qcom_llcc_init_mmio(pdev, "llcc_broadcast_base");
Jordan Crouse72d1cd02018-12-11 13:07:45 -0700372 if (IS_ERR(drv_data->bcast_regmap)) {
373 ret = PTR_ERR(drv_data->bcast_regmap);
374 goto err;
375 }
Venkata Narendra Kumar Gutta7f9c1362018-09-12 11:06:32 -0700376
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700377 ret = regmap_read(drv_data->regmap, LLCC_COMMON_STATUS0,
378 &num_banks);
379 if (ret)
Jordan Crouse72d1cd02018-12-11 13:07:45 -0700380 goto err;
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700381
382 num_banks &= LLCC_LB_CNT_MASK;
383 num_banks >>= LLCC_LB_CNT_SHIFT;
384 drv_data->num_banks = num_banks;
385
386 for (i = 0; i < sz; i++)
387 if (llcc_cfg[i].slice_id > drv_data->max_slices)
388 drv_data->max_slices = llcc_cfg[i].slice_id;
389
390 drv_data->offsets = devm_kcalloc(dev, num_banks, sizeof(u32),
391 GFP_KERNEL);
Jordan Crouse72d1cd02018-12-11 13:07:45 -0700392 if (!drv_data->offsets) {
393 ret = -ENOMEM;
394 goto err;
395 }
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700396
397 for (i = 0; i < num_banks; i++)
398 drv_data->offsets[i] = i * BANK_OFFSET_STRIDE;
399
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700400 drv_data->bitmap = devm_kcalloc(dev,
401 BITS_TO_LONGS(drv_data->max_slices), sizeof(unsigned long),
402 GFP_KERNEL);
Jordan Crouse72d1cd02018-12-11 13:07:45 -0700403 if (!drv_data->bitmap) {
404 ret = -ENOMEM;
405 goto err;
406 }
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700407
408 drv_data->cfg = llcc_cfg;
409 drv_data->cfg_size = sz;
410 mutex_init(&drv_data->lock);
411 platform_set_drvdata(pdev, drv_data);
412
Venkata Narendra Kumar Guttac081f302018-09-12 11:06:33 -0700413 ret = qcom_llcc_cfg_program(pdev);
414 if (ret)
Jordan Crouse72d1cd02018-12-11 13:07:45 -0700415 goto err;
Venkata Narendra Kumar Guttac081f302018-09-12 11:06:33 -0700416
417 drv_data->ecc_irq = platform_get_irq(pdev, 0);
418 if (drv_data->ecc_irq >= 0) {
419 llcc_edac = platform_device_register_data(&pdev->dev,
420 "qcom_llcc_edac", -1, drv_data,
421 sizeof(*drv_data));
422 if (IS_ERR(llcc_edac))
423 dev_err(dev, "Failed to register llcc edac driver\n");
424 }
425
Jordan Crouse72d1cd02018-12-11 13:07:45 -0700426 return 0;
427err:
428 drv_data = ERR_PTR(-ENODEV);
Venkata Narendra Kumar Guttac081f302018-09-12 11:06:33 -0700429 return ret;
Rishabh Bhatnagara3134fb2018-05-23 17:35:21 -0700430}
Vivek Gautama14b8202019-07-18 18:32:36 +0530431
432static int sdm845_qcom_llcc_remove(struct platform_device *pdev)
433{
434 return qcom_llcc_remove(pdev);
435}
436
437static int sdm845_qcom_llcc_probe(struct platform_device *pdev)
438{
439 return qcom_llcc_probe(pdev, sdm845_data, ARRAY_SIZE(sdm845_data));
440}
441
442static const struct of_device_id sdm845_qcom_llcc_of_match[] = {
443 { .compatible = "qcom,sdm845-llcc", },
444 { }
445};
446
447static struct platform_driver sdm845_qcom_llcc_driver = {
448 .driver = {
449 .name = "sdm845-llcc",
450 .of_match_table = sdm845_qcom_llcc_of_match,
451 },
452 .probe = sdm845_qcom_llcc_probe,
453 .remove = sdm845_qcom_llcc_remove,
454};
455module_platform_driver(sdm845_qcom_llcc_driver);
456
457MODULE_DESCRIPTION("QCOM sdm845 LLCC driver");
Niklas Cassel4da3b042018-06-29 17:44:47 +0200458MODULE_LICENSE("GPL v2");