]>
Commit | Line | Data |
---|---|---|
f65aad41 RB |
1 | /* |
2 | * This file is subject to the terms and conditions of the GNU General Public | |
3 | * License. See the file "COPYING" in the main directory of this archive | |
4 | * for more details. | |
5 | * | |
6 | * Copyright (C) 2009 Wind River Systems, | |
7 | * written by Ralf Baechle <ralf@linux-mips.org> | |
8 | */ | |
9 | #include <linux/module.h> | |
10 | #include <linux/init.h> | |
11 | #include <linux/slab.h> | |
12 | #include <linux/io.h> | |
13 | #include <linux/edac.h> | |
14 | ||
15 | #include <asm/octeon/cvmx.h> | |
16 | ||
17 | #include "edac_core.h" | |
18 | #include "edac_module.h" | |
19 | #include "octeon_edac-lmc.h" | |
20 | ||
21 | #define EDAC_MOD_STR "octeon" | |
22 | ||
23 | static struct mem_ctl_info *mc_cavium; | |
24 | static void *lmc_base; | |
25 | ||
26 | static void co_lmc_poll(struct mem_ctl_info *mci) | |
27 | { | |
28 | union lmc_mem_cfg0 cfg0; | |
29 | union lmc_fadr fadr; | |
30 | char msg[64]; | |
31 | ||
32 | fadr.u64 = readq(lmc_base + LMC_FADR); | |
33 | cfg0.u64 = readq(lmc_base + LMC_MEM_CFG0); | |
34 | snprintf(msg, sizeof(msg), "DIMM %d rank %d bank %d row %d col %d", | |
35 | fadr.fdimm, fadr.fbunk, fadr.fbank, fadr.frow, fadr.fcol); | |
36 | ||
37 | if (cfg0.sec_err) { | |
38 | edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0, -1, -1, -1, | |
39 | msg, ""); | |
40 | ||
41 | cfg0.intr_sec_ena = -1; /* Done, re-arm */ | |
42 | } | |
43 | ||
44 | if (cfg0.ded_err) { | |
45 | edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, -1, -1, -1, | |
46 | msg, ""); | |
47 | cfg0.intr_ded_ena = -1; /* Done, re-arm */ | |
48 | } | |
49 | ||
50 | writeq(cfg0.u64, lmc_base + LMC_MEM_CFG0); | |
51 | } | |
52 | ||
53 | static int __devinit co_lmc_probe(struct platform_device *pdev) | |
54 | { | |
55 | struct mem_ctl_info *mci; | |
56 | union lmc_mem_cfg0 cfg0; | |
57 | int res = 0; | |
58 | ||
59 | mci = edac_mc_alloc(0, 0, 0, 0); | |
60 | if (!mci) | |
61 | return -ENOMEM; | |
62 | ||
63 | mci->pdev = &pdev->dev; | |
64 | platform_set_drvdata(pdev, mci); | |
65 | mci->dev_name = dev_name(&pdev->dev); | |
66 | ||
67 | mci->mod_name = "octeon-lmc"; | |
68 | mci->ctl_name = "co_lmc_err"; | |
69 | mci->edac_check = co_lmc_poll; | |
70 | ||
71 | if (edac_mc_add_mc(mci) > 0) { | |
72 | pr_err("%s: edac_mc_add_mc() failed\n", __func__); | |
73 | goto err; | |
74 | } | |
75 | ||
76 | cfg0.u64 = readq(lmc_base + LMC_MEM_CFG0); /* We poll */ | |
77 | cfg0.intr_ded_ena = 0; | |
78 | cfg0.intr_sec_ena = 0; | |
79 | writeq(cfg0.u64, lmc_base + LMC_MEM_CFG0); | |
80 | ||
81 | mc_cavium = mci; | |
82 | ||
83 | return 0; | |
84 | ||
85 | err: | |
86 | edac_mc_free(mci); | |
87 | ||
88 | return res; | |
89 | } | |
90 | ||
91 | static int co_lmc_remove(struct platform_device *pdev) | |
92 | { | |
93 | struct mem_ctl_info *mci = platform_get_drvdata(pdev); | |
94 | ||
95 | mc_cavium = NULL; | |
96 | edac_mc_del_mc(&pdev->dev); | |
97 | edac_mc_free(mci); | |
98 | ||
99 | return 0; | |
100 | } | |
101 | ||
102 | static struct platform_driver co_lmc_driver = { | |
103 | .probe = co_lmc_probe, | |
104 | .remove = co_lmc_remove, | |
105 | .driver = { | |
106 | .name = "co_lmc_edac", | |
107 | } | |
108 | }; | |
109 | ||
110 | static int __init co_edac_init(void) | |
111 | { | |
112 | union lmc_mem_cfg0 cfg0; | |
113 | int ret; | |
114 | ||
115 | lmc_base = ioremap_nocache(LMC_BASE, LMC_SIZE); | |
116 | if (!lmc_base) | |
117 | return -ENOMEM; | |
118 | ||
119 | cfg0.u64 = readq(lmc_base + LMC_MEM_CFG0); | |
120 | if (!cfg0.ecc_ena) { | |
121 | pr_info(EDAC_MOD_STR " LMC EDAC: ECC disabled, good bye\n"); | |
122 | ret = -ENODEV; | |
123 | goto out; | |
124 | } | |
125 | ||
126 | ret = platform_driver_register(&co_lmc_driver); | |
127 | if (ret) { | |
128 | pr_warning(EDAC_MOD_STR " LMC EDAC failed to register\n"); | |
129 | goto out; | |
130 | } | |
131 | ||
132 | return ret; | |
133 | ||
134 | out: | |
135 | iounmap(lmc_base); | |
136 | ||
137 | return ret; | |
138 | } | |
139 | ||
140 | static void __exit co_edac_exit(void) | |
141 | { | |
142 | platform_driver_unregister(&co_lmc_driver); | |
143 | iounmap(lmc_base); | |
144 | } | |
145 | ||
146 | module_init(co_edac_init); | |
147 | module_exit(co_edac_exit); | |
148 | ||
149 | MODULE_LICENSE("GPL"); | |
150 | MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>"); |