blob: 77c2ae6d123aa85228db45717b3a9472274624c7 [file] [log] [blame]
Channagoud Kadabi259c6b72017-01-13 16:30:27 -08001/* Copyright (c) 2016-2017, The Linux Foundation. All rights reserved.
Channagoud Kadabi97335b22016-08-17 13:40:46 -07002 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#define pr_fmt(fmt) "%s:" fmt, __func__
14
15#include <linux/kernel.h>
16#include <linux/device.h>
17#include <linux/delay.h>
18#include <linux/of_device.h>
19#include <linux/platform_device.h>
20#include <linux/mutex.h>
21#include <linux/bitmap.h>
22#include <linux/bitops.h>
23#include <linux/slab.h>
24#include <linux/io.h>
25#include <linux/err.h>
26#include <linux/mfd/syscon.h>
27#include <linux/regmap.h>
28#include <linux/soc/qcom/llcc-qcom.h>
29
30#define ACTIVATE 0x1
31#define DEACTIVATE 0x2
32#define ACT_CTRL_OPCODE_ACTIVATE 0x1
33#define ACT_CTRL_OPCODE_DEACTIVATE 0x2
34#define ACT_CTRL_ACT_TRIG 0x1
35#define ACT_CTRL_OPCODE_SHIFT 0x1
36#define ATTR1_PROBE_TARGET_WAYS_SHIFT 0x2
37#define ATTR1_FIXED_SIZE_SHIFT 0x3
38#define ATTR1_PRIORITY_SHIFT 0x4
39#define ATTR1_MAX_CAP_SHIFT 0x10
40#define ATTR0_RES_WAYS_MASK 0x00000fff
41#define ATR0_BONUS_WAYS_MASK 0x0fff0000
42#define ATR0_BONUS_WAYS_SHIFT 0x10
43#define LLCC_STATUS_READ_DELAY 100
44
45#define CACHE_LINE_SIZE_SHIFT 6
Channagoud Kadabi1beb63f2016-10-10 15:32:11 -070046#define SIZE_PER_LLCC_SHIFT 2
Channagoud Kadabi97335b22016-08-17 13:40:46 -070047#define MAX_CAP_TO_BYTES(n) (n * 1024)
48#define LLCC_TRP_ACT_CTRLn(n) (n * 0x1000)
49#define LLCC_TRP_STATUSn(n) (4 + n * 0x1000)
50#define LLCC_TRP_ATTR0_CFGn(n) (0x21000 + 0x8 * n)
51#define LLCC_TRP_ATTR1_CFGn(n) (0x21004 + 0x8 * n)
Channagoud Kadabi97335b22016-08-17 13:40:46 -070052
53/**
54 * Driver data for llcc
55 * @llcc_virt_base: base address for llcc controller
56 * @slice_data: pointer to llcc slice config data
57 * @sz: Size of the config data table
58 * @llcc_slice_map: Bit map to track the active slice ids
59 */
60struct llcc_drv_data {
61 struct regmap *llcc_map;
62 const struct llcc_slice_config *slice_data;
63 struct mutex slice_mutex;
64 u32 llcc_config_data_sz;
65 u32 max_slices;
66 unsigned long *llcc_slice_map;
67};
68
69/* Get the slice entry by index */
70static struct llcc_slice_desc *llcc_slice_get_entry(struct device *dev, int n)
71{
Channagoud Kadabi97335b22016-08-17 13:40:46 -070072 struct of_phandle_args phargs;
73 struct llcc_drv_data *drv;
74 const struct llcc_slice_config *llcc_data_ptr;
75 struct llcc_slice_desc *desc;
76 struct platform_device *pdev;
77
78 if (of_parse_phandle_with_args(dev->of_node, "cache-slices",
79 "#cache-cells", n, &phargs)) {
80 pr_err("can't parse \"cache-slices\" property\n");
81 return ERR_PTR(-ENODEV);
82 }
83
84 pdev = of_find_device_by_node(phargs.np);
85 if (!pdev) {
86 pr_err("Cannot find platform device from phandle\n");
87 return ERR_PTR(-ENODEV);
88 }
89
90 drv = platform_get_drvdata(pdev);
91 if (!drv) {
92 pr_err("cannot find platform driver data\n");
93 return ERR_PTR(-EFAULT);
94 }
95
96 llcc_data_ptr = drv->slice_data;
97
98 while (llcc_data_ptr) {
99 if (llcc_data_ptr->usecase_id == phargs.args[0])
100 break;
101 llcc_data_ptr++;
102 }
103
104 if (llcc_data_ptr == NULL) {
Channagoud Kadabi075db3b2017-03-16 14:26:17 -0700105 pr_err("can't find %d usecase id\n", phargs.args[0]);
Channagoud Kadabi97335b22016-08-17 13:40:46 -0700106 return ERR_PTR(-ENODEV);
107 }
108
109 desc = kzalloc(sizeof(struct llcc_slice_desc), GFP_KERNEL);
110 if (!desc)
111 return ERR_PTR(-ENOMEM);
112
113 desc->llcc_slice_id = llcc_data_ptr->slice_id;
114 desc->llcc_slice_size = llcc_data_ptr->max_cap;
115 desc->dev = &pdev->dev;
116
117 return desc;
118}
119
120/**
121 * llcc_slice_getd - get llcc slice descriptor
122 * @dev: Device pointer of the client
123 * @name: Name of the use case
124 *
125 * A pointer to llcc slice descriptor will be returned on success and
126 * and error pointer is returned on failure
127 */
128struct llcc_slice_desc *llcc_slice_getd(struct device *dev, const char *name)
129{
130 struct device_node *np = dev->of_node;
Channagoud Kadabi38dd15b2017-01-25 13:10:48 -0800131 int index = 0;
Channagoud Kadabi97335b22016-08-17 13:40:46 -0700132 const char *slice_name;
133 struct property *prop;
134
135 if (!np) {
136 dev_err(dev, "%s() currently only supports DT\n", __func__);
137 return ERR_PTR(-ENOENT);
138 }
139
140 if (!of_get_property(np, "cache-slice-names", NULL)) {
141 dev_err(dev,
142 "%s() requires a \"cache-slice-names\" property\n",
143 __func__);
144 return ERR_PTR(-ENOENT);
145 }
146
147 of_property_for_each_string(np, "cache-slice-names", prop, slice_name) {
148 if (!strcmp(name, slice_name))
149 break;
150 index++;
151 }
152
153 return llcc_slice_get_entry(dev, index);
154}
155EXPORT_SYMBOL(llcc_slice_getd);
156
157/**
158 * llcc_slice_putd - llcc slice descritpor
159 * @desc: Pointer to llcc slice descriptor
160 */
161void llcc_slice_putd(struct llcc_slice_desc *desc)
162{
163 kfree(desc);
164}
165EXPORT_SYMBOL(llcc_slice_putd);
166
167static int llcc_update_act_ctrl(struct llcc_drv_data *drv, u32 sid,
168 u32 act_ctrl_reg_val, u32 status)
169{
170 u32 act_ctrl_reg;
171 u32 status_reg;
172 u32 slice_status;
173 unsigned long timeout;
174
175 act_ctrl_reg = LLCC_TRP_ACT_CTRLn(sid);
176 status_reg = LLCC_TRP_STATUSn(sid);
177
178 regmap_write(drv->llcc_map, act_ctrl_reg, act_ctrl_reg_val);
179
180 /* Make sure the activate trigger is applied before clearing it */
181 mb();
182
183 /* Clear the ACTIVE trigger */
184 act_ctrl_reg_val &= ~ACT_CTRL_ACT_TRIG;
185 regmap_write(drv->llcc_map, act_ctrl_reg, act_ctrl_reg_val);
186
187 timeout = jiffies + usecs_to_jiffies(LLCC_STATUS_READ_DELAY);
188 while (time_before(jiffies, timeout)) {
Kyle Yan83751052016-10-05 17:21:39 -0700189 regmap_read(drv->llcc_map, status_reg, &slice_status);
Channagoud Kadabi259c6b72017-01-13 16:30:27 -0800190 if (!(slice_status & status))
Channagoud Kadabi97335b22016-08-17 13:40:46 -0700191 return 0;
192 }
193
194 return -ETIMEDOUT;
195}
196
197/**
198 * llcc_slice_activate - Activate the llcc slice
199 * @desc: Pointer to llcc slice descriptor
200 *
201 * A value zero will be returned on success and a negative errno will
202 * be returned in error cases
203 */
204int llcc_slice_activate(struct llcc_slice_desc *desc)
205{
206 int rc = -EINVAL;
207 u32 act_ctrl_val;
208 struct llcc_drv_data *drv;
209
210 if (desc == NULL) {
211 pr_err("Input descriptor supplied is invalid\n");
212 return rc;
213 }
214
215 drv = dev_get_drvdata(desc->dev);
216 if (!drv) {
217 pr_err("Invalid device pointer in the desc\n");
218 return rc;
219 }
220
221 mutex_lock(&drv->slice_mutex);
222 if (test_bit(desc->llcc_slice_id, drv->llcc_slice_map)) {
223 mutex_unlock(&drv->slice_mutex);
224 return 0;
225 }
226
227 act_ctrl_val = ACT_CTRL_OPCODE_ACTIVATE << ACT_CTRL_OPCODE_SHIFT;
228 act_ctrl_val |= ACT_CTRL_ACT_TRIG;
229
230 rc = llcc_update_act_ctrl(drv, desc->llcc_slice_id, act_ctrl_val,
Channagoud Kadabi259c6b72017-01-13 16:30:27 -0800231 DEACTIVATE);
Channagoud Kadabi97335b22016-08-17 13:40:46 -0700232
233 __set_bit(desc->llcc_slice_id, drv->llcc_slice_map);
234 mutex_unlock(&drv->slice_mutex);
235
236 return rc;
237}
238EXPORT_SYMBOL(llcc_slice_activate);
239
240/**
241 * llcc_slice_deactivate - Deactivate the llcc slice
242 * @desc: Pointer to llcc slice descriptor
243 *
244 * A value zero will be returned on success and a negative errno will
245 * be returned in error cases
246 */
247int llcc_slice_deactivate(struct llcc_slice_desc *desc)
248{
249 u32 act_ctrl_val;
250 int rc = -EINVAL;
251 struct llcc_drv_data *drv;
252
253 if (desc == NULL) {
254 pr_err("Input descriptor supplied is invalid\n");
255 return rc;
256 }
257
258 drv = dev_get_drvdata(desc->dev);
259 if (!drv) {
260 pr_err("Invalid device pointer in the desc\n");
261 return rc;
262 }
263
264 mutex_lock(&drv->slice_mutex);
265 if (!test_bit(desc->llcc_slice_id, drv->llcc_slice_map)) {
266 mutex_unlock(&drv->slice_mutex);
267 return 0;
268 }
269 act_ctrl_val = ACT_CTRL_OPCODE_DEACTIVATE << ACT_CTRL_OPCODE_SHIFT;
270 act_ctrl_val |= ACT_CTRL_ACT_TRIG;
271
272 rc = llcc_update_act_ctrl(drv, desc->llcc_slice_id, act_ctrl_val,
Channagoud Kadabi259c6b72017-01-13 16:30:27 -0800273 ACTIVATE);
Channagoud Kadabi97335b22016-08-17 13:40:46 -0700274
275 __clear_bit(desc->llcc_slice_id, drv->llcc_slice_map);
276 mutex_unlock(&drv->slice_mutex);
277
278 return rc;
279}
280EXPORT_SYMBOL(llcc_slice_deactivate);
281
282/**
283 * llcc_get_slice_id - return the slice id
284 * @desc: Pointer to llcc slice descriptor
285 *
286 * A positive value will be returned on success and a negative errno will
287 * be returned on error
288 */
289int llcc_get_slice_id(struct llcc_slice_desc *desc)
290{
291 if (!desc)
292 return -EINVAL;
293
294 return desc->llcc_slice_id;
295}
296EXPORT_SYMBOL(llcc_get_slice_id);
297
298/**
299 * llcc_get_slice_size - return the slice id
300 * @desc: Pointer to llcc slice descriptor
301 *
302 * A positive value will be returned on success and zero will returned on
303 * error
304 */
305size_t llcc_get_slice_size(struct llcc_slice_desc *desc)
306{
307 if (!desc)
308 return 0;
309
310 return desc->llcc_slice_size;
311}
312EXPORT_SYMBOL(llcc_get_slice_size);
313
314static void qcom_llcc_cfg_program(struct platform_device *pdev)
315{
316 int i;
317 u32 attr1_cfg;
318 u32 attr0_cfg;
319 u32 attr1_val;
320 u32 attr0_val;
Channagoud Kadabi97335b22016-08-17 13:40:46 -0700321 u32 max_cap_cacheline;
322 u32 sz;
323 const struct llcc_slice_config *llcc_table;
324 struct llcc_drv_data *drv = platform_get_drvdata(pdev);
325 struct llcc_slice_desc desc;
326
327 sz = drv->llcc_config_data_sz;
328 llcc_table = drv->slice_data;
329
330 for (i = 0; i < sz; i++) {
331 attr1_cfg = LLCC_TRP_ATTR1_CFGn(llcc_table[i].slice_id);
332 attr0_cfg = LLCC_TRP_ATTR0_CFGn(llcc_table[i].slice_id);
333
334 attr1_val = llcc_table[i].cache_mode;
335 attr1_val |= (llcc_table[i].probe_target_ways <<
336 ATTR1_PROBE_TARGET_WAYS_SHIFT);
337 attr1_val |= (llcc_table[i].fixed_size <<
338 ATTR1_FIXED_SIZE_SHIFT);
339 attr1_val |= (llcc_table[i].priority << ATTR1_PRIORITY_SHIFT);
340
341 max_cap_cacheline = MAX_CAP_TO_BYTES(llcc_table[i].max_cap);
342 max_cap_cacheline >>= CACHE_LINE_SIZE_SHIFT;
Channagoud Kadabi1beb63f2016-10-10 15:32:11 -0700343 /* There are four llcc instances llcc0..llcc3. The SW writes to
344 * to broadcast register which gets propagated to each llcc.
345 * Since the size of the memory is divided equally amongst the
346 * four llcc, we need to divide the max cap by 4
347 */
348 max_cap_cacheline >>= SIZE_PER_LLCC_SHIFT;
Channagoud Kadabi97335b22016-08-17 13:40:46 -0700349 attr1_val |= (max_cap_cacheline << ATTR1_MAX_CAP_SHIFT);
350
351 attr0_val = llcc_table[i].res_ways & ATTR0_RES_WAYS_MASK;
352 attr0_val |= llcc_table[i].bonus_ways << ATR0_BONUS_WAYS_SHIFT;
353
354 regmap_write(drv->llcc_map, attr1_cfg, attr1_val);
355 regmap_write(drv->llcc_map, attr0_cfg, attr0_val);
356
Channagoud Kadabi97335b22016-08-17 13:40:46 -0700357 /* Make sure that the SCT is programmed before activating */
358 mb();
359
360 if (llcc_table[i].activate_on_init) {
361 desc.llcc_slice_id = llcc_table[i].slice_id;
362 desc.dev = &pdev->dev;
363 if (llcc_slice_activate(&desc)) {
364 pr_err("activate slice id: %d timed out\n",
365 desc.llcc_slice_id);
366 }
367 }
368 }
369}
370
371int qcom_llcc_probe(struct platform_device *pdev,
372 const struct llcc_slice_config *llcc_cfg, u32 sz)
373{
374 int rc = 0;
375 struct device *dev = &pdev->dev;
376 static struct llcc_drv_data *drv_data;
377
378 drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
379 if (!drv_data)
380 return PTR_ERR(drv_data);
381
382 drv_data->llcc_map = syscon_node_to_regmap(dev->parent->of_node);
383 if (!drv_data->llcc_map)
384 return PTR_ERR(drv_data->llcc_map);
385
386 rc = of_property_read_u32(pdev->dev.of_node, "max-slices",
387 &drv_data->max_slices);
388 if (rc) {
389 dev_info(&pdev->dev, "Invalid max-slices dt entry\n");
390 devm_kfree(&pdev->dev, drv_data);
391 return rc;
392 }
393
394 drv_data->llcc_slice_map = kcalloc(BITS_TO_LONGS(drv_data->max_slices),
395 sizeof(unsigned long), GFP_KERNEL);
396
397 if (!drv_data->llcc_slice_map) {
398 devm_kfree(&pdev->dev, drv_data);
399 return PTR_ERR(drv_data->llcc_slice_map);
400 }
401
402 bitmap_zero(drv_data->llcc_slice_map, drv_data->max_slices);
403 drv_data->slice_data = llcc_cfg;
404 drv_data->llcc_config_data_sz = sz;
405 mutex_init(&drv_data->slice_mutex);
406 platform_set_drvdata(pdev, drv_data);
407
408 qcom_llcc_cfg_program(pdev);
409
410 return rc;
411}
412EXPORT_SYMBOL(qcom_llcc_probe);
413
414int qcom_llcc_remove(struct platform_device *pdev)
415{
416 static struct llcc_drv_data *drv_data;
417
418 drv_data = platform_get_drvdata(pdev);
419
420 mutex_destroy(&drv_data->slice_mutex);
421 kfree(drv_data->llcc_slice_map);
422 devm_kfree(&pdev->dev, drv_data);
423 platform_set_drvdata(pdev, NULL);
424
425 return 0;
426}
427EXPORT_SYMBOL(qcom_llcc_remove);