Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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>
 12#include <linux/module.h>
 13#include <linux/mutex.h>
 14#include <linux/of_device.h>
 15#include <linux/regmap.h>
 16#include <linux/sizes.h>
 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
 49static struct llcc_drv_data *drv_data = (void *) -EPROBE_DEFER;
 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
 71	if (IS_ERR(drv_data))
 72		return ERR_CAST(drv_data);
 73
 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{
101	if (!IS_ERR_OR_NULL(desc))
102		kfree(desc);
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
114	if (IS_ERR(drv_data))
115		return PTR_ERR(drv_data);
116
117	act_ctrl_reg = LLCC_TRP_ACT_CTRLn(sid);
118	status_reg = LLCC_TRP_STATUSn(sid);
119
120	/* Set the ACTIVE trigger */
121	act_ctrl_reg_val |= ACT_CTRL_ACT_TRIG;
122	ret = regmap_write(drv_data->bcast_regmap, act_ctrl_reg,
123				act_ctrl_reg_val);
124	if (ret)
125		return ret;
126
127	/* Clear the ACTIVE trigger */
128	act_ctrl_reg_val &= ~ACT_CTRL_ACT_TRIG;
129	ret = regmap_write(drv_data->bcast_regmap, act_ctrl_reg,
130				act_ctrl_reg_val);
131	if (ret)
132		return ret;
133
134	ret = regmap_read_poll_timeout(drv_data->bcast_regmap, status_reg,
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
152	if (IS_ERR(drv_data))
153		return PTR_ERR(drv_data);
154
155	if (IS_ERR_OR_NULL(desc))
156		return -EINVAL;
157
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
192	if (IS_ERR(drv_data))
193		return PTR_ERR(drv_data);
194
195	if (IS_ERR_OR_NULL(desc))
196		return -EINVAL;
197
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{
225	if (IS_ERR_OR_NULL(desc))
226		return -EINVAL;
227
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{
238	if (IS_ERR_OR_NULL(desc))
239		return 0;
240
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;
254	int ret = 0;
255	const struct llcc_slice_config *llcc_table;
256	struct llcc_slice_desc desc;
257
258	sz = drv_data->cfg_size;
259	llcc_table = drv_data->cfg;
260
261	for (i = 0; i < sz; i++) {
262		attr1_cfg = LLCC_TRP_ATTR1_CFGn(llcc_table[i].slice_id);
263		attr0_cfg = LLCC_TRP_ATTR0_CFGn(llcc_table[i].slice_id);
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
288		ret = regmap_write(drv_data->bcast_regmap, attr1_cfg,
289					attr1_val);
290		if (ret)
291			return ret;
292		ret = regmap_write(drv_data->bcast_regmap, attr0_cfg,
293					attr0_val);
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
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
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
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;
334	int ret, i;
335	struct platform_device *llcc_edac;
336
337	drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
338	if (!drv_data) {
339		ret = -ENOMEM;
340		goto err;
341	}
342
343	drv_data->regmap = qcom_llcc_init_mmio(pdev, "llcc_base");
344	if (IS_ERR(drv_data->regmap)) {
345		ret = PTR_ERR(drv_data->regmap);
346		goto err;
347	}
348
349	drv_data->bcast_regmap =
350		qcom_llcc_init_mmio(pdev, "llcc_broadcast_base");
351	if (IS_ERR(drv_data->bcast_regmap)) {
352		ret = PTR_ERR(drv_data->bcast_regmap);
353		goto err;
354	}
355
356	ret = regmap_read(drv_data->regmap, LLCC_COMMON_STATUS0,
357						&num_banks);
358	if (ret)
359		goto err;
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);
371	if (!drv_data->offsets) {
372		ret = -ENOMEM;
373		goto err;
374	}
375
376	for (i = 0; i < num_banks; i++)
377		drv_data->offsets[i] = i * BANK_OFFSET_STRIDE;
378
379	drv_data->bitmap = devm_kcalloc(dev,
380	BITS_TO_LONGS(drv_data->max_slices), sizeof(unsigned long),
381						GFP_KERNEL);
382	if (!drv_data->bitmap) {
383		ret = -ENOMEM;
384		goto err;
385	}
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
392	ret = qcom_llcc_cfg_program(pdev);
393	if (ret)
394		goto err;
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
405	return 0;
406err:
407	drv_data = ERR_PTR(-ENODEV);
408	return ret;
409}
410EXPORT_SYMBOL_GPL(qcom_llcc_probe);
411MODULE_LICENSE("GPL v2");
412MODULE_DESCRIPTION("Qualcomm Last Level Cache Controller");