]>
Commit | Line | Data |
---|---|---|
4f4aeeab DJ |
1 | /* |
2 | * Marvell MV64x60 Memory Controller kernel module for PPC platforms | |
3 | * | |
4 | * Author: Dave Jiang <[email protected]> | |
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> | |
4f4aeeab DJ |
15 | #include <linux/interrupt.h> |
16 | #include <linux/io.h> | |
17 | #include <linux/edac.h> | |
5a0e3ad6 | 18 | #include <linux/gfp.h> |
4f4aeeab DJ |
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 | ||
fcb19171 DJ |
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 | ||
30a61fff | 93 | pci_serr = ioremap(r->start, resource_size(r)); |
fcb19171 DJ |
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 | ||
4f4aeeab DJ |
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; | |
031d5518 | 124 | pci->dev_name = dev_name(&pdev->dev); |
4f4aeeab DJ |
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, | |
30a61fff | 143 | resource_size(r), |
4f4aeeab DJ |
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, | |
30a61fff | 153 | resource_size(r)); |
4f4aeeab DJ |
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 | ||
fcb19171 DJ |
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 | ||
4f4aeeab DJ |
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 | debugf3("%s(): failed edac_pci_add_device()\n", __func__); | |
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 | debugf3("%s(): success\n", __func__); | |
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 | debugf0("%s()\n", __func__); | |
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); | |
031d5518 | 297 | edac_dev->dev_name = dev_name(&pdev->dev); |
4f4aeeab DJ |
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, | |
30a61fff | 309 | resource_size(r), |
4f4aeeab DJ |
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, | |
30a61fff | 319 | resource_size(r)); |
4f4aeeab DJ |
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 | debugf3("%s(): failed edac_device_add_device()\n", __func__); | |
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 | debugf3("%s(): success\n", __func__); | |
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 | debugf0("%s()\n", __func__); | |
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); | |
031d5518 | 465 | edac_dev->dev_name = dev_name(&pdev->dev); |
4f4aeeab DJ |
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, | |
30a61fff | 477 | resource_size(r), |
4f4aeeab DJ |
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, | |
30a61fff | 487 | resource_size(r)); |
4f4aeeab DJ |
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, | |
30a61fff | 504 | resource_size(r), |
4f4aeeab DJ |
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, | |
30a61fff | 514 | resource_size(r)); |
4f4aeeab DJ |
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 | debugf3("%s(): failed edac_device_add_device()\n", __func__); | |
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 | debugf3("%s(): success\n", __func__); | |
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 | debugf0("%s()\n", __func__); | |
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_ce(mci, err_addr >> PAGE_SHIFT, | |
615 | err_addr & PAGE_MASK, syndrome, 0, 0, | |
616 | mci->ctl_name); | |
617 | else /* 2 bit error, UE */ | |
618 | edac_mc_handle_ue(mci, err_addr >> PAGE_SHIFT, | |
619 | err_addr & PAGE_MASK, 0, mci->ctl_name); | |
620 | ||
621 | /* clear the error */ | |
622 | out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR, 0); | |
623 | } | |
624 | ||
625 | static irqreturn_t mv64x60_mc_isr(int irq, void *dev_id) | |
626 | { | |
627 | struct mem_ctl_info *mci = dev_id; | |
628 | struct mv64x60_mc_pdata *pdata = mci->pvt_info; | |
629 | u32 reg; | |
630 | ||
631 | reg = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR); | |
632 | if (!reg) | |
633 | return IRQ_NONE; | |
634 | ||
635 | /* writing 0's to the ECC err addr in check function clears irq */ | |
636 | mv64x60_mc_check(mci); | |
637 | ||
638 | return IRQ_HANDLED; | |
639 | } | |
640 | ||
641 | static void get_total_mem(struct mv64x60_mc_pdata *pdata) | |
642 | { | |
643 | struct device_node *np = NULL; | |
644 | const unsigned int *reg; | |
645 | ||
646 | np = of_find_node_by_type(NULL, "memory"); | |
647 | if (!np) | |
648 | return; | |
649 | ||
596d3941 | 650 | reg = of_get_property(np, "reg", NULL); |
4f4aeeab DJ |
651 | |
652 | pdata->total_mem = reg[1]; | |
653 | } | |
654 | ||
655 | static void mv64x60_init_csrows(struct mem_ctl_info *mci, | |
656 | struct mv64x60_mc_pdata *pdata) | |
657 | { | |
658 | struct csrow_info *csrow; | |
659 | u32 devtype; | |
660 | u32 ctl; | |
661 | ||
662 | get_total_mem(pdata); | |
663 | ||
664 | ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG); | |
665 | ||
666 | csrow = &mci->csrows[0]; | |
667 | csrow->first_page = 0; | |
668 | csrow->nr_pages = pdata->total_mem >> PAGE_SHIFT; | |
669 | csrow->last_page = csrow->first_page + csrow->nr_pages - 1; | |
670 | csrow->grain = 8; | |
671 | ||
672 | csrow->mtype = (ctl & MV64X60_SDRAM_REGISTERED) ? MEM_RDDR : MEM_DDR; | |
673 | ||
674 | devtype = (ctl >> 20) & 0x3; | |
675 | switch (devtype) { | |
676 | case 0x0: | |
677 | csrow->dtype = DEV_X32; | |
678 | break; | |
679 | case 0x2: /* could be X8 too, but no way to tell */ | |
680 | csrow->dtype = DEV_X16; | |
681 | break; | |
682 | case 0x3: | |
683 | csrow->dtype = DEV_X4; | |
684 | break; | |
685 | default: | |
686 | csrow->dtype = DEV_UNKNOWN; | |
687 | break; | |
688 | } | |
689 | ||
690 | csrow->edac_mode = EDAC_SECDED; | |
691 | } | |
692 | ||
693 | static int __devinit mv64x60_mc_err_probe(struct platform_device *pdev) | |
694 | { | |
695 | struct mem_ctl_info *mci; | |
696 | struct mv64x60_mc_pdata *pdata; | |
697 | struct resource *r; | |
698 | u32 ctl; | |
699 | int res = 0; | |
700 | ||
701 | if (!devres_open_group(&pdev->dev, mv64x60_mc_err_probe, GFP_KERNEL)) | |
702 | return -ENOMEM; | |
703 | ||
704 | mci = edac_mc_alloc(sizeof(struct mv64x60_mc_pdata), 1, 1, edac_mc_idx); | |
705 | if (!mci) { | |
706 | printk(KERN_ERR "%s: No memory for CPU err\n", __func__); | |
707 | devres_release_group(&pdev->dev, mv64x60_mc_err_probe); | |
708 | return -ENOMEM; | |
709 | } | |
710 | ||
711 | pdata = mci->pvt_info; | |
712 | mci->dev = &pdev->dev; | |
713 | platform_set_drvdata(pdev, mci); | |
714 | pdata->name = "mv64x60_mc_err"; | |
715 | pdata->irq = NO_IRQ; | |
031d5518 | 716 | mci->dev_name = dev_name(&pdev->dev); |
4f4aeeab DJ |
717 | pdata->edac_idx = edac_mc_idx++; |
718 | ||
719 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | |
720 | if (!r) { | |
721 | printk(KERN_ERR "%s: Unable to get resource for " | |
722 | "MC err regs\n", __func__); | |
723 | res = -ENOENT; | |
724 | goto err; | |
725 | } | |
726 | ||
727 | if (!devm_request_mem_region(&pdev->dev, | |
728 | r->start, | |
30a61fff | 729 | resource_size(r), |
4f4aeeab DJ |
730 | pdata->name)) { |
731 | printk(KERN_ERR "%s: Error while requesting mem region\n", | |
732 | __func__); | |
733 | res = -EBUSY; | |
734 | goto err; | |
735 | } | |
736 | ||
737 | pdata->mc_vbase = devm_ioremap(&pdev->dev, | |
738 | r->start, | |
30a61fff | 739 | resource_size(r)); |
4f4aeeab DJ |
740 | if (!pdata->mc_vbase) { |
741 | printk(KERN_ERR "%s: Unable to setup MC err regs\n", __func__); | |
742 | res = -ENOMEM; | |
743 | goto err; | |
744 | } | |
745 | ||
746 | ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_CONFIG); | |
747 | if (!(ctl & MV64X60_SDRAM_ECC)) { | |
748 | /* Non-ECC RAM? */ | |
749 | printk(KERN_WARNING "%s: No ECC DIMMs discovered\n", __func__); | |
750 | res = -ENODEV; | |
751 | goto err2; | |
752 | } | |
753 | ||
754 | debugf3("%s(): init mci\n", __func__); | |
755 | mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_DDR; | |
756 | mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; | |
757 | mci->edac_cap = EDAC_FLAG_SECDED; | |
758 | mci->mod_name = EDAC_MOD_STR; | |
759 | mci->mod_ver = MV64x60_REVISION; | |
760 | mci->ctl_name = mv64x60_ctl_name; | |
761 | ||
762 | if (edac_op_state == EDAC_OPSTATE_POLL) | |
763 | mci->edac_check = mv64x60_mc_check; | |
764 | ||
765 | mci->ctl_page_to_phys = NULL; | |
766 | ||
767 | mci->scrub_mode = SCRUB_SW_SRC; | |
768 | ||
769 | mv64x60_init_csrows(mci, pdata); | |
770 | ||
771 | /* setup MC registers */ | |
772 | out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ADDR, 0); | |
773 | ctl = in_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL); | |
774 | ctl = (ctl & 0xff00ffff) | 0x10000; | |
775 | out_le32(pdata->mc_vbase + MV64X60_SDRAM_ERR_ECC_CNTL, ctl); | |
776 | ||
777 | if (edac_mc_add_mc(mci)) { | |
778 | debugf3("%s(): failed edac_mc_add_mc()\n", __func__); | |
779 | goto err; | |
780 | } | |
781 | ||
782 | if (edac_op_state == EDAC_OPSTATE_INT) { | |
783 | /* acquire interrupt that reports errors */ | |
784 | pdata->irq = platform_get_irq(pdev, 0); | |
785 | res = devm_request_irq(&pdev->dev, | |
786 | pdata->irq, | |
787 | mv64x60_mc_isr, | |
788 | IRQF_DISABLED, | |
789 | "[EDAC] MC err", | |
790 | mci); | |
791 | if (res < 0) { | |
792 | printk(KERN_ERR "%s: Unable to request irq %d for " | |
793 | "MV64x60 DRAM ERR\n", __func__, pdata->irq); | |
794 | res = -ENODEV; | |
795 | goto err2; | |
796 | } | |
797 | ||
798 | printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for MC Err\n", | |
799 | pdata->irq); | |
800 | } | |
801 | ||
802 | /* get this far and it's successful */ | |
803 | debugf3("%s(): success\n", __func__); | |
804 | ||
805 | return 0; | |
806 | ||
807 | err2: | |
808 | edac_mc_del_mc(&pdev->dev); | |
809 | err: | |
810 | devres_release_group(&pdev->dev, mv64x60_mc_err_probe); | |
811 | edac_mc_free(mci); | |
812 | return res; | |
813 | } | |
814 | ||
815 | static int mv64x60_mc_err_remove(struct platform_device *pdev) | |
816 | { | |
817 | struct mem_ctl_info *mci = platform_get_drvdata(pdev); | |
818 | ||
819 | debugf0("%s()\n", __func__); | |
820 | ||
821 | edac_mc_del_mc(&pdev->dev); | |
822 | edac_mc_free(mci); | |
823 | return 0; | |
824 | } | |
825 | ||
826 | static struct platform_driver mv64x60_mc_err_driver = { | |
827 | .probe = mv64x60_mc_err_probe, | |
828 | .remove = mv64x60_mc_err_remove, | |
829 | .driver = { | |
830 | .name = "mv64x60_mc_err", | |
831 | } | |
832 | }; | |
833 | ||
834 | static int __init mv64x60_edac_init(void) | |
835 | { | |
836 | int ret = 0; | |
837 | ||
838 | printk(KERN_INFO "Marvell MV64x60 EDAC driver " MV64x60_REVISION "\n"); | |
839 | printk(KERN_INFO "\t(C) 2006-2007 MontaVista Software\n"); | |
840 | /* make sure error reporting method is sane */ | |
841 | switch (edac_op_state) { | |
842 | case EDAC_OPSTATE_POLL: | |
843 | case EDAC_OPSTATE_INT: | |
844 | break; | |
845 | default: | |
846 | edac_op_state = EDAC_OPSTATE_INT; | |
847 | break; | |
848 | } | |
849 | ||
850 | ret = platform_driver_register(&mv64x60_mc_err_driver); | |
851 | if (ret) | |
852 | printk(KERN_WARNING EDAC_MOD_STR "MC err failed to register\n"); | |
853 | ||
854 | ret = platform_driver_register(&mv64x60_cpu_err_driver); | |
855 | if (ret) | |
856 | printk(KERN_WARNING EDAC_MOD_STR | |
857 | "CPU err failed to register\n"); | |
858 | ||
859 | ret = platform_driver_register(&mv64x60_sram_err_driver); | |
860 | if (ret) | |
861 | printk(KERN_WARNING EDAC_MOD_STR | |
862 | "SRAM err failed to register\n"); | |
863 | ||
864 | #ifdef CONFIG_PCI | |
865 | ret = platform_driver_register(&mv64x60_pci_err_driver); | |
866 | if (ret) | |
867 | printk(KERN_WARNING EDAC_MOD_STR | |
868 | "PCI err failed to register\n"); | |
869 | #endif | |
870 | ||
871 | return ret; | |
872 | } | |
873 | module_init(mv64x60_edac_init); | |
874 | ||
875 | static void __exit mv64x60_edac_exit(void) | |
876 | { | |
877 | #ifdef CONFIG_PCI | |
878 | platform_driver_unregister(&mv64x60_pci_err_driver); | |
879 | #endif | |
880 | platform_driver_unregister(&mv64x60_sram_err_driver); | |
881 | platform_driver_unregister(&mv64x60_cpu_err_driver); | |
882 | platform_driver_unregister(&mv64x60_mc_err_driver); | |
883 | } | |
884 | module_exit(mv64x60_edac_exit); | |
885 | ||
886 | MODULE_LICENSE("GPL"); | |
887 | MODULE_AUTHOR("Montavista Software, Inc."); | |
888 | module_param(edac_op_state, int, 0444); | |
889 | MODULE_PARM_DESC(edac_op_state, | |
890 | "EDAC Error Reporting state: 0=Poll, 2=Interrupt"); |