Merge tag 'for-linus-20190726-2' of git://git.kernel.dk/linux-block
[linux-2.6-block.git] / drivers / soc / qcom / llcc-slice.c
CommitLineData
a3134fb0
RB
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2017-2018, The Linux Foundation. All rights reserved.
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>
4da3b045 12#include <linux/module.h>
a3134fb0
RB
13#include <linux/mutex.h>
14#include <linux/of_device.h>
15#include <linux/regmap.h>
da8eaf9a 16#include <linux/sizes.h>
a3134fb0
RB
17#include <linux/slab.h>
18#include <linux/soc/qcom/llcc-qcom.h>
19
20#define ACTIVATE BIT(0)
21#define DEACTIVATE BIT(1)
22#define ACT_CTRL_OPCODE_ACTIVATE BIT(0)
23#define ACT_CTRL_OPCODE_DEACTIVATE BIT(1)
24#define ACT_CTRL_ACT_TRIG BIT(0)
25#define ACT_CTRL_OPCODE_SHIFT 0x01
26#define ATTR1_PROBE_TARGET_WAYS_SHIFT 0x02
27#define ATTR1_FIXED_SIZE_SHIFT 0x03
28#define ATTR1_PRIORITY_SHIFT 0x04
29#define ATTR1_MAX_CAP_SHIFT 0x10
30#define ATTR0_RES_WAYS_MASK GENMASK(11, 0)
31#define ATTR0_BONUS_WAYS_MASK GENMASK(27, 16)
32#define ATTR0_BONUS_WAYS_SHIFT 0x10
33#define LLCC_STATUS_READ_DELAY 100
34
35#define CACHE_LINE_SIZE_SHIFT 6
36
37#define LLCC_COMMON_STATUS0 0x0003000c
38#define LLCC_LB_CNT_MASK GENMASK(31, 28)
39#define LLCC_LB_CNT_SHIFT 28
40
41#define MAX_CAP_TO_BYTES(n) (n * SZ_1K)
42#define LLCC_TRP_ACT_CTRLn(n) (n * SZ_4K)
43#define LLCC_TRP_STATUSn(n) (4 + n * SZ_4K)
44#define LLCC_TRP_ATTR0_CFGn(n) (0x21000 + SZ_8 * n)
45#define LLCC_TRP_ATTR1_CFGn(n) (0x21004 + SZ_8 * n)
46
47#define BANK_OFFSET_STRIDE 0x80000
48
72d1cd03 49static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER;
a3134fb0
RB
50
51static const struct regmap_config llcc_regmap_config = {
52 .reg_bits = 32,
53 .reg_stride = 4,
54 .val_bits = 32,
55 .fast_io = true,
56};
57
58/**
59 * llcc_slice_getd - get llcc slice descriptor
60 * @uid: usecase_id for the client
61 *
62 * A pointer to llcc slice descriptor will be returned on success and
63 * and error pointer is returned on failure
64 */
65struct llcc_slice_desc *llcc_slice_getd(u32 uid)
66{
67 const struct llcc_slice_config *cfg;
68 struct llcc_slice_desc *desc;
69 u32 sz, count;
70
72d1cd03
JC
71 if (IS_ERR(drv_data))
72 return ERR_CAST(drv_data);
73
a3134fb0
RB
74 cfg = drv_data->cfg;
75 sz = drv_data->cfg_size;
76
77 for (count = 0; cfg && count < sz; count++, cfg++)
78 if (cfg->usecase_id == uid)
79 break;
80
81 if (count == sz || !cfg)
82 return ERR_PTR(-ENODEV);
83
84 desc = kzalloc(sizeof(*desc), GFP_KERNEL);
85 if (!desc)
86 return ERR_PTR(-ENOMEM);
87
88 desc->slice_id = cfg->slice_id;
89 desc->slice_size = cfg->max_cap;
90
91 return desc;
92}
93EXPORT_SYMBOL_GPL(llcc_slice_getd);
94
95/**
96 * llcc_slice_putd - llcc slice descritpor
97 * @desc: Pointer to llcc slice descriptor
98 */
99void llcc_slice_putd(struct llcc_slice_desc *desc)
100{
e0f2cfeb
JC
101 if (!IS_ERR_OR_NULL(desc))
102 kfree(desc);
a3134fb0
RB
103}
104EXPORT_SYMBOL_GPL(llcc_slice_putd);
105
106static int llcc_update_act_ctrl(u32 sid,
107 u32 act_ctrl_reg_val, u32 status)
108{
109 u32 act_ctrl_reg;
110 u32 status_reg;
111 u32 slice_status;
112 int ret;
113
72d1cd03
JC
114 if (IS_ERR(drv_data))
115 return PTR_ERR(drv_data);
116
7f9c1362
VNKG
117 act_ctrl_reg = LLCC_TRP_ACT_CTRLn(sid);
118 status_reg = LLCC_TRP_STATUSn(sid);
a3134fb0
RB
119
120 /* Set the ACTIVE trigger */
121 act_ctrl_reg_val |= ACT_CTRL_ACT_TRIG;
7f9c1362
VNKG
122 ret = regmap_write(drv_data->bcast_regmap, act_ctrl_reg,
123 act_ctrl_reg_val);
a3134fb0
RB
124 if (ret)
125 return ret;
126
127 /* Clear the ACTIVE trigger */
128 act_ctrl_reg_val &= ~ACT_CTRL_ACT_TRIG;
7f9c1362
VNKG
129 ret = regmap_write(drv_data->bcast_regmap, act_ctrl_reg,
130 act_ctrl_reg_val);
a3134fb0
RB
131 if (ret)
132 return ret;
133
7f9c1362 134 ret = regmap_read_poll_timeout(drv_data->bcast_regmap, status_reg,
a3134fb0
RB
135 slice_status, !(slice_status & status),
136 0, LLCC_STATUS_READ_DELAY);
137 return ret;
138}
139
140/**
141 * llcc_slice_activate - Activate the llcc slice
142 * @desc: Pointer to llcc slice descriptor
143 *
144 * A value of zero will be returned on success and a negative errno will
145 * be returned in error cases
146 */
147int llcc_slice_activate(struct llcc_slice_desc *desc)
148{
149 int ret;
150 u32 act_ctrl_val;
151
32616b21 152 if (IS_ERR(drv_data))
72d1cd03
JC
153 return PTR_ERR(drv_data);
154
e0f2cfeb
JC
155 if (IS_ERR_OR_NULL(desc))
156 return -EINVAL;
157
a3134fb0
RB
158 mutex_lock(&drv_data->lock);
159 if (test_bit(desc->slice_id, drv_data->bitmap)) {
160 mutex_unlock(&drv_data->lock);
161 return 0;
162 }
163
164 act_ctrl_val = ACT_CTRL_OPCODE_ACTIVATE << ACT_CTRL_OPCODE_SHIFT;
165
166 ret = llcc_update_act_ctrl(desc->slice_id, act_ctrl_val,
167 DEACTIVATE);
168 if (ret) {
169 mutex_unlock(&drv_data->lock);
170 return ret;
171 }
172
173 __set_bit(desc->slice_id, drv_data->bitmap);
174 mutex_unlock(&drv_data->lock);
175
176 return ret;
177}
178EXPORT_SYMBOL_GPL(llcc_slice_activate);
179
180/**
181 * llcc_slice_deactivate - Deactivate the llcc slice
182 * @desc: Pointer to llcc slice descriptor
183 *
184 * A value of zero will be returned on success and a negative errno will
185 * be returned in error cases
186 */
187int llcc_slice_deactivate(struct llcc_slice_desc *desc)
188{
189 u32 act_ctrl_val;
190 int ret;
191
32616b21 192 if (IS_ERR(drv_data))
72d1cd03
JC
193 return PTR_ERR(drv_data);
194
e0f2cfeb
JC
195 if (IS_ERR_OR_NULL(desc))
196 return -EINVAL;
197
a3134fb0
RB
198 mutex_lock(&drv_data->lock);
199 if (!test_bit(desc->slice_id, drv_data->bitmap)) {
200 mutex_unlock(&drv_data->lock);
201 return 0;
202 }
203 act_ctrl_val = ACT_CTRL_OPCODE_DEACTIVATE << ACT_CTRL_OPCODE_SHIFT;
204
205 ret = llcc_update_act_ctrl(desc->slice_id, act_ctrl_val,
206 ACTIVATE);
207 if (ret) {
208 mutex_unlock(&drv_data->lock);
209 return ret;
210 }
211
212 __clear_bit(desc->slice_id, drv_data->bitmap);
213 mutex_unlock(&drv_data->lock);
214
215 return ret;
216}
217EXPORT_SYMBOL_GPL(llcc_slice_deactivate);
218
219/**
220 * llcc_get_slice_id - return the slice id
221 * @desc: Pointer to llcc slice descriptor
222 */
223int llcc_get_slice_id(struct llcc_slice_desc *desc)
224{
e0f2cfeb
JC
225 if (IS_ERR_OR_NULL(desc))
226 return -EINVAL;
227
a3134fb0
RB
228 return desc->slice_id;
229}
230EXPORT_SYMBOL_GPL(llcc_get_slice_id);
231
232/**
233 * llcc_get_slice_size - return the slice id
234 * @desc: Pointer to llcc slice descriptor
235 */
236size_t llcc_get_slice_size(struct llcc_slice_desc *desc)
237{
e0f2cfeb
JC
238 if (IS_ERR_OR_NULL(desc))
239 return 0;
240
a3134fb0
RB
241 return desc->slice_size;
242}
243EXPORT_SYMBOL_GPL(llcc_get_slice_size);
244
245static int qcom_llcc_cfg_program(struct platform_device *pdev)
246{
247 int i;
248 u32 attr1_cfg;
249 u32 attr0_cfg;
250 u32 attr1_val;
251 u32 attr0_val;
252 u32 max_cap_cacheline;
253 u32 sz;
c081f306 254 int ret = 0;
a3134fb0
RB
255 const struct llcc_slice_config *llcc_table;
256 struct llcc_slice_desc desc;
a3134fb0
RB
257
258 sz = drv_data->cfg_size;
259 llcc_table = drv_data->cfg;
260
261 for (i = 0; i < sz; i++) {
7f9c1362
VNKG
262 attr1_cfg = LLCC_TRP_ATTR1_CFGn(llcc_table[i].slice_id);
263 attr0_cfg = LLCC_TRP_ATTR0_CFGn(llcc_table[i].slice_id);
a3134fb0
RB
264
265 attr1_val = llcc_table[i].cache_mode;
266 attr1_val |= llcc_table[i].probe_target_ways <<
267 ATTR1_PROBE_TARGET_WAYS_SHIFT;
268 attr1_val |= llcc_table[i].fixed_size <<
269 ATTR1_FIXED_SIZE_SHIFT;
270 attr1_val |= llcc_table[i].priority <<
271 ATTR1_PRIORITY_SHIFT;
272
273 max_cap_cacheline = MAX_CAP_TO_BYTES(llcc_table[i].max_cap);
274
275 /* LLCC instances can vary for each target.
276 * The SW writes to broadcast register which gets propagated
277 * to each llcc instace (llcc0,.. llccN).
278 * Since the size of the memory is divided equally amongst the
279 * llcc instances, we need to configure the max cap accordingly.
280 */
281 max_cap_cacheline = max_cap_cacheline / drv_data->num_banks;
282 max_cap_cacheline >>= CACHE_LINE_SIZE_SHIFT;
283 attr1_val |= max_cap_cacheline << ATTR1_MAX_CAP_SHIFT;
284
285 attr0_val = llcc_table[i].res_ways & ATTR0_RES_WAYS_MASK;
286 attr0_val |= llcc_table[i].bonus_ways << ATTR0_BONUS_WAYS_SHIFT;
287
7f9c1362
VNKG
288 ret = regmap_write(drv_data->bcast_regmap, attr1_cfg,
289 attr1_val);
a3134fb0
RB
290 if (ret)
291 return ret;
7f9c1362
VNKG
292 ret = regmap_write(drv_data->bcast_regmap, attr0_cfg,
293 attr0_val);
a3134fb0
RB
294 if (ret)
295 return ret;
296 if (llcc_table[i].activate_on_init) {
297 desc.slice_id = llcc_table[i].slice_id;
298 ret = llcc_slice_activate(&desc);
299 }
300 }
301 return ret;
302}
303
72d1cd03
JC
304int qcom_llcc_remove(struct platform_device *pdev)
305{
306 /* Set the global pointer to a error code to avoid referencing it */
307 drv_data = ERR_PTR(-ENODEV);
308 return 0;
309}
310EXPORT_SYMBOL_GPL(qcom_llcc_remove);
311
ed10a259
JC
312static struct regmap *qcom_llcc_init_mmio(struct platform_device *pdev,
313 const char *name)
314{
315 struct resource *res;
316 void __iomem *base;
317
318 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
319 if (!res)
320 return ERR_PTR(-ENODEV);
321
322 base = devm_ioremap_resource(&pdev->dev, res);
323 if (IS_ERR(base))
324 return ERR_CAST(base);
325
326 return devm_regmap_init_mmio(&pdev->dev, base, &llcc_regmap_config);
327}
328
a3134fb0
RB
329int qcom_llcc_probe(struct platform_device *pdev,
330 const struct llcc_slice_config *llcc_cfg, u32 sz)
331{
332 u32 num_banks;
333 struct device *dev = &pdev->dev;
a3134fb0 334 int ret, i;
c081f306 335 struct platform_device *llcc_edac;
a3134fb0
RB
336
337 drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
72d1cd03
JC
338 if (!drv_data) {
339 ret = -ENOMEM;
340 goto err;
341 }
a3134fb0 342
ed10a259 343 drv_data->regmap = qcom_llcc_init_mmio(pdev, "llcc_base");
72d1cd03
JC
344 if (IS_ERR(drv_data->regmap)) {
345 ret = PTR_ERR(drv_data->regmap);
346 goto err;
347 }
a3134fb0 348
ed10a259
JC
349 drv_data->bcast_regmap =
350 qcom_llcc_init_mmio(pdev, "llcc_broadcast_base");
72d1cd03
JC
351 if (IS_ERR(drv_data->bcast_regmap)) {
352 ret = PTR_ERR(drv_data->bcast_regmap);
353 goto err;
354 }
7f9c1362 355
a3134fb0
RB
356 ret = regmap_read(drv_data->regmap, LLCC_COMMON_STATUS0,
357 &num_banks);
358 if (ret)
72d1cd03 359 goto err;
a3134fb0
RB
360
361 num_banks &= LLCC_LB_CNT_MASK;
362 num_banks >>= LLCC_LB_CNT_SHIFT;
363 drv_data->num_banks = num_banks;
364
365 for (i = 0; i < sz; i++)
366 if (llcc_cfg[i].slice_id > drv_data->max_slices)
367 drv_data->max_slices = llcc_cfg[i].slice_id;
368
369 drv_data->offsets = devm_kcalloc(dev, num_banks, sizeof(u32),
370 GFP_KERNEL);
72d1cd03
JC
371 if (!drv_data->offsets) {
372 ret = -ENOMEM;
373 goto err;
374 }
a3134fb0
RB
375
376 for (i = 0; i < num_banks; i++)
377 drv_data->offsets[i] = i * BANK_OFFSET_STRIDE;
378
a3134fb0
RB
379 drv_data->bitmap = devm_kcalloc(dev,
380 BITS_TO_LONGS(drv_data->max_slices), sizeof(unsigned long),
381 GFP_KERNEL);
72d1cd03
JC
382 if (!drv_data->bitmap) {
383 ret = -ENOMEM;
384 goto err;
385 }
a3134fb0
RB
386
387 drv_data->cfg = llcc_cfg;
388 drv_data->cfg_size = sz;
389 mutex_init(&drv_data->lock);
390 platform_set_drvdata(pdev, drv_data);
391
c081f306
VNKG
392 ret = qcom_llcc_cfg_program(pdev);
393 if (ret)
72d1cd03 394 goto err;
c081f306
VNKG
395
396 drv_data->ecc_irq = platform_get_irq(pdev, 0);
397 if (drv_data->ecc_irq >= 0) {
398 llcc_edac = platform_device_register_data(&pdev->dev,
399 "qcom_llcc_edac", -1, drv_data,
400 sizeof(*drv_data));
401 if (IS_ERR(llcc_edac))
402 dev_err(dev, "Failed to register llcc edac driver\n");
403 }
404
72d1cd03
JC
405 return 0;
406err:
407 drv_data = ERR_PTR(-ENODEV);
c081f306 408 return ret;
a3134fb0
RB
409}
410EXPORT_SYMBOL_GPL(qcom_llcc_probe);
4da3b045 411MODULE_LICENSE("GPL v2");
8c1919a2 412MODULE_DESCRIPTION("Qualcomm Last Level Cache Controller");