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 }; 31 32 static const struct ccf_info ccf1_info = { 33 .version = CCF1, 34 .err_reg_offs = 0xa00, 35 }; 36 37 static const struct ccf_info ccf2_info = { 38 .version = CCF2, 39 .err_reg_offs = 0xe40, 40 }; 41 42 static const struct of_device_id ccf_matches[] = { 43 { 44 .compatible = "fsl,corenet1-cf", 45 .data = &ccf1_info, 46 }, 47 { 48 .compatible = "fsl,corenet2-cf", 49 .data = &ccf2_info, 50 }, 51 {} 52 }; 53 54 struct ccf_err_regs { 55 u32 errdet; /* 0x00 Error Detect Register */ 56 /* 0x04 Error Enable (ccf1)/Disable (ccf2) Register */ 57 u32 errdis; 58 /* 0x08 Error Interrupt Enable Register (ccf2 only) */ 59 u32 errinten; 60 u32 cecar; /* 0x0c Error Capture Attribute Register */ 61 u32 cecaddrh; /* 0x10 Error Capture Address High */ 62 u32 cecaddrl; /* 0x14 Error Capture Address Low */ 63 u32 cecar2; /* 0x18 Error Capture Attribute Register 2 */ 64 }; 65 66 /* LAE/CV also valid for errdis and errinten */ 67 #define ERRDET_LAE (1 << 0) /* Local Access Error */ 68 #define ERRDET_CV (1 << 1) /* Coherency Violation */ 69 #define ERRDET_CTYPE_SHIFT 26 /* Capture Type (ccf2 only) */ 70 #define ERRDET_CTYPE_MASK (0x1f << ERRDET_CTYPE_SHIFT) 71 #define ERRDET_CAP (1 << 31) /* Capture Valid (ccf2 only) */ 72 73 #define CECAR_VAL (1 << 0) /* Valid (ccf1 only) */ 74 #define CECAR_UVT (1 << 15) /* Unavailable target ID (ccf1) */ 75 #define CECAR_SRCID_SHIFT_CCF1 24 76 #define CECAR_SRCID_MASK_CCF1 (0xff << CECAR_SRCID_SHIFT_CCF1) 77 #define CECAR_SRCID_SHIFT_CCF2 18 78 #define CECAR_SRCID_MASK_CCF2 (0xff << CECAR_SRCID_SHIFT_CCF2) 79 80 #define CECADDRH_ADDRH 0xff 81 82 struct ccf_private { 83 const struct ccf_info *info; 84 struct device *dev; 85 void __iomem *regs; 86 struct ccf_err_regs __iomem *err_regs; 87 }; 88 89 static irqreturn_t ccf_irq(int irq, void *dev_id) 90 { 91 struct ccf_private *ccf = dev_id; 92 static DEFINE_RATELIMIT_STATE(ratelimit, DEFAULT_RATELIMIT_INTERVAL, 93 DEFAULT_RATELIMIT_BURST); 94 u32 errdet, cecar, cecar2; 95 u64 addr; 96 u32 src_id; 97 bool uvt = false; 98 bool cap_valid = false; 99 100 errdet = ioread32be(&ccf->err_regs->errdet); 101 cecar = ioread32be(&ccf->err_regs->cecar); 102 cecar2 = ioread32be(&ccf->err_regs->cecar2); 103 addr = ioread32be(&ccf->err_regs->cecaddrl); 104 addr |= ((u64)(ioread32be(&ccf->err_regs->cecaddrh) & 105 CECADDRH_ADDRH)) << 32; 106 107 if (!__ratelimit(&ratelimit)) 108 goto out; 109 110 switch (ccf->info->version) { 111 case CCF1: 112 if (cecar & CECAR_VAL) { 113 if (cecar & CECAR_UVT) 114 uvt = true; 115 116 src_id = (cecar & CECAR_SRCID_MASK_CCF1) >> 117 CECAR_SRCID_SHIFT_CCF1; 118 cap_valid = true; 119 } 120 121 break; 122 case CCF2: 123 if (errdet & ERRDET_CAP) { 124 src_id = (cecar & CECAR_SRCID_MASK_CCF2) >> 125 CECAR_SRCID_SHIFT_CCF2; 126 cap_valid = true; 127 } 128 129 break; 130 } 131 132 dev_crit(ccf->dev, "errdet 0x%08x cecar 0x%08x cecar2 0x%08x\n", 133 errdet, cecar, cecar2); 134 135 if (errdet & ERRDET_LAE) { 136 if (uvt) 137 dev_crit(ccf->dev, "LAW Unavailable Target ID\n"); 138 else 139 dev_crit(ccf->dev, "Local Access Window Error\n"); 140 } 141 142 if (errdet & ERRDET_CV) 143 dev_crit(ccf->dev, "Coherency Violation\n"); 144 145 if (cap_valid) { 146 dev_crit(ccf->dev, "address 0x%09llx, src id 0x%x\n", 147 addr, src_id); 148 } 149 150 out: 151 iowrite32be(errdet, &ccf->err_regs->errdet); 152 return errdet ? IRQ_HANDLED : IRQ_NONE; 153 } 154 155 static int ccf_probe(struct platform_device *pdev) 156 { 157 struct ccf_private *ccf; 158 struct resource *r; 159 const struct of_device_id *match; 160 int ret, irq; 161 162 match = of_match_device(ccf_matches, &pdev->dev); 163 if (WARN_ON(!match)) 164 return -ENODEV; 165 166 ccf = devm_kzalloc(&pdev->dev, sizeof(*ccf), GFP_KERNEL); 167 if (!ccf) 168 return -ENOMEM; 169 170 r = platform_get_resource(pdev, IORESOURCE_MEM, 0); 171 if (!r) { 172 dev_err(&pdev->dev, "%s: no mem resource\n", __func__); 173 return -ENXIO; 174 } 175 176 ccf->regs = devm_ioremap_resource(&pdev->dev, r); 177 if (IS_ERR(ccf->regs)) { 178 dev_err(&pdev->dev, "%s: can't map mem resource\n", __func__); 179 return PTR_ERR(ccf->regs); 180 } 181 182 ccf->dev = &pdev->dev; 183 ccf->info = match->data; 184 ccf->err_regs = ccf->regs + ccf->info->err_reg_offs; 185 186 dev_set_drvdata(&pdev->dev, ccf); 187 188 irq = platform_get_irq(pdev, 0); 189 if (!irq) { 190 dev_err(&pdev->dev, "%s: no irq\n", __func__); 191 return -ENXIO; 192 } 193 194 ret = devm_request_irq(&pdev->dev, irq, ccf_irq, 0, pdev->name, ccf); 195 if (ret) { 196 dev_err(&pdev->dev, "%s: can't request irq\n", __func__); 197 return ret; 198 } 199 200 switch (ccf->info->version) { 201 case CCF1: 202 /* On CCF1 this register enables rather than disables. */ 203 iowrite32be(ERRDET_LAE | ERRDET_CV, &ccf->err_regs->errdis); 204 break; 205 206 case CCF2: 207 iowrite32be(0, &ccf->err_regs->errdis); 208 iowrite32be(ERRDET_LAE | ERRDET_CV, &ccf->err_regs->errinten); 209 break; 210 } 211 212 return 0; 213 } 214 215 static int ccf_remove(struct platform_device *pdev) 216 { 217 struct ccf_private *ccf = dev_get_drvdata(&pdev->dev); 218 219 switch (ccf->info->version) { 220 case CCF1: 221 iowrite32be(0, &ccf->err_regs->errdis); 222 break; 223 224 case CCF2: 225 /* 226 * We clear errdis on ccf1 because that's the only way to 227 * disable interrupts, but on ccf2 there's no need to disable 228 * detection. 229 */ 230 iowrite32be(0, &ccf->err_regs->errinten); 231 break; 232 } 233 234 return 0; 235 } 236 237 static struct platform_driver ccf_driver = { 238 .driver = { 239 .name = KBUILD_MODNAME, 240 .owner = THIS_MODULE, 241 .of_match_table = ccf_matches, 242 }, 243 .probe = ccf_probe, 244 .remove = ccf_remove, 245 }; 246 247 module_platform_driver(ccf_driver); 248 249 MODULE_LICENSE("GPL"); 250 MODULE_AUTHOR("Freescale Semiconductor"); 251 MODULE_DESCRIPTION("Freescale CoreNet Coherency Fabric error reporting"); 252