1 /* 2 * CoreNet Coherency Fabric error reporting 3 * 4 * Copyright 2014 Freescale Semiconductor Inc. 5 * 6 * This program is free software; you can redistribute it and/or modify it 7 * under the terms of the GNU General Public License as published by the 8 * Free Software Foundation; either version 2 of the License, or (at your 9 * option) any later version. 10 */ 11 12 #include <linux/interrupt.h> 13 #include <linux/io.h> 14 #include <linux/irq.h> 15 #include <linux/module.h> 16 #include <linux/of.h> 17 #include <linux/of_address.h> 18 #include <linux/of_device.h> 19 #include <linux/of_irq.h> 20 #include <linux/platform_device.h> 21 22 enum ccf_version { 23 CCF1, 24 CCF2, 25 }; 26 27 struct ccf_info { 28 enum ccf_version version; 29 int err_reg_offs; 30 bool has_brr; 31 }; 32 33 static const struct ccf_info ccf1_info = { 34 .version = CCF1, 35 .err_reg_offs = 0xa00, 36 .has_brr = false, 37 }; 38 39 static const struct ccf_info ccf2_info = { 40 .version = CCF2, 41 .err_reg_offs = 0xe40, 42 .has_brr = true, 43 }; 44 45 /* 46 * This register is present but not documented, with different values for 47 * IP_ID, on other chips with fsl,corenet2-cf such as t4240 and b4860. 48 */ 49 #define CCF_BRR 0xbf8 50 #define CCF_BRR_IPID 0xffff0000 51 #define CCF_BRR_IPID_T1040 0x09310000 52 53 static const struct of_device_id ccf_matches[] = { 54 { 55 .compatible = "fsl,corenet1-cf", 56 .data = &ccf1_info, 57 }, 58 { 59 .compatible = "fsl,corenet2-cf", 60 .data = &ccf2_info, 61 }, 62 {} 63 }; 64 65 struct ccf_err_regs { 66 u32 errdet; /* 0x00 Error Detect Register */ 67 /* 0x04 Error Enable (ccf1)/Disable (ccf2) Register */ 68 u32 errdis; 69 /* 0x08 Error Interrupt Enable Register (ccf2 only) */ 70 u32 errinten; 71 u32 cecar; /* 0x0c Error Capture Attribute Register */ 72 u32 cecaddrh; /* 0x10 Error Capture Address High */ 73 u32 cecaddrl; /* 0x14 Error Capture Address Low */ 74 u32 cecar2; /* 0x18 Error Capture Attribute Register 2 */ 75 }; 76 77 /* LAE/CV also valid for errdis and errinten */ 78 #define ERRDET_LAE (1 << 0) /* Local Access Error */ 79 #define ERRDET_CV (1 << 1) /* Coherency Violation */ 80 #define ERRDET_UTID (1 << 2) /* Unavailable Target ID (t1040) */ 81 #define ERRDET_MCST (1 << 3) /* Multicast Stash (t1040) */ 82 #define ERRDET_CTYPE_SHIFT 26 /* Capture Type (ccf2 only) */ 83 #define ERRDET_CTYPE_MASK (0x1f << ERRDET_CTYPE_SHIFT) 84 #define ERRDET_CAP (1 << 31) /* Capture Valid (ccf2 only) */ 85 86 #define CECAR_VAL (1 << 0) /* Valid (ccf1 only) */ 87 #define CECAR_UVT (1 << 15) /* Unavailable target ID (ccf1) */ 88 #define CECAR_SRCID_SHIFT_CCF1 24 89 #define CECAR_SRCID_MASK_CCF1 (0xff << CECAR_SRCID_SHIFT_CCF1) 90 #define CECAR_SRCID_SHIFT_CCF2 18 91 #define CECAR_SRCID_MASK_CCF2 (0xff << CECAR_SRCID_SHIFT_CCF2) 92 93 #define CECADDRH_ADDRH 0xff 94 95 struct ccf_private { 96 const struct ccf_info *info; 97 struct device *dev; 98 void __iomem *regs; 99 struct ccf_err_regs __iomem *err_regs; 100 bool t1040; 101 }; 102 103 static irqreturn_t ccf_irq(int irq, void *dev_id) 104 { 105 struct ccf_private *ccf = dev_id; 106 static DEFINE_RATELIMIT_STATE(ratelimit, DEFAULT_RATELIMIT_INTERVAL, 107 DEFAULT_RATELIMIT_BURST); 108 u32 errdet, cecar, cecar2; 109 u64 addr; 110 u32 src_id; 111 bool uvt = false; 112 bool cap_valid = false; 113 114 errdet = ioread32be(&ccf->err_regs->errdet); 115 cecar = ioread32be(&ccf->err_regs->cecar); 116 cecar2 = ioread32be(&ccf->err_regs->cecar2); 117 addr = ioread32be(&ccf->err_regs->cecaddrl); 118 addr |= ((u64)(ioread32be(&ccf->err_regs->cecaddrh) & 119 CECADDRH_ADDRH)) << 32; 120 121 if (!__ratelimit(&ratelimit)) 122 goto out; 123 124 switch (ccf->info->version) { 125 case CCF1: 126 if (cecar & CECAR_VAL) { 127 if (cecar & CECAR_UVT) 128 uvt = true; 129 130 src_id = (cecar & CECAR_SRCID_MASK_CCF1) >> 131 CECAR_SRCID_SHIFT_CCF1; 132 cap_valid = true; 133 } 134 135 break; 136 case CCF2: 137 if (errdet & ERRDET_CAP) { 138 src_id = (cecar & CECAR_SRCID_MASK_CCF2) >> 139 CECAR_SRCID_SHIFT_CCF2; 140 cap_valid = true; 141 } 142 143 break; 144 } 145 146 dev_crit(ccf->dev, "errdet 0x%08x cecar 0x%08x cecar2 0x%08x\n", 147 errdet, cecar, cecar2); 148 149 if (errdet & ERRDET_LAE) { 150 if (uvt) 151 dev_crit(ccf->dev, "LAW Unavailable Target ID\n"); 152 else 153 dev_crit(ccf->dev, "Local Access Window Error\n"); 154 } 155 156 if (errdet & ERRDET_CV) 157 dev_crit(ccf->dev, "Coherency Violation\n"); 158 159 if (errdet & ERRDET_UTID) 160 dev_crit(ccf->dev, "Unavailable Target ID\n"); 161 162 if (errdet & ERRDET_MCST) 163 dev_crit(ccf->dev, "Multicast Stash\n"); 164 165 if (cap_valid) { 166 dev_crit(ccf->dev, "address 0x%09llx, src id 0x%x\n", 167 addr, src_id); 168 } 169 170 out: 171 iowrite32be(errdet, &ccf->err_regs->errdet); 172 return errdet ? IRQ_HANDLED : IRQ_NONE; 173 } 174 175 static int ccf_probe(struct platform_device *pdev) 176 { 177 struct ccf_private *ccf; 178 struct resource *r; 179 const struct of_device_id *match; 180 u32 errinten; 181 int ret, irq; 182 183 match = of_match_device(ccf_matches, &pdev->dev); 184 if (WARN_ON(!match)) 185 return -ENODEV; 186 187 ccf = devm_kzalloc(&pdev->dev, sizeof(*ccf), GFP_KERNEL); 188 if (!ccf) 189 return -ENOMEM; 190 191 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 192 if (!r) { 193 dev_err(&pdev->dev, "%s: no mem resource\n", __func__); 194 return -ENXIO; 195 } 196 197 ccf->regs = devm_ioremap_resource(&pdev->dev, r); 198 if (IS_ERR(ccf->regs)) { 199 dev_err(&pdev->dev, "%s: can't map mem resource\n", __func__); 200 return PTR_ERR(ccf->regs); 201 } 202 203 ccf->dev = &pdev->dev; 204 ccf->info = match->data; 205 ccf->err_regs = ccf->regs + ccf->info->err_reg_offs; 206 207 if (ccf->info->has_brr) { 208 u32 brr = ioread32be(ccf->regs + CCF_BRR); 209 210 if ((brr & CCF_BRR_IPID) == CCF_BRR_IPID_T1040) 211 ccf->t1040 = true; 212 } 213 214 dev_set_drvdata(&pdev->dev, ccf); 215 216 irq = platform_get_irq(pdev, 0); 217 if (!irq) { 218 dev_err(&pdev->dev, "%s: no irq\n", __func__); 219 return -ENXIO; 220 } 221 222 ret = devm_request_irq(&pdev->dev, irq, ccf_irq, 0, pdev->name, ccf); 223 if (ret) { 224 dev_err(&pdev->dev, "%s: can't request irq\n", __func__); 225 return ret; 226 } 227 228 errinten = ERRDET_LAE | ERRDET_CV; 229 if (ccf->t1040) 230 errinten |= ERRDET_UTID | ERRDET_MCST; 231 232 switch (ccf->info->version) { 233 case CCF1: 234 /* On CCF1 this register enables rather than disables. */ 235 iowrite32be(errinten, &ccf->err_regs->errdis); 236 break; 237 238 case CCF2: 239 iowrite32be(0, &ccf->err_regs->errdis); 240 iowrite32be(errinten, &ccf->err_regs->errinten); 241 break; 242 } 243 244 return 0; 245 } 246 247 static int ccf_remove(struct platform_device *pdev) 248 { 249 struct ccf_private *ccf = dev_get_drvdata(&pdev->dev); 250 251 switch (ccf->info->version) { 252 case CCF1: 253 iowrite32be(0, &ccf->err_regs->errdis); 254 break; 255 256 case CCF2: 257 /* 258 * We clear errdis on ccf1 because that's the only way to 259 * disable interrupts, but on ccf2 there's no need to disable 260 * detection. 261 */ 262 iowrite32be(0, &ccf->err_regs->errinten); 263 break; 264 } 265 266 return 0; 267 } 268 269 static struct platform_driver ccf_driver = { 270 .driver = { 271 .name = KBUILD_MODNAME, 272 .of_match_table = ccf_matches, 273 }, 274 .probe = ccf_probe, 275 .remove = ccf_remove, 276 }; 277 278 module_platform_driver(ccf_driver); 279 280 MODULE_LICENSE("GPL"); 281 MODULE_AUTHOR("Freescale Semiconductor"); 282 MODULE_DESCRIPTION("Freescale CoreNet Coherency Fabric error reporting"); 283