diff options
author | Dave Jiang <djiang@mvista.com> | 2008-02-07 03:14:55 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-07 11:42:23 -0500 |
commit | a9a753d53204bf0f42841f65679c7e1711833bcf (patch) | |
tree | 51aeeb0bd0a87b92204db1577f6dbac51bdee050 /drivers/edac/mpc85xx_edac.c | |
parent | 4d2b165eca960ae12767a6334c51416dca45756c (diff) |
drivers-edac: add freescale mpc85xx driver
EDAC chip driver support for Freescale MPC85xx platforms. PPC based.
Signed-off-by: Dave Jiang <djiang@mvista.com>
Cc: Alan Cox <alan@lxorguk.ukuu.org.uk
Signed-off-by: Doug 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/mpc85xx_edac.c')
-rw-r--r-- | drivers/edac/mpc85xx_edac.c | 1043 |
1 files changed, 1043 insertions, 0 deletions
diff --git a/drivers/edac/mpc85xx_edac.c b/drivers/edac/mpc85xx_edac.c new file mode 100644 index 00000000000..fd1726e2b42 --- /dev/null +++ b/drivers/edac/mpc85xx_edac.c | |||
@@ -0,0 +1,1043 @@ | |||
1 | /* | ||
2 | * Freescale MPC85xx Memory Controller kenel module | ||
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 | #include <linux/module.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/slab.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/ctype.h> | ||
17 | #include <linux/io.h> | ||
18 | #include <linux/mod_devicetable.h> | ||
19 | #include <linux/edac.h> | ||
20 | |||
21 | #include <linux/of_platform.h> | ||
22 | #include <linux/of_device.h> | ||
23 | #include <asm/mpc85xx.h> | ||
24 | #include "edac_module.h" | ||
25 | #include "edac_core.h" | ||
26 | #include "mpc85xx_edac.h" | ||
27 | |||
28 | static int edac_dev_idx; | ||
29 | static int edac_pci_idx; | ||
30 | static int edac_mc_idx; | ||
31 | |||
32 | static u32 orig_ddr_err_disable; | ||
33 | static u32 orig_ddr_err_sbe; | ||
34 | |||
35 | /* | ||
36 | * PCI Err defines | ||
37 | */ | ||
38 | #ifdef CONFIG_PCI | ||
39 | static u32 orig_pci_err_cap_dr; | ||
40 | static u32 orig_pci_err_en; | ||
41 | #endif | ||
42 | |||
43 | static u32 orig_l2_err_disable; | ||
44 | static u32 orig_hid1; | ||
45 | |||
46 | const char *mpc85xx_ctl_name = "MPC85xx"; | ||
47 | |||
48 | /************************ MC SYSFS parts ***********************************/ | ||
49 | |||
50 | static ssize_t mpc85xx_mc_inject_data_hi_show(struct mem_ctl_info *mci, | ||
51 | char *data) | ||
52 | { | ||
53 | struct mpc85xx_mc_pdata *pdata = mci->pvt_info; | ||
54 | return sprintf(data, "0x%08x", | ||
55 | in_be32(pdata->mc_vbase + | ||
56 | MPC85XX_MC_DATA_ERR_INJECT_HI)); | ||
57 | } | ||
58 | |||
59 | static ssize_t mpc85xx_mc_inject_data_lo_show(struct mem_ctl_info *mci, | ||
60 | char *data) | ||
61 | { | ||
62 | struct mpc85xx_mc_pdata *pdata = mci->pvt_info; | ||
63 | return sprintf(data, "0x%08x", | ||
64 | in_be32(pdata->mc_vbase + | ||
65 | MPC85XX_MC_DATA_ERR_INJECT_LO)); | ||
66 | } | ||
67 | |||
68 | static ssize_t mpc85xx_mc_inject_ctrl_show(struct mem_ctl_info *mci, char *data) | ||
69 | { | ||
70 | struct mpc85xx_mc_pdata *pdata = mci->pvt_info; | ||
71 | return sprintf(data, "0x%08x", | ||
72 | in_be32(pdata->mc_vbase + MPC85XX_MC_ECC_ERR_INJECT)); | ||
73 | } | ||
74 | |||
75 | static ssize_t mpc85xx_mc_inject_data_hi_store(struct mem_ctl_info *mci, | ||
76 | const char *data, size_t count) | ||
77 | { | ||
78 | struct mpc85xx_mc_pdata *pdata = mci->pvt_info; | ||
79 | if (isdigit(*data)) { | ||
80 | out_be32(pdata->mc_vbase + MPC85XX_MC_DATA_ERR_INJECT_HI, | ||
81 | simple_strtoul(data, NULL, 0)); | ||
82 | return count; | ||
83 | } | ||
84 | return 0; | ||
85 | } | ||
86 | |||
87 | static ssize_t mpc85xx_mc_inject_data_lo_store(struct mem_ctl_info *mci, | ||
88 | const char *data, size_t count) | ||
89 | { | ||
90 | struct mpc85xx_mc_pdata *pdata = mci->pvt_info; | ||
91 | if (isdigit(*data)) { | ||
92 | out_be32(pdata->mc_vbase + MPC85XX_MC_DATA_ERR_INJECT_LO, | ||
93 | simple_strtoul(data, NULL, 0)); | ||
94 | return count; | ||
95 | } | ||
96 | return 0; | ||
97 | } | ||
98 | |||
99 | static ssize_t mpc85xx_mc_inject_ctrl_store(struct mem_ctl_info *mci, | ||
100 | const char *data, size_t count) | ||
101 | { | ||
102 | struct mpc85xx_mc_pdata *pdata = mci->pvt_info; | ||
103 | if (isdigit(*data)) { | ||
104 | out_be32(pdata->mc_vbase + MPC85XX_MC_ECC_ERR_INJECT, | ||
105 | simple_strtoul(data, NULL, 0)); | ||
106 | return count; | ||
107 | } | ||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static struct mcidev_sysfs_attribute mpc85xx_mc_sysfs_attributes[] = { | ||
112 | { | ||
113 | .attr = { | ||
114 | .name = "inject_data_hi", | ||
115 | .mode = (S_IRUGO | S_IWUSR) | ||
116 | }, | ||
117 | .show = mpc85xx_mc_inject_data_hi_show, | ||
118 | .store = mpc85xx_mc_inject_data_hi_store}, | ||
119 | { | ||
120 | .attr = { | ||
121 | .name = "inject_data_lo", | ||
122 | .mode = (S_IRUGO | S_IWUSR) | ||
123 | }, | ||
124 | .show = mpc85xx_mc_inject_data_lo_show, | ||
125 | .store = mpc85xx_mc_inject_data_lo_store}, | ||
126 | { | ||
127 | .attr = { | ||
128 | .name = "inject_ctrl", | ||
129 | .mode = (S_IRUGO | S_IWUSR) | ||
130 | }, | ||
131 | .show = mpc85xx_mc_inject_ctrl_show, | ||
132 | .store = mpc85xx_mc_inject_ctrl_store}, | ||
133 | |||
134 | /* End of list */ | ||
135 | { | ||
136 | .attr = {.name = NULL} | ||
137 | } | ||
138 | }; | ||
139 | |||
140 | static void mpc85xx_set_mc_sysfs_attributes(struct mem_ctl_info *mci) | ||
141 | { | ||
142 | mci->mc_driver_sysfs_attributes = mpc85xx_mc_sysfs_attributes; | ||
143 | } | ||
144 | |||
145 | /**************************** PCI Err device ***************************/ | ||
146 | #ifdef CONFIG_PCI | ||
147 | |||
148 | static void mpc85xx_pci_check(struct edac_pci_ctl_info *pci) | ||
149 | { | ||
150 | struct mpc85xx_pci_pdata *pdata = pci->pvt_info; | ||
151 | u32 err_detect; | ||
152 | |||
153 | err_detect = in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR); | ||
154 | |||
155 | /* master aborts can happen during PCI config cycles */ | ||
156 | if (!(err_detect & ~(PCI_EDE_MULTI_ERR | PCI_EDE_MST_ABRT))) { | ||
157 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, err_detect); | ||
158 | return; | ||
159 | } | ||
160 | |||
161 | printk(KERN_ERR "PCI error(s) detected\n"); | ||
162 | printk(KERN_ERR "PCI/X ERR_DR register: %#08x\n", err_detect); | ||
163 | |||
164 | printk(KERN_ERR "PCI/X ERR_ATTRIB register: %#08x\n", | ||
165 | in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ATTRIB)); | ||
166 | printk(KERN_ERR "PCI/X ERR_ADDR register: %#08x\n", | ||
167 | in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ADDR)); | ||
168 | printk(KERN_ERR "PCI/X ERR_EXT_ADDR register: %#08x\n", | ||
169 | in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EXT_ADDR)); | ||
170 | printk(KERN_ERR "PCI/X ERR_DL register: %#08x\n", | ||
171 | in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DL)); | ||
172 | printk(KERN_ERR "PCI/X ERR_DH register: %#08x\n", | ||
173 | in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DH)); | ||
174 | |||
175 | /* clear error bits */ | ||
176 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, err_detect); | ||
177 | |||
178 | if (err_detect & PCI_EDE_PERR_MASK) | ||
179 | edac_pci_handle_pe(pci, pci->ctl_name); | ||
180 | |||
181 | if ((err_detect & ~PCI_EDE_MULTI_ERR) & ~PCI_EDE_PERR_MASK) | ||
182 | edac_pci_handle_npe(pci, pci->ctl_name); | ||
183 | } | ||
184 | |||
185 | static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id) | ||
186 | { | ||
187 | struct edac_pci_ctl_info *pci = dev_id; | ||
188 | struct mpc85xx_pci_pdata *pdata = pci->pvt_info; | ||
189 | u32 err_detect; | ||
190 | |||
191 | err_detect = in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR); | ||
192 | |||
193 | if (!err_detect) | ||
194 | return IRQ_NONE; | ||
195 | |||
196 | mpc85xx_pci_check(pci); | ||
197 | |||
198 | return IRQ_HANDLED; | ||
199 | } | ||
200 | |||
201 | static int __devinit mpc85xx_pci_err_probe(struct platform_device *pdev) | ||
202 | { | ||
203 | struct edac_pci_ctl_info *pci; | ||
204 | struct mpc85xx_pci_pdata *pdata; | ||
205 | struct resource *r; | ||
206 | int res = 0; | ||
207 | |||
208 | if (!devres_open_group(&pdev->dev, mpc85xx_pci_err_probe, GFP_KERNEL)) | ||
209 | return -ENOMEM; | ||
210 | |||
211 | pci = edac_pci_alloc_ctl_info(sizeof(*pdata), "mpc85xx_pci_err"); | ||
212 | if (!pci) | ||
213 | return -ENOMEM; | ||
214 | |||
215 | pdata = pci->pvt_info; | ||
216 | pdata->name = "mpc85xx_pci_err"; | ||
217 | pdata->irq = NO_IRQ; | ||
218 | platform_set_drvdata(pdev, pci); | ||
219 | pci->dev = &pdev->dev; | ||
220 | pci->mod_name = EDAC_MOD_STR; | ||
221 | pci->ctl_name = pdata->name; | ||
222 | pci->dev_name = pdev->dev.bus_id; | ||
223 | |||
224 | if (edac_op_state == EDAC_OPSTATE_POLL) | ||
225 | pci->edac_check = mpc85xx_pci_check; | ||
226 | |||
227 | pdata->edac_idx = edac_pci_idx++; | ||
228 | |||
229 | r = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
230 | if (!r) { | ||
231 | printk(KERN_ERR "%s: Unable to get resource for " | ||
232 | "PCI err regs\n", __func__); | ||
233 | goto err; | ||
234 | } | ||
235 | |||
236 | if (!devm_request_mem_region(&pdev->dev, r->start, | ||
237 | r->end - r->start + 1, pdata->name)) { | ||
238 | printk(KERN_ERR "%s: Error while requesting mem region\n", | ||
239 | __func__); | ||
240 | res = -EBUSY; | ||
241 | goto err; | ||
242 | } | ||
243 | |||
244 | pdata->pci_vbase = devm_ioremap(&pdev->dev, r->start, | ||
245 | r->end - r->start + 1); | ||
246 | if (!pdata->pci_vbase) { | ||
247 | printk(KERN_ERR "%s: Unable to setup PCI err regs\n", __func__); | ||
248 | res = -ENOMEM; | ||
249 | goto err; | ||
250 | } | ||
251 | |||
252 | orig_pci_err_cap_dr = | ||
253 | in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR); | ||
254 | |||
255 | /* PCI master abort is expected during config cycles */ | ||
256 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR, 0x40); | ||
257 | |||
258 | orig_pci_err_en = in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN); | ||
259 | |||
260 | /* disable master abort reporting */ | ||
261 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, ~0x40); | ||
262 | |||
263 | /* clear error bits */ | ||
264 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, ~0); | ||
265 | |||
266 | if (edac_pci_add_device(pci, pdata->edac_idx) > 0) { | ||
267 | debugf3("%s(): failed edac_pci_add_device()\n", __func__); | ||
268 | goto err; | ||
269 | } | ||
270 | |||
271 | if (edac_op_state == EDAC_OPSTATE_INT) { | ||
272 | pdata->irq = platform_get_irq(pdev, 0); | ||
273 | res = devm_request_irq(&pdev->dev, pdata->irq, | ||
274 | mpc85xx_pci_isr, IRQF_DISABLED, | ||
275 | "[EDAC] PCI err", pci); | ||
276 | if (res < 0) { | ||
277 | printk(KERN_ERR | ||
278 | "%s: Unable to requiest irq %d for " | ||
279 | "MPC85xx PCI err\n", __func__, pdata->irq); | ||
280 | res = -ENODEV; | ||
281 | goto err2; | ||
282 | } | ||
283 | |||
284 | printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for PCI Err\n", | ||
285 | pdata->irq); | ||
286 | } | ||
287 | |||
288 | devres_remove_group(&pdev->dev, mpc85xx_pci_err_probe); | ||
289 | debugf3("%s(): success\n", __func__); | ||
290 | printk(KERN_INFO EDAC_MOD_STR " PCI err registered\n"); | ||
291 | |||
292 | return 0; | ||
293 | |||
294 | err2: | ||
295 | edac_pci_del_device(&pdev->dev); | ||
296 | err: | ||
297 | edac_pci_free_ctl_info(pci); | ||
298 | devres_release_group(&pdev->dev, mpc85xx_pci_err_probe); | ||
299 | return res; | ||
300 | } | ||
301 | |||
302 | static int mpc85xx_pci_err_remove(struct platform_device *pdev) | ||
303 | { | ||
304 | struct edac_pci_ctl_info *pci = platform_get_drvdata(pdev); | ||
305 | struct mpc85xx_pci_pdata *pdata = pci->pvt_info; | ||
306 | |||
307 | debugf0("%s()\n", __func__); | ||
308 | |||
309 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR, | ||
310 | orig_pci_err_cap_dr); | ||
311 | |||
312 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, orig_pci_err_en); | ||
313 | |||
314 | edac_pci_del_device(pci->dev); | ||
315 | |||
316 | if (edac_op_state == EDAC_OPSTATE_INT) | ||
317 | irq_dispose_mapping(pdata->irq); | ||
318 | |||
319 | edac_pci_free_ctl_info(pci); | ||
320 | |||
321 | return 0; | ||
322 | } | ||
323 | |||
324 | static struct platform_driver mpc85xx_pci_err_driver = { | ||
325 | .probe = mpc85xx_pci_err_probe, | ||
326 | .remove = __devexit_p(mpc85xx_pci_err_remove), | ||
327 | .driver = { | ||
328 | .name = "mpc85xx_pci_err", | ||
329 | } | ||
330 | }; | ||
331 | |||
332 | #endif /* CONFIG_PCI */ | ||
333 | |||
334 | /**************************** L2 Err device ***************************/ | ||
335 | |||
336 | /************************ L2 SYSFS parts ***********************************/ | ||
337 | |||
338 | static ssize_t mpc85xx_l2_inject_data_hi_show(struct edac_device_ctl_info | ||
339 | *edac_dev, char *data) | ||
340 | { | ||
341 | struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; | ||
342 | return sprintf(data, "0x%08x", | ||
343 | in_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJHI)); | ||
344 | } | ||
345 | |||
346 | static ssize_t mpc85xx_l2_inject_data_lo_show(struct edac_device_ctl_info | ||
347 | *edac_dev, char *data) | ||
348 | { | ||
349 | struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; | ||
350 | return sprintf(data, "0x%08x", | ||
351 | in_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJLO)); | ||
352 | } | ||
353 | |||
354 | static ssize_t mpc85xx_l2_inject_ctrl_show(struct edac_device_ctl_info | ||
355 | *edac_dev, char *data) | ||
356 | { | ||
357 | struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; | ||
358 | return sprintf(data, "0x%08x", | ||
359 | in_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJCTL)); | ||
360 | } | ||
361 | |||
362 | static ssize_t mpc85xx_l2_inject_data_hi_store(struct edac_device_ctl_info | ||
363 | *edac_dev, const char *data, | ||
364 | size_t count) | ||
365 | { | ||
366 | struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; | ||
367 | if (isdigit(*data)) { | ||
368 | out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJHI, | ||
369 | simple_strtoul(data, NULL, 0)); | ||
370 | return count; | ||
371 | } | ||
372 | return 0; | ||
373 | } | ||
374 | |||
375 | static ssize_t mpc85xx_l2_inject_data_lo_store(struct edac_device_ctl_info | ||
376 | *edac_dev, const char *data, | ||
377 | size_t count) | ||
378 | { | ||
379 | struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; | ||
380 | if (isdigit(*data)) { | ||
381 | out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJLO, | ||
382 | simple_strtoul(data, NULL, 0)); | ||
383 | return count; | ||
384 | } | ||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | static ssize_t mpc85xx_l2_inject_ctrl_store(struct edac_device_ctl_info | ||
389 | *edac_dev, const char *data, | ||
390 | size_t count) | ||
391 | { | ||
392 | struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; | ||
393 | if (isdigit(*data)) { | ||
394 | out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJCTL, | ||
395 | simple_strtoul(data, NULL, 0)); | ||
396 | return count; | ||
397 | } | ||
398 | return 0; | ||
399 | } | ||
400 | |||
401 | static struct edac_dev_sysfs_attribute mpc85xx_l2_sysfs_attributes[] = { | ||
402 | { | ||
403 | .attr = { | ||
404 | .name = "inject_data_hi", | ||
405 | .mode = (S_IRUGO | S_IWUSR) | ||
406 | }, | ||
407 | .show = mpc85xx_l2_inject_data_hi_show, | ||
408 | .store = mpc85xx_l2_inject_data_hi_store}, | ||
409 | { | ||
410 | .attr = { | ||
411 | .name = "inject_data_lo", | ||
412 | .mode = (S_IRUGO | S_IWUSR) | ||
413 | }, | ||
414 | .show = mpc85xx_l2_inject_data_lo_show, | ||
415 | .store = mpc85xx_l2_inject_data_lo_store}, | ||
416 | { | ||
417 | .attr = { | ||
418 | .name = "inject_ctrl", | ||
419 | .mode = (S_IRUGO | S_IWUSR) | ||
420 | }, | ||
421 | .show = mpc85xx_l2_inject_ctrl_show, | ||
422 | .store = mpc85xx_l2_inject_ctrl_store}, | ||
423 | |||
424 | /* End of list */ | ||
425 | { | ||
426 | .attr = {.name = NULL} | ||
427 | } | ||
428 | }; | ||
429 | |||
430 | static void mpc85xx_set_l2_sysfs_attributes(struct edac_device_ctl_info | ||
431 | *edac_dev) | ||
432 | { | ||
433 | edac_dev->sysfs_attributes = mpc85xx_l2_sysfs_attributes; | ||
434 | } | ||
435 | |||
436 | /***************************** L2 ops ***********************************/ | ||
437 | |||
438 | static void mpc85xx_l2_check(struct edac_device_ctl_info *edac_dev) | ||
439 | { | ||
440 | struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; | ||
441 | u32 err_detect; | ||
442 | |||
443 | err_detect = in_be32(pdata->l2_vbase + MPC85XX_L2_ERRDET); | ||
444 | |||
445 | if (!(err_detect & L2_EDE_MASK)) | ||
446 | return; | ||
447 | |||
448 | printk(KERN_ERR "ECC Error in CPU L2 cache\n"); | ||
449 | printk(KERN_ERR "L2 Error Detect Register: 0x%08x\n", err_detect); | ||
450 | printk(KERN_ERR "L2 Error Capture Data High Register: 0x%08x\n", | ||
451 | in_be32(pdata->l2_vbase + MPC85XX_L2_CAPTDATAHI)); | ||
452 | printk(KERN_ERR "L2 Error Capture Data Lo Register: 0x%08x\n", | ||
453 | in_be32(pdata->l2_vbase + MPC85XX_L2_CAPTDATALO)); | ||
454 | printk(KERN_ERR "L2 Error Syndrome Register: 0x%08x\n", | ||
455 | in_be32(pdata->l2_vbase + MPC85XX_L2_CAPTECC)); | ||
456 | printk(KERN_ERR "L2 Error Attributes Capture Register: 0x%08x\n", | ||
457 | in_be32(pdata->l2_vbase + MPC85XX_L2_ERRATTR)); | ||
458 | printk(KERN_ERR "L2 Error Address Capture Register: 0x%08x\n", | ||
459 | in_be32(pdata->l2_vbase + MPC85XX_L2_ERRADDR)); | ||
460 | |||
461 | /* clear error detect register */ | ||
462 | out_be32(pdata->l2_vbase + MPC85XX_L2_ERRDET, err_detect); | ||
463 | |||
464 | if (err_detect & L2_EDE_CE_MASK) | ||
465 | edac_device_handle_ce(edac_dev, 0, 0, edac_dev->ctl_name); | ||
466 | |||
467 | if (err_detect & L2_EDE_UE_MASK) | ||
468 | edac_device_handle_ue(edac_dev, 0, 0, edac_dev->ctl_name); | ||
469 | } | ||
470 | |||
471 | static irqreturn_t mpc85xx_l2_isr(int irq, void *dev_id) | ||
472 | { | ||
473 | struct edac_device_ctl_info *edac_dev = dev_id; | ||
474 | struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; | ||
475 | u32 err_detect; | ||
476 | |||
477 | err_detect = in_be32(pdata->l2_vbase + MPC85XX_L2_ERRDET); | ||
478 | |||
479 | if (!(err_detect & L2_EDE_MASK)) | ||
480 | return IRQ_NONE; | ||
481 | |||
482 | mpc85xx_l2_check(edac_dev); | ||
483 | |||
484 | return IRQ_HANDLED; | ||
485 | } | ||
486 | |||
487 | static int __devinit mpc85xx_l2_err_probe(struct of_device *op, | ||
488 | const struct of_device_id *match) | ||
489 | { | ||
490 | struct edac_device_ctl_info *edac_dev; | ||
491 | struct mpc85xx_l2_pdata *pdata; | ||
492 | struct resource r; | ||
493 | int res; | ||
494 | |||
495 | if (!devres_open_group(&op->dev, mpc85xx_l2_err_probe, GFP_KERNEL)) | ||
496 | return -ENOMEM; | ||
497 | |||
498 | edac_dev = edac_device_alloc_ctl_info(sizeof(*pdata), | ||
499 | "cpu", 1, "L", 1, 2, NULL, 0, | ||
500 | edac_dev_idx); | ||
501 | if (!edac_dev) { | ||
502 | devres_release_group(&op->dev, mpc85xx_l2_err_probe); | ||
503 | return -ENOMEM; | ||
504 | } | ||
505 | |||
506 | pdata = edac_dev->pvt_info; | ||
507 | pdata->name = "mpc85xx_l2_err"; | ||
508 | pdata->irq = NO_IRQ; | ||
509 | edac_dev->dev = &op->dev; | ||
510 | dev_set_drvdata(edac_dev->dev, edac_dev); | ||
511 | edac_dev->ctl_name = pdata->name; | ||
512 | edac_dev->dev_name = pdata->name; | ||
513 | |||
514 | res = of_address_to_resource(op->node, 0, &r); | ||
515 | if (res) { | ||
516 | printk(KERN_ERR "%s: Unable to get resource for " | ||
517 | "L2 err regs\n", __func__); | ||
518 | goto err; | ||
519 | } | ||
520 | |||
521 | /* we only need the error registers */ | ||
522 | r.start += 0xe00; | ||
523 | |||
524 | if (!devm_request_mem_region(&op->dev, r.start, | ||
525 | r.end - r.start + 1, pdata->name)) { | ||
526 | printk(KERN_ERR "%s: Error while requesting mem region\n", | ||
527 | __func__); | ||
528 | res = -EBUSY; | ||
529 | goto err; | ||
530 | } | ||
531 | |||
532 | pdata->l2_vbase = devm_ioremap(&op->dev, r.start, r.end - r.start + 1); | ||
533 | if (!pdata->l2_vbase) { | ||
534 | printk(KERN_ERR "%s: Unable to setup L2 err regs\n", __func__); | ||
535 | res = -ENOMEM; | ||
536 | goto err; | ||
537 | } | ||
538 | |||
539 | out_be32(pdata->l2_vbase + MPC85XX_L2_ERRDET, ~0); | ||
540 | |||
541 | orig_l2_err_disable = in_be32(pdata->l2_vbase + MPC85XX_L2_ERRDIS); | ||
542 | |||
543 | /* clear the err_dis */ | ||
544 | out_be32(pdata->l2_vbase + MPC85XX_L2_ERRDIS, 0); | ||
545 | |||
546 | edac_dev->mod_name = EDAC_MOD_STR; | ||
547 | |||
548 | if (edac_op_state == EDAC_OPSTATE_POLL) | ||
549 | edac_dev->edac_check = mpc85xx_l2_check; | ||
550 | |||
551 | mpc85xx_set_l2_sysfs_attributes(edac_dev); | ||
552 | |||
553 | pdata->edac_idx = edac_dev_idx++; | ||
554 | |||
555 | if (edac_device_add_device(edac_dev) > 0) { | ||
556 | debugf3("%s(): failed edac_device_add_device()\n", __func__); | ||
557 | goto err; | ||
558 | } | ||
559 | |||
560 | if (edac_op_state == EDAC_OPSTATE_INT) { | ||
561 | pdata->irq = irq_of_parse_and_map(op->node, 0); | ||
562 | res = devm_request_irq(&op->dev, pdata->irq, | ||
563 | mpc85xx_l2_isr, IRQF_DISABLED, | ||
564 | "[EDAC] L2 err", edac_dev); | ||
565 | if (res < 0) { | ||
566 | printk(KERN_ERR | ||
567 | "%s: Unable to requiest irq %d for " | ||
568 | "MPC85xx L2 err\n", __func__, pdata->irq); | ||
569 | irq_dispose_mapping(pdata->irq); | ||
570 | res = -ENODEV; | ||
571 | goto err2; | ||
572 | } | ||
573 | |||
574 | printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for L2 Err\n", | ||
575 | pdata->irq); | ||
576 | |||
577 | edac_dev->op_state = OP_RUNNING_INTERRUPT; | ||
578 | |||
579 | out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINTEN, L2_EIE_MASK); | ||
580 | } | ||
581 | |||
582 | devres_remove_group(&op->dev, mpc85xx_l2_err_probe); | ||
583 | |||
584 | debugf3("%s(): success\n", __func__); | ||
585 | printk(KERN_INFO EDAC_MOD_STR " L2 err registered\n"); | ||
586 | |||
587 | return 0; | ||
588 | |||
589 | err2: | ||
590 | edac_device_del_device(&op->dev); | ||
591 | err: | ||
592 | devres_release_group(&op->dev, mpc85xx_l2_err_probe); | ||
593 | edac_device_free_ctl_info(edac_dev); | ||
594 | return res; | ||
595 | } | ||
596 | |||
597 | static int mpc85xx_l2_err_remove(struct of_device *op) | ||
598 | { | ||
599 | struct edac_device_ctl_info *edac_dev = dev_get_drvdata(&op->dev); | ||
600 | struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; | ||
601 | |||
602 | debugf0("%s()\n", __func__); | ||
603 | |||
604 | if (edac_op_state == EDAC_OPSTATE_INT) { | ||
605 | out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINTEN, 0); | ||
606 | irq_dispose_mapping(pdata->irq); | ||
607 | } | ||
608 | |||
609 | out_be32(pdata->l2_vbase + MPC85XX_L2_ERRDIS, orig_l2_err_disable); | ||
610 | edac_device_del_device(&op->dev); | ||
611 | edac_device_free_ctl_info(edac_dev); | ||
612 | return 0; | ||
613 | } | ||
614 | |||
615 | static struct of_device_id mpc85xx_l2_err_of_match[] = { | ||
616 | { | ||
617 | .compatible = "fsl,8540-l2-cache-controller", | ||
618 | }, | ||
619 | { | ||
620 | .compatible = "fsl,8541-l2-cache-controller", | ||
621 | }, | ||
622 | { | ||
623 | .compatible = "fsl,8544-l2-cache-controller", | ||
624 | }, | ||
625 | { | ||
626 | .compatible = "fsl,8548-l2-cache-controller", | ||
627 | }, | ||
628 | { | ||
629 | .compatible = "fsl,8555-l2-cache-controller", | ||
630 | }, | ||
631 | { | ||
632 | .compatible = "fsl,8568-l2-cache-controller", | ||
633 | }, | ||
634 | {}, | ||
635 | }; | ||
636 | |||
637 | static struct of_platform_driver mpc85xx_l2_err_driver = { | ||
638 | .owner = THIS_MODULE, | ||
639 | .name = "mpc85xx_l2_err", | ||
640 | .match_table = mpc85xx_l2_err_of_match, | ||
641 | .probe = mpc85xx_l2_err_probe, | ||
642 | .remove = mpc85xx_l2_err_remove, | ||
643 | .driver = { | ||
644 | .name = "mpc85xx_l2_err", | ||
645 | .owner = THIS_MODULE, | ||
646 | }, | ||
647 | }; | ||
648 | |||
649 | /**************************** MC Err device ***************************/ | ||
650 | |||
651 | static void mpc85xx_mc_check(struct mem_ctl_info *mci) | ||
652 | { | ||
653 | struct mpc85xx_mc_pdata *pdata = mci->pvt_info; | ||
654 | struct csrow_info *csrow; | ||
655 | u32 err_detect; | ||
656 | u32 syndrome; | ||
657 | u32 err_addr; | ||
658 | u32 pfn; | ||
659 | int row_index; | ||
660 | |||
661 | err_detect = in_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT); | ||
662 | if (err_detect) | ||
663 | return; | ||
664 | |||
665 | mpc85xx_mc_printk(mci, KERN_ERR, "Err Detect Register: %#8.8x\n", | ||
666 | err_detect); | ||
667 | |||
668 | /* no more processing if not ECC bit errors */ | ||
669 | if (!(err_detect & (DDR_EDE_SBE | DDR_EDE_MBE))) { | ||
670 | out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, err_detect); | ||
671 | return; | ||
672 | } | ||
673 | |||
674 | syndrome = in_be32(pdata->mc_vbase + MPC85XX_MC_CAPTURE_ECC); | ||
675 | err_addr = in_be32(pdata->mc_vbase + MPC85XX_MC_CAPTURE_ADDRESS); | ||
676 | pfn = err_addr >> PAGE_SHIFT; | ||
677 | |||
678 | for (row_index = 0; row_index < mci->nr_csrows; row_index++) { | ||
679 | csrow = &mci->csrows[row_index]; | ||
680 | if ((pfn >= csrow->first_page) && (pfn <= csrow->last_page)) | ||
681 | break; | ||
682 | } | ||
683 | |||
684 | mpc85xx_mc_printk(mci, KERN_ERR, "Capture Data High: %#8.8x\n", | ||
685 | in_be32(pdata->mc_vbase + | ||
686 | MPC85XX_MC_CAPTURE_DATA_HI)); | ||
687 | mpc85xx_mc_printk(mci, KERN_ERR, "Capture Data Low: %#8.8x\n", | ||
688 | in_be32(pdata->mc_vbase + | ||
689 | MPC85XX_MC_CAPTURE_DATA_LO)); | ||
690 | mpc85xx_mc_printk(mci, KERN_ERR, "syndrome: %#8.8x\n", syndrome); | ||
691 | mpc85xx_mc_printk(mci, KERN_ERR, "err addr: %#8.8x\n", err_addr); | ||
692 | mpc85xx_mc_printk(mci, KERN_ERR, "PFN: %#8.8x\n", pfn); | ||
693 | |||
694 | /* we are out of range */ | ||
695 | if (row_index == mci->nr_csrows) | ||
696 | mpc85xx_mc_printk(mci, KERN_ERR, "PFN out of range!\n"); | ||
697 | |||
698 | if (err_detect & DDR_EDE_SBE) | ||
699 | edac_mc_handle_ce(mci, pfn, err_addr & PAGE_MASK, | ||
700 | syndrome, row_index, 0, mci->ctl_name); | ||
701 | |||
702 | if (err_detect & DDR_EDE_MBE) | ||
703 | edac_mc_handle_ue(mci, pfn, err_addr & PAGE_MASK, | ||
704 | row_index, mci->ctl_name); | ||
705 | |||
706 | out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, err_detect); | ||
707 | } | ||
708 | |||
709 | static irqreturn_t mpc85xx_mc_isr(int irq, void *dev_id) | ||
710 | { | ||
711 | struct mem_ctl_info *mci = dev_id; | ||
712 | struct mpc85xx_mc_pdata *pdata = mci->pvt_info; | ||
713 | u32 err_detect; | ||
714 | |||
715 | err_detect = in_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT); | ||
716 | if (!err_detect) | ||
717 | return IRQ_NONE; | ||
718 | |||
719 | mpc85xx_mc_check(mci); | ||
720 | |||
721 | return IRQ_HANDLED; | ||
722 | } | ||
723 | |||
724 | static void __devinit mpc85xx_init_csrows(struct mem_ctl_info *mci) | ||
725 | { | ||
726 | struct mpc85xx_mc_pdata *pdata = mci->pvt_info; | ||
727 | struct csrow_info *csrow; | ||
728 | u32 sdram_ctl; | ||
729 | u32 sdtype; | ||
730 | enum mem_type mtype; | ||
731 | u32 cs_bnds; | ||
732 | int index; | ||
733 | |||
734 | sdram_ctl = in_be32(pdata->mc_vbase + MPC85XX_MC_DDR_SDRAM_CFG); | ||
735 | |||
736 | sdtype = sdram_ctl & DSC_SDTYPE_MASK; | ||
737 | if (sdram_ctl & DSC_RD_EN) { | ||
738 | switch (sdtype) { | ||
739 | case DSC_SDTYPE_DDR: | ||
740 | mtype = MEM_RDDR; | ||
741 | break; | ||
742 | case DSC_SDTYPE_DDR2: | ||
743 | mtype = MEM_RDDR2; | ||
744 | break; | ||
745 | default: | ||
746 | mtype = MEM_UNKNOWN; | ||
747 | break; | ||
748 | } | ||
749 | } else { | ||
750 | switch (sdtype) { | ||
751 | case DSC_SDTYPE_DDR: | ||
752 | mtype = MEM_DDR; | ||
753 | break; | ||
754 | case DSC_SDTYPE_DDR2: | ||
755 | mtype = MEM_DDR2; | ||
756 | break; | ||
757 | default: | ||
758 | mtype = MEM_UNKNOWN; | ||
759 | break; | ||
760 | } | ||
761 | } | ||
762 | |||
763 | for (index = 0; index < mci->nr_csrows; index++) { | ||
764 | u32 start; | ||
765 | u32 end; | ||
766 | |||
767 | csrow = &mci->csrows[index]; | ||
768 | cs_bnds = in_be32(pdata->mc_vbase + MPC85XX_MC_CS_BNDS_0 + | ||
769 | (index * MPC85XX_MC_CS_BNDS_OFS)); | ||
770 | start = (cs_bnds & 0xfff0000) << 4; | ||
771 | end = ((cs_bnds & 0xfff) << 20); | ||
772 | if (start) | ||
773 | start |= 0xfffff; | ||
774 | if (end) | ||
775 | end |= 0xfffff; | ||
776 | |||
777 | if (start == end) | ||
778 | continue; /* not populated */ | ||
779 | |||
780 | csrow->first_page = start >> PAGE_SHIFT; | ||
781 | csrow->last_page = end >> PAGE_SHIFT; | ||
782 | csrow->nr_pages = csrow->last_page + 1 - csrow->first_page; | ||
783 | csrow->grain = 8; | ||
784 | csrow->mtype = mtype; | ||
785 | csrow->dtype = DEV_UNKNOWN; | ||
786 | if (sdram_ctl & DSC_X32_EN) | ||
787 | csrow->dtype = DEV_X32; | ||
788 | csrow->edac_mode = EDAC_SECDED; | ||
789 | } | ||
790 | } | ||
791 | |||
792 | static int __devinit mpc85xx_mc_err_probe(struct of_device *op, | ||
793 | const struct of_device_id *match) | ||
794 | { | ||
795 | struct mem_ctl_info *mci; | ||
796 | struct mpc85xx_mc_pdata *pdata; | ||
797 | struct resource r; | ||
798 | u32 sdram_ctl; | ||
799 | int res; | ||
800 | |||
801 | if (!devres_open_group(&op->dev, mpc85xx_mc_err_probe, GFP_KERNEL)) | ||
802 | return -ENOMEM; | ||
803 | |||
804 | mci = edac_mc_alloc(sizeof(*pdata), 4, 1, edac_mc_idx); | ||
805 | if (!mci) { | ||
806 | devres_release_group(&op->dev, mpc85xx_mc_err_probe); | ||
807 | return -ENOMEM; | ||
808 | } | ||
809 | |||
810 | pdata = mci->pvt_info; | ||
811 | pdata->name = "mpc85xx_mc_err"; | ||
812 | pdata->irq = NO_IRQ; | ||
813 | mci->dev = &op->dev; | ||
814 | pdata->edac_idx = edac_mc_idx++; | ||
815 | dev_set_drvdata(mci->dev, mci); | ||
816 | mci->ctl_name = pdata->name; | ||
817 | mci->dev_name = pdata->name; | ||
818 | |||
819 | res = of_address_to_resource(op->node, 0, &r); | ||
820 | if (res) { | ||
821 | printk(KERN_ERR "%s: Unable to get resource for MC err regs\n", | ||
822 | __func__); | ||
823 | goto err; | ||
824 | } | ||
825 | |||
826 | if (!devm_request_mem_region(&op->dev, r.start, | ||
827 | r.end - r.start + 1, pdata->name)) { | ||
828 | printk(KERN_ERR "%s: Error while requesting mem region\n", | ||
829 | __func__); | ||
830 | res = -EBUSY; | ||
831 | goto err; | ||
832 | } | ||
833 | |||
834 | pdata->mc_vbase = devm_ioremap(&op->dev, r.start, r.end - r.start + 1); | ||
835 | if (!pdata->mc_vbase) { | ||
836 | printk(KERN_ERR "%s: Unable to setup MC err regs\n", __func__); | ||
837 | res = -ENOMEM; | ||
838 | goto err; | ||
839 | } | ||
840 | |||
841 | sdram_ctl = in_be32(pdata->mc_vbase + MPC85XX_MC_DDR_SDRAM_CFG); | ||
842 | if (!(sdram_ctl & DSC_ECC_EN)) { | ||
843 | /* no ECC */ | ||
844 | printk(KERN_WARNING "%s: No ECC DIMMs discovered\n", __func__); | ||
845 | res = -ENODEV; | ||
846 | goto err; | ||
847 | } | ||
848 | |||
849 | debugf3("%s(): init mci\n", __func__); | ||
850 | mci->mtype_cap = MEM_FLAG_RDDR | MEM_FLAG_RDDR2 | | ||
851 | MEM_FLAG_DDR | MEM_FLAG_DDR2; | ||
852 | mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; | ||
853 | mci->edac_cap = EDAC_FLAG_SECDED; | ||
854 | mci->mod_name = EDAC_MOD_STR; | ||
855 | mci->mod_ver = MPC85XX_REVISION; | ||
856 | |||
857 | if (edac_op_state == EDAC_OPSTATE_POLL) | ||
858 | mci->edac_check = mpc85xx_mc_check; | ||
859 | |||
860 | mci->ctl_page_to_phys = NULL; | ||
861 | |||
862 | mci->scrub_mode = SCRUB_SW_SRC; | ||
863 | |||
864 | mpc85xx_set_mc_sysfs_attributes(mci); | ||
865 | |||
866 | mpc85xx_init_csrows(mci); | ||
867 | |||
868 | #ifdef CONFIG_EDAC_DEBUG | ||
869 | edac_mc_register_mcidev_debug((struct attribute **)debug_attr); | ||
870 | #endif | ||
871 | |||
872 | /* store the original error disable bits */ | ||
873 | orig_ddr_err_disable = | ||
874 | in_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DISABLE); | ||
875 | out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DISABLE, 0); | ||
876 | |||
877 | /* clear all error bits */ | ||
878 | out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DETECT, ~0); | ||
879 | |||
880 | if (edac_mc_add_mc(mci)) { | ||
881 | debugf3("%s(): failed edac_mc_add_mc()\n", __func__); | ||
882 | goto err; | ||
883 | } | ||
884 | |||
885 | if (edac_op_state == EDAC_OPSTATE_INT) { | ||
886 | out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_INT_EN, | ||
887 | DDR_EIE_MBEE | DDR_EIE_SBEE); | ||
888 | |||
889 | /* store the original error management threshold */ | ||
890 | orig_ddr_err_sbe = in_be32(pdata->mc_vbase + | ||
891 | MPC85XX_MC_ERR_SBE) & 0xff0000; | ||
892 | |||
893 | /* set threshold to 1 error per interrupt */ | ||
894 | out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_SBE, 0x10000); | ||
895 | |||
896 | /* register interrupts */ | ||
897 | pdata->irq = irq_of_parse_and_map(op->node, 0); | ||
898 | res = devm_request_irq(&op->dev, pdata->irq, | ||
899 | mpc85xx_mc_isr, IRQF_DISABLED, | ||
900 | "[EDAC] MC err", mci); | ||
901 | if (res < 0) { | ||
902 | printk(KERN_ERR "%s: Unable to request irq %d for " | ||
903 | "MPC85xx DRAM ERR\n", __func__, pdata->irq); | ||
904 | irq_dispose_mapping(pdata->irq); | ||
905 | res = -ENODEV; | ||
906 | goto err2; | ||
907 | } | ||
908 | |||
909 | printk(KERN_INFO EDAC_MOD_STR " acquired irq %d for MC\n", | ||
910 | pdata->irq); | ||
911 | } | ||
912 | |||
913 | devres_remove_group(&op->dev, mpc85xx_mc_err_probe); | ||
914 | debugf3("%s(): success\n", __func__); | ||
915 | printk(KERN_INFO EDAC_MOD_STR " MC err registered\n"); | ||
916 | |||
917 | return 0; | ||
918 | |||
919 | err2: | ||
920 | edac_mc_del_mc(&op->dev); | ||
921 | err: | ||
922 | devres_release_group(&op->dev, mpc85xx_mc_err_probe); | ||
923 | edac_mc_free(mci); | ||
924 | return res; | ||
925 | } | ||
926 | |||
927 | static int mpc85xx_mc_err_remove(struct of_device *op) | ||
928 | { | ||
929 | struct mem_ctl_info *mci = dev_get_drvdata(&op->dev); | ||
930 | struct mpc85xx_mc_pdata *pdata = mci->pvt_info; | ||
931 | |||
932 | debugf0("%s()\n", __func__); | ||
933 | |||
934 | if (edac_op_state == EDAC_OPSTATE_INT) { | ||
935 | out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_INT_EN, 0); | ||
936 | irq_dispose_mapping(pdata->irq); | ||
937 | } | ||
938 | |||
939 | out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_DISABLE, | ||
940 | orig_ddr_err_disable); | ||
941 | out_be32(pdata->mc_vbase + MPC85XX_MC_ERR_SBE, orig_ddr_err_sbe); | ||
942 | |||
943 | edac_mc_del_mc(&op->dev); | ||
944 | edac_mc_free(mci); | ||
945 | return 0; | ||
946 | } | ||
947 | |||
948 | static struct of_device_id mpc85xx_mc_err_of_match[] = { | ||
949 | { | ||
950 | .compatible = "fsl,8540-memory-controller", | ||
951 | }, | ||
952 | { | ||
953 | .compatible = "fsl,8541-memory-controller", | ||
954 | }, | ||
955 | { | ||
956 | .compatible = "fsl,8544-memory-controller", | ||
957 | }, | ||
958 | { | ||
959 | .compatible = "fsl,8548-memory-controller", | ||
960 | }, | ||
961 | { | ||
962 | .compatible = "fsl,8555-memory-controller", | ||
963 | }, | ||
964 | { | ||
965 | .compatible = "fsl,8568-memory-controller", | ||
966 | }, | ||
967 | {}, | ||
968 | }; | ||
969 | |||
970 | static struct of_platform_driver mpc85xx_mc_err_driver = { | ||
971 | .owner = THIS_MODULE, | ||
972 | .name = "mpc85xx_mc_err", | ||
973 | .match_table = mpc85xx_mc_err_of_match, | ||
974 | .probe = mpc85xx_mc_err_probe, | ||
975 | .remove = mpc85xx_mc_err_remove, | ||
976 | .driver = { | ||
977 | .name = "mpc85xx_mc_err", | ||
978 | .owner = THIS_MODULE, | ||
979 | }, | ||
980 | }; | ||
981 | |||
982 | static int __init mpc85xx_mc_init(void) | ||
983 | { | ||
984 | int res = 0; | ||
985 | |||
986 | printk(KERN_INFO "Freescale(R) MPC85xx EDAC driver, " | ||
987 | "(C) 2006 Montavista Software\n"); | ||
988 | |||
989 | /* make sure error reporting method is sane */ | ||
990 | switch (edac_op_state) { | ||
991 | case EDAC_OPSTATE_POLL: | ||
992 | case EDAC_OPSTATE_INT: | ||
993 | break; | ||
994 | default: | ||
995 | edac_op_state = EDAC_OPSTATE_INT; | ||
996 | break; | ||
997 | } | ||
998 | |||
999 | res = of_register_platform_driver(&mpc85xx_mc_err_driver); | ||
1000 | if (res) | ||
1001 | printk(KERN_WARNING EDAC_MOD_STR "MC fails to register\n"); | ||
1002 | |||
1003 | res = of_register_platform_driver(&mpc85xx_l2_err_driver); | ||
1004 | if (res) | ||
1005 | printk(KERN_WARNING EDAC_MOD_STR "L2 fails to register\n"); | ||
1006 | |||
1007 | #ifdef CONFIG_PCI | ||
1008 | res = platform_driver_register(&mpc85xx_pci_err_driver); | ||
1009 | if (res) | ||
1010 | printk(KERN_WARNING EDAC_MOD_STR "PCI fails to register\n"); | ||
1011 | #endif | ||
1012 | |||
1013 | /* | ||
1014 | * need to clear HID1[RFXE] to disable machine check int | ||
1015 | * so we can catch it | ||
1016 | */ | ||
1017 | if (edac_op_state == EDAC_OPSTATE_INT) { | ||
1018 | orig_hid1 = mfspr(SPRN_HID1); | ||
1019 | mtspr(SPRN_HID1, (orig_hid1 & ~0x20000)); | ||
1020 | } | ||
1021 | |||
1022 | return 0; | ||
1023 | } | ||
1024 | |||
1025 | module_init(mpc85xx_mc_init); | ||
1026 | |||
1027 | static void __exit mpc85xx_mc_exit(void) | ||
1028 | { | ||
1029 | mtspr(SPRN_HID1, orig_hid1); | ||
1030 | #ifdef CONFIG_PCI | ||
1031 | platform_driver_unregister(&mpc85xx_pci_err_driver); | ||
1032 | #endif | ||
1033 | of_unregister_platform_driver(&mpc85xx_l2_err_driver); | ||
1034 | of_unregister_platform_driver(&mpc85xx_mc_err_driver); | ||
1035 | } | ||
1036 | |||
1037 | module_exit(mpc85xx_mc_exit); | ||
1038 | |||
1039 | MODULE_LICENSE("GPL"); | ||
1040 | MODULE_AUTHOR("Montavista Software, Inc."); | ||
1041 | module_param(edac_op_state, int, 0444); | ||
1042 | MODULE_PARM_DESC(edac_op_state, | ||
1043 | "EDAC Error Reporting state: 0=Poll, 2=Interrupt"); | ||