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