aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThor Thayer <tthayer@opensource.altera.com>2014-09-03 11:27:54 -0400
committerDinh Nguyen <dinguyen@opensource.altera.com>2014-09-04 14:41:46 -0400
commit71bcada88b0f3c7f11fa5b8a4b30ae66dbfabbf3 (patch)
tree58168841f8d78f888eb40b6e69d01abe17545064
parent52addcf9d6669fa439387610bc65c92fa0980cef (diff)
edac: altera: Add Altera SDRAM EDAC support
This patch adds support for the CycloneV and ArriaV SDRAM controllers. Correction and reporting of SBEs, Panic on DBEs. There was a discussion thread on whether this driver should be an mfd driver or just make use of syscon, which is already a mfd. Ultimately, the decision to use a simple syscon interface was reached.[1] [1] https://lkml.org/lkml/2014/7/30/514 [dinguyen] Fixed Kconfig to have EDAC_ALTERA_MC as a tristate to prevent a build failure for allmodconfig. Signed-off-by: Thor Thayer <tthayer@opensource.altera.com> Acked-by: Borislav Petkov <bp@suse.de> [dinguyen] cleaned up commit message Signed-off-by: Dinh Nguyen <dinguyen@opensource.altera.com>
-rw-r--r--MAINTAINERS5
-rw-r--r--drivers/edac/Kconfig9
-rw-r--r--drivers/edac/Makefile2
-rw-r--r--drivers/edac/altera_edac.c410
4 files changed, 426 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS
index 1ff06dee651d..a5dd2d61777a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1376,6 +1376,11 @@ M: Dinh Nguyen <dinguyen@altera.com>
1376S: Maintained 1376S: Maintained
1377F: drivers/clk/socfpga/ 1377F: drivers/clk/socfpga/
1378 1378
1379ARM/SOCFPGA EDAC SUPPORT
1380M: Thor Thayer <tthayer@opensource.altera.com>
1381S: Maintained
1382F: drivers/edac/altera_edac.
1383
1379ARM/STI ARCHITECTURE 1384ARM/STI ARCHITECTURE
1380M: Srinivas Kandagatla <srinivas.kandagatla@gmail.com> 1385M: Srinivas Kandagatla <srinivas.kandagatla@gmail.com>
1381M: Maxime Coquelin <maxime.coquelin@st.com> 1386M: Maxime Coquelin <maxime.coquelin@st.com>
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index fd89ca982748..7072c2892d63 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -376,4 +376,13 @@ config EDAC_OCTEON_PCI
376 Support for error detection and correction on the 376 Support for error detection and correction on the
377 Cavium Octeon family of SOCs. 377 Cavium Octeon family of SOCs.
378 378
379config EDAC_ALTERA_MC
380 tristate "Altera SDRAM Memory Controller EDAC"
381 depends on EDAC_MM_EDAC && ARCH_SOCFPGA
382 help
383 Support for error detection and correction on the
384 Altera SDRAM memory controller. Note that the
385 preloader must initialize the SDRAM before loading
386 the kernel.
387
379endif # EDAC 388endif # EDAC
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index c479a24d8f77..359aa499b200 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -65,3 +65,5 @@ obj-$(CONFIG_EDAC_OCTEON_PC) += octeon_edac-pc.o
65obj-$(CONFIG_EDAC_OCTEON_L2C) += octeon_edac-l2c.o 65obj-$(CONFIG_EDAC_OCTEON_L2C) += octeon_edac-l2c.o
66obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o 66obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o
67obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o 67obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o
68
69obj-$(CONFIG_EDAC_ALTERA_MC) += altera_edac.o
diff --git a/drivers/edac/altera_edac.c b/drivers/edac/altera_edac.c
new file mode 100644
index 000000000000..3c4929fda9d5
--- /dev/null
+++ b/drivers/edac/altera_edac.c
@@ -0,0 +1,410 @@
1/*
2 * Copyright Altera Corporation (C) 2014. All rights reserved.
3 * Copyright 2011-2012 Calxeda, Inc.
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along with
15 * this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * Adapted from the highbank_mc_edac driver.
18 */
19
20#include <linux/ctype.h>
21#include <linux/edac.h>
22#include <linux/interrupt.h>
23#include <linux/kernel.h>
24#include <linux/mfd/syscon.h>
25#include <linux/of_platform.h>
26#include <linux/platform_device.h>
27#include <linux/regmap.h>
28#include <linux/types.h>
29#include <linux/uaccess.h>
30
31#include "edac_core.h"
32#include "edac_module.h"
33
34#define EDAC_MOD_STR "altera_edac"
35#define EDAC_VERSION "1"
36
37/* SDRAM Controller CtrlCfg Register */
38#define CTLCFG_OFST 0x00
39
40/* SDRAM Controller CtrlCfg Register Bit Masks */
41#define CTLCFG_ECC_EN 0x400
42#define CTLCFG_ECC_CORR_EN 0x800
43#define CTLCFG_GEN_SB_ERR 0x2000
44#define CTLCFG_GEN_DB_ERR 0x4000
45
46#define CTLCFG_ECC_AUTO_EN (CTLCFG_ECC_EN | \
47 CTLCFG_ECC_CORR_EN)
48
49/* SDRAM Controller Address Width Register */
50#define DRAMADDRW_OFST 0x2C
51
52/* SDRAM Controller Address Widths Field Register */
53#define DRAMADDRW_COLBIT_MASK 0x001F
54#define DRAMADDRW_COLBIT_SHIFT 0
55#define DRAMADDRW_ROWBIT_MASK 0x03E0
56#define DRAMADDRW_ROWBIT_SHIFT 5
57#define DRAMADDRW_BANKBIT_MASK 0x1C00
58#define DRAMADDRW_BANKBIT_SHIFT 10
59#define DRAMADDRW_CSBIT_MASK 0xE000
60#define DRAMADDRW_CSBIT_SHIFT 13
61
62/* SDRAM Controller Interface Data Width Register */
63#define DRAMIFWIDTH_OFST 0x30
64
65/* SDRAM Controller Interface Data Width Defines */
66#define DRAMIFWIDTH_16B_ECC 24
67#define DRAMIFWIDTH_32B_ECC 40
68
69/* SDRAM Controller DRAM Status Register */
70#define DRAMSTS_OFST 0x38
71
72/* SDRAM Controller DRAM Status Register Bit Masks */
73#define DRAMSTS_SBEERR 0x04
74#define DRAMSTS_DBEERR 0x08
75#define DRAMSTS_CORR_DROP 0x10
76
77/* SDRAM Controller DRAM IRQ Register */
78#define DRAMINTR_OFST 0x3C
79
80/* SDRAM Controller DRAM IRQ Register Bit Masks */
81#define DRAMINTR_INTREN 0x01
82#define DRAMINTR_SBEMASK 0x02
83#define DRAMINTR_DBEMASK 0x04
84#define DRAMINTR_CORRDROPMASK 0x08
85#define DRAMINTR_INTRCLR 0x10
86
87/* SDRAM Controller Single Bit Error Count Register */
88#define SBECOUNT_OFST 0x40
89
90/* SDRAM Controller Single Bit Error Count Register Bit Masks */
91#define SBECOUNT_MASK 0x0F
92
93/* SDRAM Controller Double Bit Error Count Register */
94#define DBECOUNT_OFST 0x44
95
96/* SDRAM Controller Double Bit Error Count Register Bit Masks */
97#define DBECOUNT_MASK 0x0F
98
99/* SDRAM Controller ECC Error Address Register */
100#define ERRADDR_OFST 0x48
101
102/* SDRAM Controller ECC Error Address Register Bit Masks */
103#define ERRADDR_MASK 0xFFFFFFFF
104
105/* Altera SDRAM Memory Controller data */
106struct altr_sdram_mc_data {
107 struct regmap *mc_vbase;
108};
109
110static irqreturn_t altr_sdram_mc_err_handler(int irq, void *dev_id)
111{
112 struct mem_ctl_info *mci = dev_id;
113 struct altr_sdram_mc_data *drvdata = mci->pvt_info;
114 u32 status, err_count, err_addr;
115
116 /* Error Address is shared by both SBE & DBE */
117 regmap_read(drvdata->mc_vbase, ERRADDR_OFST, &err_addr);
118
119 regmap_read(drvdata->mc_vbase, DRAMSTS_OFST, &status);
120
121 if (status & DRAMSTS_DBEERR) {
122 regmap_read(drvdata->mc_vbase, DBECOUNT_OFST, &err_count);
123 panic("\nEDAC: [%d Uncorrectable errors @ 0x%08X]\n",
124 err_count, err_addr);
125 }
126 if (status & DRAMSTS_SBEERR) {
127 regmap_read(drvdata->mc_vbase, SBECOUNT_OFST, &err_count);
128 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, err_count,
129 err_addr >> PAGE_SHIFT,
130 err_addr & ~PAGE_MASK, 0,
131 0, 0, -1, mci->ctl_name, "");
132 }
133
134 regmap_write(drvdata->mc_vbase, DRAMINTR_OFST,
135 (DRAMINTR_INTRCLR | DRAMINTR_INTREN));
136
137 return IRQ_HANDLED;
138}
139
140#ifdef CONFIG_EDAC_DEBUG
141static ssize_t altr_sdr_mc_err_inject_write(struct file *file,
142 const char __user *data,
143 size_t count, loff_t *ppos)
144{
145 struct mem_ctl_info *mci = file->private_data;
146 struct altr_sdram_mc_data *drvdata = mci->pvt_info;
147 u32 *ptemp;
148 dma_addr_t dma_handle;
149 u32 reg, read_reg;
150
151 ptemp = dma_alloc_coherent(mci->pdev, 16, &dma_handle, GFP_KERNEL);
152 if (!ptemp) {
153 dma_free_coherent(mci->pdev, 16, ptemp, dma_handle);
154 edac_printk(KERN_ERR, EDAC_MC,
155 "Inject: Buffer Allocation error\n");
156 return -ENOMEM;
157 }
158
159 regmap_read(drvdata->mc_vbase, CTLCFG_OFST, &read_reg);
160 read_reg &= ~(CTLCFG_GEN_SB_ERR | CTLCFG_GEN_DB_ERR);
161
162 /* Error are injected by writing a word while the SBE or DBE
163 * bit in the CTLCFG register is set. Reading the word will
164 * trigger the SBE or DBE error and the corresponding IRQ.
165 */
166 if (count == 3) {
167 edac_printk(KERN_ALERT, EDAC_MC,
168 "Inject Double bit error\n");
169 regmap_write(drvdata->mc_vbase, CTLCFG_OFST,
170 (read_reg | CTLCFG_GEN_DB_ERR));
171 } else {
172 edac_printk(KERN_ALERT, EDAC_MC,
173 "Inject Single bit error\n");
174 regmap_write(drvdata->mc_vbase, CTLCFG_OFST,
175 (read_reg | CTLCFG_GEN_SB_ERR));
176 }
177
178 ptemp[0] = 0x5A5A5A5A;
179 ptemp[1] = 0xA5A5A5A5;
180
181 /* Clear the error injection bits */
182 regmap_write(drvdata->mc_vbase, CTLCFG_OFST, read_reg);
183 /* Ensure it has been written out */
184 wmb();
185
186 /*
187 * To trigger the error, we need to read the data back
188 * (the data was written with errors above).
189 * The ACCESS_ONCE macros and printk are used to prevent the
190 * the compiler optimizing these reads out.
191 */
192 reg = ACCESS_ONCE(ptemp[0]);
193 read_reg = ACCESS_ONCE(ptemp[1]);
194 /* Force Read */
195 rmb();
196
197 edac_printk(KERN_ALERT, EDAC_MC, "Read Data [0x%X, 0x%X]\n",
198 reg, read_reg);
199
200 dma_free_coherent(mci->pdev, 16, ptemp, dma_handle);
201
202 return count;
203}
204
205static const struct file_operations altr_sdr_mc_debug_inject_fops = {
206 .open = simple_open,
207 .write = altr_sdr_mc_err_inject_write,
208 .llseek = generic_file_llseek,
209};
210
211static void altr_sdr_mc_create_debugfs_nodes(struct mem_ctl_info *mci)
212{
213 if (mci->debugfs)
214 debugfs_create_file("inject_ctrl", S_IWUSR, mci->debugfs, mci,
215 &altr_sdr_mc_debug_inject_fops);
216}
217#else
218static void altr_sdr_mc_create_debugfs_nodes(struct mem_ctl_info *mci)
219{}
220#endif
221
222/* Get total memory size in bytes */
223static u32 altr_sdram_get_total_mem_size(struct regmap *mc_vbase)
224{
225 u32 size, read_reg, row, bank, col, cs, width;
226
227 if (regmap_read(mc_vbase, DRAMADDRW_OFST, &read_reg) < 0)
228 return 0;
229
230 if (regmap_read(mc_vbase, DRAMIFWIDTH_OFST, &width) < 0)
231 return 0;
232
233 col = (read_reg & DRAMADDRW_COLBIT_MASK) >>
234 DRAMADDRW_COLBIT_SHIFT;
235 row = (read_reg & DRAMADDRW_ROWBIT_MASK) >>
236 DRAMADDRW_ROWBIT_SHIFT;
237 bank = (read_reg & DRAMADDRW_BANKBIT_MASK) >>
238 DRAMADDRW_BANKBIT_SHIFT;
239 cs = (read_reg & DRAMADDRW_CSBIT_MASK) >>
240 DRAMADDRW_CSBIT_SHIFT;
241
242 /* Correct for ECC as its not addressible */
243 if (width == DRAMIFWIDTH_32B_ECC)
244 width = 32;
245 if (width == DRAMIFWIDTH_16B_ECC)
246 width = 16;
247
248 /* calculate the SDRAM size base on this info */
249 size = 1 << (row + bank + col);
250 size = size * cs * (width / 8);
251 return size;
252}
253
254static int altr_sdram_probe(struct platform_device *pdev)
255{
256 struct edac_mc_layer layers[2];
257 struct mem_ctl_info *mci;
258 struct altr_sdram_mc_data *drvdata;
259 struct regmap *mc_vbase;
260 struct dimm_info *dimm;
261 u32 read_reg, mem_size;
262 int irq;
263 int res = 0;
264
265 /* Validate the SDRAM controller has ECC enabled */
266 /* Grab the register range from the sdr controller in device tree */
267 mc_vbase = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
268 "altr,sdr-syscon");
269 if (IS_ERR(mc_vbase)) {
270 edac_printk(KERN_ERR, EDAC_MC,
271 "regmap for altr,sdr-syscon lookup failed.\n");
272 return -ENODEV;
273 }
274
275 if (regmap_read(mc_vbase, CTLCFG_OFST, &read_reg) ||
276 ((read_reg & CTLCFG_ECC_AUTO_EN) != CTLCFG_ECC_AUTO_EN)) {
277 edac_printk(KERN_ERR, EDAC_MC,
278 "No ECC/ECC disabled [0x%08X]\n", read_reg);
279 return -ENODEV;
280 }
281
282 /* Grab memory size from device tree. */
283 mem_size = altr_sdram_get_total_mem_size(mc_vbase);
284 if (!mem_size) {
285 edac_printk(KERN_ERR, EDAC_MC,
286 "Unable to calculate memory size\n");
287 return -ENODEV;
288 }
289
290 /* Ensure the SDRAM Interrupt is disabled and cleared */
291 if (regmap_write(mc_vbase, DRAMINTR_OFST, DRAMINTR_INTRCLR)) {
292 edac_printk(KERN_ERR, EDAC_MC,
293 "Error clearing SDRAM ECC IRQ\n");
294 return -ENODEV;
295 }
296
297 irq = platform_get_irq(pdev, 0);
298 if (irq < 0) {
299 edac_printk(KERN_ERR, EDAC_MC,
300 "No irq %d in DT\n", irq);
301 return -ENODEV;
302 }
303
304 layers[0].type = EDAC_MC_LAYER_CHIP_SELECT;
305 layers[0].size = 1;
306 layers[0].is_virt_csrow = true;
307 layers[1].type = EDAC_MC_LAYER_CHANNEL;
308 layers[1].size = 1;
309 layers[1].is_virt_csrow = false;
310 mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers,
311 sizeof(struct altr_sdram_mc_data));
312 if (!mci)
313 return -ENOMEM;
314
315 mci->pdev = &pdev->dev;
316 drvdata = mci->pvt_info;
317 drvdata->mc_vbase = mc_vbase;
318 platform_set_drvdata(pdev, mci);
319
320 if (!devres_open_group(&pdev->dev, NULL, GFP_KERNEL)) {
321 res = -ENOMEM;
322 goto free;
323 }
324
325 mci->mtype_cap = MEM_FLAG_DDR3;
326 mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED;
327 mci->edac_cap = EDAC_FLAG_SECDED;
328 mci->mod_name = EDAC_MOD_STR;
329 mci->mod_ver = EDAC_VERSION;
330 mci->ctl_name = dev_name(&pdev->dev);
331 mci->scrub_mode = SCRUB_SW_SRC;
332 mci->dev_name = dev_name(&pdev->dev);
333
334 dimm = *mci->dimms;
335 dimm->nr_pages = ((mem_size - 1) >> PAGE_SHIFT) + 1;
336 dimm->grain = 8;
337 dimm->dtype = DEV_X8;
338 dimm->mtype = MEM_DDR3;
339 dimm->edac_mode = EDAC_SECDED;
340
341 res = edac_mc_add_mc(mci);
342 if (res < 0)
343 goto err;
344
345 res = devm_request_irq(&pdev->dev, irq, altr_sdram_mc_err_handler,
346 0, dev_name(&pdev->dev), mci);
347 if (res < 0) {
348 edac_mc_printk(mci, KERN_ERR,
349 "Unable to request irq %d\n", irq);
350 res = -ENODEV;
351 goto err2;
352 }
353
354 if (regmap_write(drvdata->mc_vbase, DRAMINTR_OFST,
355 (DRAMINTR_INTRCLR | DRAMINTR_INTREN))) {
356 edac_mc_printk(mci, KERN_ERR,
357 "Error enabling SDRAM ECC IRQ\n");
358 res = -ENODEV;
359 goto err2;
360 }
361
362 altr_sdr_mc_create_debugfs_nodes(mci);
363
364 devres_close_group(&pdev->dev, NULL);
365
366 return 0;
367
368err2:
369 edac_mc_del_mc(&pdev->dev);
370err:
371 devres_release_group(&pdev->dev, NULL);
372free:
373 edac_mc_free(mci);
374 edac_printk(KERN_ERR, EDAC_MC,
375 "EDAC Probe Failed; Error %d\n", res);
376
377 return res;
378}
379
380static int altr_sdram_remove(struct platform_device *pdev)
381{
382 struct mem_ctl_info *mci = platform_get_drvdata(pdev);
383
384 edac_mc_del_mc(&pdev->dev);
385 edac_mc_free(mci);
386 platform_set_drvdata(pdev, NULL);
387
388 return 0;
389}
390
391static const struct of_device_id altr_sdram_ctrl_of_match[] = {
392 { .compatible = "altr,sdram-edac", },
393 {},
394};
395MODULE_DEVICE_TABLE(of, altr_sdram_ctrl_of_match);
396
397static struct platform_driver altr_sdram_edac_driver = {
398 .probe = altr_sdram_probe,
399 .remove = altr_sdram_remove,
400 .driver = {
401 .name = "altr_sdram_edac",
402 .of_match_table = altr_sdram_ctrl_of_match,
403 },
404};
405
406module_platform_driver(altr_sdram_edac_driver);
407
408MODULE_LICENSE("GPL v2");
409MODULE_AUTHOR("Thor Thayer");
410MODULE_DESCRIPTION("EDAC Driver for Altera SDRAM Controller");