aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDave Jiang <djiang@mvista.com>2008-02-07 03:14:56 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-07 11:42:23 -0500
commit4f4aeeabc061826376c9a72b4714d062664999ea (patch)
tree888c63a646bd3fef2943531d7187ffacc7c4b6fe
parenta9a753d53204bf0f42841f65679c7e1711833bcf (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>
-rw-r--r--drivers/edac/Kconfig7
-rw-r--r--drivers/edac/Makefile1
-rw-r--r--drivers/edac/mv64x60_edac.c855
-rw-r--r--drivers/edac/mv64x60_edac.h114
4 files changed, 977 insertions, 0 deletions
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 1727a00e57e5..10119d7dbcff 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -130,6 +130,13 @@ config EDAC_MPC85XX
130 Support for error detection and correction on the Freescale 130 Support for error detection and correction on the Freescale
131 MPC8560, MPC8540, MPC8548 131 MPC8560, MPC8540, MPC8548
132 132
133config EDAC_MV64X60
134 tristate "Marvell MV64x60"
135 depends on EDAC_MM_EDAC && MV64X60
136 help
137 Support for error detection and correction on the Marvell
138 MV64360 and MV64460 chipsets.
139
133config EDAC_PASEMI 140config EDAC_PASEMI
134 tristate "PA Semi PWRficient" 141 tristate "PA Semi PWRficient"
135 depends on EDAC_MM_EDAC && PCI 142 depends on EDAC_MM_EDAC && PCI
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index 293f9b0fc762..83807731d4a9 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -29,5 +29,6 @@ obj-$(CONFIG_EDAC_I82860) += i82860_edac.o
29obj-$(CONFIG_EDAC_R82600) += r82600_edac.o 29obj-$(CONFIG_EDAC_R82600) += r82600_edac.o
30obj-$(CONFIG_EDAC_PASEMI) += pasemi_edac.o 30obj-$(CONFIG_EDAC_PASEMI) += pasemi_edac.o
31obj-$(CONFIG_EDAC_MPC85XX) += mpc85xx_edac.o 31obj-$(CONFIG_EDAC_MPC85XX) += mpc85xx_edac.o
32obj-$(CONFIG_EDAC_MV64X60) += mv64x60_edac.o
32obj-$(CONFIG_EDAC_CELL) += cell_edac.o 33obj-$(CONFIG_EDAC_CELL) += cell_edac.o
33 34
diff --git a/drivers/edac/mv64x60_edac.c b/drivers/edac/mv64x60_edac.c
new file mode 100644
index 000000000000..bf071f140a05
--- /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
24static const char *mv64x60_ctl_name = "MV64x60";
25static int edac_dev_idx;
26static int edac_pci_idx;
27static int edac_mc_idx;
28
29/*********************** PCI err device **********************************/
30#ifdef CONFIG_PCI
31static 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
59static 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
74static 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
166err2:
167 edac_pci_del_device(&pdev->dev);
168err:
169 edac_pci_free_ctl_info(pci);
170 devres_release_group(&pdev->dev, mv64x60_pci_err_probe);
171 return res;
172}
173
174static 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
187static 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 **********************************/
198static 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
224static 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
239static 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
335err2:
336 edac_device_del_device(&pdev->dev);
337err:
338 devres_release_group(&pdev->dev, mv64x60_sram_err_probe);
339 edac_device_free_ctl_info(edac_dev);
340 return res;
341}
342
343static 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
355static 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 **********************************/
364static 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
391static 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
407static 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
530err2:
531 edac_device_del_device(&pdev->dev);
532err:
533 devres_release_group(&pdev->dev, mv64x60_cpu_err_probe);
534 edac_device_free_ctl_info(edac_dev);
535 return res;
536}
537
538static 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
549static 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
559static 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
590static 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
606static 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
620static 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
658static 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
772err2:
773 edac_mc_del_mc(&pdev->dev);
774err:
775 devres_release_group(&pdev->dev, mv64x60_mc_err_probe);
776 edac_mc_free(mci);
777 return res;
778}
779
780static 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
791static 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
799static 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}
838module_init(mv64x60_edac_init);
839
840static 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}
849module_exit(mv64x60_edac_exit);
850
851MODULE_LICENSE("GPL");
852MODULE_AUTHOR("Montavista Software, Inc.");
853module_param(edac_op_state, int, 0444);
854MODULE_PARM_DESC(edac_op_state,
855 "EDAC Error Reporting state: 0=Poll, 2=Interrupt");
diff --git a/drivers/edac/mv64x60_edac.h b/drivers/edac/mv64x60_edac.h
new file mode 100644
index 000000000000..e042e2daa8f4
--- /dev/null
+++ b/drivers/edac/mv64x60_edac.h
@@ -0,0 +1,114 @@
1/*
2 * EDAC defs for Marvell MV64x60 bridge chip
3 *
4 * Author: Dave Jiang <djiang@mvista.com>
5 *
6 * 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#ifndef _MV64X60_EDAC_H_
13#define _MV64X60_EDAC_H_
14
15#define MV64x60_REVISION " Ver: 2.0.0 " __DATE__
16#define EDAC_MOD_STR "MV64x60_edac"
17
18#define mv64x60_printk(level, fmt, arg...) \
19 edac_printk(level, "MV64x60", fmt, ##arg)
20
21#define mv64x60_mc_printk(mci, level, fmt, arg...) \
22 edac_mc_chipset_printk(mci, level, "MV64x60", fmt, ##arg)
23
24/* CPU Error Report Registers */
25#define MV64x60_CPU_ERR_ADDR_LO 0x00 /* 0x0070 */
26#define MV64x60_CPU_ERR_ADDR_HI 0x08 /* 0x0078 */
27#define MV64x60_CPU_ERR_DATA_LO 0x00 /* 0x0128 */
28#define MV64x60_CPU_ERR_DATA_HI 0x08 /* 0x0130 */
29#define MV64x60_CPU_ERR_PARITY 0x10 /* 0x0138 */
30#define MV64x60_CPU_ERR_CAUSE 0x18 /* 0x0140 */
31#define MV64x60_CPU_ERR_MASK 0x20 /* 0x0148 */
32
33#define MV64x60_CPU_CAUSE_MASK 0x07ffffff
34
35/* SRAM Error Report Registers */
36#define MV64X60_SRAM_ERR_CAUSE 0x08 /* 0x0388 */
37#define MV64X60_SRAM_ERR_ADDR_LO 0x10 /* 0x0390 */
38#define MV64X60_SRAM_ERR_ADDR_HI 0x78 /* 0x03f8 */
39#define MV64X60_SRAM_ERR_DATA_LO 0x18 /* 0x0398 */
40#define MV64X60_SRAM_ERR_DATA_HI 0x20 /* 0x03a0 */
41#define MV64X60_SRAM_ERR_PARITY 0x28 /* 0x03a8 */
42
43/* SDRAM Controller Registers */
44#define MV64X60_SDRAM_CONFIG 0x00 /* 0x1400 */
45#define MV64X60_SDRAM_ERR_DATA_HI 0x40 /* 0x1440 */
46#define MV64X60_SDRAM_ERR_DATA_LO 0x44 /* 0x1444 */
47#define MV64X60_SDRAM_ERR_ECC_RCVD 0x48 /* 0x1448 */
48#define MV64X60_SDRAM_ERR_ECC_CALC 0x4c /* 0x144c */
49#define MV64X60_SDRAM_ERR_ADDR 0x50 /* 0x1450 */
50#define MV64X60_SDRAM_ERR_ECC_CNTL 0x54 /* 0x1454 */
51#define MV64X60_SDRAM_ERR_ECC_ERR_CNT 0x58 /* 0x1458 */
52
53#define MV64X60_SDRAM_REGISTERED 0x20000
54#define MV64X60_SDRAM_ECC 0x40000
55
56#ifdef CONFIG_PCI
57/*
58 * Bit 0 of MV64x60_PCIx_ERR_MASK does not exist on the 64360 and because of
59 * errata FEr-#11 and FEr-##16 for the 64460, it should be 0 on that chip as
60 * well. IOW, don't set bit 0.
61 */
62#define MV64X60_PCIx_ERR_MASK_VAL 0x00a50c24
63
64/* Register offsets from PCIx error address low register */
65#define MV64X60_PCI_ERROR_ADDR_LO 0x00
66#define MV64X60_PCI_ERROR_ADDR_HI 0x04
67#define MV64X60_PCI_ERROR_ATTR 0x08
68#define MV64X60_PCI_ERROR_CMD 0x10
69#define MV64X60_PCI_ERROR_CAUSE 0x18
70#define MV64X60_PCI_ERROR_MASK 0x1c
71
72#define MV64X60_PCI_ERR_SWrPerr 0x0002
73#define MV64X60_PCI_ERR_SRdPerr 0x0004
74#define MV64X60_PCI_ERR_MWrPerr 0x0020
75#define MV64X60_PCI_ERR_MRdPerr 0x0040
76
77#define MV64X60_PCI_PE_MASK (MV64X60_PCI_ERR_SWrPerr | \
78 MV64X60_PCI_ERR_SRdPerr | \
79 MV64X60_PCI_ERR_MWrPerr | \
80 MV64X60_PCI_ERR_MRdPerr)
81
82struct mv64x60_pci_pdata {
83 int pci_hose;
84 void __iomem *pci_vbase;
85 char *name;
86 int irq;
87 int edac_idx;
88};
89
90#endif /* CONFIG_PCI */
91
92struct mv64x60_mc_pdata {
93 void __iomem *mc_vbase;
94 int total_mem;
95 char *name;
96 int irq;
97 int edac_idx;
98};
99
100struct mv64x60_cpu_pdata {
101 void __iomem *cpu_vbase[2];
102 char *name;
103 int irq;
104 int edac_idx;
105};
106
107struct mv64x60_sram_pdata {
108 void __iomem *sram_vbase;
109 char *name;
110 int irq;
111 int edac_idx;
112};
113
114#endif