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 47 static void __iomem *ccache_base; 48 static int g_irq[SIFIVE_CCACHE_MAX_ECCINTR]; 49 static struct riscv_cacheinfo_ops ccache_cache_ops; 50 static int level; 51 52 enum { 53 DIR_CORR = 0, 54 DATA_CORR, 55 DATA_UNCORR, 56 DIR_UNCORR, 57 }; 58 59 #ifdef CONFIG_DEBUG_FS 60 static struct dentry *sifive_test; 61 62 static 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 76 static const struct file_operations ccache_fops = { 77 .owner = THIS_MODULE, 78 .open = simple_open, 79 .write = ccache_write 80 }; 81 82 static 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 91 static 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 106 static 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 113 static ATOMIC_NOTIFIER_HEAD(ccache_err_chain); 114 115 int register_sifive_ccache_error_notifier(struct notifier_block *nb) 116 { 117 return atomic_notifier_chain_register(&ccache_err_chain, nb); 118 } 119 EXPORT_SYMBOL_GPL(register_sifive_ccache_error_notifier); 120 121 int unregister_sifive_ccache_error_notifier(struct notifier_block *nb) 122 { 123 return atomic_notifier_chain_unregister(&ccache_err_chain, nb); 124 } 125 EXPORT_SYMBOL_GPL(unregister_sifive_ccache_error_notifier); 126 127 static int ccache_largest_wayenabled(void) 128 { 129 return readl(ccache_base + SIFIVE_CCACHE_WAYENABLE) & 0xFF; 130 } 131 132 static 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 139 static DEVICE_ATTR_RO(number_of_ways_enabled); 140 141 static struct attribute *priv_attrs[] = { 142 &dev_attr_number_of_ways_enabled.attr, 143 NULL, 144 }; 145 146 static const struct attribute_group priv_attr_group = { 147 .attrs = priv_attrs, 148 }; 149 150 static 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 160 static 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 208 static 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 return -ENODEV; 220 221 ccache_base = ioremap(res.start, resource_size(&res)); 222 if (!ccache_base) 223 return -ENOMEM; 224 225 if (of_property_read_u32(np, "cache-level", &level)) { 226 rc = -ENOENT; 227 goto err_unmap; 228 } 229 230 intr_num = of_property_count_u32_elems(np, "interrupts"); 231 if (!intr_num) { 232 pr_err("No interrupts property\n"); 233 rc = -ENODEV; 234 goto err_unmap; 235 } 236 237 for (i = 0; i < intr_num; i++) { 238 g_irq[i] = irq_of_parse_and_map(np, i); 239 rc = request_irq(g_irq[i], ccache_int_handler, 0, "ccache_ecc", 240 NULL); 241 if (rc) { 242 pr_err("Could not request IRQ %d\n", g_irq[i]); 243 goto err_free_irq; 244 } 245 } 246 247 ccache_config_read(); 248 249 ccache_cache_ops.get_priv_group = ccache_get_priv_group; 250 riscv_set_cacheinfo_ops(&ccache_cache_ops); 251 252 #ifdef CONFIG_DEBUG_FS 253 setup_sifive_debug(); 254 #endif 255 return 0; 256 257 err_free_irq: 258 while (--i >= 0) 259 free_irq(g_irq[i], NULL); 260 err_unmap: 261 iounmap(ccache_base); 262 return rc; 263 } 264 265 device_initcall(sifive_ccache_init); 266