aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/edac/octeon_edac-lmc.c
diff options
context:
space:
mode:
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>");