aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/edac
diff options
context:
space:
mode:
authorRobert Richter <robert.richter@linaro.org>2013-10-10 11:32:30 -0400
committerRobert Richter <rric@kernel.org>2013-11-04 18:01:06 -0500
commit0ec8579e16bfaed5587ae8bb02adcb089f29c397 (patch)
treee5480110f7bc94d08beade443f199142ae29d943 /drivers/edac
parent982ac2a7b757708845df4339243a91ba180a5744 (diff)
edac, highbank: Add Calxeda ECX-2000 support
Implement edac support for Calxeda ECX-2000. The ECX-2000 memory controller is similar to Highbank but has different register bases for error and interrupt registers. There is an own device tree name "calxeda,ecx-2000-ddr-ctrl" for identification and initialization of the ECX-2000 and its base addresses. Signed-off-by: Robert Richter <robert.richter@linaro.org> Acked-by: Rob Herring <rob.herring@calxeda.com> Signed-off-by: Robert Richter <rric@kernel.org>
Diffstat (limited to 'drivers/edac')
-rw-r--r--drivers/edac/highbank_mc_edac.c105
1 files changed, 70 insertions, 35 deletions
diff --git a/drivers/edac/highbank_mc_edac.c b/drivers/edac/highbank_mc_edac.c
index 7a78307588bc..79cb872eef5f 100644
--- a/drivers/edac/highbank_mc_edac.c
+++ b/drivers/edac/highbank_mc_edac.c
@@ -26,31 +26,40 @@
26#include "edac_module.h" 26#include "edac_module.h"
27 27
28/* DDR Ctrlr Error Registers */ 28/* DDR Ctrlr Error Registers */
29#define HB_DDR_ECC_OPT 0x128
30#define HB_DDR_ECC_U_ERR_ADDR 0x130
31#define HB_DDR_ECC_U_ERR_STAT 0x134
32#define HB_DDR_ECC_U_ERR_DATAL 0x138
33#define HB_DDR_ECC_U_ERR_DATAH 0x13c
34#define HB_DDR_ECC_C_ERR_ADDR 0x140
35#define HB_DDR_ECC_C_ERR_STAT 0x144
36#define HB_DDR_ECC_C_ERR_DATAL 0x148
37#define HB_DDR_ECC_C_ERR_DATAH 0x14c
38#define HB_DDR_ECC_INT_STATUS 0x180
39#define HB_DDR_ECC_INT_ACK 0x184
40#define HB_DDR_ECC_U_ERR_ID 0x424
41#define HB_DDR_ECC_C_ERR_ID 0x428
42 29
43#define HB_DDR_ECC_INT_STAT_CE 0x8 30#define HB_DDR_ECC_ERR_BASE 0x128
44#define HB_DDR_ECC_INT_STAT_DOUBLE_CE 0x10 31#define MW_DDR_ECC_ERR_BASE 0x1b4
45#define HB_DDR_ECC_INT_STAT_UE 0x20 32
46#define HB_DDR_ECC_INT_STAT_DOUBLE_UE 0x40 33#define HB_DDR_ECC_OPT 0x00
34#define HB_DDR_ECC_U_ERR_ADDR 0x08
35#define HB_DDR_ECC_U_ERR_STAT 0x0c
36#define HB_DDR_ECC_U_ERR_DATAL 0x10
37#define HB_DDR_ECC_U_ERR_DATAH 0x14
38#define HB_DDR_ECC_C_ERR_ADDR 0x18
39#define HB_DDR_ECC_C_ERR_STAT 0x1c
40#define HB_DDR_ECC_C_ERR_DATAL 0x20
41#define HB_DDR_ECC_C_ERR_DATAH 0x24
47 42
48#define HB_DDR_ECC_OPT_MODE_MASK 0x3 43#define HB_DDR_ECC_OPT_MODE_MASK 0x3
49#define HB_DDR_ECC_OPT_FWC 0x100 44#define HB_DDR_ECC_OPT_FWC 0x100
50#define HB_DDR_ECC_OPT_XOR_SHIFT 16 45#define HB_DDR_ECC_OPT_XOR_SHIFT 16
51 46
47/* DDR Ctrlr Interrupt Registers */
48
49#define HB_DDR_ECC_INT_BASE 0x180
50#define MW_DDR_ECC_INT_BASE 0x218
51
52#define HB_DDR_ECC_INT_STATUS 0x00
53#define HB_DDR_ECC_INT_ACK 0x04
54
55#define HB_DDR_ECC_INT_STAT_CE 0x8
56#define HB_DDR_ECC_INT_STAT_DOUBLE_CE 0x10
57#define HB_DDR_ECC_INT_STAT_UE 0x20
58#define HB_DDR_ECC_INT_STAT_DOUBLE_UE 0x40
59
52struct hb_mc_drvdata { 60struct hb_mc_drvdata {
53 void __iomem *mc_vbase; 61 void __iomem *mc_err_base;
62 void __iomem *mc_int_base;
54}; 63};
55 64
56static irqreturn_t highbank_mc_err_handler(int irq, void *dev_id) 65static irqreturn_t highbank_mc_err_handler(int irq, void *dev_id)
@@ -60,10 +69,10 @@ static irqreturn_t highbank_mc_err_handler(int irq, void *dev_id)
60 u32 status, err_addr; 69 u32 status, err_addr;
61 70
62 /* Read the interrupt status register */ 71 /* Read the interrupt status register */
63 status = readl(drvdata->mc_vbase + HB_DDR_ECC_INT_STATUS); 72 status = readl(drvdata->mc_int_base + HB_DDR_ECC_INT_STATUS);
64 73
65 if (status & HB_DDR_ECC_INT_STAT_UE) { 74 if (status & HB_DDR_ECC_INT_STAT_UE) {
66 err_addr = readl(drvdata->mc_vbase + HB_DDR_ECC_U_ERR_ADDR); 75 err_addr = readl(drvdata->mc_err_base + HB_DDR_ECC_U_ERR_ADDR);
67 edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 76 edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1,
68 err_addr >> PAGE_SHIFT, 77 err_addr >> PAGE_SHIFT,
69 err_addr & ~PAGE_MASK, 0, 78 err_addr & ~PAGE_MASK, 0,
@@ -71,9 +80,9 @@ static irqreturn_t highbank_mc_err_handler(int irq, void *dev_id)
71 mci->ctl_name, ""); 80 mci->ctl_name, "");
72 } 81 }
73 if (status & HB_DDR_ECC_INT_STAT_CE) { 82 if (status & HB_DDR_ECC_INT_STAT_CE) {
74 u32 syndrome = readl(drvdata->mc_vbase + HB_DDR_ECC_C_ERR_STAT); 83 u32 syndrome = readl(drvdata->mc_err_base + HB_DDR_ECC_C_ERR_STAT);
75 syndrome = (syndrome >> 8) & 0xff; 84 syndrome = (syndrome >> 8) & 0xff;
76 err_addr = readl(drvdata->mc_vbase + HB_DDR_ECC_C_ERR_ADDR); 85 err_addr = readl(drvdata->mc_err_base + HB_DDR_ECC_C_ERR_ADDR);
77 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 86 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1,
78 err_addr >> PAGE_SHIFT, 87 err_addr >> PAGE_SHIFT,
79 err_addr & ~PAGE_MASK, syndrome, 88 err_addr & ~PAGE_MASK, syndrome,
@@ -82,7 +91,7 @@ static irqreturn_t highbank_mc_err_handler(int irq, void *dev_id)
82 } 91 }
83 92
84 /* clear the error, clears the interrupt */ 93 /* clear the error, clears the interrupt */
85 writel(status, drvdata->mc_vbase + HB_DDR_ECC_INT_ACK); 94 writel(status, drvdata->mc_int_base + HB_DDR_ECC_INT_ACK);
86 return IRQ_HANDLED; 95 return IRQ_HANDLED;
87} 96}
88 97
@@ -104,10 +113,10 @@ static ssize_t highbank_mc_err_inject_write(struct file *file,
104 buf[buf_size] = 0; 113 buf[buf_size] = 0;
105 114
106 if (!kstrtou8(buf, 16, &synd)) { 115 if (!kstrtou8(buf, 16, &synd)) {
107 reg = readl(pdata->mc_vbase + HB_DDR_ECC_OPT); 116 reg = readl(pdata->mc_err_base + HB_DDR_ECC_OPT);
108 reg &= HB_DDR_ECC_OPT_MODE_MASK; 117 reg &= HB_DDR_ECC_OPT_MODE_MASK;
109 reg |= (synd << HB_DDR_ECC_OPT_XOR_SHIFT) | HB_DDR_ECC_OPT_FWC; 118 reg |= (synd << HB_DDR_ECC_OPT_XOR_SHIFT) | HB_DDR_ECC_OPT_FWC;
110 writel(reg, pdata->mc_vbase + HB_DDR_ECC_OPT); 119 writel(reg, pdata->mc_err_base + HB_DDR_ECC_OPT);
111 } 120 }
112 121
113 return count; 122 return count;
@@ -131,17 +140,46 @@ static void highbank_mc_create_debugfs_nodes(struct mem_ctl_info *mci)
131{} 140{}
132#endif 141#endif
133 142
143struct hb_mc_settings {
144 int err_offset;
145 int int_offset;
146};
147
148static struct hb_mc_settings hb_settings = {
149 .err_offset = HB_DDR_ECC_ERR_BASE,
150 .int_offset = HB_DDR_ECC_INT_BASE,
151};
152
153static struct hb_mc_settings mw_settings = {
154 .err_offset = MW_DDR_ECC_ERR_BASE,
155 .int_offset = MW_DDR_ECC_INT_BASE,
156};
157
158static struct of_device_id hb_ddr_ctrl_of_match[] = {
159 { .compatible = "calxeda,hb-ddr-ctrl", .data = &hb_settings },
160 { .compatible = "calxeda,ecx-2000-ddr-ctrl", .data = &mw_settings },
161 {},
162};
163MODULE_DEVICE_TABLE(of, hb_ddr_ctrl_of_match);
164
134static int highbank_mc_probe(struct platform_device *pdev) 165static int highbank_mc_probe(struct platform_device *pdev)
135{ 166{
167 const struct of_device_id *id;
168 const struct hb_mc_settings *settings;
136 struct edac_mc_layer layers[2]; 169 struct edac_mc_layer layers[2];
137 struct mem_ctl_info *mci; 170 struct mem_ctl_info *mci;
138 struct hb_mc_drvdata *drvdata; 171 struct hb_mc_drvdata *drvdata;
139 struct dimm_info *dimm; 172 struct dimm_info *dimm;
140 struct resource *r; 173 struct resource *r;
174 void __iomem *base;
141 u32 control; 175 u32 control;
142 int irq; 176 int irq;
143 int res = 0; 177 int res = 0;
144 178
179 id = of_match_device(hb_ddr_ctrl_of_match, &pdev->dev);
180 if (!id)
181 return -ENODEV;
182
145 layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 183 layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
146 layers[0].size = 1; 184 layers[0].size = 1;
147 layers[0].is_virt_csrow = true; 185 layers[0].is_virt_csrow = true;
@@ -174,15 +212,18 @@ static int highbank_mc_probe(struct platform_device *pdev)
174 goto err; 212 goto err;
175 } 213 }
176 214
177 drvdata->mc_vbase = devm_ioremap(&pdev->dev, 215 base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
178 r->start, resource_size(r)); 216 if (!base) {
179 if (!drvdata->mc_vbase) {
180 dev_err(&pdev->dev, "Unable to map regs\n"); 217 dev_err(&pdev->dev, "Unable to map regs\n");
181 res = -ENOMEM; 218 res = -ENOMEM;
182 goto err; 219 goto err;
183 } 220 }
184 221
185 control = readl(drvdata->mc_vbase + HB_DDR_ECC_OPT) & 0x3; 222 settings = id->data;
223 drvdata->mc_err_base = base + settings->err_offset;
224 drvdata->mc_int_base = base + settings->int_offset;
225
226 control = readl(drvdata->mc_err_base + HB_DDR_ECC_OPT) & 0x3;
186 if (!control || (control == 0x2)) { 227 if (!control || (control == 0x2)) {
187 dev_err(&pdev->dev, "No ECC present, or ECC disabled\n"); 228 dev_err(&pdev->dev, "No ECC present, or ECC disabled\n");
188 res = -ENODEV; 229 res = -ENODEV;
@@ -238,12 +279,6 @@ static int highbank_mc_remove(struct platform_device *pdev)
238 return 0; 279 return 0;
239} 280}
240 281
241static const struct of_device_id hb_ddr_ctrl_of_match[] = {
242 { .compatible = "calxeda,hb-ddr-ctrl", },
243 {},
244};
245MODULE_DEVICE_TABLE(of, hb_ddr_ctrl_of_match);
246
247static struct platform_driver highbank_mc_edac_driver = { 282static struct platform_driver highbank_mc_edac_driver = {
248 .probe = highbank_mc_probe, 283 .probe = highbank_mc_probe,
249 .remove = highbank_mc_remove, 284 .remove = highbank_mc_remove,