aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/edac
diff options
context:
space:
mode:
authorDaniel Walker <dwalker@fifo99.com>2013-09-20 18:46:41 -0400
committerRalf Baechle <ralf@linux-mips.org>2014-03-31 12:17:12 -0400
commit1bc021e815659190c31b19acead70d4e96516bf1 (patch)
treed2ecd6b517d0af7b5e362a7da7d1d070db12dc68 /drivers/edac
parent5331de0570b95a105b74eff9d39d9a79656743b2 (diff)
EDAC: Octeon: Add error injection support
This adds an ad-hoc error injection method. Octeon II doesn't have hardware support for injection, so this simulates it. Signed-off-by: Daniel Walker <dwalker@fifo99.com> Cc: David Daney <david.daney@cavium.com> Cc: Doug Thompson <dougthompson@xmission.com> Cc: linux-edac@vger.kernel.org Cc: linux-mips@linux-mips.org Cc: linux-kernel@vger.kernel.org Patchwork: https://patchwork.linux-mips.org/patch/5873/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'drivers/edac')
-rw-r--r--drivers/edac/octeon_edac-lmc.c177
1 files changed, 171 insertions, 6 deletions
diff --git a/drivers/edac/octeon_edac-lmc.c b/drivers/edac/octeon_edac-lmc.c
index 4881ad0b0bd6..4bd10f94f068 100644
--- a/drivers/edac/octeon_edac-lmc.c
+++ b/drivers/edac/octeon_edac-lmc.c
@@ -5,12 +5,16 @@
5 * 5 *
6 * Copyright (C) 2009 Wind River Systems, 6 * Copyright (C) 2009 Wind River Systems,
7 * written by Ralf Baechle <ralf@linux-mips.org> 7 * written by Ralf Baechle <ralf@linux-mips.org>
8 *
9 * Copyright (c) 2013 by Cisco Systems, Inc.
10 * All rights reserved.
8 */ 11 */
9#include <linux/module.h> 12#include <linux/module.h>
10#include <linux/init.h> 13#include <linux/init.h>
11#include <linux/slab.h> 14#include <linux/slab.h>
12#include <linux/io.h> 15#include <linux/io.h>
13#include <linux/edac.h> 16#include <linux/edac.h>
17#include <linux/ctype.h>
14 18
15#include <asm/octeon/octeon.h> 19#include <asm/octeon/octeon.h>
16#include <asm/octeon/cvmx-lmcx-defs.h> 20#include <asm/octeon/cvmx-lmcx-defs.h>
@@ -20,6 +24,18 @@
20 24
21#define OCTEON_MAX_MC 4 25#define OCTEON_MAX_MC 4
22 26
27#define to_mci(k) container_of(k, struct mem_ctl_info, dev)
28
29struct octeon_lmc_pvt {
30 unsigned long inject;
31 unsigned long error_type;
32 unsigned long dimm;
33 unsigned long rank;
34 unsigned long bank;
35 unsigned long row;
36 unsigned long col;
37};
38
23static void octeon_lmc_edac_poll(struct mem_ctl_info *mci) 39static void octeon_lmc_edac_poll(struct mem_ctl_info *mci)
24{ 40{
25 union cvmx_lmcx_mem_cfg0 cfg0; 41 union cvmx_lmcx_mem_cfg0 cfg0;
@@ -55,14 +71,31 @@ static void octeon_lmc_edac_poll(struct mem_ctl_info *mci)
55 71
56static void octeon_lmc_edac_poll_o2(struct mem_ctl_info *mci) 72static void octeon_lmc_edac_poll_o2(struct mem_ctl_info *mci)
57{ 73{
74 struct octeon_lmc_pvt *pvt = mci->pvt_info;
58 union cvmx_lmcx_int int_reg; 75 union cvmx_lmcx_int int_reg;
59 bool do_clear = false; 76 bool do_clear = false;
60 char msg[64]; 77 char msg[64];
61 78
62 int_reg.u64 = cvmx_read_csr(CVMX_LMCX_INT(mci->mc_idx)); 79 if (!pvt->inject)
80 int_reg.u64 = cvmx_read_csr(CVMX_LMCX_INT(mci->mc_idx));
81 else {
82 if (pvt->error_type == 1)
83 int_reg.s.sec_err = 1;
84 if (pvt->error_type == 2)
85 int_reg.s.ded_err = 1;
86 }
87
63 if (int_reg.s.sec_err || int_reg.s.ded_err) { 88 if (int_reg.s.sec_err || int_reg.s.ded_err) {
64 union cvmx_lmcx_fadr fadr; 89 union cvmx_lmcx_fadr fadr;
65 fadr.u64 = cvmx_read_csr(CVMX_LMCX_FADR(mci->mc_idx)); 90 if (likely(!pvt->inject))
91 fadr.u64 = cvmx_read_csr(CVMX_LMCX_FADR(mci->mc_idx));
92 else {
93 fadr.cn61xx.fdimm = pvt->dimm;
94 fadr.cn61xx.fbunk = pvt->rank;
95 fadr.cn61xx.fbank = pvt->bank;
96 fadr.cn61xx.frow = pvt->row;
97 fadr.cn61xx.fcol = pvt->col;
98 }
66 snprintf(msg, sizeof(msg), 99 snprintf(msg, sizeof(msg),
67 "DIMM %d rank %d bank %d row %d col %d", 100 "DIMM %d rank %d bank %d row %d col %d",
68 fadr.cn61xx.fdimm, fadr.cn61xx.fbunk, 101 fadr.cn61xx.fdimm, fadr.cn61xx.fbunk,
@@ -82,8 +115,128 @@ static void octeon_lmc_edac_poll_o2(struct mem_ctl_info *mci)
82 int_reg.s.ded_err = -1; /* Done, re-arm */ 115 int_reg.s.ded_err = -1; /* Done, re-arm */
83 do_clear = true; 116 do_clear = true;
84 } 117 }
85 if (do_clear) 118
86 cvmx_write_csr(CVMX_LMCX_INT(mci->mc_idx), int_reg.u64); 119 if (do_clear) {
120 if (likely(!pvt->inject))
121 cvmx_write_csr(CVMX_LMCX_INT(mci->mc_idx), int_reg.u64);
122 else
123 pvt->inject = 0;
124 }
125}
126
127/************************ MC SYSFS parts ***********************************/
128
129/* Only a couple naming differences per template, so very similar */
130#define TEMPLATE_SHOW(reg) \
131static ssize_t octeon_mc_inject_##reg##_show(struct device *dev, \
132 struct device_attribute *attr, \
133 char *data) \
134{ \
135 struct mem_ctl_info *mci = to_mci(dev); \
136 struct octeon_lmc_pvt *pvt = mci->pvt_info; \
137 return sprintf(data, "%016llu\n", (u64)pvt->reg); \
138}
139
140#define TEMPLATE_STORE(reg) \
141static ssize_t octeon_mc_inject_##reg##_store(struct device *dev, \
142 struct device_attribute *attr, \
143 const char *data, size_t count) \
144{ \
145 struct mem_ctl_info *mci = to_mci(dev); \
146 struct octeon_lmc_pvt *pvt = mci->pvt_info; \
147 if (isdigit(*data)) { \
148 if (!kstrtoul(data, 0, &pvt->reg)) \
149 return count; \
150 } \
151 return 0; \
152}
153
154TEMPLATE_SHOW(inject);
155TEMPLATE_STORE(inject);
156TEMPLATE_SHOW(dimm);
157TEMPLATE_STORE(dimm);
158TEMPLATE_SHOW(bank);
159TEMPLATE_STORE(bank);
160TEMPLATE_SHOW(rank);
161TEMPLATE_STORE(rank);
162TEMPLATE_SHOW(row);
163TEMPLATE_STORE(row);
164TEMPLATE_SHOW(col);
165TEMPLATE_STORE(col);
166
167static ssize_t octeon_mc_inject_error_type_store(struct device *dev,
168 struct device_attribute *attr,
169 const char *data,
170 size_t count)
171{
172 struct mem_ctl_info *mci = to_mci(dev);
173 struct octeon_lmc_pvt *pvt = mci->pvt_info;
174
175 if (!strncmp(data, "single", 6))
176 pvt->error_type = 1;
177 else if (!strncmp(data, "double", 6))
178 pvt->error_type = 2;
179
180 return count;
181}
182
183static ssize_t octeon_mc_inject_error_type_show(struct device *dev,
184 struct device_attribute *attr,
185 char *data)
186{
187 struct mem_ctl_info *mci = to_mci(dev);
188 struct octeon_lmc_pvt *pvt = mci->pvt_info;
189 if (pvt->error_type == 1)
190 return sprintf(data, "single");
191 else if (pvt->error_type == 2)
192 return sprintf(data, "double");
193
194 return 0;
195}
196
197static DEVICE_ATTR(inject, S_IRUGO | S_IWUSR,
198 octeon_mc_inject_inject_show, octeon_mc_inject_inject_store);
199static DEVICE_ATTR(error_type, S_IRUGO | S_IWUSR,
200 octeon_mc_inject_error_type_show, octeon_mc_inject_error_type_store);
201static DEVICE_ATTR(dimm, S_IRUGO | S_IWUSR,
202 octeon_mc_inject_dimm_show, octeon_mc_inject_dimm_store);
203static DEVICE_ATTR(rank, S_IRUGO | S_IWUSR,
204 octeon_mc_inject_rank_show, octeon_mc_inject_rank_store);
205static DEVICE_ATTR(bank, S_IRUGO | S_IWUSR,
206 octeon_mc_inject_bank_show, octeon_mc_inject_bank_store);
207static DEVICE_ATTR(row, S_IRUGO | S_IWUSR,
208 octeon_mc_inject_row_show, octeon_mc_inject_row_store);
209static DEVICE_ATTR(col, S_IRUGO | S_IWUSR,
210 octeon_mc_inject_col_show, octeon_mc_inject_col_store);
211
212
213static int octeon_set_mc_sysfs_attributes(struct mem_ctl_info *mci)
214{
215 int rc;
216
217 rc = device_create_file(&mci->dev, &dev_attr_inject);
218 if (rc < 0)
219 return rc;
220 rc = device_create_file(&mci->dev, &dev_attr_error_type);
221 if (rc < 0)
222 return rc;
223 rc = device_create_file(&mci->dev, &dev_attr_dimm);
224 if (rc < 0)
225 return rc;
226 rc = device_create_file(&mci->dev, &dev_attr_rank);
227 if (rc < 0)
228 return rc;
229 rc = device_create_file(&mci->dev, &dev_attr_bank);
230 if (rc < 0)
231 return rc;
232 rc = device_create_file(&mci->dev, &dev_attr_row);
233 if (rc < 0)
234 return rc;
235 rc = device_create_file(&mci->dev, &dev_attr_col);
236 if (rc < 0)
237 return rc;
238
239 return 0;
87} 240}
88 241
89static int octeon_lmc_edac_probe(struct platform_device *pdev) 242static int octeon_lmc_edac_probe(struct platform_device *pdev)
@@ -107,7 +260,7 @@ static int octeon_lmc_edac_probe(struct platform_device *pdev)
107 return 0; 260 return 0;
108 } 261 }
109 262
110 mci = edac_mc_alloc(mc, ARRAY_SIZE(layers), layers, 0); 263 mci = edac_mc_alloc(mc, ARRAY_SIZE(layers), layers, sizeof(struct octeon_lmc_pvt));
111 if (!mci) 264 if (!mci)
112 return -ENXIO; 265 return -ENXIO;
113 266
@@ -124,6 +277,12 @@ static int octeon_lmc_edac_probe(struct platform_device *pdev)
124 return -ENXIO; 277 return -ENXIO;
125 } 278 }
126 279
280 if (octeon_set_mc_sysfs_attributes(mci)) {
281 dev_err(&pdev->dev, "octeon_set_mc_sysfs_attributes() failed\n");
282 return -ENXIO;
283 }
284
285
127 cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mc)); 286 cfg0.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mc));
128 cfg0.s.intr_ded_ena = 0; /* We poll */ 287 cfg0.s.intr_ded_ena = 0; /* We poll */
129 cfg0.s.intr_sec_ena = 0; 288 cfg0.s.intr_sec_ena = 0;
@@ -139,7 +298,7 @@ static int octeon_lmc_edac_probe(struct platform_device *pdev)
139 return 0; 298 return 0;
140 } 299 }
141 300
142 mci = edac_mc_alloc(mc, ARRAY_SIZE(layers), layers, 0); 301 mci = edac_mc_alloc(mc, ARRAY_SIZE(layers), layers, sizeof(struct octeon_lmc_pvt));
143 if (!mci) 302 if (!mci)
144 return -ENXIO; 303 return -ENXIO;
145 304
@@ -156,6 +315,12 @@ static int octeon_lmc_edac_probe(struct platform_device *pdev)
156 return -ENXIO; 315 return -ENXIO;
157 } 316 }
158 317
318 if (octeon_set_mc_sysfs_attributes(mci)) {
319 dev_err(&pdev->dev, "octeon_set_mc_sysfs_attributes() failed\n");
320 return -ENXIO;
321 }
322
323
159 en.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mc)); 324 en.u64 = cvmx_read_csr(CVMX_LMCX_MEM_CFG0(mc));
160 en.s.intr_ded_ena = 0; /* We poll */ 325 en.s.intr_ded_ena = 0; /* We poll */
161 en.s.intr_sec_ena = 0; 326 en.s.intr_sec_ena = 0;