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