Skip to content

Commit 3bfc6e3

Browse files
Thor ThayerDinh Nguyen
authored andcommitted
FogBugz #173183: L2 EDAC addition for Altera SOCFPGA.
Add L2 ECC to the Error Detection And Correction module for tracking ECC errors in Altera's SOCFPGA Level 2 cache. This patch makes the assumption that the L2 cache has been cleaned and ECC turned on before Linux is started (i.e. in the preloader). V2 Changes: - Implement dual interrupts as suggested in socfpga.dtsi. - Put include files in alphabetical order. - Abstract the module checks for future additions. - Correct the compatible string. - Remove dependency check of Parity. V3 Changes: - Fix Copyright in header. - Cleanup return codes. - Add panic to Double Bit Error. - Implement a cleaner way of getting ECC function pointers. Signed-off-by: Thor Thayer <[email protected]>
1 parent 1a9dda1 commit 3bfc6e3

File tree

5 files changed

+268
-1
lines changed

5 files changed

+268
-1
lines changed
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
Altera SoCFPGA L2 cache Error Detection and Correction [EDAC]
2+
3+
Required Properties:
4+
- compatible : Should be "altr,l2-edac"
5+
- reg : Address and size for ECC error interrupt clear registers.
6+
- interrupts : Should be single bit error interrupt, then double bit error
7+
interrupt. Note the rising edge type.
8+
9+
Example:
10+
11+
l2edac@xffd08140 {
12+
compatible = "altr,l2-edac";
13+
reg = <0xffd08140 0x4>;
14+
interrupts = <0 36 1>, <0 37 1>;
15+
};

arch/arm/boot/dts/socfpga.dtsi

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@
585585
};
586586

587587
L2: l2-cache@fffef000 {
588-
compatible = "arm,pl310-cache";
588+
compatible = "arm,pl310-cache", "syscon";
589589
reg = <0xfffef000 0x1000>;
590590
interrupts = <0 38 0x04>;
591591
cache-unified;
@@ -684,6 +684,12 @@
684684
interrupts = <0 39 4>;
685685
};
686686

687+
l2edac@xffd08140 {
688+
compatible = "altr,l2-edac";
689+
reg = <0xffd08140 0x4>;
690+
interrupts = <0 36 1>, <0 37 1>;
691+
};
692+
687693
l3regs@0xff800000 {
688694
compatible = "altr,l3regs", "syscon";
689695
reg = <0xff800000 0x1000>;

drivers/edac/Kconfig

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,4 +377,14 @@ config EDAC_ALTERA_MC
377377
preloader must initialize the SDRAM before loading
378378
the kernel.
379379

380+
config EDAC_ALTERA_ECC_MGR
381+
bool "Altera ECC Manager for EDAC"
382+
depends on EDAC_MM_EDAC && ARCH_SOCFPGA
383+
help
384+
Support for error detection and correction on the
385+
Altera memories. Ensure these requirements are met.
386+
-L2 Note that the preloader must initialize
387+
the L2 memory & enable the L2 ECC before
388+
loading the kernel.
389+
380390
endif # EDAC

drivers/edac/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,4 @@ obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o
6666
obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o
6767

6868
obj-$(CONFIG_EDAC_ALTERA_MC) += altera_mc_edac.o
69+
obj-$(CONFIG_EDAC_ALTERA_ECC_MGR) += altera_ecc_mgr_edac.o

drivers/edac/altera_ecc_mgr_edac.c

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
/*
2+
* Copyright (C) 2013 Altera Corporation
3+
* Copyright 2011-2012 Calxeda, Inc.
4+
*
5+
* This program is free software; you can redistribute it and/or modify it
6+
* under the terms and conditions of the GNU General Public License,
7+
* version 2, as published by the Free Software Foundation.
8+
*
9+
* This program is distributed in the hope it will be useful, but WITHOUT
10+
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11+
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12+
* more details.
13+
*
14+
* Adapted from the highbank_l2_edac driver
15+
*
16+
* You should have received a copy of the GNU General Public License along with
17+
* this program. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
#include <asm/cacheflush.h>
20+
#include <asm/system.h>
21+
22+
#include "edac_core.h"
23+
#include "edac_module.h"
24+
25+
#include <linux/ctype.h>
26+
#include <linux/edac.h>
27+
#include <linux/interrupt.h>
28+
#include <linux/kernel.h>
29+
#include <linux/mfd/syscon.h>
30+
#include <linux/of_platform.h>
31+
#include <linux/platform_device.h>
32+
#include <linux/regmap.h>
33+
#include <linux/slab.h>
34+
#include <linux/types.h>
35+
36+
/* MPU L2 Register Defines */
37+
#define ALTR_MPUL2_CONTROL_OFFSET 0x100
38+
#define ALTR_MPUL2_CTL_CACHE_EN_MASK 0x00000001
39+
40+
/* L2 ECC Management Group Defines */
41+
#define ALTR_MAN_GRP_L2_ECC_OFFSET 0x00
42+
#define ALTR_L2_ECC_EN_MASK 0x00000001
43+
44+
struct ecc_mgr_of_data {
45+
int (*setup)(struct platform_device *pdev, void __iomem *base);
46+
};
47+
48+
struct altr_ecc_mgr_dev {
49+
void __iomem *base;
50+
int sb_irq;
51+
int db_irq;
52+
const struct ecc_mgr_of_data *data;
53+
char *edac_dev_name;
54+
};
55+
56+
static irqreturn_t altr_ecc_mgr_handler(int irq, void *dev_id)
57+
{
58+
struct edac_device_ctl_info *dci = dev_id;
59+
struct altr_ecc_mgr_dev *drvdata = dci->pvt_info;
60+
61+
if (irq == drvdata->sb_irq)
62+
edac_device_handle_ce(dci, 0, 0, drvdata->edac_dev_name);
63+
if (irq == drvdata->db_irq) {
64+
edac_device_handle_ue(dci, 0, 0, drvdata->edac_dev_name);
65+
panic("\nEDAC:ECC_MGR[Uncorrectable errors]\n");
66+
}
67+
68+
return IRQ_HANDLED;
69+
}
70+
71+
/*
72+
* altr_l2_dependencies()
73+
* Test for L2 cache ECC dependencies upon entry because
74+
* the preloader/UBoot should have initialized the L2
75+
* memory and enabled the ECC.
76+
* Can't turn on ECC here because accessing un-initialized
77+
* memory will cause CE/UE errors possibly causing an ABORT.
78+
* Bail if ECC is not on.
79+
* Test For 1) L2 ECC is enabled and 2) L2 Cache is enabled.
80+
*/
81+
static int altr_l2_dependencies(struct platform_device *pdev,
82+
void __iomem *base)
83+
{
84+
u32 control;
85+
struct regmap *l2_vbase;
86+
87+
control = readl(base) & ALTR_L2_ECC_EN_MASK;
88+
if (!control) {
89+
dev_err(&pdev->dev, "L2: No ECC present, or ECC disabled\n");
90+
return -ENODEV;
91+
}
92+
93+
l2_vbase = syscon_regmap_lookup_by_compatible("arm,pl310-cache");
94+
if (IS_ERR(l2_vbase)) {
95+
dev_err(&pdev->dev,
96+
"L2 ECC:regmap for arm,pl310-cache lookup failed.\n");
97+
return -ENODEV;
98+
}
99+
100+
regmap_read(l2_vbase, ALTR_MPUL2_CONTROL_OFFSET, &control);
101+
if (!(control & ALTR_MPUL2_CTL_CACHE_EN_MASK)) {
102+
dev_err(&pdev->dev, "L2: Cache disabled\n");
103+
return -ENODEV;
104+
}
105+
106+
return 0;
107+
}
108+
109+
static const struct ecc_mgr_of_data l2ecc_data = {
110+
.setup = altr_l2_dependencies,
111+
};
112+
113+
static const struct of_device_id altr_ecc_mgr_of_match[] = {
114+
{ .compatible = "altr,l2-edac", .data = (void *)&l2ecc_data },
115+
{},
116+
};
117+
118+
/*
119+
* altr_ecc_mgr_probe()
120+
* This is a generic EDAC device driver that will support
121+
* various Altera memory devices such as the L2 cache ECC and
122+
* OCRAM ECC as well as the memories for other peripherals.
123+
* Module specific initialization is done by passing the
124+
* function index in the device tree.
125+
*/
126+
static int altr_ecc_mgr_probe(struct platform_device *pdev)
127+
{
128+
struct edac_device_ctl_info *dci;
129+
struct altr_ecc_mgr_dev *drvdata;
130+
struct resource *r;
131+
int res = 0;
132+
struct device_node *np = pdev->dev.of_node;
133+
char *ecc_name = (char *)np->name;
134+
135+
dci = edac_device_alloc_ctl_info(sizeof(*drvdata), "ecc",
136+
1, ecc_name, 1, 0, NULL, 0, 0);
137+
138+
if (!dci)
139+
return -ENOMEM;
140+
141+
drvdata = dci->pvt_info;
142+
dci->dev = &pdev->dev;
143+
platform_set_drvdata(pdev, dci);
144+
drvdata->edac_dev_name = ecc_name;
145+
146+
if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL))
147+
return -ENOMEM;
148+
149+
r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
150+
if (!r) {
151+
dev_err(&pdev->dev, "%s:Unable to get mem resource\n",
152+
ecc_name);
153+
res = -ENODEV;
154+
goto err;
155+
}
156+
157+
if (!devm_request_mem_region(&pdev->dev, r->start, resource_size(r),
158+
dev_name(&pdev->dev))) {
159+
dev_err(&pdev->dev, "%s:Error requesting mem region\n",
160+
ecc_name);
161+
res = -EBUSY;
162+
goto err;
163+
}
164+
165+
drvdata->base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
166+
if (!drvdata->base) {
167+
dev_err(&pdev->dev, "%s:Unable to map regs\n", ecc_name);
168+
res = -ENOMEM;
169+
goto err;
170+
}
171+
172+
/* Check specific dependencies for the module */
173+
drvdata->data = of_match_node(altr_ecc_mgr_of_match, np)->data;
174+
if (drvdata->data->setup) {
175+
res = drvdata->data->setup(pdev, drvdata->base);
176+
if (res < 0)
177+
goto err;
178+
}
179+
180+
drvdata->sb_irq = platform_get_irq(pdev, 0);
181+
res = devm_request_irq(&pdev->dev, drvdata->sb_irq,
182+
altr_ecc_mgr_handler,
183+
0, dev_name(&pdev->dev), dci);
184+
if (res < 0)
185+
goto err;
186+
187+
drvdata->db_irq = platform_get_irq(pdev, 1);
188+
res = devm_request_irq(&pdev->dev, drvdata->db_irq,
189+
altr_ecc_mgr_handler,
190+
0, dev_name(&pdev->dev), dci);
191+
if (res < 0)
192+
goto err;
193+
194+
dci->mod_name = "ECC_MGR";
195+
dci->dev_name = drvdata->edac_dev_name;
196+
197+
if (edac_device_add_device(dci))
198+
goto err;
199+
200+
devres_close_group(&pdev->dev, NULL);
201+
202+
return 0;
203+
err:
204+
devres_release_group(&pdev->dev, NULL);
205+
edac_device_free_ctl_info(dci);
206+
207+
return res;
208+
}
209+
210+
static int altr_ecc_mgr_remove(struct platform_device *pdev)
211+
{
212+
struct edac_device_ctl_info *dci = platform_get_drvdata(pdev);
213+
214+
edac_device_del_device(&pdev->dev);
215+
edac_device_free_ctl_info(dci);
216+
217+
return 0;
218+
}
219+
220+
static struct platform_driver altr_ecc_mgr_driver = {
221+
.probe = altr_ecc_mgr_probe,
222+
.remove = altr_ecc_mgr_remove,
223+
.driver = {
224+
.name = "altr_ecc_mgr",
225+
.of_match_table = of_match_ptr(altr_ecc_mgr_of_match),
226+
},
227+
};
228+
229+
MODULE_DEVICE_TABLE(of, altr_ecc_mgr_of_match);
230+
231+
module_platform_driver(altr_ecc_mgr_driver);
232+
233+
MODULE_LICENSE("GPL v2");
234+
MODULE_AUTHOR("Altera Corporation");
235+
MODULE_DESCRIPTION("EDAC Driver for Altera SoC L2 Cache");

0 commit comments

Comments
 (0)