Root/
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/interrupt.h> |
16 | #include <linux/io.h> |
17 | #include <linux/edac.h> |
18 | #include <linux/gfp.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 | /* |
75 | * Bit 0 of MV64x60_PCIx_ERR_MASK does not exist on the 64360 and because of |
76 | * errata FEr-#11 and FEr-##16 for the 64460, it should be 0 on that chip as |
77 | * well. IOW, don't set bit 0. |
78 | */ |
79 | |
80 | /* Erratum FEr PCI-#16: clear bit 0 of PCI SERRn Mask reg. */ |
81 | static int __init mv64x60_pci_fixup(struct platform_device *pdev) |
82 | { |
83 | struct resource *r; |
84 | void __iomem *pci_serr; |
85 | |
86 | r = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
87 | if (!r) { |
88 | printk(KERN_ERR "%s: Unable to get resource for " |
89 | "PCI err regs\n", __func__); |
90 | return -ENOENT; |
91 | } |
92 | |
93 | pci_serr = ioremap(r->start, resource_size(r)); |
94 | if (!pci_serr) |
95 | return -ENOMEM; |
96 | |
97 | out_le32(pci_serr, in_le32(pci_serr) & ~0x1); |
98 | iounmap(pci_serr); |
99 | |
100 | return 0; |
101 | } |
102 | |
103 | static int __devinit mv64x60_pci_err_probe(struct platform_device *pdev) |
104 | { |
105 | struct edac_pci_ctl_info *pci; |
106 | struct mv64x60_pci_pdata *pdata; |
107 | struct resource *r; |
108 | int res = 0; |
109 | |
110 | if (!devres_open_group(&pdev->dev, mv64x60_pci_err_probe, GFP_KERNEL)) |
111 | return -ENOMEM; |
112 | |
113 | pci = edac_pci_alloc_ctl_info(sizeof(*pdata), "mv64x60_pci_err"); |
114 | if (!pci) |
115 | return -ENOMEM; |
116 | |
117 | pdata = pci->pvt_info; |
118 | |
119 | pdata->pci_hose = pdev->id; |
120 | pdata->name = "mpc85xx_pci_err"; |
121 | pdata->irq = NO_IRQ; |
122 | platform_set_drvdata(pdev, pci); |
123 | pci->dev = &pdev->dev; |
124 | pci->dev_name = dev_name(&pdev->dev); |
125 | pci->mod_name = EDAC_MOD_STR; |
126 | pci->ctl_name = pdata->name; |
127 | |
128 | if (edac_op_state == EDAC_OPSTATE_POLL) |
129 | pci->edac_check = mv64x60_pci_check; |
130 | |
131 | pdata->edac_idx = edac_pci_idx++; |
132 | |
133 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
134 | if (!r) { |
135 | printk(KERN_ERR "%s: Unable to get resource for " |
136 | "PCI err regs\n", __func__); |
137 | res = -ENOENT; |
138 | goto err; |
139 | } |
140 | |
141 | if (!devm_request_mem_region(&pdev->dev, |
142 | r->start, |
143 | resource_size(r), |
144 | pdata->name)) { |
145 | printk(KERN_ERR "%s: Error while requesting mem region\n", |
146 | __func__); |
147 | res = -EBUSY; |
148 | goto err; |
149 | } |
150 | |
151 | pdata->pci_vbase = devm_ioremap(&pdev->dev, |
152 | r->start, |
153 | resource_size(r)); |
154 | if (!pdata->pci_vbase) { |
155 | printk(KERN_ERR "%s: Unable to setup PCI err regs\n", __func__); |
156 | res = -ENOMEM; |
157 | goto err; |
158 | } |
159 | |
160 | res = mv64x60_pci_fixup(pdev); |
161 | if (res < 0) { |
162 | printk(KERN_ERR "%s: PCI fixup failed\n", __func__); |
163 | goto err; |
164 | } |
165 | |
166 | out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_CAUSE, 0); |
167 | out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_MASK, 0); |
168 | out_le32(pdata->pci_vbase + MV64X60_PCI_ERROR_MASK, |
169 | MV64X60_PCIx_ERR_MASK_VAL); |
170 | |
171 | if (edac_pci_add_device(pci, pdata->edac_idx) > 0) { |
172 | edac_dbg(3, "failed edac_pci_add_device()\n"); |
173 | goto err; |
174 | } |
175 | |
176 | if (edac_op_state == EDAC_OPSTATE_INT) { |
177 | pdata->irq = platform_get_irq(pdev, 0); |
178 | res = devm_request_irq(&pdev->dev, |
179 | pdata->irq, |
180 | mv64x60_pci_isr, |
181 | IRQF_DISABLED, |
182 | "[EDAC] PCI err", |
183 | pci); |
184 | if (res < 0) { |
185 | printk(KERN_ERR "%s: Unable to request irq %d for " |
186 | "MV64x60 PCI ERR\n", __func__, pdata->irq); |
187 | res = -ENODEV; |
188 | goto err2; |
189 | } |
190 | printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for PCI Err\n", |
191 | pdata->irq); |
192 | } |
193 | |
194 | devres_remove_group(&pdev->dev, mv64x60_pci_err_probe); |
195 | |
196 | /* get this far and it's successful */ |
197 | edac_dbg(3, "success\n"); |
198 | |
199 | return 0; |
200 | |
201 | err2: |
202 | edac_pci_del_device(&pdev->dev); |
203 | err: |
204 | edac_pci_free_ctl_info(pci); |
205 | devres_release_group(&pdev->dev, mv64x60_pci_err_probe); |
206 | return res; |
207 | } |
208 | |
209 | static int mv64x60_pci_err_remove(struct platform_device *pdev) |
210 | { |
211 | struct edac_pci_ctl_info *pci = platform_get_drvdata(pdev); |
212 | |
213 | edac_dbg(0, "\n"); |
214 | |
215 | edac_pci_del_device(&pdev->dev); |
216 | |
217 | edac_pci_free_ctl_info(pci); |
218 | |
219 | return 0; |
220 | } |
221 | |
222 | static struct platform_driver mv64x60_pci_err_driver = { |
223 | .probe = mv64x60_pci_err_probe, |
224 | .remove = __devexit_p(mv64x60_pci_err_remove), |
225 | .driver = { |
226 | .name = "mv64x60_pci_err", |
227 | } |
228 | }; |
229 | |
230 | #endif /* CONFIG_PCI */ |
231 | |
232 | /*********************** SRAM err device **********************************/ |
233 | static void mv64x60_sram_check(struct edac_device_ctl_info *edac_dev) |
234 | { |
235 | struct mv64x60_sram_pdata *pdata = edac_dev->pvt_info; |
236 | u32 cause; |
237 | |
238 | cause = in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE); |
239 | if (!cause) |
240 | return; |
241 | |
242 | printk(KERN_ERR "Error in internal SRAM\n"); |
243 | printk(KERN_ERR "Cause register: 0x%08x\n", cause); |
244 | printk(KERN_ERR "Address Low: 0x%08x\n", |
245 | in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_ADDR_LO)); |
246 | printk(KERN_ERR "Address High: 0x%08x\n", |
247 | in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_ADDR_HI)); |
248 | printk(KERN_ERR "Data Low: 0x%08x\n", |
249 | in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_DATA_LO)); |
250 | printk(KERN_ERR "Data High: 0x%08x\n", |
251 | in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_DATA_HI)); |
252 | printk(KERN_ERR "Parity: 0x%08x\n", |
253 | in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_PARITY)); |
254 | out_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE, 0); |
255 | |
256 | edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name); |
257 | } |
258 | |
259 | static irqreturn_t mv64x60_sram_isr(int irq, void *dev_id) |
260 | { |
261 | struct edac_device_ctl_info *edac_dev = dev_id; |
262 | struct mv64x60_sram_pdata *pdata = edac_dev->pvt_info; |
263 | u32 cause; |
264 | |
265 | cause = in_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE); |
266 | if (!cause) |
267 | return IRQ_NONE; |
268 | |
269 | mv64x60_sram_check(edac_dev); |
270 | |
271 | return IRQ_HANDLED; |
272 | } |
273 | |
274 | static int __devinit mv64x60_sram_err_probe(struct platform_device *pdev) |
275 | { |
276 | struct edac_device_ctl_info *edac_dev; |
277 | struct mv64x60_sram_pdata *pdata; |
278 | struct resource *r; |
279 | int res = 0; |
280 | |
281 | if (!devres_open_group(&pdev->dev, mv64x60_sram_err_probe, GFP_KERNEL)) |
282 | return -ENOMEM; |
283 | |
284 | edac_dev = edac_device_alloc_ctl_info(sizeof(*pdata), |
285 | "sram", 1, NULL, 0, 0, NULL, 0, |
286 | edac_dev_idx); |
287 | if (!edac_dev) { |
288 | devres_release_group(&pdev->dev, mv64x60_sram_err_probe); |
289 | return -ENOMEM; |
290 | } |
291 | |
292 | pdata = edac_dev->pvt_info; |
293 | pdata->name = "mv64x60_sram_err"; |
294 | pdata->irq = NO_IRQ; |
295 | edac_dev->dev = &pdev->dev; |
296 | platform_set_drvdata(pdev, edac_dev); |
297 | edac_dev->dev_name = dev_name(&pdev->dev); |
298 | |
299 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
300 | if (!r) { |
301 | printk(KERN_ERR "%s: Unable to get resource for " |
302 | "SRAM err regs\n", __func__); |
303 | res = -ENOENT; |
304 | goto err; |
305 | } |
306 | |
307 | if (!devm_request_mem_region(&pdev->dev, |
308 | r->start, |
309 | resource_size(r), |
310 | pdata->name)) { |
311 | printk(KERN_ERR "%s: Error while request mem region\n", |
312 | __func__); |
313 | res = -EBUSY; |
314 | goto err; |
315 | } |
316 | |
317 | pdata->sram_vbase = devm_ioremap(&pdev->dev, |
318 | r->start, |
319 | resource_size(r)); |
320 | if (!pdata->sram_vbase) { |
321 | printk(KERN_ERR "%s: Unable to setup SRAM err regs\n", |
322 | __func__); |
323 | res = -ENOMEM; |
324 | goto err; |
325 | } |
326 | |
327 | /* setup SRAM err registers */ |
328 | out_le32(pdata->sram_vbase + MV64X60_SRAM_ERR_CAUSE, 0); |
329 | |
330 | edac_dev->mod_name = EDAC_MOD_STR; |
331 | edac_dev->ctl_name = pdata->name; |
332 | |
333 | if (edac_op_state == EDAC_OPSTATE_POLL) |
334 | edac_dev->edac_check = mv64x60_sram_check; |
335 | |
336 | pdata->edac_idx = edac_dev_idx++; |
337 | |
338 | if (edac_device_add_device(edac_dev) > 0) { |
339 | edac_dbg(3, "failed edac_device_add_device()\n"); |
340 | goto err; |
341 | } |
342 | |
343 | if (edac_op_state == EDAC_OPSTATE_INT) { |
344 | pdata->irq = platform_get_irq(pdev, 0); |
345 | res = devm_request_irq(&pdev->dev, |
346 | pdata->irq, |
347 | mv64x60_sram_isr, |
348 | IRQF_DISABLED, |
349 | "[EDAC] SRAM err", |
350 | edac_dev); |
351 | if (res < 0) { |
352 | printk(KERN_ERR |
353 | "%s: Unable to request irq %d for " |
354 | "MV64x60 SRAM ERR\n", __func__, pdata->irq); |
355 | res = -ENODEV; |
356 | goto err2; |
357 | } |
358 | |
359 | printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for SRAM Err\n", |
360 | pdata->irq); |
361 | } |
362 | |
363 | devres_remove_group(&pdev->dev, mv64x60_sram_err_probe); |
364 | |
365 | /* get this far and it's successful */ |
366 | edac_dbg(3, "success\n"); |
367 | |
368 | return 0; |
369 | |
370 | err2: |
371 | edac_device_del_device(&pdev->dev); |
372 | err: |
373 | devres_release_group(&pdev->dev, mv64x60_sram_err_probe); |
374 | edac_device_free_ctl_info(edac_dev); |
375 | return res; |
376 | } |
377 | |
378 | static int mv64x60_sram_err_remove(struct platform_device *pdev) |
379 | { |
380 | struct edac_device_ctl_info *edac_dev = platform_get_drvdata(pdev); |
381 | |
382 | edac_dbg(0, "\n"); |
383 | |
384 | edac_device_del_device(&pdev->dev); |
385 | edac_device_free_ctl_info(edac_dev); |
386 | |
387 | return 0; |
388 | } |
389 | |
390 | static struct platform_driver mv64x60_sram_err_driver = { |
391 | .probe = mv64x60_sram_err_probe, |
392 | .remove = mv64x60_sram_err_remove, |
393 | .driver = { |
394 | .name = "mv64x60_sram_err", |
395 | } |
396 | }; |
397 | |
398 | /*********************** CPU err device **********************************/ |
399 | static void mv64x60_cpu_check(struct edac_device_ctl_info *edac_dev) |
400 | { |
401 | struct mv64x60_cpu_pdata *pdata = edac_dev->pvt_info; |
402 | u32 cause; |
403 | |
404 | cause = in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE) & |
405 | MV64x60_CPU_CAUSE_MASK; |
406 | if (!cause) |
407 | return; |
408 | |
409 | printk(KERN_ERR "Error on CPU interface\n"); |
410 | printk(KERN_ERR "Cause register: 0x%08x\n", cause); |
411 | printk(KERN_ERR "Address Low: 0x%08x\n", |
412 | in_le32(pdata->cpu_vbase[0] + MV64x60_CPU_ERR_ADDR_LO)); |
413 | printk(KERN_ERR "Address High: 0x%08x\n", |
414 | in_le32(pdata->cpu_vbase[0] + MV64x60_CPU_ERR_ADDR_HI)); |
415 | printk(KERN_ERR "Data Low: 0x%08x\n", |
416 | in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_DATA_LO)); |
417 | printk(KERN_ERR "Data High: 0x%08x\n", |
418 | in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_DATA_HI)); |
419 | printk(KERN_ERR "Parity: 0x%08x\n", |
420 | in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_PARITY)); |
421 | out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE, 0); |
422 | |
423 | edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name); |
424 | } |
425 | |
426 | static irqreturn_t mv64x60_cpu_isr(int irq, void *dev_id) |
427 | { |
428 | struct edac_device_ctl_info *edac_dev = dev_id; |
429 | struct mv64x60_cpu_pdata *pdata = edac_dev->pvt_info; |
430 | u32 cause; |
431 | |
432 | cause = in_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE) & |
433 | MV64x60_CPU_CAUSE_MASK; |
434 | if (!cause) |
435 | return IRQ_NONE; |
436 | |
437 | mv64x60_cpu_check(edac_dev); |
438 | |
439 | return IRQ_HANDLED; |
440 | } |
441 | |
442 | static int __devinit mv64x60_cpu_err_probe(struct platform_device *pdev) |
443 | { |
444 | struct edac_device_ctl_info *edac_dev; |
445 | struct resource *r; |
446 | struct mv64x60_cpu_pdata *pdata; |
447 | int res = 0; |
448 | |
449 | if (!devres_open_group(&pdev->dev, mv64x60_cpu_err_probe, GFP_KERNEL)) |
450 | return -ENOMEM; |
451 | |
452 | edac_dev = edac_device_alloc_ctl_info(sizeof(*pdata), |
453 | "cpu", 1, NULL, 0, 0, NULL, 0, |
454 | edac_dev_idx); |
455 | if (!edac_dev) { |
456 | devres_release_group(&pdev->dev, mv64x60_cpu_err_probe); |
457 | return -ENOMEM; |
458 | } |
459 | |
460 | pdata = edac_dev->pvt_info; |
461 | pdata->name = "mv64x60_cpu_err"; |
462 | pdata->irq = NO_IRQ; |
463 | edac_dev->dev = &pdev->dev; |
464 | platform_set_drvdata(pdev, edac_dev); |
465 | edac_dev->dev_name = dev_name(&pdev->dev); |
466 | |
467 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
468 | if (!r) { |
469 | printk(KERN_ERR "%s: Unable to get resource for " |
470 | "CPU err regs\n", __func__); |
471 | res = -ENOENT; |
472 | goto err; |
473 | } |
474 | |
475 | if (!devm_request_mem_region(&pdev->dev, |
476 | r->start, |
477 | resource_size(r), |
478 | pdata->name)) { |
479 | printk(KERN_ERR "%s: Error while requesting mem region\n", |
480 | __func__); |
481 | res = -EBUSY; |
482 | goto err; |
483 | } |
484 | |
485 | pdata->cpu_vbase[0] = devm_ioremap(&pdev->dev, |
486 | r->start, |
487 | resource_size(r)); |
488 | if (!pdata->cpu_vbase[0]) { |
489 | printk(KERN_ERR "%s: Unable to setup CPU err regs\n", __func__); |
490 | res = -ENOMEM; |
491 | goto err; |
492 | } |
493 | |
494 | r = platform_get_resource(pdev, IORESOURCE_MEM, 1); |
495 | if (!r) { |
496 | printk(KERN_ERR "%s: Unable to get resource for " |
497 | "CPU err regs\n", __func__); |
498 | res = -ENOENT; |
499 | goto err; |
500 | } |
501 | |
502 | if (!devm_request_mem_region(&pdev->dev, |
503 | r->start, |
504 | resource_size(r), |
505 | pdata->name)) { |
506 | printk(KERN_ERR "%s: Error while requesting mem region\n", |
507 | __func__); |
508 | res = -EBUSY; |
509 | goto err; |
510 | } |
511 | |
512 | pdata->cpu_vbase[1] = devm_ioremap(&pdev->dev, |
513 | r->start, |
514 | resource_size(r)); |
515 | if (!pdata->cpu_vbase[1]) { |
516 | printk(KERN_ERR "%s: Unable to setup CPU err regs\n", __func__); |
517 | res = -ENOMEM; |
518 | goto err; |
519 | } |
520 | |
521 | /* setup CPU err registers */ |
522 | out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_CAUSE, 0); |
523 | out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_MASK, 0); |
524 | out_le32(pdata->cpu_vbase[1] + MV64x60_CPU_ERR_MASK, 0x000000ff); |
525 | |
526 | edac_dev->mod_name = EDAC_MOD_STR; |
527 | edac_dev->ctl_name = pdata->name; |
528 | if (edac_op_state == EDAC_OPSTATE_POLL) |
529 | edac_dev->edac_check = mv64x60_cpu_check; |
530 | |
531 | pdata->edac_idx = edac_dev_idx++; |
532 | |
533 | if (edac_device_add_device(edac_dev) > 0) { |
534 | edac_dbg(3, "failed edac_device_add_device()\n"); |
535 | goto err; |
536 | } |
537 | |
538 | if (edac_op_state == EDAC_OPSTATE_INT) { |
539 | pdata->irq = platform_get_irq(pdev, 0); |
540 | res = devm_request_irq(&pdev->dev, |
541 | pdata->irq, |
542 | mv64x60_cpu_isr, |
543 | IRQF_DISABLED, |
544 | "[EDAC] CPU err", |
545 | edac_dev); |
546 | if (res < 0) { |
547 | printk(KERN_ERR |
548 | "%s: Unable to request irq %d for MV64x60 " |
549 | "CPU ERR\n", __func__, pdata->irq); |
550 | res = -ENODEV; |
551 | goto err2; |
552 | } |
553 | |
554 | printk(KERN_INFO EDAC_MOD_STR |
555 | " acquired irq %d for CPU Err\n", pdata->irq); |
556 | } |
557 | |
558 | devres_remove_group(&pdev->dev, mv64x60_cpu_err_probe); |
559 | |
560 | /* get this far and it's successful */ |
561 | edac_dbg(3, "success\n"); |
562 | |
563 | return 0; |
564 | |
565 | err2: |
566 | edac_device_del_device(&pdev->dev); |
567 | err: |
568 | devres_release_group(&pdev->dev, mv64x60_cpu_err_probe); |
569 | edac_device_free_ctl_info(edac_dev); |
570 | return res; |
571 | } |
572 | |
573 | static int mv64x60_cpu_err_remove(struct platform_device *pdev) |
574 | { |
575 | struct edac_device_ctl_info *edac_dev = platform_get_drvdata(pdev); |
576 | |
577 | edac_dbg(0, "\n"); |
578 | |
579 | edac_device_del_device(&pdev->dev); |
580 | edac_device_free_ctl_info(edac_dev); |
581 | return 0; |
582 | } |
583 | |
584 | static struct platform_driver mv64x60_cpu_err_driver = { |
585 | .probe = mv64x60_cpu_err_probe, |
586 | .remove = mv64x60_cpu_err_remove, |
587 | .driver = { |
588 | .name = "mv64x60_cpu_err", |
589 | } |
590 | }; |
591 | |
592 | /*********************** DRAM err device **********************************/ |
593 | |
594 | static void mv64x60_mc_check(struct mem_ctl_info *mci) |
595 | { |
596 | struct mv64x60_mc_pdata *pdata = mci->pvt_info; |
597 | u32 reg; |
598 | u32 err_addr; |
599 | u32 sdram_ecc; |
600 | u32 comp_ecc; |
601 | u32 syndrome; |
602 | |
603 | reg = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR); |
604 | if (!reg) |
605 | return; |
606 | |
607 | err_addr = reg & ~0x3; |
608 | sdram_ecc = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_RCVD); |
609 | comp_ecc = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CALC); |
610 | syndrome = sdram_ecc ^ comp_ecc; |
611 | |
612 | /* first bit clear in ECC Err Reg, 1 bit error, correctable by HW */ |
613 | if (!(reg & 0x1)) |
614 | edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, |
615 | err_addr >> PAGE_SHIFT, |
616 | err_addr & PAGE_MASK, syndrome, |
617 | 0, 0, -1, |
618 | mci->ctl_name, ""); |
619 | else /* 2 bit error, UE */ |
620 | edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, |
621 | err_addr >> PAGE_SHIFT, |
622 | err_addr & PAGE_MASK, 0, |
623 | 0, 0, -1, |
624 | mci->ctl_name, ""); |
625 | |
626 | /* clear the error */ |
627 | out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR, 0); |
628 | } |
629 | |
630 | static irqreturn_t mv64x60_mc_isr(int irq, void *dev_id) |
631 | { |
632 | struct mem_ctl_info *mci = dev_id; |
633 | struct mv64x60_mc_pdata *pdata = mci->pvt_info; |
634 | u32 reg; |
635 | |
636 | reg = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR); |
637 | if (!reg) |
638 | return IRQ_NONE; |
639 | |
640 | /* writing 0's to the ECC err addr in check function clears irq */ |
641 | mv64x60_mc_check(mci); |
642 | |
643 | return IRQ_HANDLED; |
644 | } |
645 | |
646 | static void get_total_mem(struct mv64x60_mc_pdata *pdata) |
647 | { |
648 | struct device_node *np = NULL; |
649 | const unsigned int *reg; |
650 | |
651 | np = of_find_node_by_type(NULL, "memory"); |
652 | if (!np) |
653 | return; |
654 | |
655 | reg = of_get_property(np, "reg", NULL); |
656 | |
657 | pdata->total_mem = reg[1]; |
658 | } |
659 | |
660 | static void mv64x60_init_csrows(struct mem_ctl_info *mci, |
661 | struct mv64x60_mc_pdata *pdata) |
662 | { |
663 | struct csrow_info *csrow; |
664 | struct dimm_info *dimm; |
665 | |
666 | u32 devtype; |
667 | u32 ctl; |
668 | |
669 | get_total_mem(pdata); |
670 | |
671 | ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG); |
672 | |
673 | csrow = mci->csrows[0]; |
674 | dimm = csrow->channels[0]->dimm; |
675 | |
676 | dimm->nr_pages = pdata->total_mem >> PAGE_SHIFT; |
677 | dimm->grain = 8; |
678 | |
679 | dimm->mtype = (ctl & MV64X60_SDRAM_REGISTERED) ? MEM_RDDR : MEM_DDR; |
680 | |
681 | devtype = (ctl >> 20) & 0x3; |
682 | switch (devtype) { |
683 | case 0x0: |
684 | dimm->dtype = DEV_X32; |
685 | break; |
686 | case 0x2: /* could be X8 too, but no way to tell */ |
687 | dimm->dtype = DEV_X16; |
688 | break; |
689 | case 0x3: |
690 | dimm->dtype = DEV_X4; |
691 | break; |
692 | default: |
693 | dimm->dtype = DEV_UNKNOWN; |
694 | break; |
695 | } |
696 | |
697 | dimm->edac_mode = EDAC_SECDED; |
698 | } |
699 | |
700 | static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev) |
701 | { |
702 | struct mem_ctl_info *mci; |
703 | struct edac_mc_layer layers[2]; |
704 | struct mv64x60_mc_pdata *pdata; |
705 | struct resource *r; |
706 | u32 ctl; |
707 | int res = 0; |
708 | |
709 | if (!devres_open_group(&pdev->dev, mv64x60_mc_err_probe, GFP_KERNEL)) |
710 | return -ENOMEM; |
711 | |
712 | layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; |
713 | layers[0].size = 1; |
714 | layers[0].is_virt_csrow = true; |
715 | layers[1].type = EDAC_MC_LAYER_CHANNEL; |
716 | layers[1].size = 1; |
717 | layers[1].is_virt_csrow = false; |
718 | mci = edac_mc_alloc(edac_mc_idx, ARRAY_SIZE(layers), layers, |
719 | sizeof(struct mv64x60_mc_pdata)); |
720 | if (!mci) { |
721 | printk(KERN_ERR "%s: No memory for CPU err\n", __func__); |
722 | devres_release_group(&pdev->dev, mv64x60_mc_err_probe); |
723 | return -ENOMEM; |
724 | } |
725 | |
726 | pdata = mci->pvt_info; |
727 | mci->pdev = &pdev->dev; |
728 | platform_set_drvdata(pdev, mci); |
729 | pdata->name = "mv64x60_mc_err"; |
730 | pdata->irq = NO_IRQ; |
731 | mci->dev_name = dev_name(&pdev->dev); |
732 | pdata->edac_idx = edac_mc_idx++; |
733 | |
734 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
735 | if (!r) { |
736 | printk(KERN_ERR "%s: Unable to get resource for " |
737 | "MC err regs\n", __func__); |
738 | res = -ENOENT; |
739 | goto err; |
740 | } |
741 | |
742 | if (!devm_request_mem_region(&pdev->dev, |
743 | r->start, |
744 | resource_size(r), |
745 | pdata->name)) { |
746 | printk(KERN_ERR "%s: Error while requesting mem region\n", |
747 | __func__); |
748 | res = -EBUSY; |
749 | goto err; |
750 | } |
751 | |
752 | pdata->mc_vbase = devm_ioremap(&pdev->dev, |
753 | r->start, |
754 | resource_size(r)); |
755 | if (!pdata->mc_vbase) { |
756 | printk(KERN_ERR "%s: Unable to setup MC err regs\n", __func__); |
757 | res = -ENOMEM; |
758 | goto err; |
759 | } |
760 | |
761 | ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG); |
762 | if (!(ctl & MV64X60_SDRAM_ECC)) { |
763 | /* Non-ECC RAM? */ |
764 | printk(KERN_WARNING "%s: No ECC DIMMs discovered\n", __func__); |
765 | res = -ENODEV; |
766 | goto err2; |
767 | } |
768 | |
769 | edac_dbg(3, "init mci\n"); |
770 | mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR; |
771 | mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; |
772 | mci->edac_cap = EDAC_FLAG_SECDED; |
773 | mci->mod_name = EDAC_MOD_STR; |
774 | mci->mod_ver = MV64x60_REVISION; |
775 | mci->ctl_name = mv64x60_ctl_name; |
776 | |
777 | if (edac_op_state == EDAC_OPSTATE_POLL) |
778 | mci->edac_check = mv64x60_mc_check; |
779 | |
780 | mci->ctl_page_to_phys = NULL; |
781 | |
782 | mci->scrub_mode = SCRUB_SW_SRC; |
783 | |
784 | mv64x60_init_csrows(mci, pdata); |
785 | |
786 | /* setup MC registers */ |
787 | out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR, 0); |
788 | ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL); |
789 | ctl = (ctl & 0xff00ffff) | 0x10000; |
790 | out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL, ctl); |
791 | |
792 | if (edac_mc_add_mc(mci)) { |
793 | edac_dbg(3, "failed edac_mc_add_mc()\n"); |
794 | goto err; |
795 | } |
796 | |
797 | if (edac_op_state == EDAC_OPSTATE_INT) { |
798 | /* acquire interrupt that reports errors */ |
799 | pdata->irq = platform_get_irq(pdev, 0); |
800 | res = devm_request_irq(&pdev->dev, |
801 | pdata->irq, |
802 | mv64x60_mc_isr, |
803 | IRQF_DISABLED, |
804 | "[EDAC] MC err", |
805 | mci); |
806 | if (res < 0) { |
807 | printk(KERN_ERR "%s: Unable to request irq %d for " |
808 | "MV64x60 DRAM ERR\n", __func__, pdata->irq); |
809 | res = -ENODEV; |
810 | goto err2; |
811 | } |
812 | |
813 | printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for MC Err\n", |
814 | pdata->irq); |
815 | } |
816 | |
817 | /* get this far and it's successful */ |
818 | edac_dbg(3, "success\n"); |
819 | |
820 | return 0; |
821 | |
822 | err2: |
823 | edac_mc_del_mc(&pdev->dev); |
824 | err: |
825 | devres_release_group(&pdev->dev, mv64x60_mc_err_probe); |
826 | edac_mc_free(mci); |
827 | return res; |
828 | } |
829 | |
830 | static int mv64x60_mc_err_remove(struct platform_device *pdev) |
831 | { |
832 | struct mem_ctl_info *mci = platform_get_drvdata(pdev); |
833 | |
834 | edac_dbg(0, "\n"); |
835 | |
836 | edac_mc_del_mc(&pdev->dev); |
837 | edac_mc_free(mci); |
838 | return 0; |
839 | } |
840 | |
841 | static struct platform_driver mv64x60_mc_err_driver = { |
842 | .probe = mv64x60_mc_err_probe, |
843 | .remove = mv64x60_mc_err_remove, |
844 | .driver = { |
845 | .name = "mv64x60_mc_err", |
846 | } |
847 | }; |
848 | |
849 | static int __init mv64x60_edac_init(void) |
850 | { |
851 | int ret = 0; |
852 | |
853 | printk(KERN_INFO "Marvell MV64x60 EDAC driver " MV64x60_REVISION "\n"); |
854 | printk(KERN_INFO "\t(C) 2006-2007 MontaVista Software\n"); |
855 | /* make sure error reporting method is sane */ |
856 | switch (edac_op_state) { |
857 | case EDAC_OPSTATE_POLL: |
858 | case EDAC_OPSTATE_INT: |
859 | break; |
860 | default: |
861 | edac_op_state = EDAC_OPSTATE_INT; |
862 | break; |
863 | } |
864 | |
865 | ret = platform_driver_register(&mv64x60_mc_err_driver); |
866 | if (ret) |
867 | printk(KERN_WARNING EDAC_MOD_STR "MC err failed to register\n"); |
868 | |
869 | ret = platform_driver_register(&mv64x60_cpu_err_driver); |
870 | if (ret) |
871 | printk(KERN_WARNING EDAC_MOD_STR |
872 | "CPU err failed to register\n"); |
873 | |
874 | ret = platform_driver_register(&mv64x60_sram_err_driver); |
875 | if (ret) |
876 | printk(KERN_WARNING EDAC_MOD_STR |
877 | "SRAM err failed to register\n"); |
878 | |
879 | #ifdef CONFIG_PCI |
880 | ret = platform_driver_register(&mv64x60_pci_err_driver); |
881 | if (ret) |
882 | printk(KERN_WARNING EDAC_MOD_STR |
883 | "PCI err failed to register\n"); |
884 | #endif |
885 | |
886 | return ret; |
887 | } |
888 | module_init(mv64x60_edac_init); |
889 | |
890 | static void __exit mv64x60_edac_exit(void) |
891 | { |
892 | #ifdef CONFIG_PCI |
893 | platform_driver_unregister(&mv64x60_pci_err_driver); |
894 | #endif |
895 | platform_driver_unregister(&mv64x60_sram_err_driver); |
896 | platform_driver_unregister(&mv64x60_cpu_err_driver); |
897 | platform_driver_unregister(&mv64x60_mc_err_driver); |
898 | } |
899 | module_exit(mv64x60_edac_exit); |
900 | |
901 | MODULE_LICENSE("GPL"); |
902 | MODULE_AUTHOR("Montavista Software, Inc."); |
903 | module_param(edac_op_state, int, 0444); |
904 | MODULE_PARM_DESC(edac_op_state, |
905 | "EDAC Error Reporting state: 0=Poll, 2=Interrupt"); |
906 |
Branches:
ben-wpan
ben-wpan-stefan
javiroman/ks7010
jz-2.6.34
jz-2.6.34-rc5
jz-2.6.34-rc6
jz-2.6.34-rc7
jz-2.6.35
jz-2.6.36
jz-2.6.37
jz-2.6.38
jz-2.6.39
jz-3.0
jz-3.1
jz-3.11
jz-3.12
jz-3.13
jz-3.15
jz-3.16
jz-3.18-dt
jz-3.2
jz-3.3
jz-3.4
jz-3.5
jz-3.6
jz-3.6-rc2-pwm
jz-3.9
jz-3.9-clk
jz-3.9-rc8
jz47xx
jz47xx-2.6.38
master
Tags:
od-2011-09-04
od-2011-09-18
v2.6.34-rc5
v2.6.34-rc6
v2.6.34-rc7
v3.9