diff options
author | Dave Jiang <djiang@mvista.com> | 2008-02-07 03:14:56 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-07 11:42:23 -0500 |
commit | 4f4aeeabc061826376c9a72b4714d062664999ea (patch) | |
tree | 888c63a646bd3fef2943531d7187ffacc7c4b6fe /drivers/edac/mv64x60_edac.c | |
parent | a9a753d53204bf0f42841f65679c7e1711833bcf (diff) |
drivers-edac: add marvell mv64x60 driver
Marvell mv64x60 SoC support for EDAC. Used on PPC and MIPS platforms.
Development and testing done on PPC Motorola prpmc2800 ATCA board.
[akpm@linux-foundation.org: make mv64x60_ctl_name static]
Signed-off-by: Dave Jiang <djiang@mvista.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk
Signed-off-by: Douglas Thompson <dougthompson@xmission.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/edac/mv64x60_edac.c')
-rw-r--r-- | drivers/edac/mv64x60_edac.c | 855 |
1 files changed, 855 insertions, 0 deletions
diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c new file mode 100644 index 00000000000..bf071f140a0 --- /dev/null +++ b/drivers/edac/mv64x60_edac.c | |||
@@ -0,0 +1,855 @@ | |||
1 | /* | ||
2 | * Marvell MV64x60 Memory Controller kernel module for PPC platforms | ||
3 | * | ||
4 | * Author: Dave Jiang <djiang@mvista.com> | ||
5 | * | ||
6 | * 2006-2007 (c) MontaVista Software, Inc. This file is licensed under | ||
7 | * the terms of the GNU General Public License version 2. This program | ||
8 | * is licensed "as is" without any warranty of any kind, whether express | ||
9 | * or implied. | ||
10 | * | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/slab.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/edac.h> | ||
19 | |||
20 | #include "edac_core.h" | ||
21 | #include "edac_module.h" | ||
22 | #include "mv64x60_edac.h" | ||
23 | |||
24 | static const char *mv64x60_ctl_name = "MV64x60"; | ||
25 | static int edac_dev_idx; | ||
26 | static int edac_pci_idx; | ||
27 | static int edac_mc_idx; | ||
28 | |||
29 | /*********************** PCI err device **********************************/ | ||
30 | #ifdef CONFIG_PCI | ||
31 | static void mv64x60_pci_check(struct edac_pci_ctl_info *pci) | ||
32 | { | ||
33 | struct mv64x60_pci_pdata *pdata = pci->pvt_info; | ||
34 | u32 cause; | ||
35 | |||
36 | cause = in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE); | ||
37 | if (!cause) | ||
38 | return; | ||
39 | |||
40 | printk(KERN_ERR "Error in PCI %d Interface\n", pdata->pci_hose); | ||
41 | printk(KERN_ERR "Cause register: 0x%08x\n", cause); | ||
42 | printk(KERN_ERR "Address Low: 0x%08x\n", | ||
43 | in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_ADDR_LO)); | ||
44 | printk(KERN_ERR "Address High: 0x%08x\n", | ||
45 | in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_ADDR_HI)); | ||
46 | printk(KERN_ERR "Attribute: 0x%08x\n", | ||
47 | in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_ATTR)); | ||
48 | printk(KERN_ERR "Command: 0x%08x\n", | ||
49 | in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CMD)); | ||
50 | out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE, ~cause); | ||
51 | |||
52 | if (cause & MV64X60_PCI_PE_MASK) | ||
53 | edac_pci_handle_pe(pci, pci->ctl_name); | ||
54 | |||
55 | if (!(cause & MV64X60_PCI_PE_MASK)) | ||
56 | edac_pci_handle_npe(pci, pci->ctl_name); | ||
57 | } | ||
58 | |||
59 | static irqreturn_t mv64x60_pci_isr(int irq, void *dev_id) | ||
60 | { | ||
61 | struct edac_pci_ctl_info *pci = dev_id; | ||
62 | struct mv64x60_pci_pdata *pdata = pci->pvt_info; | ||
63 | u32 val; | ||
64 | |||
65 | val = in_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE); | ||
66 | if (!val) | ||
67 | return IRQ_NONE; | ||
68 | |||
69 | mv64x60_pci_check(pci); | ||
70 | |||
71 | return IRQ_HANDLED; | ||
72 | } | ||
73 | |||
74 | static int __devinit mv64x60_pci_err_probe(struct platform_device *pdev) | ||
75 | { | ||
76 | struct edac_pci_ctl_info *pci; | ||
77 | struct mv64x60_pci_pdata *pdata; | ||
78 | struct resource *r; | ||
79 | int res = 0; | ||
80 | |||
81 | if (!devres_open_group(&pdev->dev, mv64x60_pci_err_probe, GFP_KERNEL)) | ||
82 | return -ENOMEM; | ||
83 | |||
84 | pci = edac_pci_alloc_ctl_info(sizeof(*pdata), "mv64x60_pci_err"); | ||
85 | if (!pci) | ||
86 | return -ENOMEM; | ||
87 | |||
88 | pdata = pci->pvt_info; | ||
89 | |||
90 | pdata->pci_hose = pdev->id; | ||
91 | pdata->name = "mpc85xx_pci_err"; | ||
92 | pdata->irq = NO_IRQ; | ||
93 | platform_set_drvdata(pdev, pci); | ||
94 | pci->dev = &pdev->dev; | ||
95 | pci->dev_name = pdev->dev.bus_id; | ||
96 | pci->mod_name = EDAC_MOD_STR; | ||
97 | pci->ctl_name = pdata->name; | ||
98 | |||
99 | if (edac_op_state == EDAC_OPSTATE_POLL) | ||
100 | pci->edac_check = mv64x60_pci_check; | ||
101 | |||
102 | pdata->edac_idx = edac_pci_idx++; | ||
103 | |||
104 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
105 | if (!r) { | ||
106 | printk(KERN_ERR "%s: Unable to get resource for " | ||
107 | "PCI err regs\n", __func__); | ||
108 | res = -ENOENT; | ||
109 | goto err; | ||
110 | } | ||
111 | |||
112 | if (!devm_request_mem_region(&pdev->dev, | ||
113 | r->start, | ||
114 | r->end - r->start + 1, | ||
115 | pdata->name)) { | ||
116 | printk(KERN_ERR "%s: Error while requesting mem region\n", | ||
117 | __func__); | ||
118 | res = -EBUSY; | ||
119 | goto err; | ||
120 | } | ||
121 | |||
122 | pdata->pci_vbase = devm_ioremap(&pdev->dev, | ||
123 | r->start, | ||
124 | r->end - r->start + 1); | ||
125 | if (!pdata->pci_vbase) { | ||
126 | printk(KERN_ERR "%s: Unable to setup PCI err regs\n", __func__); | ||
127 | res = -ENOMEM; | ||
128 | goto err; | ||
129 | } | ||
130 | |||
131 | out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE, 0); | ||
132 | out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_MASK, 0); | ||
133 | out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_MASK, | ||
134 | MV64X60_PCIx_ERR_MASK_VAL); | ||
135 | |||
136 | if (edac_pci_add_device(pci, pdata->edac_idx) > 0) { | ||
137 | debugf3("%s(): failed edac_pci_add_device()\n", __func__); | ||
138 | goto err; | ||
139 | } | ||
140 | |||
141 | if (edac_op_state == EDAC_OPSTATE_INT) { | ||
142 | pdata->irq = platform_get_irq(pdev, 0); | ||
143 | res = devm_request_irq(&pdev->dev, | ||
144 | pdata->irq, | ||
145 | mv64x60_pci_isr, | ||
146 | IRQF_DISABLED, | ||
147 | "[EDAC] PCI err", | ||
148 | pci); | ||
149 | if (res < 0) { | ||
150 | printk(KERN_ERR "%s: Unable to request irq %d for " | ||
151 | "MV64x60 PCI ERR\n", __func__, pdata->irq); | ||
152 | res = -ENODEV; | ||
153 | goto err2; | ||
154 | } | ||
155 | printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for PCI Err\n", | ||
156 | pdata->irq); | ||
157 | } | ||
158 | |||
159 | devres_remove_group(&pdev->dev, mv64x60_pci_err_probe); | ||
160 | |||
161 | /* get this far and it's successful */ | ||
162 | debugf3("%s(): success\n", __func__); | ||
163 | |||
164 | return 0; | ||
165 | |||
166 | err2: | ||
167 | edac_pci_del_device(&pdev->dev); | ||
168 | err: | ||
169 | edac_pci_free_ctl_info(pci); | ||
170 | devres_release_group(&pdev->dev, mv64x60_pci_err_probe); | ||
171 | return res; | ||
172 | } | ||
173 | |||
174 | static int mv64x60_pci_err_remove(struct platform_device *pdev) | ||
175 | { | ||
176 | struct edac_pci_ctl_info *pci = platform_get_drvdata(pdev); | ||
177 | |||
178 | debugf0("%s()\n", __func__); | ||
179 | |||
180 | edac_pci_del_device(&pdev->dev); | ||
181 | |||
182 | edac_pci_free_ctl_info(pci); | ||
183 | |||
184 | return 0; | ||
185 | } | ||
186 | |||
187 | static struct platform_driver mv64x60_pci_err_driver = { | ||
188 | .probe = mv64x60_pci_err_probe, | ||
189 | .remove = __devexit_p(mv64x60_pci_err_remove), | ||
190 | .driver = { | ||
191 | .name = "mv64x60_pci_err", | ||
192 | } | ||
193 | }; | ||
194 | |||
195 | #endif /* CONFIG_PCI */ | ||
196 | |||
197 | /*********************** SRAM err device **********************************/ | ||
198 | static void mv64x60_sram_check(struct edac_device_ctl_info *edac_dev) | ||
199 | { | ||
200 | struct mv64x60_sram_pdata *pdata = edac_dev->pvt_info; | ||
201 | u32 cause; | ||
202 | |||
203 | cause = in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE); | ||
204 | if (!cause) | ||
205 | return; | ||
206 | |||
207 | printk(KERN_ERR "Error in internal SRAM\n"); | ||
208 | printk(KERN_ERR "Cause register: 0x%08x\n", cause); | ||
209 | printk(KERN_ERR "Address Low: 0x%08x\n", | ||
210 | in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_ADDR_LO)); | ||
211 | printk(KERN_ERR "Address High: 0x%08x\n", | ||
212 | in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_ADDR_HI)); | ||
213 | printk(KERN_ERR "Data Low: 0x%08x\n", | ||
214 | in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_DATA_LO)); | ||
215 | printk(KERN_ERR "Data High: 0x%08x\n", | ||
216 | in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_DATA_HI)); | ||
217 | printk(KERN_ERR "Parity: 0x%08x\n", | ||
218 | in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_PARITY)); | ||
219 | out_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE, 0); | ||
220 | |||
221 | edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name); | ||
222 | } | ||
223 | |||
224 | static irqreturn_t mv64x60_sram_isr(int irq, void *dev_id) | ||
225 | { | ||
226 | struct edac_device_ctl_info *edac_dev = dev_id; | ||
227 | struct mv64x60_sram_pdata *pdata = edac_dev->pvt_info; | ||
228 | u32 cause; | ||
229 | |||
230 | cause = in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE); | ||
231 | if (!cause) | ||
232 | return IRQ_NONE; | ||
233 | |||
234 | mv64x60_sram_check(edac_dev); | ||
235 | |||
236 | return IRQ_HANDLED; | ||
237 | } | ||
238 | |||
239 | static int __devinit mv64x60_sram_err_probe(struct platform_device *pdev) | ||
240 | { | ||
241 | struct edac_device_ctl_info *edac_dev; | ||
242 | struct mv64x60_sram_pdata *pdata; | ||
243 | struct resource *r; | ||
244 | int res = 0; | ||
245 | |||
246 | if (!devres_open_group(&pdev->dev, mv64x60_sram_err_probe, GFP_KERNEL)) | ||
247 | return -ENOMEM; | ||
248 | |||
249 | edac_dev = edac_device_alloc_ctl_info(sizeof(*pdata), | ||
250 | "sram", 1, NULL, 0, 0, NULL, 0, | ||
251 | edac_dev_idx); | ||
252 | if (!edac_dev) { | ||
253 | devres_release_group(&pdev->dev, mv64x60_sram_err_probe); | ||
254 | return -ENOMEM; | ||
255 | } | ||
256 | |||
257 | pdata = edac_dev->pvt_info; | ||
258 | pdata->name = "mv64x60_sram_err"; | ||
259 | pdata->irq = NO_IRQ; | ||
260 | edac_dev->dev = &pdev->dev; | ||
261 | platform_set_drvdata(pdev, edac_dev); | ||
262 | edac_dev->dev_name = pdev->dev.bus_id; | ||
263 | |||
264 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
265 | if (!r) { | ||
266 | printk(KERN_ERR "%s: Unable to get resource for " | ||
267 | "SRAM err regs\n", __func__); | ||
268 | res = -ENOENT; | ||
269 | goto err; | ||
270 | } | ||
271 | |||
272 | if (!devm_request_mem_region(&pdev->dev, | ||
273 | r->start, | ||
274 | r->end - r->start + 1, | ||
275 | pdata->name)) { | ||
276 | printk(KERN_ERR "%s: Error while request mem region\n", | ||
277 | __func__); | ||
278 | res = -EBUSY; | ||
279 | goto err; | ||
280 | } | ||
281 | |||
282 | pdata->sram_vbase = devm_ioremap(&pdev->dev, | ||
283 | r->start, | ||
284 | r->end - r->start + 1); | ||
285 | if (!pdata->sram_vbase) { | ||
286 | printk(KERN_ERR "%s: Unable to setup SRAM err regs\n", | ||
287 | __func__); | ||
288 | res = -ENOMEM; | ||
289 | goto err; | ||
290 | } | ||
291 | |||
292 | /* setup SRAM err registers */ | ||
293 | out_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE, 0); | ||
294 | |||
295 | edac_dev->mod_name = EDAC_MOD_STR; | ||
296 | edac_dev->ctl_name = pdata->name; | ||
297 | |||
298 | if (edac_op_state == EDAC_OPSTATE_POLL) | ||
299 | edac_dev->edac_check = mv64x60_sram_check; | ||
300 | |||
301 | pdata->edac_idx = edac_dev_idx++; | ||
302 | |||
303 | if (edac_device_add_device(edac_dev) > 0) { | ||
304 | debugf3("%s(): failed edac_device_add_device()\n", __func__); | ||
305 | goto err; | ||
306 | } | ||
307 | |||
308 | if (edac_op_state == EDAC_OPSTATE_INT) { | ||
309 | pdata->irq = platform_get_irq(pdev, 0); | ||
310 | res = devm_request_irq(&pdev->dev, | ||
311 | pdata->irq, | ||
312 | mv64x60_sram_isr, | ||
313 | IRQF_DISABLED, | ||
314 | "[EDAC] SRAM err", | ||
315 | edac_dev); | ||
316 | if (res < 0) { | ||
317 | printk(KERN_ERR | ||
318 | "%s: Unable to request irq %d for " | ||
319 | "MV64x60 SRAM ERR\n", __func__, pdata->irq); | ||
320 | res = -ENODEV; | ||
321 | goto err2; | ||
322 | } | ||
323 | |||
324 | printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for SRAM Err\n", | ||
325 | pdata->irq); | ||
326 | } | ||
327 | |||
328 | devres_remove_group(&pdev->dev, mv64x60_sram_err_probe); | ||
329 | |||
330 | /* get this far and it's successful */ | ||
331 | debugf3("%s(): success\n", __func__); | ||
332 | |||
333 | return 0; | ||
334 | |||
335 | err2: | ||
336 | edac_device_del_device(&pdev->dev); | ||
337 | err: | ||
338 | devres_release_group(&pdev->dev, mv64x60_sram_err_probe); | ||
339 | edac_device_free_ctl_info(edac_dev); | ||
340 | return res; | ||
341 | } | ||
342 | |||
343 | static int mv64x60_sram_err_remove(struct platform_device *pdev) | ||
344 | { | ||
345 | struct edac_device_ctl_info *edac_dev = platform_get_drvdata(pdev); | ||
346 | |||
347 | debugf0("%s()\n", __func__); | ||
348 | |||
349 | edac_device_del_device(&pdev->dev); | ||
350 | edac_device_free_ctl_info(edac_dev); | ||
351 | |||
352 | return 0; | ||
353 | } | ||
354 | |||
355 | static struct platform_driver mv64x60_sram_err_driver = { | ||
356 | .probe = mv64x60_sram_err_probe, | ||
357 | .remove = mv64x60_sram_err_remove, | ||
358 | .driver = { | ||
359 | .name = "mv64x60_sram_err", | ||
360 | } | ||
361 | }; | ||
362 | |||
363 | /*********************** CPU err device **********************************/ | ||
364 | static void mv64x60_cpu_check(struct edac_device_ctl_info *edac_dev) | ||
365 | { | ||
366 | struct mv64x60_cpu_pdata *pdata = edac_dev->pvt_info; | ||
367 | u32 cause; | ||
368 | |||
369 | cause = in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE) & | ||
370 | MV64x60_CPU_CAUSE_MASK; | ||
371 | if (!cause) | ||
372 | return; | ||
373 | |||
374 | printk(KERN_ERR "Error on CPU interface\n"); | ||
375 | printk(KERN_ERR "Cause register: 0x%08x\n", cause); | ||
376 | printk(KERN_ERR "Address Low: 0x%08x\n", | ||
377 | in_le32(pdata->cpu_vbase[0] + MV64x60_CPU_ERR_ADDR_LO)); | ||
378 | printk(KERN_ERR "Address High: 0x%08x\n", | ||
379 | in_le32(pdata->cpu_vbase[0] + MV64x60_CPU_ERR_ADDR_HI)); | ||
380 | printk(KERN_ERR "Data Low: 0x%08x\n", | ||
381 | in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_DATA_LO)); | ||
382 | printk(KERN_ERR "Data High: 0x%08x\n", | ||
383 | in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_DATA_HI)); | ||
384 | printk(KERN_ERR "Parity: 0x%08x\n", | ||
385 | in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_PARITY)); | ||
386 | out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE, 0); | ||
387 | |||
388 | edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name); | ||
389 | } | ||
390 | |||
391 | static irqreturn_t mv64x60_cpu_isr(int irq, void *dev_id) | ||
392 | { | ||
393 | struct edac_device_ctl_info *edac_dev = dev_id; | ||
394 | struct mv64x60_cpu_pdata *pdata = edac_dev->pvt_info; | ||
395 | u32 cause; | ||
396 | |||
397 | cause = in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE) & | ||
398 | MV64x60_CPU_CAUSE_MASK; | ||
399 | if (!cause) | ||
400 | return IRQ_NONE; | ||
401 | |||
402 | mv64x60_cpu_check(edac_dev); | ||
403 | |||
404 | return IRQ_HANDLED; | ||
405 | } | ||
406 | |||
407 | static int __devinit mv64x60_cpu_err_probe(struct platform_device *pdev) | ||
408 | { | ||
409 | struct edac_device_ctl_info *edac_dev; | ||
410 | struct resource *r; | ||
411 | struct mv64x60_cpu_pdata *pdata; | ||
412 | int res = 0; | ||
413 | |||
414 | if (!devres_open_group(&pdev->dev, mv64x60_cpu_err_probe, GFP_KERNEL)) | ||
415 | return -ENOMEM; | ||
416 | |||
417 | edac_dev = edac_device_alloc_ctl_info(sizeof(*pdata), | ||
418 | "cpu", 1, NULL, 0, 0, NULL, 0, | ||
419 | edac_dev_idx); | ||
420 | if (!edac_dev) { | ||
421 | devres_release_group(&pdev->dev, mv64x60_cpu_err_probe); | ||
422 | return -ENOMEM; | ||
423 | } | ||
424 | |||
425 | pdata = edac_dev->pvt_info; | ||
426 | pdata->name = "mv64x60_cpu_err"; | ||
427 | pdata->irq = NO_IRQ; | ||
428 | edac_dev->dev = &pdev->dev; | ||
429 | platform_set_drvdata(pdev, edac_dev); | ||
430 | edac_dev->dev_name = pdev->dev.bus_id; | ||
431 | |||
432 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
433 | if (!r) { | ||
434 | printk(KERN_ERR "%s: Unable to get resource for " | ||
435 | "CPU err regs\n", __func__); | ||
436 | res = -ENOENT; | ||
437 | goto err; | ||
438 | } | ||
439 | |||
440 | if (!devm_request_mem_region(&pdev->dev, | ||
441 | r->start, | ||
442 | r->end - r->start + 1, | ||
443 | pdata->name)) { | ||
444 | printk(KERN_ERR "%s: Error while requesting mem region\n", | ||
445 | __func__); | ||
446 | res = -EBUSY; | ||
447 | goto err; | ||
448 | } | ||
449 | |||
450 | pdata->cpu_vbase[0] = devm_ioremap(&pdev->dev, | ||
451 | r->start, | ||
452 | r->end - r->start + 1); | ||
453 | if (!pdata->cpu_vbase[0]) { | ||
454 | printk(KERN_ERR "%s: Unable to setup CPU err regs\n", __func__); | ||
455 | res = -ENOMEM; | ||
456 | goto err; | ||
457 | } | ||
458 | |||
459 | r = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
460 | if (!r) { | ||
461 | printk(KERN_ERR "%s: Unable to get resource for " | ||
462 | "CPU err regs\n", __func__); | ||
463 | res = -ENOENT; | ||
464 | goto err; | ||
465 | } | ||
466 | |||
467 | if (!devm_request_mem_region(&pdev->dev, | ||
468 | r->start, | ||
469 | r->end - r->start + 1, | ||
470 | pdata->name)) { | ||
471 | printk(KERN_ERR "%s: Error while requesting mem region\n", | ||
472 | __func__); | ||
473 | res = -EBUSY; | ||
474 | goto err; | ||
475 | } | ||
476 | |||
477 | pdata->cpu_vbase[1] = devm_ioremap(&pdev->dev, | ||
478 | r->start, | ||
479 | r->end - r->start + 1); | ||
480 | if (!pdata->cpu_vbase[1]) { | ||
481 | printk(KERN_ERR "%s: Unable to setup CPU err regs\n", __func__); | ||
482 | res = -ENOMEM; | ||
483 | goto err; | ||
484 | } | ||
485 | |||
486 | /* setup CPU err registers */ | ||
487 | out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE, 0); | ||
488 | out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_MASK, 0); | ||
489 | out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_MASK, 0x000000ff); | ||
490 | |||
491 | edac_dev->mod_name = EDAC_MOD_STR; | ||
492 | edac_dev->ctl_name = pdata->name; | ||
493 | if (edac_op_state == EDAC_OPSTATE_POLL) | ||
494 | edac_dev->edac_check = mv64x60_cpu_check; | ||
495 | |||
496 | pdata->edac_idx = edac_dev_idx++; | ||
497 | |||
498 | if (edac_device_add_device(edac_dev) > 0) { | ||
499 | debugf3("%s(): failed edac_device_add_device()\n", __func__); | ||
500 | goto err; | ||
501 | } | ||
502 | |||
503 | if (edac_op_state == EDAC_OPSTATE_INT) { | ||
504 | pdata->irq = platform_get_irq(pdev, 0); | ||
505 | res = devm_request_irq(&pdev->dev, | ||
506 | pdata->irq, | ||
507 | mv64x60_cpu_isr, | ||
508 | IRQF_DISABLED, | ||
509 | "[EDAC] CPU err", | ||
510 | edac_dev); | ||
511 | if (res < 0) { | ||
512 | printk(KERN_ERR | ||
513 | "%s: Unable to request irq %d for MV64x60 " | ||
514 | "CPU ERR\n", __func__, pdata->irq); | ||
515 | res = -ENODEV; | ||
516 | goto err2; | ||
517 | } | ||
518 | |||
519 | printk(KERN_INFO EDAC_MOD_STR | ||
520 | " acquired irq %d for CPU Err\n", pdata->irq); | ||
521 | } | ||
522 | |||
523 | devres_remove_group(&pdev->dev, mv64x60_cpu_err_probe); | ||
524 | |||
525 | /* get this far and it's successful */ | ||
526 | debugf3("%s(): success\n", __func__); | ||
527 | |||
528 | return 0; | ||
529 | |||
530 | err2: | ||
531 | edac_device_del_device(&pdev->dev); | ||
532 | err: | ||
533 | devres_release_group(&pdev->dev, mv64x60_cpu_err_probe); | ||
534 | edac_device_free_ctl_info(edac_dev); | ||
535 | return res; | ||
536 | } | ||
537 | |||
538 | static int mv64x60_cpu_err_remove(struct platform_device *pdev) | ||
539 | { | ||
540 | struct edac_device_ctl_info *edac_dev = platform_get_drvdata(pdev); | ||
541 | |||
542 | debugf0("%s()\n", __func__); | ||
543 | |||
544 | edac_device_del_device(&pdev->dev); | ||
545 | edac_device_free_ctl_info(edac_dev); | ||
546 | return 0; | ||
547 | } | ||
548 | |||
549 | static struct platform_driver mv64x60_cpu_err_driver = { | ||
550 | .probe = mv64x60_cpu_err_probe, | ||
551 | .remove = mv64x60_cpu_err_remove, | ||
552 | .driver = { | ||
553 | .name = "mv64x60_cpu_err", | ||
554 | } | ||
555 | }; | ||
556 | |||
557 | /*********************** DRAM err device **********************************/ | ||
558 | |||
559 | static void mv64x60_mc_check(struct mem_ctl_info *mci) | ||
560 | { | ||
561 | struct mv64x60_mc_pdata *pdata = mci->pvt_info; | ||
562 | u32 reg; | ||
563 | u32 err_addr; | ||
564 | u32 sdram_ecc; | ||
565 | u32 comp_ecc; | ||
566 | u32 syndrome; | ||
567 | |||
568 | reg = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR); | ||
569 | if (!reg) | ||
570 | return; | ||
571 | |||
572 | err_addr = reg & ~0x3; | ||
573 | sdram_ecc = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_RCVD); | ||
574 | comp_ecc = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CALC); | ||
575 | syndrome = sdram_ecc ^ comp_ecc; | ||
576 | |||
577 | /* first bit clear in ECC Err Reg, 1 bit error, correctable by HW */ | ||
578 | if (!(reg & 0x1)) | ||
579 | edac_mc_handle_ce(mci, err_addr >> PAGE_SHIFT, | ||
580 | err_addr & PAGE_MASK, syndrome, 0, 0, | ||
581 | mci->ctl_name); | ||
582 | else /* 2 bit error, UE */ | ||
583 | edac_mc_handle_ue(mci, err_addr >> PAGE_SHIFT, | ||
584 | err_addr & PAGE_MASK, 0, mci->ctl_name); | ||
585 | |||
586 | /* clear the error */ | ||
587 | out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR, 0); | ||
588 | } | ||
589 | |||
590 | static irqreturn_t mv64x60_mc_isr(int irq, void *dev_id) | ||
591 | { | ||
592 | struct mem_ctl_info *mci = dev_id; | ||
593 | struct mv64x60_mc_pdata *pdata = mci->pvt_info; | ||
594 | u32 reg; | ||
595 | |||
596 | reg = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR); | ||
597 | if (!reg) | ||
598 | return IRQ_NONE; | ||
599 | |||
600 | /* writing 0's to the ECC err addr in check function clears irq */ | ||
601 | mv64x60_mc_check(mci); | ||
602 | |||
603 | return IRQ_HANDLED; | ||
604 | } | ||
605 | |||
606 | static void get_total_mem(struct mv64x60_mc_pdata *pdata) | ||
607 | { | ||
608 | struct device_node *np = NULL; | ||
609 | const unsigned int *reg; | ||
610 | |||
611 | np = of_find_node_by_type(NULL, "memory"); | ||
612 | if (!np) | ||
613 | return; | ||
614 | |||
615 | reg = get_property(np, "reg", NULL); | ||
616 | |||
617 | pdata->total_mem = reg[1]; | ||
618 | } | ||
619 | |||
620 | static void mv64x60_init_csrows(struct mem_ctl_info *mci, | ||
621 | struct mv64x60_mc_pdata *pdata) | ||
622 | { | ||
623 | struct csrow_info *csrow; | ||
624 | u32 devtype; | ||
625 | u32 ctl; | ||
626 | |||
627 | get_total_mem(pdata); | ||
628 | |||
629 | ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG); | ||
630 | |||
631 | csrow = &mci->csrows[0]; | ||
632 | csrow->first_page = 0; | ||
633 | csrow->nr_pages = pdata->total_mem >> PAGE_SHIFT; | ||
634 | csrow->last_page = csrow->first_page + csrow->nr_pages - 1; | ||
635 | csrow->grain = 8; | ||
636 | |||
637 | csrow->mtype = (ctl & MV64X60_SDRAM_REGISTERED) ? MEM_RDDR : MEM_DDR; | ||
638 | |||
639 | devtype = (ctl >> 20) & 0x3; | ||
640 | switch (devtype) { | ||
641 | case 0x0: | ||
642 | csrow->dtype = DEV_X32; | ||
643 | break; | ||
644 | case 0x2: /* could be X8 too, but no way to tell */ | ||
645 | csrow->dtype = DEV_X16; | ||
646 | break; | ||
647 | case 0x3: | ||
648 | csrow->dtype = DEV_X4; | ||
649 | break; | ||
650 | default: | ||
651 | csrow->dtype = DEV_UNKNOWN; | ||
652 | break; | ||
653 | } | ||
654 | |||
655 | csrow->edac_mode = EDAC_SECDED; | ||
656 | } | ||
657 | |||
658 | static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev) | ||
659 | { | ||
660 | struct mem_ctl_info *mci; | ||
661 | struct mv64x60_mc_pdata *pdata; | ||
662 | struct resource *r; | ||
663 | u32 ctl; | ||
664 | int res = 0; | ||
665 | |||
666 | if (!devres_open_group(&pdev->dev, mv64x60_mc_err_probe, GFP_KERNEL)) | ||
667 | return -ENOMEM; | ||
668 | |||
669 | mci = edac_mc_alloc(sizeof(struct mv64x60_mc_pdata), 1, 1, edac_mc_idx); | ||
670 | if (!mci) { | ||
671 | printk(KERN_ERR "%s: No memory for CPU err\n", __func__); | ||
672 | devres_release_group(&pdev->dev, mv64x60_mc_err_probe); | ||
673 | return -ENOMEM; | ||
674 | } | ||
675 | |||
676 | pdata = mci->pvt_info; | ||
677 | mci->dev = &pdev->dev; | ||
678 | platform_set_drvdata(pdev, mci); | ||
679 | pdata->name = "mv64x60_mc_err"; | ||
680 | pdata->irq = NO_IRQ; | ||
681 | mci->dev_name = pdev->dev.bus_id; | ||
682 | pdata->edac_idx = edac_mc_idx++; | ||
683 | |||
684 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
685 | if (!r) { | ||
686 | printk(KERN_ERR "%s: Unable to get resource for " | ||
687 | "MC err regs\n", __func__); | ||
688 | res = -ENOENT; | ||
689 | goto err; | ||
690 | } | ||
691 | |||
692 | if (!devm_request_mem_region(&pdev->dev, | ||
693 | r->start, | ||
694 | r->end - r->start + 1, | ||
695 | pdata->name)) { | ||
696 | printk(KERN_ERR "%s: Error while requesting mem region\n", | ||
697 | __func__); | ||
698 | res = -EBUSY; | ||
699 | goto err; | ||
700 | } | ||
701 | |||
702 | pdata->mc_vbase = devm_ioremap(&pdev->dev, | ||
703 | r->start, | ||
704 | r->end - r->start + 1); | ||
705 | if (!pdata->mc_vbase) { | ||
706 | printk(KERN_ERR "%s: Unable to setup MC err regs\n", __func__); | ||
707 | res = -ENOMEM; | ||
708 | goto err; | ||
709 | } | ||
710 | |||
711 | ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG); | ||
712 | if (!(ctl & MV64X60_SDRAM_ECC)) { | ||
713 | /* Non-ECC RAM? */ | ||
714 | printk(KERN_WARNING "%s: No ECC DIMMs discovered\n", __func__); | ||
715 | res = -ENODEV; | ||
716 | goto err2; | ||
717 | } | ||
718 | |||
719 | debugf3("%s(): init mci\n", __func__); | ||
720 | mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR; | ||
721 | mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; | ||
722 | mci->edac_cap = EDAC_FLAG_SECDED; | ||
723 | mci->mod_name = EDAC_MOD_STR; | ||
724 | mci->mod_ver = MV64x60_REVISION; | ||
725 | mci->ctl_name = mv64x60_ctl_name; | ||
726 | |||
727 | if (edac_op_state == EDAC_OPSTATE_POLL) | ||
728 | mci->edac_check = mv64x60_mc_check; | ||
729 | |||
730 | mci->ctl_page_to_phys = NULL; | ||
731 | |||
732 | mci->scrub_mode = SCRUB_SW_SRC; | ||
733 | |||
734 | mv64x60_init_csrows(mci, pdata); | ||
735 | |||
736 | /* setup MC registers */ | ||
737 | out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR, 0); | ||
738 | ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL); | ||
739 | ctl = (ctl & 0xff00ffff) | 0x10000; | ||
740 | out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL, ctl); | ||
741 | |||
742 | if (edac_mc_add_mc(mci)) { | ||
743 | debugf3("%s(): failed edac_mc_add_mc()\n", __func__); | ||
744 | goto err; | ||
745 | } | ||
746 | |||
747 | if (edac_op_state == EDAC_OPSTATE_INT) { | ||
748 | /* acquire interrupt that reports errors */ | ||
749 | pdata->irq = platform_get_irq(pdev, 0); | ||
750 | res = devm_request_irq(&pdev->dev, | ||
751 | pdata->irq, | ||
752 | mv64x60_mc_isr, | ||
753 | IRQF_DISABLED, | ||
754 | "[EDAC] MC err", | ||
755 | mci); | ||
756 | if (res < 0) { | ||
757 | printk(KERN_ERR "%s: Unable to request irq %d for " | ||
758 | "MV64x60 DRAM ERR\n", __func__, pdata->irq); | ||
759 | res = -ENODEV; | ||
760 | goto err2; | ||
761 | } | ||
762 | |||
763 | printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for MC Err\n", | ||
764 | pdata->irq); | ||
765 | } | ||
766 | |||
767 | /* get this far and it's successful */ | ||
768 | debugf3("%s(): success\n", __func__); | ||
769 | |||
770 | return 0; | ||
771 | |||
772 | err2: | ||
773 | edac_mc_del_mc(&pdev->dev); | ||
774 | err: | ||
775 | devres_release_group(&pdev->dev, mv64x60_mc_err_probe); | ||
776 | edac_mc_free(mci); | ||
777 | return res; | ||
778 | } | ||
779 | |||
780 | static int mv64x60_mc_err_remove(struct platform_device *pdev) | ||
781 | { | ||
782 | struct mem_ctl_info *mci = platform_get_drvdata(pdev); | ||
783 | |||
784 | debugf0("%s()\n", __func__); | ||
785 | |||
786 | edac_mc_del_mc(&pdev->dev); | ||
787 | edac_mc_free(mci); | ||
788 | return 0; | ||
789 | } | ||
790 | |||
791 | static struct platform_driver mv64x60_mc_err_driver = { | ||
792 | .probe = mv64x60_mc_err_probe, | ||
793 | .remove = mv64x60_mc_err_remove, | ||
794 | .driver = { | ||
795 | .name = "mv64x60_mc_err", | ||
796 | } | ||
797 | }; | ||
798 | |||
799 | static int __init mv64x60_edac_init(void) | ||
800 | { | ||
801 | int ret = 0; | ||
802 | |||
803 | printk(KERN_INFO "Marvell MV64x60 EDAC driver " MV64x60_REVISION "\n"); | ||
804 | printk(KERN_INFO "\t(C) 2006-2007 MontaVista Software\n"); | ||
805 | /* make sure error reporting method is sane */ | ||
806 | switch (edac_op_state) { | ||
807 | case EDAC_OPSTATE_POLL: | ||
808 | case EDAC_OPSTATE_INT: | ||
809 | break; | ||
810 | default: | ||
811 | edac_op_state = EDAC_OPSTATE_INT; | ||
812 | break; | ||
813 | } | ||
814 | |||
815 | ret = platform_driver_register(&mv64x60_mc_err_driver); | ||
816 | if (ret) | ||
817 | printk(KERN_WARNING EDAC_MOD_STR "MC err failed to register\n"); | ||
818 | |||
819 | ret = platform_driver_register(&mv64x60_cpu_err_driver); | ||
820 | if (ret) | ||
821 | printk(KERN_WARNING EDAC_MOD_STR | ||
822 | "CPU err failed to register\n"); | ||
823 | |||
824 | ret = platform_driver_register(&mv64x60_sram_err_driver); | ||
825 | if (ret) | ||
826 | printk(KERN_WARNING EDAC_MOD_STR | ||
827 | "SRAM err failed to register\n"); | ||
828 | |||
829 | #ifdef CONFIG_PCI | ||
830 | ret = platform_driver_register(&mv64x60_pci_err_driver); | ||
831 | if (ret) | ||
832 | printk(KERN_WARNING EDAC_MOD_STR | ||
833 | "PCI err failed to register\n"); | ||
834 | #endif | ||
835 | |||
836 | return ret; | ||
837 | } | ||
838 | module_init(mv64x60_edac_init); | ||
839 | |||
840 | static void __exit mv64x60_edac_exit(void) | ||
841 | { | ||
842 | #ifdef CONFIG_PCI | ||
843 | platform_driver_unregister(&mv64x60_pci_err_driver); | ||
844 | #endif | ||
845 | platform_driver_unregister(&mv64x60_sram_err_driver); | ||
846 | platform_driver_unregister(&mv64x60_cpu_err_driver); | ||
847 | platform_driver_unregister(&mv64x60_mc_err_driver); | ||
848 | } | ||
849 | module_exit(mv64x60_edac_exit); | ||
850 | |||
851 | MODULE_LICENSE("GPL"); | ||
852 | MODULE_AUTHOR("Montavista Software, Inc."); | ||
853 | module_param(edac_op_state, int, 0444); | ||
854 | MODULE_PARM_DESC(edac_op_state, | ||
855 | "EDAC Error Reporting state: 0=Poll, 2=Interrupt"); | ||