summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Luebbe <jlu@pengutronix.de>2019-07-12 00:46:57 -0400
committerRussell King <rmk+kernel@armlinux.org.uk>2019-08-29 02:58:01 -0400
commit7f6998a41257a8930ee5b6866ba56a25230841ed (patch)
tree53c63fdc05cc93ca72e940986f3b8e766c1b379a
parent0ecace04a3b8444f3ffa6f18083e98198d9b9ec6 (diff)
ARM: 8888/1: EDAC: Add driver for the Marvell Armada XP SDRAM and L2 cache ECC
Add support for the ECC functionality as found in the DDR RAM and L2 cache controllers on the MV78230/MV78x60 SoCs. This driver has been tested on the MV78460 (on a custom board with a DDR3 ECC DIMM). [cp use SPDX license] Signed-off-by: Jan Luebbe <jlu@pengutronix.de> Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz> Reviewed-by: Borislav Petkov <bp@suse.de> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
-rw-r--r--MAINTAINERS6
-rw-r--r--drivers/edac/Kconfig7
-rw-r--r--drivers/edac/Makefile1
-rw-r--r--drivers/edac/armada_xp_edac.c630
4 files changed, 644 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 43604d6ab96c..4ffc3b1fd9d1 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5755,6 +5755,12 @@ L: linux-edac@vger.kernel.org
5755S: Maintained 5755S: Maintained
5756F: drivers/edac/amd64_edac* 5756F: drivers/edac/amd64_edac*
5757 5757
5758EDAC-ARMADA
5759M: Jan Luebbe <jlu@pengutronix.de>
5760L: linux-edac@vger.kernel.org
5761S: Maintained
5762F: drivers/edac/armada_xp_*
5763
5758EDAC-AST2500 5764EDAC-AST2500
5759M: Stefan Schaeckeler <sschaeck@cisco.com> 5765M: Stefan Schaeckeler <sschaeck@cisco.com>
5760S: Supported 5766S: Supported
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 200c04ce5b0e..39c58b37b889 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -466,6 +466,13 @@ config EDAC_SIFIVE
466 help 466 help
467 Support for error detection and correction on the SiFive SoCs. 467 Support for error detection and correction on the SiFive SoCs.
468 468
469config EDAC_ARMADA_XP
470 bool "Marvell Armada XP DDR and L2 Cache ECC"
471 depends on MACH_MVEBU_V7
472 help
473 Support for error correction and detection on the Marvell Aramada XP
474 DDR RAM and L2 cache controllers.
475
469config EDAC_SYNOPSYS 476config EDAC_SYNOPSYS
470 tristate "Synopsys DDR Memory Controller" 477 tristate "Synopsys DDR Memory Controller"
471 depends on ARCH_ZYNQ || ARCH_ZYNQMP 478 depends on ARCH_ZYNQ || ARCH_ZYNQMP
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index 165ca65e1a3a..7b6348339a3b 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -80,6 +80,7 @@ obj-$(CONFIG_EDAC_THUNDERX) += thunderx_edac.o
80 80
81obj-$(CONFIG_EDAC_ALTERA) += altera_edac.o 81obj-$(CONFIG_EDAC_ALTERA) += altera_edac.o
82obj-$(CONFIG_EDAC_SIFIVE) += sifive_edac.o 82obj-$(CONFIG_EDAC_SIFIVE) += sifive_edac.o
83obj-$(CONFIG_EDAC_ARMADA_XP) += armada_xp_edac.o
83obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o 84obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o
84obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o 85obj-$(CONFIG_EDAC_XGENE) += xgene_edac.o
85obj-$(CONFIG_EDAC_TI) += ti_edac.o 86obj-$(CONFIG_EDAC_TI) += ti_edac.o
diff --git a/drivers/edac/armada_xp_edac.c b/drivers/edac/armada_xp_edac.c
new file mode 100644
index 000000000000..3759a4fbbdee
--- /dev/null
+++ b/drivers/edac/armada_xp_edac.c
@@ -0,0 +1,630 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2017 Pengutronix, Jan Luebbe <kernel@pengutronix.de>
4 */
5
6#include <linux/kernel.h>
7#include <linux/edac.h>
8#include <linux/of_platform.h>
9
10#include <asm/hardware/cache-l2x0.h>
11#include <asm/hardware/cache-aurora-l2.h>
12
13#include "edac_mc.h"
14#include "edac_device.h"
15#include "edac_module.h"
16
17/************************ EDAC MC (DDR RAM) ********************************/
18
19#define SDRAM_NUM_CS 4
20
21#define SDRAM_CONFIG_REG 0x0
22#define SDRAM_CONFIG_ECC_MASK BIT(18)
23#define SDRAM_CONFIG_REGISTERED_MASK BIT(17)
24#define SDRAM_CONFIG_BUS_WIDTH_MASK BIT(15)
25
26#define SDRAM_ADDR_CTRL_REG 0x10
27#define SDRAM_ADDR_CTRL_SIZE_HIGH_OFFSET(cs) (20+cs)
28#define SDRAM_ADDR_CTRL_SIZE_HIGH_MASK(cs) (0x1 << SDRAM_ADDR_CTRL_SIZE_HIGH_OFFSET(cs))
29#define SDRAM_ADDR_CTRL_ADDR_SEL_MASK(cs) BIT(16+cs)
30#define SDRAM_ADDR_CTRL_SIZE_LOW_OFFSET(cs) (cs*4+2)
31#define SDRAM_ADDR_CTRL_SIZE_LOW_MASK(cs) (0x3 << SDRAM_ADDR_CTRL_SIZE_LOW_OFFSET(cs))
32#define SDRAM_ADDR_CTRL_STRUCT_OFFSET(cs) (cs*4)
33#define SDRAM_ADDR_CTRL_STRUCT_MASK(cs) (0x3 << SDRAM_ADDR_CTRL_STRUCT_OFFSET(cs))
34
35#define SDRAM_ERR_DATA_H_REG 0x40
36#define SDRAM_ERR_DATA_L_REG 0x44
37
38#define SDRAM_ERR_RECV_ECC_REG 0x48
39#define SDRAM_ERR_RECV_ECC_VALUE_MASK 0xff
40
41#define SDRAM_ERR_CALC_ECC_REG 0x4c
42#define SDRAM_ERR_CALC_ECC_ROW_OFFSET 8
43#define SDRAM_ERR_CALC_ECC_ROW_MASK (0xffff << SDRAM_ERR_CALC_ECC_ROW_OFFSET)
44#define SDRAM_ERR_CALC_ECC_VALUE_MASK 0xff
45
46#define SDRAM_ERR_ADDR_REG 0x50
47#define SDRAM_ERR_ADDR_BANK_OFFSET 23
48#define SDRAM_ERR_ADDR_BANK_MASK (0x7 << SDRAM_ERR_ADDR_BANK_OFFSET)
49#define SDRAM_ERR_ADDR_COL_OFFSET 8
50#define SDRAM_ERR_ADDR_COL_MASK (0x7fff << SDRAM_ERR_ADDR_COL_OFFSET)
51#define SDRAM_ERR_ADDR_CS_OFFSET 1
52#define SDRAM_ERR_ADDR_CS_MASK (0x3 << SDRAM_ERR_ADDR_CS_OFFSET)
53#define SDRAM_ERR_ADDR_TYPE_MASK BIT(0)
54
55#define SDRAM_ERR_CTRL_REG 0x54
56#define SDRAM_ERR_CTRL_THR_OFFSET 16
57#define SDRAM_ERR_CTRL_THR_MASK (0xff << SDRAM_ERR_CTRL_THR_OFFSET)
58#define SDRAM_ERR_CTRL_PROP_MASK BIT(9)
59
60#define SDRAM_ERR_SBE_COUNT_REG 0x58
61#define SDRAM_ERR_DBE_COUNT_REG 0x5c
62
63#define SDRAM_ERR_CAUSE_ERR_REG 0xd0
64#define SDRAM_ERR_CAUSE_MSG_REG 0xd8
65#define SDRAM_ERR_CAUSE_DBE_MASK BIT(1)
66#define SDRAM_ERR_CAUSE_SBE_MASK BIT(0)
67
68#define SDRAM_RANK_CTRL_REG 0x1e0
69#define SDRAM_RANK_CTRL_EXIST_MASK(cs) BIT(cs)
70
71struct axp_mc_drvdata {
72 void __iomem *base;
73 /* width in bytes */
74 unsigned int width;
75 /* bank interleaving */
76 bool cs_addr_sel[SDRAM_NUM_CS];
77
78 char msg[128];
79};
80
81/* derived from "DRAM Address Multiplexing" in the ARAMDA XP Functional Spec */
82static uint32_t axp_mc_calc_address(struct axp_mc_drvdata *drvdata,
83 uint8_t cs, uint8_t bank, uint16_t row,
84 uint16_t col)
85{
86 if (drvdata->width == 8) {
87 /* 64 bit */
88 if (drvdata->cs_addr_sel[cs])
89 /* bank interleaved */
90 return (((row & 0xfff8) << 16) |
91 ((bank & 0x7) << 16) |
92 ((row & 0x7) << 13) |
93 ((col & 0x3ff) << 3));
94 else
95 return (((row & 0xffff << 16) |
96 ((bank & 0x7) << 13) |
97 ((col & 0x3ff)) << 3));
98 } else if (drvdata->width == 4) {
99 /* 32 bit */
100 if (drvdata->cs_addr_sel[cs])
101 /* bank interleaved */
102 return (((row & 0xfff0) << 15) |
103 ((bank & 0x7) << 16) |
104 ((row & 0xf) << 12) |
105 ((col & 0x3ff) << 2));
106 else
107 return (((row & 0xffff << 15) |
108 ((bank & 0x7) << 12) |
109 ((col & 0x3ff)) << 2));
110 } else {
111 /* 16 bit */
112 if (drvdata->cs_addr_sel[cs])
113 /* bank interleaved */
114 return (((row & 0xffe0) << 14) |
115 ((bank & 0x7) << 16) |
116 ((row & 0x1f) << 11) |
117 ((col & 0x3ff) << 1));
118 else
119 return (((row & 0xffff << 14) |
120 ((bank & 0x7) << 11) |
121 ((col & 0x3ff)) << 1));
122 }
123}
124
125static void axp_mc_check(struct mem_ctl_info *mci)
126{
127 struct axp_mc_drvdata *drvdata = mci->pvt_info;
128 uint32_t data_h, data_l, recv_ecc, calc_ecc, addr;
129 uint32_t cnt_sbe, cnt_dbe, cause_err, cause_msg;
130 uint32_t row_val, col_val, bank_val, addr_val;
131 uint8_t syndrome_val, cs_val;
132 char *msg = drvdata->msg;
133
134 data_h = readl(drvdata->base + SDRAM_ERR_DATA_H_REG);
135 data_l = readl(drvdata->base + SDRAM_ERR_DATA_L_REG);
136 recv_ecc = readl(drvdata->base + SDRAM_ERR_RECV_ECC_REG);
137 calc_ecc = readl(drvdata->base + SDRAM_ERR_CALC_ECC_REG);
138 addr = readl(drvdata->base + SDRAM_ERR_ADDR_REG);
139 cnt_sbe = readl(drvdata->base + SDRAM_ERR_SBE_COUNT_REG);
140 cnt_dbe = readl(drvdata->base + SDRAM_ERR_DBE_COUNT_REG);
141 cause_err = readl(drvdata->base + SDRAM_ERR_CAUSE_ERR_REG);
142 cause_msg = readl(drvdata->base + SDRAM_ERR_CAUSE_MSG_REG);
143
144 /* clear cause registers */
145 writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK),
146 drvdata->base + SDRAM_ERR_CAUSE_ERR_REG);
147 writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK),
148 drvdata->base + SDRAM_ERR_CAUSE_MSG_REG);
149
150 /* clear error counter registers */
151 if (cnt_sbe)
152 writel(0, drvdata->base + SDRAM_ERR_SBE_COUNT_REG);
153 if (cnt_dbe)
154 writel(0, drvdata->base + SDRAM_ERR_DBE_COUNT_REG);
155
156 if (!cnt_sbe && !cnt_dbe)
157 return;
158
159 if (!(addr & SDRAM_ERR_ADDR_TYPE_MASK)) {
160 if (cnt_sbe)
161 cnt_sbe--;
162 else
163 dev_warn(mci->pdev, "inconsistent SBE count detected");
164 } else {
165 if (cnt_dbe)
166 cnt_dbe--;
167 else
168 dev_warn(mci->pdev, "inconsistent DBE count detected");
169 }
170
171 /* report earlier errors */
172 if (cnt_sbe)
173 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
174 cnt_sbe, /* error count */
175 0, 0, 0, /* pfn, offset, syndrome */
176 -1, -1, -1, /* top, mid, low layer */
177 mci->ctl_name,
178 "details unavailable (multiple errors)");
179 if (cnt_dbe)
180 edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
181 cnt_sbe, /* error count */
182 0, 0, 0, /* pfn, offset, syndrome */
183 -1, -1, -1, /* top, mid, low layer */
184 mci->ctl_name,
185 "details unavailable (multiple errors)");
186
187 /* report details for most recent error */
188 cs_val = (addr & SDRAM_ERR_ADDR_CS_MASK) >> SDRAM_ERR_ADDR_CS_OFFSET;
189 bank_val = (addr & SDRAM_ERR_ADDR_BANK_MASK) >> SDRAM_ERR_ADDR_BANK_OFFSET;
190 row_val = (calc_ecc & SDRAM_ERR_CALC_ECC_ROW_MASK) >> SDRAM_ERR_CALC_ECC_ROW_OFFSET;
191 col_val = (addr & SDRAM_ERR_ADDR_COL_MASK) >> SDRAM_ERR_ADDR_COL_OFFSET;
192 syndrome_val = (recv_ecc ^ calc_ecc) & 0xff;
193 addr_val = axp_mc_calc_address(drvdata, cs_val, bank_val, row_val,
194 col_val);
195 msg += sprintf(msg, "row=0x%04x ", row_val); /* 11 chars */
196 msg += sprintf(msg, "bank=0x%x ", bank_val); /* 9 chars */
197 msg += sprintf(msg, "col=0x%04x ", col_val); /* 11 chars */
198 msg += sprintf(msg, "cs=%d", cs_val); /* 4 chars */
199
200 if (!(addr & SDRAM_ERR_ADDR_TYPE_MASK)) {
201 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci,
202 1, /* error count */
203 addr_val >> PAGE_SHIFT,
204 addr_val & ~PAGE_MASK,
205 syndrome_val,
206 cs_val, -1, -1, /* top, mid, low layer */
207 mci->ctl_name, drvdata->msg);
208 } else {
209 edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci,
210 1, /* error count */
211 addr_val >> PAGE_SHIFT,
212 addr_val & ~PAGE_MASK,
213 syndrome_val,
214 cs_val, -1, -1, /* top, mid, low layer */
215 mci->ctl_name, drvdata->msg);
216 }
217}
218
219static void axp_mc_read_config(struct mem_ctl_info *mci)
220{
221 struct axp_mc_drvdata *drvdata = mci->pvt_info;
222 uint32_t config, addr_ctrl, rank_ctrl;
223 unsigned int i, cs_struct, cs_size;
224 struct dimm_info *dimm;
225
226 config = readl(drvdata->base + SDRAM_CONFIG_REG);
227 if (config & SDRAM_CONFIG_BUS_WIDTH_MASK)
228 /* 64 bit */
229 drvdata->width = 8;
230 else
231 /* 32 bit */
232 drvdata->width = 4;
233
234 addr_ctrl = readl(drvdata->base + SDRAM_ADDR_CTRL_REG);
235 rank_ctrl = readl(drvdata->base + SDRAM_RANK_CTRL_REG);
236 for (i = 0; i < SDRAM_NUM_CS; i++) {
237 dimm = mci->dimms[i];
238
239 if (!(rank_ctrl & SDRAM_RANK_CTRL_EXIST_MASK(i)))
240 continue;
241
242 drvdata->cs_addr_sel[i] =
243 !!(addr_ctrl & SDRAM_ADDR_CTRL_ADDR_SEL_MASK(i));
244
245 cs_struct = (addr_ctrl & SDRAM_ADDR_CTRL_STRUCT_MASK(i)) >> SDRAM_ADDR_CTRL_STRUCT_OFFSET(i);
246 cs_size = ((addr_ctrl & SDRAM_ADDR_CTRL_SIZE_HIGH_MASK(i)) >> (SDRAM_ADDR_CTRL_SIZE_HIGH_OFFSET(i) - 2) |
247 ((addr_ctrl & SDRAM_ADDR_CTRL_SIZE_LOW_MASK(i)) >> SDRAM_ADDR_CTRL_SIZE_LOW_OFFSET(i)));
248
249 switch (cs_size) {
250 case 0: /* 2GBit */
251 dimm->nr_pages = 524288;
252 break;
253 case 1: /* 256MBit */
254 dimm->nr_pages = 65536;
255 break;
256 case 2: /* 512MBit */
257 dimm->nr_pages = 131072;
258 break;
259 case 3: /* 1GBit */
260 dimm->nr_pages = 262144;
261 break;
262 case 4: /* 4GBit */
263 dimm->nr_pages = 1048576;
264 break;
265 case 5: /* 8GBit */
266 dimm->nr_pages = 2097152;
267 break;
268 }
269 dimm->grain = 8;
270 dimm->dtype = cs_struct ? DEV_X16 : DEV_X8;
271 dimm->mtype = (config & SDRAM_CONFIG_REGISTERED_MASK) ?
272 MEM_RDDR3 : MEM_DDR3;
273 dimm->edac_mode = EDAC_SECDED;
274 }
275}
276
277static const struct of_device_id axp_mc_of_match[] = {
278 {.compatible = "marvell,armada-xp-sdram-controller",},
279 {},
280};
281MODULE_DEVICE_TABLE(of, axp_mc_of_match);
282
283static int axp_mc_probe(struct platform_device *pdev)
284{
285 struct axp_mc_drvdata *drvdata;
286 struct edac_mc_layer layers[1];
287 const struct of_device_id *id;
288 struct mem_ctl_info *mci;
289 struct resource *r;
290 void __iomem *base;
291 uint32_t config;
292
293 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
294 if (!r) {
295 dev_err(&pdev->dev, "Unable to get mem resource\n");
296 return -ENODEV;
297 }
298
299 base = devm_ioremap_resource(&pdev->dev, r);
300 if (IS_ERR(base)) {
301 dev_err(&pdev->dev, "Unable to map regs\n");
302 return PTR_ERR(base);
303 }
304
305 config = readl(base + SDRAM_CONFIG_REG);
306 if (!(config & SDRAM_CONFIG_ECC_MASK)) {
307 dev_warn(&pdev->dev, "SDRAM ECC is not enabled");
308 return -EINVAL;
309 }
310
311 layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
312 layers[0].size = SDRAM_NUM_CS;
313 layers[0].is_virt_csrow = true;
314
315 mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, sizeof(*drvdata));
316 if (!mci)
317 return -ENOMEM;
318
319 drvdata = mci->pvt_info;
320 drvdata->base = base;
321 mci->pdev = &pdev->dev;
322 platform_set_drvdata(pdev, mci);
323
324 id = of_match_device(axp_mc_of_match, &pdev->dev);
325 mci->edac_check = axp_mc_check;
326 mci->mtype_cap = MEM_FLAG_DDR3;
327 mci->edac_cap = EDAC_FLAG_SECDED;
328 mci->mod_name = pdev->dev.driver->name;
329 mci->ctl_name = id ? id->compatible : "unknown";
330 mci->dev_name = dev_name(&pdev->dev);
331 mci->scrub_mode = SCRUB_NONE;
332
333 axp_mc_read_config(mci);
334
335 /* configure SBE threshold */
336 /* it seems that SBEs are not captured otherwise */
337 writel(1 << SDRAM_ERR_CTRL_THR_OFFSET, drvdata->base + SDRAM_ERR_CTRL_REG);
338
339 /* clear cause registers */
340 writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK), drvdata->base + SDRAM_ERR_CAUSE_ERR_REG);
341 writel(~(SDRAM_ERR_CAUSE_DBE_MASK | SDRAM_ERR_CAUSE_SBE_MASK), drvdata->base + SDRAM_ERR_CAUSE_MSG_REG);
342
343 /* clear counter registers */
344 writel(0, drvdata->base + SDRAM_ERR_SBE_COUNT_REG);
345 writel(0, drvdata->base + SDRAM_ERR_DBE_COUNT_REG);
346
347 if (edac_mc_add_mc(mci)) {
348 edac_mc_free(mci);
349 return -EINVAL;
350 }
351 edac_op_state = EDAC_OPSTATE_POLL;
352
353 return 0;
354}
355
356static int axp_mc_remove(struct platform_device *pdev)
357{
358 struct mem_ctl_info *mci = platform_get_drvdata(pdev);
359
360 edac_mc_del_mc(&pdev->dev);
361 edac_mc_free(mci);
362 platform_set_drvdata(pdev, NULL);
363
364 return 0;
365}
366
367static struct platform_driver axp_mc_driver = {
368 .probe = axp_mc_probe,
369 .remove = axp_mc_remove,
370 .driver = {
371 .name = "armada_xp_mc_edac",
372 .of_match_table = of_match_ptr(axp_mc_of_match),
373 },
374};
375
376/************************ EDAC Device (L2 Cache) ***************************/
377
378struct aurora_l2_drvdata {
379 void __iomem *base;
380
381 char msg[128];
382
383 /* error injection via debugfs */
384 uint32_t inject_addr;
385 uint32_t inject_mask;
386 uint8_t inject_ctl;
387
388 struct dentry *debugfs;
389};
390
391#ifdef CONFIG_EDAC_DEBUG
392static void aurora_l2_inject(struct aurora_l2_drvdata *drvdata)
393{
394 drvdata->inject_addr &= AURORA_ERR_INJECT_CTL_ADDR_MASK;
395 drvdata->inject_ctl &= AURORA_ERR_INJECT_CTL_EN_MASK;
396 writel(0, drvdata->base + AURORA_ERR_INJECT_CTL_REG);
397 writel(drvdata->inject_mask, drvdata->base + AURORA_ERR_INJECT_MASK_REG);
398 writel(drvdata->inject_addr | drvdata->inject_ctl, drvdata->base + AURORA_ERR_INJECT_CTL_REG);
399}
400#endif
401
402static void aurora_l2_check(struct edac_device_ctl_info *dci)
403{
404 struct aurora_l2_drvdata *drvdata = dci->pvt_info;
405 uint32_t cnt, src, txn, err, attr_cap, addr_cap, way_cap;
406 unsigned int cnt_ce, cnt_ue;
407 char *msg = drvdata->msg;
408 size_t size = sizeof(drvdata->msg);
409 size_t len = 0;
410
411 cnt = readl(drvdata->base + AURORA_ERR_CNT_REG);
412 attr_cap = readl(drvdata->base + AURORA_ERR_ATTR_CAP_REG);
413 addr_cap = readl(drvdata->base + AURORA_ERR_ADDR_CAP_REG);
414 way_cap = readl(drvdata->base + AURORA_ERR_WAY_CAP_REG);
415
416 cnt_ce = (cnt & AURORA_ERR_CNT_CE_MASK) >> AURORA_ERR_CNT_CE_OFFSET;
417 cnt_ue = (cnt & AURORA_ERR_CNT_UE_MASK) >> AURORA_ERR_CNT_UE_OFFSET;
418 /* clear error counter registers */
419 if (cnt_ce || cnt_ue)
420 writel(AURORA_ERR_CNT_CLR, drvdata->base + AURORA_ERR_CNT_REG);
421
422 if (!(attr_cap & AURORA_ERR_ATTR_CAP_VALID))
423 goto clear_remaining;
424
425 src = (attr_cap & AURORA_ERR_ATTR_SRC_MSK) >> AURORA_ERR_ATTR_SRC_OFF;
426 if (src <= 3)
427 len += snprintf(msg+len, size-len, "src=CPU%d ", src);
428 else
429 len += snprintf(msg+len, size-len, "src=IO ");
430
431 txn = (attr_cap & AURORA_ERR_ATTR_TXN_MSK) >> AURORA_ERR_ATTR_TXN_OFF;
432 switch (txn) {
433 case 0:
434 len += snprintf(msg+len, size-len, "txn=Data-Read ");
435 break;
436 case 1:
437 len += snprintf(msg+len, size-len, "txn=Isn-Read ");
438 break;
439 case 2:
440 len += snprintf(msg+len, size-len, "txn=Clean-Flush ");
441 break;
442 case 3:
443 len += snprintf(msg+len, size-len, "txn=Eviction ");
444 break;
445 case 4:
446 len += snprintf(msg+len, size-len,
447 "txn=Read-Modify-Write ");
448 break;
449 }
450
451 err = (attr_cap & AURORA_ERR_ATTR_ERR_MSK) >> AURORA_ERR_ATTR_ERR_OFF;
452 switch (err) {
453 case 0:
454 len += snprintf(msg+len, size-len, "err=CorrECC ");
455 break;
456 case 1:
457 len += snprintf(msg+len, size-len, "err=UnCorrECC ");
458 break;
459 case 2:
460 len += snprintf(msg+len, size-len, "err=TagParity ");
461 break;
462 }
463
464 len += snprintf(msg+len, size-len, "addr=0x%x ", addr_cap & AURORA_ERR_ADDR_CAP_ADDR_MASK);
465 len += snprintf(msg+len, size-len, "index=0x%x ", (way_cap & AURORA_ERR_WAY_IDX_MSK) >> AURORA_ERR_WAY_IDX_OFF);
466 len += snprintf(msg+len, size-len, "way=0x%x", (way_cap & AURORA_ERR_WAY_CAP_WAY_MASK) >> AURORA_ERR_WAY_CAP_WAY_OFFSET);
467
468 /* clear error capture registers */
469 writel(AURORA_ERR_ATTR_CAP_VALID, drvdata->base + AURORA_ERR_ATTR_CAP_REG);
470 if (err) {
471 /* UnCorrECC or TagParity */
472 if (cnt_ue)
473 cnt_ue--;
474 edac_device_handle_ue(dci, 0, 0, drvdata->msg);
475 } else {
476 if (cnt_ce)
477 cnt_ce--;
478 edac_device_handle_ce(dci, 0, 0, drvdata->msg);
479 }
480
481clear_remaining:
482 /* report remaining errors */
483 while (cnt_ue--)
484 edac_device_handle_ue(dci, 0, 0, "details unavailable (multiple errors)");
485 while (cnt_ce--)
486 edac_device_handle_ue(dci, 0, 0, "details unavailable (multiple errors)");
487}
488
489static void aurora_l2_poll(struct edac_device_ctl_info *dci)
490{
491#ifdef CONFIG_EDAC_DEBUG
492 struct aurora_l2_drvdata *drvdata = dci->pvt_info;
493#endif
494
495 aurora_l2_check(dci);
496#ifdef CONFIG_EDAC_DEBUG
497 aurora_l2_inject(drvdata);
498#endif
499}
500
501static const struct of_device_id aurora_l2_of_match[] = {
502 {.compatible = "marvell,aurora-system-cache",},
503 {},
504};
505MODULE_DEVICE_TABLE(of, aurora_l2_of_match);
506
507static int aurora_l2_probe(struct platform_device *pdev)
508{
509 struct aurora_l2_drvdata *drvdata;
510 struct edac_device_ctl_info *dci;
511 const struct of_device_id *id;
512 uint32_t l2x0_aux_ctrl;
513 void __iomem *base;
514 struct resource *r;
515
516 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
517 if (!r) {
518 dev_err(&pdev->dev, "Unable to get mem resource\n");
519 return -ENODEV;
520 }
521
522 base = devm_ioremap_resource(&pdev->dev, r);
523 if (IS_ERR(base)) {
524 dev_err(&pdev->dev, "Unable to map regs\n");
525 return PTR_ERR(base);
526 }
527
528 l2x0_aux_ctrl = readl(base + L2X0_AUX_CTRL);
529 if (!(l2x0_aux_ctrl & AURORA_ACR_PARITY_EN))
530 dev_warn(&pdev->dev, "tag parity is not enabled");
531 if (!(l2x0_aux_ctrl & AURORA_ACR_ECC_EN))
532 dev_warn(&pdev->dev, "data ECC is not enabled");
533
534 dci = edac_device_alloc_ctl_info(sizeof(*drvdata),
535 "cpu", 1, "L", 1, 2, NULL, 0, 0);
536 if (!dci)
537 return -ENOMEM;
538
539 drvdata = dci->pvt_info;
540 drvdata->base = base;
541 dci->dev = &pdev->dev;
542 platform_set_drvdata(pdev, dci);
543
544 id = of_match_device(aurora_l2_of_match, &pdev->dev);
545 dci->edac_check = aurora_l2_poll;
546 dci->mod_name = pdev->dev.driver->name;
547 dci->ctl_name = id ? id->compatible : "unknown";
548 dci->dev_name = dev_name(&pdev->dev);
549
550 /* clear registers */
551 writel(AURORA_ERR_CNT_CLR, drvdata->base + AURORA_ERR_CNT_REG);
552 writel(AURORA_ERR_ATTR_CAP_VALID, drvdata->base + AURORA_ERR_ATTR_CAP_REG);
553
554 if (edac_device_add_device(dci)) {
555 edac_device_free_ctl_info(dci);
556 return -EINVAL;
557 }
558
559#ifdef CONFIG_EDAC_DEBUG
560 drvdata->debugfs = edac_debugfs_create_dir(dev_name(&pdev->dev));
561 if (drvdata->debugfs) {
562 edac_debugfs_create_x32("inject_addr", 0644,
563 drvdata->debugfs,
564 &drvdata->inject_addr);
565 edac_debugfs_create_x32("inject_mask", 0644,
566 drvdata->debugfs,
567 &drvdata->inject_mask);
568 edac_debugfs_create_x8("inject_ctl", 0644,
569 drvdata->debugfs, &drvdata->inject_ctl);
570 }
571#endif
572
573 return 0;
574}
575
576static int aurora_l2_remove(struct platform_device *pdev)
577{
578 struct edac_device_ctl_info *dci = platform_get_drvdata(pdev);
579#ifdef CONFIG_EDAC_DEBUG
580 struct aurora_l2_drvdata *drvdata = dci->pvt_info;
581
582 edac_debugfs_remove_recursive(drvdata->debugfs);
583#endif
584 edac_device_del_device(&pdev->dev);
585 edac_device_free_ctl_info(dci);
586 platform_set_drvdata(pdev, NULL);
587
588 return 0;
589}
590
591static struct platform_driver aurora_l2_driver = {
592 .probe = aurora_l2_probe,
593 .remove = aurora_l2_remove,
594 .driver = {
595 .name = "aurora_l2_edac",
596 .of_match_table = of_match_ptr(aurora_l2_of_match),
597 },
598};
599
600/************************ Driver registration ******************************/
601
602static struct platform_driver * const drivers[] = {
603 &axp_mc_driver,
604 &aurora_l2_driver,
605};
606
607static int __init armada_xp_edac_init(void)
608{
609 int res;
610
611 /* only polling is supported */
612 edac_op_state = EDAC_OPSTATE_POLL;
613
614 res = platform_register_drivers(drivers, ARRAY_SIZE(drivers));
615 if (res)
616 pr_warn("Aramda XP EDAC drivers fail to register\n");
617
618 return 0;
619}
620module_init(armada_xp_edac_init);
621
622static void __exit armada_xp_edac_exit(void)
623{
624 platform_unregister_drivers(drivers, ARRAY_SIZE(drivers));
625}
626module_exit(armada_xp_edac_exit);
627
628MODULE_LICENSE("GPL v2");
629MODULE_AUTHOR("Pengutronix");
630MODULE_DESCRIPTION("EDAC Drivers for Marvell Armada XP SDRAM and L2 Cache Controller");