1// SPDX-License-Identifier: GPL-2.0 2/* 3 * SiFive composable cache controller Driver 4 * 5 * Copyright (C) 2018-2022 SiFive, Inc. 6 * 7 */ 8 9#define pr_fmt(fmt) "CCACHE: " fmt 10 11#include <linux/debugfs.h> 12#include <linux/interrupt.h> 13#include <linux/of_irq.h> 14#include <linux/of_address.h> 15#include <linux/device.h> 16#include <linux/bitfield.h> 17#include <asm/cacheinfo.h> 18#include <soc/sifive/sifive_ccache.h> 19 20#define SIFIVE_CCACHE_DIRECCFIX_LOW 0x100 21#define SIFIVE_CCACHE_DIRECCFIX_HIGH 0x104 22#define SIFIVE_CCACHE_DIRECCFIX_COUNT 0x108 23 24#define SIFIVE_CCACHE_DIRECCFAIL_LOW 0x120 25#define SIFIVE_CCACHE_DIRECCFAIL_HIGH 0x124 26#define SIFIVE_CCACHE_DIRECCFAIL_COUNT 0x128 27 28#define SIFIVE_CCACHE_DATECCFIX_LOW 0x140 29#define SIFIVE_CCACHE_DATECCFIX_HIGH 0x144 30#define SIFIVE_CCACHE_DATECCFIX_COUNT 0x148 31 32#define SIFIVE_CCACHE_DATECCFAIL_LOW 0x160 33#define SIFIVE_CCACHE_DATECCFAIL_HIGH 0x164 34#define SIFIVE_CCACHE_DATECCFAIL_COUNT 0x168 35 36#define SIFIVE_CCACHE_CONFIG 0x00 37#define SIFIVE_CCACHE_CONFIG_BANK_MASK GENMASK_ULL(7, 0) 38#define SIFIVE_CCACHE_CONFIG_WAYS_MASK GENMASK_ULL(15, 8) 39#define SIFIVE_CCACHE_CONFIG_SETS_MASK GENMASK_ULL(23, 16) 40#define SIFIVE_CCACHE_CONFIG_BLKS_MASK GENMASK_ULL(31, 24) 41 42#define SIFIVE_CCACHE_WAYENABLE 0x08 43#define SIFIVE_CCACHE_ECCINJECTERR 0x40 44 45#define SIFIVE_CCACHE_MAX_ECCINTR 4 46 47static void __iomem *ccache_base; 48static int g_irq[SIFIVE_CCACHE_MAX_ECCINTR]; 49static struct riscv_cacheinfo_ops ccache_cache_ops; 50static int level; 51 52enum { 53 DIR_CORR = 0, 54 DATA_CORR, 55 DATA_UNCORR, 56 DIR_UNCORR, 57}; 58 59#ifdef CONFIG_DEBUG_FS 60static struct dentry *sifive_test; 61 62static ssize_t ccache_write(struct file *file, const char __user *data, 63 size_t count, loff_t *ppos) 64{ 65 unsigned int val; 66 67 if (kstrtouint_from_user(data, count, 0, &val)) 68 return -EINVAL; 69 if ((val < 0xFF) || (val >= 0x10000 && val < 0x100FF)) 70 writel(val, ccache_base + SIFIVE_CCACHE_ECCINJECTERR); 71 else 72 return -EINVAL; 73 return count; 74} 75 76static const struct file_operations ccache_fops = { 77 .owner = THIS_MODULE, 78 .open = simple_open, 79 .write = ccache_write 80}; 81 82static void setup_sifive_debug(void) 83{ 84 sifive_test = debugfs_create_dir("sifive_ccache_cache", NULL); 85 86 debugfs_create_file("sifive_debug_inject_error", 0200, 87 sifive_test, NULL, &ccache_fops); 88} 89#endif 90 91static void ccache_config_read(void) 92{ 93 u32 cfg; 94 95 cfg = readl(ccache_base + SIFIVE_CCACHE_CONFIG); 96 pr_info("%llu banks, %llu ways, sets/bank=%llu, bytes/block=%llu\n", 97 FIELD_GET(SIFIVE_CCACHE_CONFIG_BANK_MASK, cfg), 98 FIELD_GET(SIFIVE_CCACHE_CONFIG_WAYS_MASK, cfg), 99 BIT_ULL(FIELD_GET(SIFIVE_CCACHE_CONFIG_SETS_MASK, cfg)), 100 BIT_ULL(FIELD_GET(SIFIVE_CCACHE_CONFIG_BLKS_MASK, cfg))); 101 102 cfg = readl(ccache_base + SIFIVE_CCACHE_WAYENABLE); 103 pr_info("Index of the largest way enabled: %u\n", cfg); 104} 105 106static const struct of_device_id sifive_ccache_ids[] = { 107 { .compatible = "sifive,fu540-c000-ccache" }, 108 { .compatible = "sifive,fu740-c000-ccache" }, 109 { .compatible = "sifive,ccache0" }, 110 { /* end of table */ } 111}; 112 113static ATOMIC_NOTIFIER_HEAD(ccache_err_chain); 114 115int register_sifive_ccache_error_notifier(struct notifier_block *nb) 116{ 117 return atomic_notifier_chain_register(&ccache_err_chain, nb); 118} 119EXPORT_SYMBOL_GPL(register_sifive_ccache_error_notifier); 120 121int unregister_sifive_ccache_error_notifier(struct notifier_block *nb) 122{ 123 return atomic_notifier_chain_unregister(&ccache_err_chain, nb); 124} 125EXPORT_SYMBOL_GPL(unregister_sifive_ccache_error_notifier); 126 127static int ccache_largest_wayenabled(void) 128{ 129 return readl(ccache_base + SIFIVE_CCACHE_WAYENABLE) & 0xFF; 130} 131 132static ssize_t number_of_ways_enabled_show(struct device *dev, 133 struct device_attribute *attr, 134 char *buf) 135{ 136 return sprintf(buf, "%u\n", ccache_largest_wayenabled()); 137} 138 139static DEVICE_ATTR_RO(number_of_ways_enabled); 140 141static struct attribute *priv_attrs[] = { 142 &dev_attr_number_of_ways_enabled.attr, 143 NULL, 144}; 145 146static const struct attribute_group priv_attr_group = { 147 .attrs = priv_attrs, 148}; 149 150static const struct attribute_group *ccache_get_priv_group(struct cacheinfo 151 *this_leaf) 152{ 153 /* We want to use private group for composable cache only */ 154 if (this_leaf->level == level) 155 return &priv_attr_group; 156 else 157 return NULL; 158} 159 160static irqreturn_t ccache_int_handler(int irq, void *device) 161{ 162 unsigned int add_h, add_l; 163 164 if (irq == g_irq[DIR_CORR]) { 165 add_h = readl(ccache_base + SIFIVE_CCACHE_DIRECCFIX_HIGH); 166 add_l = readl(ccache_base + SIFIVE_CCACHE_DIRECCFIX_LOW); 167 pr_err("DirError @ 0x%08X.%08X\n", add_h, add_l); 168 /* Reading this register clears the DirError interrupt sig */ 169 readl(ccache_base + SIFIVE_CCACHE_DIRECCFIX_COUNT); 170 atomic_notifier_call_chain(&ccache_err_chain, 171 SIFIVE_CCACHE_ERR_TYPE_CE, 172 "DirECCFix"); 173 } 174 if (irq == g_irq[DIR_UNCORR]) { 175 add_h = readl(ccache_base + SIFIVE_CCACHE_DIRECCFAIL_HIGH); 176 add_l = readl(ccache_base + SIFIVE_CCACHE_DIRECCFAIL_LOW); 177 /* Reading this register clears the DirFail interrupt sig */ 178 readl(ccache_base + SIFIVE_CCACHE_DIRECCFAIL_COUNT); 179 atomic_notifier_call_chain(&ccache_err_chain, 180 SIFIVE_CCACHE_ERR_TYPE_UE, 181 "DirECCFail"); 182 panic("CCACHE: DirFail @ 0x%08X.%08X\n", add_h, add_l); 183 } 184 if (irq == g_irq[DATA_CORR]) { 185 add_h = readl(ccache_base + SIFIVE_CCACHE_DATECCFIX_HIGH); 186 add_l = readl(ccache_base + SIFIVE_CCACHE_DATECCFIX_LOW); 187 pr_err("DataError @ 0x%08X.%08X\n", add_h, add_l); 188 /* Reading this register clears the DataError interrupt sig */ 189 readl(ccache_base + SIFIVE_CCACHE_DATECCFIX_COUNT); 190 atomic_notifier_call_chain(&ccache_err_chain, 191 SIFIVE_CCACHE_ERR_TYPE_CE, 192 "DatECCFix"); 193 } 194 if (irq == g_irq[DATA_UNCORR]) { 195 add_h = readl(ccache_base + SIFIVE_CCACHE_DATECCFAIL_HIGH); 196 add_l = readl(ccache_base + SIFIVE_CCACHE_DATECCFAIL_LOW); 197 pr_err("DataFail @ 0x%08X.%08X\n", add_h, add_l); 198 /* Reading this register clears the DataFail interrupt sig */ 199 readl(ccache_base + SIFIVE_CCACHE_DATECCFAIL_COUNT); 200 atomic_notifier_call_chain(&ccache_err_chain, 201 SIFIVE_CCACHE_ERR_TYPE_UE, 202 "DatECCFail"); 203 } 204 205 return IRQ_HANDLED; 206} 207 208static int __init sifive_ccache_init(void) 209{ 210 struct device_node *np; 211 struct resource res; 212 int i, rc, intr_num; 213 214 np = of_find_matching_node(NULL, sifive_ccache_ids); 215 if (!np) 216 return -ENODEV; 217 218 if (of_address_to_resource(np, 0, &res)) { 219 rc = -ENODEV; 220 goto err_node_put; 221 } 222 223 ccache_base = ioremap(res.start, resource_size(&res)); 224 if (!ccache_base) { 225 rc = -ENOMEM; 226 goto err_node_put; 227 } 228 229 if (of_property_read_u32(np, "cache-level", &level)) { 230 rc = -ENOENT; 231 goto err_unmap; 232 } 233 234 intr_num = of_property_count_u32_elems(np, "interrupts"); 235 if (!intr_num) { 236 pr_err("No interrupts property\n"); 237 rc = -ENODEV; 238 goto err_unmap; 239 } 240 241 for (i = 0; i < intr_num; i++) { 242 g_irq[i] = irq_of_parse_and_map(np, i); 243 rc = request_irq(g_irq[i], ccache_int_handler, 0, "ccache_ecc", 244 NULL); 245 if (rc) { 246 pr_err("Could not request IRQ %d\n", g_irq[i]); 247 goto err_free_irq; 248 } 249 } 250 of_node_put(np); 251 252 ccache_config_read(); 253 254 ccache_cache_ops.get_priv_group = ccache_get_priv_group; 255 riscv_set_cacheinfo_ops(&ccache_cache_ops); 256 257#ifdef CONFIG_DEBUG_FS 258 setup_sifive_debug(); 259#endif 260 return 0; 261 262err_free_irq: 263 while (--i >= 0) 264 free_irq(g_irq[i], NULL); 265err_unmap: 266 iounmap(ccache_base); 267err_node_put: 268 of_node_put(np); 269 return rc; 270} 271 272device_initcall(sifive_ccache_init); 273