aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/edac/highbank_mc_edac.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/edac/highbank_mc_edac.c')
-rw-r--r--drivers/edac/highbank_mc_edac.c175
1 files changed, 99 insertions, 76 deletions
diff --git a/drivers/edac/highbank_mc_edac.c b/drivers/edac/highbank_mc_edac.c
index 4695dd2d71fd..f784de1dc793 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,66 +91,79 @@ 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
89#ifdef CONFIG_EDAC_DEBUG 98static void highbank_mc_err_inject(struct mem_ctl_info *mci, u8 synd)
90static ssize_t highbank_mc_err_inject_write(struct file *file,
91 const char __user *data,
92 size_t count, loff_t *ppos)
93{ 99{
94 struct mem_ctl_info *mci = file->private_data;
95 struct hb_mc_drvdata *pdata = mci->pvt_info; 100 struct hb_mc_drvdata *pdata = mci->pvt_info;
96 char buf[32];
97 size_t buf_size;
98 u32 reg; 101 u32 reg;
102
103 reg = readl(pdata->mc_err_base + HB_DDR_ECC_OPT);
104 reg &= HB_DDR_ECC_OPT_MODE_MASK;
105 reg |= (synd << HB_DDR_ECC_OPT_XOR_SHIFT) | HB_DDR_ECC_OPT_FWC;
106 writel(reg, pdata->mc_err_base + HB_DDR_ECC_OPT);
107}
108
109#define to_mci(k) container_of(k, struct mem_ctl_info, dev)
110
111static ssize_t highbank_mc_inject_ctrl(struct device *dev,
112 struct device_attribute *attr, const char *buf, size_t count)
113{
114 struct mem_ctl_info *mci = to_mci(dev);
99 u8 synd; 115 u8 synd;
100 116
101 buf_size = min(count, (sizeof(buf)-1)); 117 if (kstrtou8(buf, 16, &synd))
102 if (copy_from_user(buf, data, buf_size)) 118 return -EINVAL;
103 return -EFAULT;
104 buf[buf_size] = 0;
105 119
106 if (!kstrtou8(buf, 16, &synd)) { 120 highbank_mc_err_inject(mci, synd);
107 reg = readl(pdata->mc_vbase + HB_DDR_ECC_OPT);
108 reg &= HB_DDR_ECC_OPT_MODE_MASK;
109 reg |= (synd << HB_DDR_ECC_OPT_XOR_SHIFT) | HB_DDR_ECC_OPT_FWC;
110 writel(reg, pdata->mc_vbase + HB_DDR_ECC_OPT);
111 }
112 121
113 return count; 122 return count;
114} 123}
115 124
116static const struct file_operations highbank_mc_debug_inject_fops = { 125static DEVICE_ATTR(inject_ctrl, S_IWUSR, NULL, highbank_mc_inject_ctrl);
117 .open = simple_open, 126
118 .write = highbank_mc_err_inject_write, 127struct hb_mc_settings {
119 .llseek = generic_file_llseek, 128 int err_offset;
129 int int_offset;
120}; 130};
121 131
122static void highbank_mc_create_debugfs_nodes(struct mem_ctl_info *mci) 132static struct hb_mc_settings hb_settings = {
123{ 133 .err_offset = HB_DDR_ECC_ERR_BASE,
124 if (mci->debugfs) 134 .int_offset = HB_DDR_ECC_INT_BASE,
125 debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci, 135};
126 &highbank_mc_debug_inject_fops); 136
127; 137static struct hb_mc_settings mw_settings = {
128} 138 .err_offset = MW_DDR_ECC_ERR_BASE,
129#else 139 .int_offset = MW_DDR_ECC_INT_BASE,
130static void highbank_mc_create_debugfs_nodes(struct mem_ctl_info *mci) 140};
131{} 141
132#endif 142static struct of_device_id hb_ddr_ctrl_of_match[] = {
143 { .compatible = "calxeda,hb-ddr-ctrl", .data = &hb_settings },
144 { .compatible = "calxeda,ecx-2000-ddr-ctrl", .data = &mw_settings },
145 {},
146};
147MODULE_DEVICE_TABLE(of, hb_ddr_ctrl_of_match);
133 148
134static int highbank_mc_probe(struct platform_device *pdev) 149static int highbank_mc_probe(struct platform_device *pdev)
135{ 150{
151 const struct of_device_id *id;
152 const struct hb_mc_settings *settings;
136 struct edac_mc_layer layers[2]; 153 struct edac_mc_layer layers[2];
137 struct mem_ctl_info *mci; 154 struct mem_ctl_info *mci;
138 struct hb_mc_drvdata *drvdata; 155 struct hb_mc_drvdata *drvdata;
139 struct dimm_info *dimm; 156 struct dimm_info *dimm;
140 struct resource *r; 157 struct resource *r;
158 void __iomem *base;
141 u32 control; 159 u32 control;
142 int irq; 160 int irq;
143 int res = 0; 161 int res = 0;
144 162
163 id = of_match_device(hb_ddr_ctrl_of_match, &pdev->dev);
164 if (!id)
165 return -ENODEV;
166
145 layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; 167 layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
146 layers[0].size = 1; 168 layers[0].size = 1;
147 layers[0].is_virt_csrow = true; 169 layers[0].is_virt_csrow = true;
@@ -174,35 +196,31 @@ static int highbank_mc_probe(struct platform_device *pdev)
174 goto err; 196 goto err;
175 } 197 }
176 198
177 drvdata->mc_vbase = devm_ioremap(&pdev->dev, 199 base = devm_ioremap(&pdev->dev, r->start, resource_size(r));
178 r->start, resource_size(r)); 200 if (!base) {
179 if (!drvdata->mc_vbase) {
180 dev_err(&pdev->dev, "Unable to map regs\n"); 201 dev_err(&pdev->dev, "Unable to map regs\n");
181 res = -ENOMEM; 202 res = -ENOMEM;
182 goto err; 203 goto err;
183 } 204 }
184 205
185 control = readl(drvdata->mc_vbase + HB_DDR_ECC_OPT) & 0x3; 206 settings = id->data;
207 drvdata->mc_err_base = base + settings->err_offset;
208 drvdata->mc_int_base = base + settings->int_offset;
209
210 control = readl(drvdata->mc_err_base + HB_DDR_ECC_OPT) & 0x3;
186 if (!control || (control == 0x2)) { 211 if (!control || (control == 0x2)) {
187 dev_err(&pdev->dev, "No ECC present, or ECC disabled\n"); 212 dev_err(&pdev->dev, "No ECC present, or ECC disabled\n");
188 res = -ENODEV; 213 res = -ENODEV;
189 goto err; 214 goto err;
190 } 215 }
191 216
192 irq = platform_get_irq(pdev, 0);
193 res = devm_request_irq(&pdev->dev, irq, highbank_mc_err_handler,
194 0, dev_name(&pdev->dev), mci);
195 if (res < 0) {
196 dev_err(&pdev->dev, "Unable to request irq %d\n", irq);
197 goto err;
198 }
199
200 mci->mtype_cap = MEM_FLAG_DDR3; 217 mci->mtype_cap = MEM_FLAG_DDR3;
201 mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; 218 mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
202 mci->edac_cap = EDAC_FLAG_SECDED; 219 mci->edac_cap = EDAC_FLAG_SECDED;
203 mci->mod_name = dev_name(&pdev->dev); 220 mci->mod_name = pdev->dev.driver->name;
204 mci->mod_ver = "1"; 221 mci->mod_ver = "1";
205 mci->ctl_name = dev_name(&pdev->dev); 222 mci->ctl_name = id->compatible;
223 mci->dev_name = dev_name(&pdev->dev);
206 mci->scrub_mode = SCRUB_SW_SRC; 224 mci->scrub_mode = SCRUB_SW_SRC;
207 225
208 /* Only a single 4GB DIMM is supported */ 226 /* Only a single 4GB DIMM is supported */
@@ -217,10 +235,20 @@ static int highbank_mc_probe(struct platform_device *pdev)
217 if (res < 0) 235 if (res < 0)
218 goto err; 236 goto err;
219 237
220 highbank_mc_create_debugfs_nodes(mci); 238 irq = platform_get_irq(pdev, 0);
239 res = devm_request_irq(&pdev->dev, irq, highbank_mc_err_handler,
240 0, dev_name(&pdev->dev), mci);
241 if (res < 0) {
242 dev_err(&pdev->dev, "Unable to request irq %d\n", irq);
243 goto err2;
244 }
245
246 device_create_file(&mci->dev, &dev_attr_inject_ctrl);
221 247
222 devres_close_group(&pdev->dev, NULL); 248 devres_close_group(&pdev->dev, NULL);
223 return 0; 249 return 0;
250err2:
251 edac_mc_del_mc(&pdev->dev);
224err: 252err:
225 devres_release_group(&pdev->dev, NULL); 253 devres_release_group(&pdev->dev, NULL);
226 edac_mc_free(mci); 254 edac_mc_free(mci);
@@ -231,17 +259,12 @@ static int highbank_mc_remove(struct platform_device *pdev)
231{ 259{
232 struct mem_ctl_info *mci = platform_get_drvdata(pdev); 260 struct mem_ctl_info *mci = platform_get_drvdata(pdev);
233 261
262 device_remove_file(&mci->dev, &dev_attr_inject_ctrl);
234 edac_mc_del_mc(&pdev->dev); 263 edac_mc_del_mc(&pdev->dev);
235 edac_mc_free(mci); 264 edac_mc_free(mci);
236 return 0; 265 return 0;
237} 266}
238 267
239static const struct of_device_id hb_ddr_ctrl_of_match[] = {
240 { .compatible = "calxeda,hb-ddr-ctrl", },
241 {},
242};
243MODULE_DEVICE_TABLE(of, hb_ddr_ctrl_of_match);
244
245static struct platform_driver highbank_mc_edac_driver = { 268static struct platform_driver highbank_mc_edac_driver = {
246 .probe = highbank_mc_probe, 269 .probe = highbank_mc_probe,
247 .remove = highbank_mc_remove, 270 .remove = highbank_mc_remove,