1 | /* |
2 | * Freescale MPC85xx Memory Controller kernel module |
3 | * |
4 | * Parts Copyrighted (c) 2013 by Freescale Semiconductor, Inc. |
5 | * |
6 | * Author: Dave Jiang <djiang@mvista.com> |
7 | * |
8 | * 2006-2007 (c) MontaVista Software, Inc. This file is licensed under |
9 | * the terms of the GNU General Public License version 2. This program |
10 | * is licensed "as is" without any warranty of any kind, whether express |
11 | * or implied. |
12 | * |
13 | */ |
14 | #include <linux/module.h> |
15 | #include <linux/init.h> |
16 | #include <linux/interrupt.h> |
17 | #include <linux/ctype.h> |
18 | #include <linux/io.h> |
19 | #include <linux/mod_devicetable.h> |
20 | #include <linux/edac.h> |
21 | #include <linux/smp.h> |
22 | #include <linux/gfp.h> |
23 | #include <linux/fsl/edac.h> |
24 | |
25 | #include <linux/of.h> |
26 | #include <linux/of_address.h> |
27 | #include <linux/of_irq.h> |
28 | #include "edac_module.h" |
29 | #include "mpc85xx_edac.h" |
30 | #include "fsl_ddr_edac.h" |
31 | |
32 | static int edac_dev_idx; |
33 | #ifdef CONFIG_PCI |
34 | static int edac_pci_idx; |
35 | #endif |
36 | |
37 | /* |
38 | * PCI Err defines |
39 | */ |
40 | #ifdef CONFIG_PCI |
41 | static u32 orig_pci_err_cap_dr; |
42 | static u32 orig_pci_err_en; |
43 | #endif |
44 | |
45 | static u32 orig_l2_err_disable; |
46 | |
47 | /**************************** PCI Err device ***************************/ |
48 | #ifdef CONFIG_PCI |
49 | |
50 | static void mpc85xx_pci_check(struct edac_pci_ctl_info *pci) |
51 | { |
52 | struct mpc85xx_pci_pdata *pdata = pci->pvt_info; |
53 | u32 err_detect; |
54 | |
55 | err_detect = in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR); |
56 | |
57 | /* master aborts can happen during PCI config cycles */ |
58 | if (!(err_detect & ~(PCI_EDE_MULTI_ERR | PCI_EDE_MST_ABRT))) { |
59 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, err_detect); |
60 | return; |
61 | } |
62 | |
63 | pr_err("PCI error(s) detected\n" ); |
64 | pr_err("PCI/X ERR_DR register: %#08x\n" , err_detect); |
65 | |
66 | pr_err("PCI/X ERR_ATTRIB register: %#08x\n" , |
67 | in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ATTRIB)); |
68 | pr_err("PCI/X ERR_ADDR register: %#08x\n" , |
69 | in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ADDR)); |
70 | pr_err("PCI/X ERR_EXT_ADDR register: %#08x\n" , |
71 | in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EXT_ADDR)); |
72 | pr_err("PCI/X ERR_DL register: %#08x\n" , |
73 | in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DL)); |
74 | pr_err("PCI/X ERR_DH register: %#08x\n" , |
75 | in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DH)); |
76 | |
77 | /* clear error bits */ |
78 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, err_detect); |
79 | |
80 | if (err_detect & PCI_EDE_PERR_MASK) |
81 | edac_pci_handle_pe(pci, msg: pci->ctl_name); |
82 | |
83 | if ((err_detect & ~PCI_EDE_MULTI_ERR) & ~PCI_EDE_PERR_MASK) |
84 | edac_pci_handle_npe(pci, msg: pci->ctl_name); |
85 | } |
86 | |
87 | static void mpc85xx_pcie_check(struct edac_pci_ctl_info *pci) |
88 | { |
89 | struct mpc85xx_pci_pdata *pdata = pci->pvt_info; |
90 | u32 err_detect, err_cap_stat; |
91 | |
92 | err_detect = in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR); |
93 | err_cap_stat = in_be32(pdata->pci_vbase + MPC85XX_PCI_GAS_TIMR); |
94 | |
95 | pr_err("PCIe error(s) detected\n" ); |
96 | pr_err("PCIe ERR_DR register: 0x%08x\n" , err_detect); |
97 | pr_err("PCIe ERR_CAP_STAT register: 0x%08x\n" , err_cap_stat); |
98 | pr_err("PCIe ERR_CAP_R0 register: 0x%08x\n" , |
99 | in_be32(pdata->pci_vbase + MPC85XX_PCIE_ERR_CAP_R0)); |
100 | pr_err("PCIe ERR_CAP_R1 register: 0x%08x\n" , |
101 | in_be32(pdata->pci_vbase + MPC85XX_PCIE_ERR_CAP_R1)); |
102 | pr_err("PCIe ERR_CAP_R2 register: 0x%08x\n" , |
103 | in_be32(pdata->pci_vbase + MPC85XX_PCIE_ERR_CAP_R2)); |
104 | pr_err("PCIe ERR_CAP_R3 register: 0x%08x\n" , |
105 | in_be32(pdata->pci_vbase + MPC85XX_PCIE_ERR_CAP_R3)); |
106 | |
107 | /* clear error bits */ |
108 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, err_detect); |
109 | |
110 | /* reset error capture */ |
111 | out_be32(pdata->pci_vbase + MPC85XX_PCI_GAS_TIMR, err_cap_stat | 0x1); |
112 | } |
113 | |
114 | static int mpc85xx_pcie_find_capability(struct device_node *np) |
115 | { |
116 | struct pci_controller *hose; |
117 | |
118 | if (!np) |
119 | return -EINVAL; |
120 | |
121 | hose = pci_find_hose_for_OF_device(np); |
122 | |
123 | return early_find_capability(hose, 0, 0, PCI_CAP_ID_EXP); |
124 | } |
125 | |
126 | static irqreturn_t mpc85xx_pci_isr(int irq, void *dev_id) |
127 | { |
128 | struct edac_pci_ctl_info *pci = dev_id; |
129 | struct mpc85xx_pci_pdata *pdata = pci->pvt_info; |
130 | u32 err_detect; |
131 | |
132 | err_detect = in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR); |
133 | |
134 | if (!err_detect) |
135 | return IRQ_NONE; |
136 | |
137 | if (pdata->is_pcie) |
138 | mpc85xx_pcie_check(pci); |
139 | else |
140 | mpc85xx_pci_check(pci); |
141 | |
142 | return IRQ_HANDLED; |
143 | } |
144 | |
145 | static int mpc85xx_pci_err_probe(struct platform_device *op) |
146 | { |
147 | struct edac_pci_ctl_info *pci; |
148 | struct mpc85xx_pci_pdata *pdata; |
149 | struct mpc85xx_edac_pci_plat_data *plat_data; |
150 | struct device_node *of_node; |
151 | struct resource r; |
152 | int res = 0; |
153 | |
154 | if (!devres_open_group(dev: &op->dev, id: mpc85xx_pci_err_probe, GFP_KERNEL)) |
155 | return -ENOMEM; |
156 | |
157 | pci = edac_pci_alloc_ctl_info(sz_pvt: sizeof(*pdata), edac_pci_name: "mpc85xx_pci_err" ); |
158 | if (!pci) |
159 | return -ENOMEM; |
160 | |
161 | /* make sure error reporting method is sane */ |
162 | switch (edac_op_state) { |
163 | case EDAC_OPSTATE_POLL: |
164 | case EDAC_OPSTATE_INT: |
165 | break; |
166 | default: |
167 | edac_op_state = EDAC_OPSTATE_INT; |
168 | break; |
169 | } |
170 | |
171 | pdata = pci->pvt_info; |
172 | pdata->name = "mpc85xx_pci_err" ; |
173 | |
174 | plat_data = op->dev.platform_data; |
175 | if (!plat_data) { |
176 | dev_err(&op->dev, "no platform data" ); |
177 | res = -ENXIO; |
178 | goto err; |
179 | } |
180 | of_node = plat_data->of_node; |
181 | |
182 | if (mpc85xx_pcie_find_capability(np: of_node) > 0) |
183 | pdata->is_pcie = true; |
184 | |
185 | dev_set_drvdata(dev: &op->dev, data: pci); |
186 | pci->dev = &op->dev; |
187 | pci->mod_name = EDAC_MOD_STR; |
188 | pci->ctl_name = pdata->name; |
189 | pci->dev_name = dev_name(dev: &op->dev); |
190 | |
191 | if (edac_op_state == EDAC_OPSTATE_POLL) { |
192 | if (pdata->is_pcie) |
193 | pci->edac_check = mpc85xx_pcie_check; |
194 | else |
195 | pci->edac_check = mpc85xx_pci_check; |
196 | } |
197 | |
198 | pdata->edac_idx = edac_pci_idx++; |
199 | |
200 | res = of_address_to_resource(dev: of_node, index: 0, r: &r); |
201 | if (res) { |
202 | pr_err("%s: Unable to get resource for PCI err regs\n" , __func__); |
203 | goto err; |
204 | } |
205 | |
206 | /* we only need the error registers */ |
207 | r.start += 0xe00; |
208 | |
209 | if (!devm_request_mem_region(&op->dev, r.start, resource_size(&r), |
210 | pdata->name)) { |
211 | pr_err("%s: Error while requesting mem region\n" , __func__); |
212 | res = -EBUSY; |
213 | goto err; |
214 | } |
215 | |
216 | pdata->pci_vbase = devm_ioremap(dev: &op->dev, offset: r.start, size: resource_size(res: &r)); |
217 | if (!pdata->pci_vbase) { |
218 | pr_err("%s: Unable to setup PCI err regs\n" , __func__); |
219 | res = -ENOMEM; |
220 | goto err; |
221 | } |
222 | |
223 | if (pdata->is_pcie) { |
224 | orig_pci_err_cap_dr = |
225 | in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ADDR); |
226 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ADDR, ~0); |
227 | orig_pci_err_en = |
228 | in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN); |
229 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, 0); |
230 | } else { |
231 | orig_pci_err_cap_dr = |
232 | in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR); |
233 | |
234 | /* PCI master abort is expected during config cycles */ |
235 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_CAP_DR, 0x40); |
236 | |
237 | orig_pci_err_en = |
238 | in_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN); |
239 | |
240 | /* disable master abort reporting */ |
241 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, ~0x40); |
242 | } |
243 | |
244 | /* clear error bits */ |
245 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_DR, ~0); |
246 | |
247 | /* reset error capture */ |
248 | out_be32(pdata->pci_vbase + MPC85XX_PCI_GAS_TIMR, 0x1); |
249 | |
250 | if (edac_pci_add_device(pci, edac_idx: pdata->edac_idx) > 0) { |
251 | edac_dbg(3, "failed edac_pci_add_device()\n" ); |
252 | goto err; |
253 | } |
254 | |
255 | if (edac_op_state == EDAC_OPSTATE_INT) { |
256 | pdata->irq = irq_of_parse_and_map(node: of_node, index: 0); |
257 | res = devm_request_irq(dev: &op->dev, irq: pdata->irq, |
258 | handler: mpc85xx_pci_isr, |
259 | IRQF_SHARED, |
260 | devname: "[EDAC] PCI err" , dev_id: pci); |
261 | if (res < 0) { |
262 | pr_err("%s: Unable to request irq %d for MPC85xx PCI err\n" , |
263 | __func__, pdata->irq); |
264 | irq_dispose_mapping(virq: pdata->irq); |
265 | res = -ENODEV; |
266 | goto err2; |
267 | } |
268 | |
269 | pr_info(EDAC_MOD_STR " acquired irq %d for PCI Err\n" , |
270 | pdata->irq); |
271 | } |
272 | |
273 | if (pdata->is_pcie) { |
274 | /* |
275 | * Enable all PCIe error interrupt & error detect except invalid |
276 | * PEX_CONFIG_ADDR/PEX_CONFIG_DATA access interrupt generation |
277 | * enable bit and invalid PEX_CONFIG_ADDR/PEX_CONFIG_DATA access |
278 | * detection enable bit. Because PCIe bus code to initialize and |
279 | * configure these PCIe devices on booting will use some invalid |
280 | * PEX_CONFIG_ADDR/PEX_CONFIG_DATA, edac driver prints the much |
281 | * notice information. So disable this detect to fix ugly print. |
282 | */ |
283 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, ~0 |
284 | & ~PEX_ERR_ICCAIE_EN_BIT); |
285 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ADDR, 0 |
286 | | PEX_ERR_ICCAD_DISR_BIT); |
287 | } |
288 | |
289 | devres_remove_group(dev: &op->dev, id: mpc85xx_pci_err_probe); |
290 | edac_dbg(3, "success\n" ); |
291 | pr_info(EDAC_MOD_STR " PCI err registered\n" ); |
292 | |
293 | return 0; |
294 | |
295 | err2: |
296 | edac_pci_del_device(dev: &op->dev); |
297 | err: |
298 | edac_pci_free_ctl_info(pci); |
299 | devres_release_group(dev: &op->dev, id: mpc85xx_pci_err_probe); |
300 | return res; |
301 | } |
302 | |
303 | static void mpc85xx_pci_err_remove(struct platform_device *op) |
304 | { |
305 | struct edac_pci_ctl_info *pci = dev_get_drvdata(dev: &op->dev); |
306 | struct mpc85xx_pci_pdata *pdata = pci->pvt_info; |
307 | |
308 | edac_dbg(0, "\n" ); |
309 | |
310 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_ADDR, orig_pci_err_cap_dr); |
311 | out_be32(pdata->pci_vbase + MPC85XX_PCI_ERR_EN, orig_pci_err_en); |
312 | |
313 | edac_pci_del_device(dev: &op->dev); |
314 | edac_pci_free_ctl_info(pci); |
315 | } |
316 | |
317 | static const struct platform_device_id mpc85xx_pci_err_match[] = { |
318 | { |
319 | .name = "mpc85xx-pci-edac" |
320 | }, |
321 | {} |
322 | }; |
323 | |
324 | static struct platform_driver mpc85xx_pci_err_driver = { |
325 | .probe = mpc85xx_pci_err_probe, |
326 | .remove_new = mpc85xx_pci_err_remove, |
327 | .id_table = mpc85xx_pci_err_match, |
328 | .driver = { |
329 | .name = "mpc85xx_pci_err" , |
330 | .suppress_bind_attrs = true, |
331 | }, |
332 | }; |
333 | #endif /* CONFIG_PCI */ |
334 | |
335 | /**************************** L2 Err device ***************************/ |
336 | |
337 | /************************ L2 SYSFS parts ***********************************/ |
338 | |
339 | static ssize_t mpc85xx_l2_inject_data_hi_show(struct edac_device_ctl_info |
340 | *edac_dev, char *data) |
341 | { |
342 | struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; |
343 | return sprintf(buf: data, fmt: "0x%08x" , |
344 | in_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJHI)); |
345 | } |
346 | |
347 | static ssize_t mpc85xx_l2_inject_data_lo_show(struct edac_device_ctl_info |
348 | *edac_dev, char *data) |
349 | { |
350 | struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; |
351 | return sprintf(buf: data, fmt: "0x%08x" , |
352 | in_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJLO)); |
353 | } |
354 | |
355 | static ssize_t mpc85xx_l2_inject_ctrl_show(struct edac_device_ctl_info |
356 | *edac_dev, char *data) |
357 | { |
358 | struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; |
359 | return sprintf(buf: data, fmt: "0x%08x" , |
360 | in_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJCTL)); |
361 | } |
362 | |
363 | static ssize_t mpc85xx_l2_inject_data_hi_store(struct edac_device_ctl_info |
364 | *edac_dev, const char *data, |
365 | size_t count) |
366 | { |
367 | struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; |
368 | if (isdigit(c: *data)) { |
369 | out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJHI, |
370 | simple_strtoul(data, NULL, 0)); |
371 | return count; |
372 | } |
373 | return 0; |
374 | } |
375 | |
376 | static ssize_t mpc85xx_l2_inject_data_lo_store(struct edac_device_ctl_info |
377 | *edac_dev, const char *data, |
378 | size_t count) |
379 | { |
380 | struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; |
381 | if (isdigit(c: *data)) { |
382 | out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJLO, |
383 | simple_strtoul(data, NULL, 0)); |
384 | return count; |
385 | } |
386 | return 0; |
387 | } |
388 | |
389 | static ssize_t mpc85xx_l2_inject_ctrl_store(struct edac_device_ctl_info |
390 | *edac_dev, const char *data, |
391 | size_t count) |
392 | { |
393 | struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; |
394 | if (isdigit(c: *data)) { |
395 | out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINJCTL, |
396 | simple_strtoul(data, NULL, 0)); |
397 | return count; |
398 | } |
399 | return 0; |
400 | } |
401 | |
402 | static struct edac_dev_sysfs_attribute mpc85xx_l2_sysfs_attributes[] = { |
403 | { |
404 | .attr = { |
405 | .name = "inject_data_hi" , |
406 | .mode = (S_IRUGO | S_IWUSR) |
407 | }, |
408 | .show = mpc85xx_l2_inject_data_hi_show, |
409 | .store = mpc85xx_l2_inject_data_hi_store}, |
410 | { |
411 | .attr = { |
412 | .name = "inject_data_lo" , |
413 | .mode = (S_IRUGO | S_IWUSR) |
414 | }, |
415 | .show = mpc85xx_l2_inject_data_lo_show, |
416 | .store = mpc85xx_l2_inject_data_lo_store}, |
417 | { |
418 | .attr = { |
419 | .name = "inject_ctrl" , |
420 | .mode = (S_IRUGO | S_IWUSR) |
421 | }, |
422 | .show = mpc85xx_l2_inject_ctrl_show, |
423 | .store = mpc85xx_l2_inject_ctrl_store}, |
424 | |
425 | /* End of list */ |
426 | { |
427 | .attr = {.name = NULL} |
428 | } |
429 | }; |
430 | |
431 | static void mpc85xx_set_l2_sysfs_attributes(struct edac_device_ctl_info |
432 | *edac_dev) |
433 | { |
434 | edac_dev->sysfs_attributes = mpc85xx_l2_sysfs_attributes; |
435 | } |
436 | |
437 | /***************************** L2 ops ***********************************/ |
438 | |
439 | static void mpc85xx_l2_check(struct edac_device_ctl_info *edac_dev) |
440 | { |
441 | struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; |
442 | u32 err_detect; |
443 | |
444 | err_detect = in_be32(pdata->l2_vbase + MPC85XX_L2_ERRDET); |
445 | |
446 | if (!(err_detect & L2_EDE_MASK)) |
447 | return; |
448 | |
449 | pr_err("ECC Error in CPU L2 cache\n" ); |
450 | pr_err("L2 Error Detect Register: 0x%08x\n" , err_detect); |
451 | pr_err("L2 Error Capture Data High Register: 0x%08x\n" , |
452 | in_be32(pdata->l2_vbase + MPC85XX_L2_CAPTDATAHI)); |
453 | pr_err("L2 Error Capture Data Lo Register: 0x%08x\n" , |
454 | in_be32(pdata->l2_vbase + MPC85XX_L2_CAPTDATALO)); |
455 | pr_err("L2 Error Syndrome Register: 0x%08x\n" , |
456 | in_be32(pdata->l2_vbase + MPC85XX_L2_CAPTECC)); |
457 | pr_err("L2 Error Attributes Capture Register: 0x%08x\n" , |
458 | in_be32(pdata->l2_vbase + MPC85XX_L2_ERRATTR)); |
459 | pr_err("L2 Error Address Capture Register: 0x%08x\n" , |
460 | in_be32(pdata->l2_vbase + MPC85XX_L2_ERRADDR)); |
461 | |
462 | /* clear error detect register */ |
463 | out_be32(pdata->l2_vbase + MPC85XX_L2_ERRDET, err_detect); |
464 | |
465 | if (err_detect & L2_EDE_CE_MASK) |
466 | edac_device_handle_ce(edac_dev, inst_nr: 0, block_nr: 0, msg: edac_dev->ctl_name); |
467 | |
468 | if (err_detect & L2_EDE_UE_MASK) |
469 | edac_device_handle_ue(edac_dev, inst_nr: 0, block_nr: 0, msg: edac_dev->ctl_name); |
470 | } |
471 | |
472 | static irqreturn_t mpc85xx_l2_isr(int irq, void *dev_id) |
473 | { |
474 | struct edac_device_ctl_info *edac_dev = dev_id; |
475 | struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; |
476 | u32 err_detect; |
477 | |
478 | err_detect = in_be32(pdata->l2_vbase + MPC85XX_L2_ERRDET); |
479 | |
480 | if (!(err_detect & L2_EDE_MASK)) |
481 | return IRQ_NONE; |
482 | |
483 | mpc85xx_l2_check(edac_dev); |
484 | |
485 | return IRQ_HANDLED; |
486 | } |
487 | |
488 | static int mpc85xx_l2_err_probe(struct platform_device *op) |
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(dev: &op->dev, id: mpc85xx_l2_err_probe, GFP_KERNEL)) |
496 | return -ENOMEM; |
497 | |
498 | edac_dev = edac_device_alloc_ctl_info(sizeof_private: sizeof(*pdata), |
499 | edac_device_name: "cpu" , nr_instances: 1, edac_block_name: "L" , nr_blocks: 1, offset_value: 2, NULL, nr_attribs: 0, |
500 | device_index: edac_dev_idx); |
501 | if (!edac_dev) { |
502 | devres_release_group(dev: &op->dev, id: mpc85xx_l2_err_probe); |
503 | return -ENOMEM; |
504 | } |
505 | |
506 | pdata = edac_dev->pvt_info; |
507 | pdata->name = "mpc85xx_l2_err" ; |
508 | edac_dev->dev = &op->dev; |
509 | dev_set_drvdata(dev: edac_dev->dev, data: edac_dev); |
510 | edac_dev->ctl_name = pdata->name; |
511 | edac_dev->dev_name = pdata->name; |
512 | |
513 | res = of_address_to_resource(dev: op->dev.of_node, index: 0, r: &r); |
514 | if (res) { |
515 | pr_err("%s: Unable to get resource for L2 err regs\n" , __func__); |
516 | goto err; |
517 | } |
518 | |
519 | /* we only need the error registers */ |
520 | r.start += 0xe00; |
521 | |
522 | if (!devm_request_mem_region(&op->dev, r.start, resource_size(&r), |
523 | pdata->name)) { |
524 | pr_err("%s: Error while requesting mem region\n" , __func__); |
525 | res = -EBUSY; |
526 | goto err; |
527 | } |
528 | |
529 | pdata->l2_vbase = devm_ioremap(dev: &op->dev, offset: r.start, size: resource_size(res: &r)); |
530 | if (!pdata->l2_vbase) { |
531 | pr_err("%s: Unable to setup L2 err regs\n" , __func__); |
532 | res = -ENOMEM; |
533 | goto err; |
534 | } |
535 | |
536 | out_be32(pdata->l2_vbase + MPC85XX_L2_ERRDET, ~0); |
537 | |
538 | orig_l2_err_disable = in_be32(pdata->l2_vbase + MPC85XX_L2_ERRDIS); |
539 | |
540 | /* clear the err_dis */ |
541 | out_be32(pdata->l2_vbase + MPC85XX_L2_ERRDIS, 0); |
542 | |
543 | edac_dev->mod_name = EDAC_MOD_STR; |
544 | |
545 | if (edac_op_state == EDAC_OPSTATE_POLL) |
546 | edac_dev->edac_check = mpc85xx_l2_check; |
547 | |
548 | mpc85xx_set_l2_sysfs_attributes(edac_dev); |
549 | |
550 | pdata->edac_idx = edac_dev_idx++; |
551 | |
552 | if (edac_device_add_device(edac_dev) > 0) { |
553 | edac_dbg(3, "failed edac_device_add_device()\n" ); |
554 | goto err; |
555 | } |
556 | |
557 | if (edac_op_state == EDAC_OPSTATE_INT) { |
558 | pdata->irq = irq_of_parse_and_map(node: op->dev.of_node, index: 0); |
559 | res = devm_request_irq(dev: &op->dev, irq: pdata->irq, |
560 | handler: mpc85xx_l2_isr, IRQF_SHARED, |
561 | devname: "[EDAC] L2 err" , dev_id: edac_dev); |
562 | if (res < 0) { |
563 | pr_err("%s: Unable to request irq %d for MPC85xx L2 err\n" , |
564 | __func__, pdata->irq); |
565 | irq_dispose_mapping(virq: pdata->irq); |
566 | res = -ENODEV; |
567 | goto err2; |
568 | } |
569 | |
570 | pr_info(EDAC_MOD_STR " acquired irq %d for L2 Err\n" , pdata->irq); |
571 | |
572 | edac_dev->op_state = OP_RUNNING_INTERRUPT; |
573 | |
574 | out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINTEN, L2_EIE_MASK); |
575 | } |
576 | |
577 | devres_remove_group(dev: &op->dev, id: mpc85xx_l2_err_probe); |
578 | |
579 | edac_dbg(3, "success\n" ); |
580 | pr_info(EDAC_MOD_STR " L2 err registered\n" ); |
581 | |
582 | return 0; |
583 | |
584 | err2: |
585 | edac_device_del_device(dev: &op->dev); |
586 | err: |
587 | devres_release_group(dev: &op->dev, id: mpc85xx_l2_err_probe); |
588 | edac_device_free_ctl_info(ctl_info: edac_dev); |
589 | return res; |
590 | } |
591 | |
592 | static void mpc85xx_l2_err_remove(struct platform_device *op) |
593 | { |
594 | struct edac_device_ctl_info *edac_dev = dev_get_drvdata(dev: &op->dev); |
595 | struct mpc85xx_l2_pdata *pdata = edac_dev->pvt_info; |
596 | |
597 | edac_dbg(0, "\n" ); |
598 | |
599 | if (edac_op_state == EDAC_OPSTATE_INT) { |
600 | out_be32(pdata->l2_vbase + MPC85XX_L2_ERRINTEN, 0); |
601 | irq_dispose_mapping(virq: pdata->irq); |
602 | } |
603 | |
604 | out_be32(pdata->l2_vbase + MPC85XX_L2_ERRDIS, orig_l2_err_disable); |
605 | edac_device_del_device(dev: &op->dev); |
606 | edac_device_free_ctl_info(ctl_info: edac_dev); |
607 | } |
608 | |
609 | static const struct of_device_id mpc85xx_l2_err_of_match[] = { |
610 | { .compatible = "fsl,mpc8536-l2-cache-controller" , }, |
611 | { .compatible = "fsl,mpc8540-l2-cache-controller" , }, |
612 | { .compatible = "fsl,mpc8541-l2-cache-controller" , }, |
613 | { .compatible = "fsl,mpc8544-l2-cache-controller" , }, |
614 | { .compatible = "fsl,mpc8548-l2-cache-controller" , }, |
615 | { .compatible = "fsl,mpc8555-l2-cache-controller" , }, |
616 | { .compatible = "fsl,mpc8560-l2-cache-controller" , }, |
617 | { .compatible = "fsl,mpc8568-l2-cache-controller" , }, |
618 | { .compatible = "fsl,mpc8569-l2-cache-controller" , }, |
619 | { .compatible = "fsl,mpc8572-l2-cache-controller" , }, |
620 | { .compatible = "fsl,p1020-l2-cache-controller" , }, |
621 | { .compatible = "fsl,p1021-l2-cache-controller" , }, |
622 | { .compatible = "fsl,p2020-l2-cache-controller" , }, |
623 | { .compatible = "fsl,t2080-l2-cache-controller" , }, |
624 | {}, |
625 | }; |
626 | MODULE_DEVICE_TABLE(of, mpc85xx_l2_err_of_match); |
627 | |
628 | static struct platform_driver mpc85xx_l2_err_driver = { |
629 | .probe = mpc85xx_l2_err_probe, |
630 | .remove_new = mpc85xx_l2_err_remove, |
631 | .driver = { |
632 | .name = "mpc85xx_l2_err" , |
633 | .of_match_table = mpc85xx_l2_err_of_match, |
634 | }, |
635 | }; |
636 | |
637 | static const struct of_device_id mpc85xx_mc_err_of_match[] = { |
638 | { .compatible = "fsl,mpc8536-memory-controller" , }, |
639 | { .compatible = "fsl,mpc8540-memory-controller" , }, |
640 | { .compatible = "fsl,mpc8541-memory-controller" , }, |
641 | { .compatible = "fsl,mpc8544-memory-controller" , }, |
642 | { .compatible = "fsl,mpc8548-memory-controller" , }, |
643 | { .compatible = "fsl,mpc8555-memory-controller" , }, |
644 | { .compatible = "fsl,mpc8560-memory-controller" , }, |
645 | { .compatible = "fsl,mpc8568-memory-controller" , }, |
646 | { .compatible = "fsl,mpc8569-memory-controller" , }, |
647 | { .compatible = "fsl,mpc8572-memory-controller" , }, |
648 | { .compatible = "fsl,mpc8349-memory-controller" , }, |
649 | { .compatible = "fsl,p1020-memory-controller" , }, |
650 | { .compatible = "fsl,p1021-memory-controller" , }, |
651 | { .compatible = "fsl,p2020-memory-controller" , }, |
652 | { .compatible = "fsl,qoriq-memory-controller" , }, |
653 | {}, |
654 | }; |
655 | MODULE_DEVICE_TABLE(of, mpc85xx_mc_err_of_match); |
656 | |
657 | static struct platform_driver mpc85xx_mc_err_driver = { |
658 | .probe = fsl_mc_err_probe, |
659 | .remove_new = fsl_mc_err_remove, |
660 | .driver = { |
661 | .name = "mpc85xx_mc_err" , |
662 | .of_match_table = mpc85xx_mc_err_of_match, |
663 | }, |
664 | }; |
665 | |
666 | static struct platform_driver * const drivers[] = { |
667 | &mpc85xx_mc_err_driver, |
668 | &mpc85xx_l2_err_driver, |
669 | #ifdef CONFIG_PCI |
670 | &mpc85xx_pci_err_driver, |
671 | #endif |
672 | }; |
673 | |
674 | static int __init mpc85xx_mc_init(void) |
675 | { |
676 | int res = 0; |
677 | u32 __maybe_unused pvr = 0; |
678 | |
679 | pr_info("Freescale(R) MPC85xx EDAC driver, (C) 2006 Montavista Software\n" ); |
680 | |
681 | /* make sure error reporting method is sane */ |
682 | switch (edac_op_state) { |
683 | case EDAC_OPSTATE_POLL: |
684 | case EDAC_OPSTATE_INT: |
685 | break; |
686 | default: |
687 | edac_op_state = EDAC_OPSTATE_INT; |
688 | break; |
689 | } |
690 | |
691 | res = platform_register_drivers(drivers, ARRAY_SIZE(drivers)); |
692 | if (res) |
693 | pr_warn(EDAC_MOD_STR "drivers fail to register\n" ); |
694 | |
695 | return 0; |
696 | } |
697 | |
698 | module_init(mpc85xx_mc_init); |
699 | |
700 | static void __exit mpc85xx_mc_exit(void) |
701 | { |
702 | platform_unregister_drivers(drivers, ARRAY_SIZE(drivers)); |
703 | } |
704 | |
705 | module_exit(mpc85xx_mc_exit); |
706 | |
707 | MODULE_LICENSE("GPL" ); |
708 | MODULE_AUTHOR("Montavista Software, Inc." ); |
709 | module_param(edac_op_state, int, 0444); |
710 | MODULE_PARM_DESC(edac_op_state, "EDAC Error Reporting state: 0=Poll, 2=Interrupt" ); |
711 | |