Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v5.4 412 lines 10 kB view raw
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");