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