]>
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 <[email protected]> | |
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 | ||
e1ced097 DD |
15 | #include <asm/octeon/octeon.h> |
16 | #include <asm/octeon/cvmx-lmcx-defs.h> | |
f65aad41 RB |
17 | |
18 | #include "edac_core.h" | |
19 | #include "edac_module.h" | |
f65aad41 | 20 | |
e1ced097 | 21 | #define OCTEON_MAX_MC 4 |
f65aad41 | 22 | |
e1ced097 | 23 | static void octeon_lmc_edac_poll(struct mem_ctl_info *mci) |
f65aad41 | 24 | { |
e1ced097 DD |
25 | union cvmx_lmcx_mem_cfg0 cfg0; |
26 | bool do_clear = false; | |
f65aad41 RB |
27 | char msg[64]; |
28 | ||
e1ced097 DD |
29 | cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mci->mc_idx)); |
30 | if (cfg0.s.sec_err || cfg0.s.ded_err) { | |
31 | union cvmx_lmcx_fadr fadr; | |
32 | fadr.u64 = cvmx_read_csr(CVMX_LMCX_FADR(mci->mc_idx)); | |
33 | snprintf(msg, sizeof(msg), | |
34 | "DIMM %d rank %d bank %d row %d col %d", | |
35 | fadr.cn30xx.fdimm, fadr.cn30xx.fbunk, | |
36 | fadr.cn30xx.fbank, fadr.cn30xx.frow, fadr.cn30xx.fcol); | |
f65aad41 RB |
37 | } |
38 | ||
e1ced097 DD |
39 | if (cfg0.s.sec_err) { |
40 | edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0, | |
41 | -1, -1, -1, msg, ""); | |
42 | cfg0.s.sec_err = -1; /* Done, re-arm */ | |
43 | do_clear = true; | |
f65aad41 RB |
44 | } |
45 | ||
e1ced097 DD |
46 | if (cfg0.s.ded_err) { |
47 | edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, | |
48 | -1, -1, -1, msg, ""); | |
49 | cfg0.s.ded_err = -1; /* Done, re-arm */ | |
50 | do_clear = true; | |
51 | } | |
52 | if (do_clear) | |
53 | cvmx_write_csr(CVMX_LMCX_MEM_CFG0(mci->mc_idx), cfg0.u64); | |
f65aad41 RB |
54 | } |
55 | ||
e1ced097 | 56 | static void octeon_lmc_edac_poll_o2(struct mem_ctl_info *mci) |
f65aad41 | 57 | { |
e1ced097 DD |
58 | union cvmx_lmcx_int int_reg; |
59 | bool do_clear = false; | |
60 | char msg[64]; | |
f65aad41 | 61 | |
e1ced097 DD |
62 | int_reg.u64 = cvmx_read_csr(CVMX_LMCX_INT(mci->mc_idx)); |
63 | if (int_reg.s.sec_err || int_reg.s.ded_err) { | |
64 | union cvmx_lmcx_fadr fadr; | |
65 | fadr.u64 = cvmx_read_csr(CVMX_LMCX_FADR(mci->mc_idx)); | |
66 | snprintf(msg, sizeof(msg), | |
67 | "DIMM %d rank %d bank %d row %d col %d", | |
68 | fadr.cn61xx.fdimm, fadr.cn61xx.fbunk, | |
69 | fadr.cn61xx.fbank, fadr.cn61xx.frow, fadr.cn61xx.fcol); | |
70 | } | |
f65aad41 | 71 | |
e1ced097 DD |
72 | if (int_reg.s.sec_err) { |
73 | edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0, | |
74 | -1, -1, -1, msg, ""); | |
75 | int_reg.s.sec_err = -1; /* Done, re-arm */ | |
76 | do_clear = true; | |
f65aad41 RB |
77 | } |
78 | ||
e1ced097 DD |
79 | if (int_reg.s.ded_err) { |
80 | edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, | |
81 | -1, -1, -1, msg, ""); | |
82 | int_reg.s.ded_err = -1; /* Done, re-arm */ | |
83 | do_clear = true; | |
84 | } | |
85 | if (do_clear) | |
86 | cvmx_write_csr(CVMX_LMCX_INT(mci->mc_idx), int_reg.u64); | |
87 | } | |
f65aad41 | 88 | |
9b3c6e85 | 89 | static int octeon_lmc_edac_probe(struct platform_device *pdev) |
e1ced097 DD |
90 | { |
91 | struct mem_ctl_info *mci; | |
92 | struct edac_mc_layer layers[1]; | |
93 | int mc = pdev->id; | |
94 | ||
95 | layers[0].type = EDAC_MC_LAYER_CHANNEL; | |
96 | layers[0].size = 1; | |
97 | layers[0].is_virt_csrow = false; | |
98 | ||
99 | if (OCTEON_IS_MODEL(OCTEON_FAM_1_PLUS)) { | |
100 | union cvmx_lmcx_mem_cfg0 cfg0; | |
101 | ||
102 | cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(0)); | |
103 | if (!cfg0.s.ecc_ena) { | |
104 | dev_info(&pdev->dev, "Disabled (ECC not enabled)\n"); | |
105 | return 0; | |
106 | } | |
107 | ||
108 | mci = edac_mc_alloc(mc, ARRAY_SIZE(layers), layers, 0); | |
109 | if (!mci) | |
110 | return -ENXIO; | |
111 | ||
112 | mci->pdev = &pdev->dev; | |
113 | mci->dev_name = dev_name(&pdev->dev); | |
114 | ||
115 | mci->mod_name = "octeon-lmc"; | |
116 | mci->ctl_name = "octeon-lmc-err"; | |
117 | mci->edac_check = octeon_lmc_edac_poll; | |
118 | ||
119 | if (edac_mc_add_mc(mci)) { | |
120 | dev_err(&pdev->dev, "edac_mc_add_mc() failed\n"); | |
121 | edac_mc_free(mci); | |
122 | return -ENXIO; | |
123 | } | |
124 | ||
125 | cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mc)); | |
126 | cfg0.s.intr_ded_ena = 0; /* We poll */ | |
127 | cfg0.s.intr_sec_ena = 0; | |
128 | cvmx_write_csr(CVMX_LMCX_MEM_CFG0(mc), cfg0.u64); | |
129 | } else { | |
130 | /* OCTEON II */ | |
131 | union cvmx_lmcx_int_en en; | |
132 | union cvmx_lmcx_config config; | |
133 | ||
134 | config.u64 = cvmx_read_csr(CVMX_LMCX_CONFIG(0)); | |
135 | if (!config.s.ecc_ena) { | |
136 | dev_info(&pdev->dev, "Disabled (ECC not enabled)\n"); | |
137 | return 0; | |
138 | } | |
139 | ||
140 | mci = edac_mc_alloc(mc, ARRAY_SIZE(layers), layers, 0); | |
141 | if (!mci) | |
142 | return -ENXIO; | |
143 | ||
144 | mci->pdev = &pdev->dev; | |
145 | mci->dev_name = dev_name(&pdev->dev); | |
146 | ||
147 | mci->mod_name = "octeon-lmc"; | |
148 | mci->ctl_name = "co_lmc_err"; | |
149 | mci->edac_check = octeon_lmc_edac_poll_o2; | |
150 | ||
151 | if (edac_mc_add_mc(mci)) { | |
152 | dev_err(&pdev->dev, "edac_mc_add_mc() failed\n"); | |
153 | edac_mc_free(mci); | |
154 | return -ENXIO; | |
155 | } | |
156 | ||
157 | en.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mc)); | |
158 | en.s.intr_ded_ena = 0; /* We poll */ | |
159 | en.s.intr_sec_ena = 0; | |
160 | cvmx_write_csr(CVMX_LMCX_MEM_CFG0(mc), en.u64); | |
161 | } | |
162 | platform_set_drvdata(pdev, mci); | |
f65aad41 RB |
163 | |
164 | return 0; | |
f65aad41 RB |
165 | } |
166 | ||
e1ced097 | 167 | static int octeon_lmc_edac_remove(struct platform_device *pdev) |
f65aad41 RB |
168 | { |
169 | struct mem_ctl_info *mci = platform_get_drvdata(pdev); | |
170 | ||
f65aad41 RB |
171 | edac_mc_del_mc(&pdev->dev); |
172 | edac_mc_free(mci); | |
f65aad41 RB |
173 | return 0; |
174 | } | |
175 | ||
e1ced097 DD |
176 | static struct platform_driver octeon_lmc_edac_driver = { |
177 | .probe = octeon_lmc_edac_probe, | |
178 | .remove = octeon_lmc_edac_remove, | |
f65aad41 | 179 | .driver = { |
e1ced097 | 180 | .name = "octeon_lmc_edac", |
f65aad41 RB |
181 | } |
182 | }; | |
e1ced097 | 183 | module_platform_driver(octeon_lmc_edac_driver); |
f65aad41 RB |
184 | |
185 | MODULE_LICENSE("GPL"); | |
186 | MODULE_AUTHOR("Ralf Baechle <[email protected]>"); |