blob: c9443fc136db72ef431174c2443ca9b9269bb206 [file] [log] [blame]
Scott Wood54afbec2014-07-02 18:52:11 -05001/*
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};
31
32static const struct ccf_info ccf1_info = {
33 .version = CCF1,
34 .err_reg_offs = 0xa00,
35};
36
37static const struct ccf_info ccf2_info = {
38 .version = CCF2,
39 .err_reg_offs = 0xe40,
40};
41
42static 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
54struct 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
82struct 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
89static 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
150out:
151 iowrite32be(errdet, &ccf->err_regs->errdet);
152 return errdet ? IRQ_HANDLED : IRQ_NONE;
153}
154
155static 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
215static 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
237static 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
247module_platform_driver(ccf_driver);
248
249MODULE_LICENSE("GPL");
250MODULE_AUTHOR("Freescale Semiconductor");
251MODULE_DESCRIPTION("Freescale CoreNet Coherency Fabric error reporting");