aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/edac/octeon_edac-lmc.c
diff options
context:
space:
mode:
authorDavid Daney <david.daney@cavium.com>2012-11-15 16:58:59 -0500
committerRalf Baechle <ralf@linux-mips.org>2012-12-13 12:15:26 -0500
commite1ced09797776dfd4a2a7b04b9ee7e97ab1e64be (patch)
tree473934ca424e0e10f235bcd9ae97781349af5495 /drivers/edac/octeon_edac-lmc.c
parentabe105a4d8c5ee2aa2acef33c5d163e5d187598f (diff)
MIPS/EDAC: Improve OCTEON EDAC support.
Some initialization errors are reported with the existing OCTEON EDAC support patch. Also some parts have more than one memory controller. Fix the errors and add multiple controllers if present. Signed-off-by: David Daney <david.daney@cavium.com>
Diffstat (limited to 'drivers/edac/octeon_edac-lmc.c')
-rw-r--r--drivers/edac/octeon_edac-lmc.c232
1 files changed, 134 insertions, 98 deletions
diff --git a/drivers/edac/octeon_edac-lmc.c b/drivers/edac/octeon_edac-lmc.c
index e0c1e44187bc..33bca766e37d 100644
--- a/drivers/edac/octeon_edac-lmc.c
+++ b/drivers/edac/octeon_edac-lmc.c
@@ -12,139 +12,175 @@
12#include <linux/io.h> 12#include <linux/io.h>
13#include <linux/edac.h> 13#include <linux/edac.h>
14 14
15#include <asm/octeon/cvmx.h> 15#include <asm/octeon/octeon.h>
16#include <asm/octeon/cvmx-lmcx-defs.h>
16 17
17#include "edac_core.h" 18#include "edac_core.h"
18#include "edac_module.h" 19#include "edac_module.h"
19#include "octeon_edac-lmc.h"
20 20
21#define EDAC_MOD_STR "octeon" 21#define OCTEON_MAX_MC 4
22 22
23static struct mem_ctl_info *mc_cavium; 23static void octeon_lmc_edac_poll(struct mem_ctl_info *mci)
24static void *lmc_base;
25
26static void co_lmc_poll(struct mem_ctl_info *mci)
27{ 24{
28 union lmc_mem_cfg0 cfg0; 25 union cvmx_lmcx_mem_cfg0 cfg0;
29 union lmc_fadr fadr; 26 bool do_clear = false;
30 char msg[64]; 27 char msg[64];
31 28
32 fadr.u64 = readq(lmc_base + LMC_FADR); 29 cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mci->mc_idx));
33 cfg0.u64 = readq(lmc_base + LMC_MEM_CFG0); 30 if (cfg0.s.sec_err || cfg0.s.ded_err) {
34 snprintf(msg, sizeof(msg), "DIMM %d rank %d bank %d row %d col %d", 31 union cvmx_lmcx_fadr fadr;
35 fadr.fdimm, fadr.fbunk, fadr.fbank, fadr.frow, fadr.fcol); 32 fadr.u64 = cvmx_read_csr(CVMX_LMCX_FADR(mci->mc_idx));
36 33 snprintf(msg, sizeof(msg),
37 if (cfg0.sec_err) { 34 "DIMM %d rank %d bank %d row %d col %d",
38 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0, -1, -1, -1, 35 fadr.cn30xx.fdimm, fadr.cn30xx.fbunk,
39 msg, ""); 36 fadr.cn30xx.fbank, fadr.cn30xx.frow, fadr.cn30xx.fcol);
40
41 cfg0.intr_sec_ena = -1; /* Done, re-arm */
42 } 37 }
43 38
44 if (cfg0.ded_err) { 39 if (cfg0.s.sec_err) {
45 edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, -1, -1, -1, 40 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0,
46 msg, ""); 41 -1, -1, -1, msg, "");
47 cfg0.intr_ded_ena = -1; /* Done, re-arm */ 42 cfg0.s.sec_err = -1; /* Done, re-arm */
43 do_clear = true;
48 } 44 }
49 45
50 writeq(cfg0.u64, lmc_base + LMC_MEM_CFG0); 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);
51} 54}
52 55
53static int __devinit co_lmc_probe(struct platform_device *pdev) 56static void octeon_lmc_edac_poll_o2(struct mem_ctl_info *mci)
54{ 57{
55 struct mem_ctl_info *mci; 58 union cvmx_lmcx_int int_reg;
56 union lmc_mem_cfg0 cfg0; 59 bool do_clear = false;
57 int res = 0; 60 char msg[64];
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 61
67 mci->mod_name = "octeon-lmc"; 62 int_reg.u64 = cvmx_read_csr(CVMX_LMCX_INT(mci->mc_idx));
68 mci->ctl_name = "co_lmc_err"; 63 if (int_reg.s.sec_err || int_reg.s.ded_err) {
69 mci->edac_check = co_lmc_poll; 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 }
70 71
71 if (edac_mc_add_mc(mci) > 0) { 72 if (int_reg.s.sec_err) {
72 pr_err("%s: edac_mc_add_mc() failed\n", __func__); 73 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0,
73 goto err; 74 -1, -1, -1, msg, "");
75 int_reg.s.sec_err = -1; /* Done, re-arm */
76 do_clear = true;
74 } 77 }
75 78
76 cfg0.u64 = readq(lmc_base + LMC_MEM_CFG0); /* We poll */ 79 if (int_reg.s.ded_err) {
77 cfg0.intr_ded_ena = 0; 80 edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0,
78 cfg0.intr_sec_ena = 0; 81 -1, -1, -1, msg, "");
79 writeq(cfg0.u64, lmc_base + LMC_MEM_CFG0); 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}
80 88
81 mc_cavium = mci; 89static int __devinit octeon_lmc_edac_probe(struct platform_device *pdev)
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);
82 163
83 return 0; 164 return 0;
84
85err:
86 edac_mc_free(mci);
87
88 return res;
89} 165}
90 166
91static int co_lmc_remove(struct platform_device *pdev) 167static int octeon_lmc_edac_remove(struct platform_device *pdev)
92{ 168{
93 struct mem_ctl_info *mci = platform_get_drvdata(pdev); 169 struct mem_ctl_info *mci = platform_get_drvdata(pdev);
94 170
95 mc_cavium = NULL;
96 edac_mc_del_mc(&pdev->dev); 171 edac_mc_del_mc(&pdev->dev);
97 edac_mc_free(mci); 172 edac_mc_free(mci);
98
99 return 0; 173 return 0;
100} 174}
101 175
102static struct platform_driver co_lmc_driver = { 176static struct platform_driver octeon_lmc_edac_driver = {
103 .probe = co_lmc_probe, 177 .probe = octeon_lmc_edac_probe,
104 .remove = co_lmc_remove, 178 .remove = octeon_lmc_edac_remove,
105 .driver = { 179 .driver = {
106 .name = "co_lmc_edac", 180 .name = "octeon_lmc_edac",
107 } 181 }
108}; 182};
109 183module_platform_driver(octeon_lmc_edac_driver);
110static 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
134out:
135 iounmap(lmc_base);
136
137 return ret;
138}
139
140static void __exit co_edac_exit(void)
141{
142 platform_driver_unregister(&co_lmc_driver);
143 iounmap(lmc_base);
144}
145
146module_init(co_edac_init);
147module_exit(co_edac_exit);
148 184
149MODULE_LICENSE("GPL"); 185MODULE_LICENSE("GPL");
150MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>"); 186MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");