diff options
-rw-r--r-- | MAINTAINERS | 1 | ||||
-rw-r--r-- | drivers/edac/Kconfig | 7 | ||||
-rw-r--r-- | drivers/edac/Makefile | 1 | ||||
-rw-r--r-- | drivers/edac/synopsys_edac.c | 535 |
4 files changed, 544 insertions, 0 deletions
diff --git a/MAINTAINERS b/MAINTAINERS index ddb9ac8d32b3..375e2488d7ca 100644 --- a/MAINTAINERS +++ b/MAINTAINERS | |||
@@ -1583,6 +1583,7 @@ N: xilinx | |||
1583 | F: drivers/clocksource/cadence_ttc_timer.c | 1583 | F: drivers/clocksource/cadence_ttc_timer.c |
1584 | F: drivers/i2c/busses/i2c-cadence.c | 1584 | F: drivers/i2c/busses/i2c-cadence.c |
1585 | F: drivers/mmc/host/sdhci-of-arasan.c | 1585 | F: drivers/mmc/host/sdhci-of-arasan.c |
1586 | F: drivers/edac/synopsys_edac.c | ||
1586 | 1587 | ||
1587 | ARM SMMU DRIVER | 1588 | ARM SMMU DRIVER |
1588 | M: Will Deacon <will.deacon@arm.com> | 1589 | M: Will Deacon <will.deacon@arm.com> |
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index 49c265255a07..cb59619df23f 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig | |||
@@ -385,4 +385,11 @@ config EDAC_ALTERA_MC | |||
385 | preloader must initialize the SDRAM before loading | 385 | preloader must initialize the SDRAM before loading |
386 | the kernel. | 386 | the kernel. |
387 | 387 | ||
388 | config EDAC_SYNOPSYS | ||
389 | tristate "Synopsys DDR Memory Controller" | ||
390 | depends on EDAC_MM_EDAC && ARCH_ZYNQ | ||
391 | help | ||
392 | Support for error detection and correction on the Synopsys DDR | ||
393 | memory controller. | ||
394 | |||
388 | endif # EDAC | 395 | endif # EDAC |
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile index d40c69a04df7..b255f362b1db 100644 --- a/drivers/edac/Makefile +++ b/drivers/edac/Makefile | |||
@@ -67,3 +67,4 @@ obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o | |||
67 | obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o | 67 | obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o |
68 | 68 | ||
69 | obj-$(CONFIG_EDAC_ALTERA_MC) += altera_edac.o | 69 | obj-$(CONFIG_EDAC_ALTERA_MC) += altera_edac.o |
70 | obj-$(CONFIG_EDAC_SYNOPSYS) += synopsys_edac.o | ||
diff --git a/drivers/edac/synopsys_edac.c b/drivers/edac/synopsys_edac.c new file mode 100644 index 000000000000..1c9691535e13 --- /dev/null +++ b/drivers/edac/synopsys_edac.c | |||
@@ -0,0 +1,535 @@ | |||
1 | /* | ||
2 | * Synopsys DDR ECC Driver | ||
3 | * This driver is based on ppc4xx_edac.c drivers | ||
4 | * | ||
5 | * Copyright (C) 2012 - 2014 Xilinx, Inc. | ||
6 | * | ||
7 | * This program is free software: you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation, either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | * | ||
17 | * This file is subject to the terms and conditions of the GNU General Public | ||
18 | * License. See the file "COPYING" in the main directory of this archive | ||
19 | * for more details | ||
20 | */ | ||
21 | |||
22 | #include <linux/edac.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | |||
26 | #include "edac_core.h" | ||
27 | |||
28 | /* Number of cs_rows needed per memory controller */ | ||
29 | #define SYNPS_EDAC_NR_CSROWS 1 | ||
30 | |||
31 | /* Number of channels per memory controller */ | ||
32 | #define SYNPS_EDAC_NR_CHANS 1 | ||
33 | |||
34 | /* Granularity of reported error in bytes */ | ||
35 | #define SYNPS_EDAC_ERR_GRAIN 1 | ||
36 | |||
37 | #define SYNPS_EDAC_MSG_SIZE 256 | ||
38 | |||
39 | #define SYNPS_EDAC_MOD_STRING "synps_edac" | ||
40 | #define SYNPS_EDAC_MOD_VER "1" | ||
41 | |||
42 | /* Synopsys DDR memory controller registers that are relevant to ECC */ | ||
43 | #define CTRL_OFST 0x0 | ||
44 | #define T_ZQ_OFST 0xA4 | ||
45 | |||
46 | /* ECC control register */ | ||
47 | #define ECC_CTRL_OFST 0xC4 | ||
48 | /* ECC log register */ | ||
49 | #define CE_LOG_OFST 0xC8 | ||
50 | /* ECC address register */ | ||
51 | #define CE_ADDR_OFST 0xCC | ||
52 | /* ECC data[31:0] register */ | ||
53 | #define CE_DATA_31_0_OFST 0xD0 | ||
54 | |||
55 | /* Uncorrectable error info registers */ | ||
56 | #define UE_LOG_OFST 0xDC | ||
57 | #define UE_ADDR_OFST 0xE0 | ||
58 | #define UE_DATA_31_0_OFST 0xE4 | ||
59 | |||
60 | #define STAT_OFST 0xF0 | ||
61 | #define SCRUB_OFST 0xF4 | ||
62 | |||
63 | /* Control register bit field definitions */ | ||
64 | #define CTRL_BW_MASK 0xC | ||
65 | #define CTRL_BW_SHIFT 2 | ||
66 | |||
67 | #define DDRCTL_WDTH_16 1 | ||
68 | #define DDRCTL_WDTH_32 0 | ||
69 | |||
70 | /* ZQ register bit field definitions */ | ||
71 | #define T_ZQ_DDRMODE_MASK 0x2 | ||
72 | |||
73 | /* ECC control register bit field definitions */ | ||
74 | #define ECC_CTRL_CLR_CE_ERR 0x2 | ||
75 | #define ECC_CTRL_CLR_UE_ERR 0x1 | ||
76 | |||
77 | /* ECC correctable/uncorrectable error log register definitions */ | ||
78 | #define LOG_VALID 0x1 | ||
79 | #define CE_LOG_BITPOS_MASK 0xFE | ||
80 | #define CE_LOG_BITPOS_SHIFT 1 | ||
81 | |||
82 | /* ECC correctable/uncorrectable error address register definitions */ | ||
83 | #define ADDR_COL_MASK 0xFFF | ||
84 | #define ADDR_ROW_MASK 0xFFFF000 | ||
85 | #define ADDR_ROW_SHIFT 12 | ||
86 | #define ADDR_BANK_MASK 0x70000000 | ||
87 | #define ADDR_BANK_SHIFT 28 | ||
88 | |||
89 | /* ECC statistic register definitions */ | ||
90 | #define STAT_UECNT_MASK 0xFF | ||
91 | #define STAT_CECNT_MASK 0xFF00 | ||
92 | #define STAT_CECNT_SHIFT 8 | ||
93 | |||
94 | /* ECC scrub register definitions */ | ||
95 | #define SCRUB_MODE_MASK 0x7 | ||
96 | #define SCRUB_MODE_SECDED 0x4 | ||
97 | |||
98 | /** | ||
99 | * struct ecc_error_info - ECC error log information | ||
100 | * @row: Row number | ||
101 | * @col: Column number | ||
102 | * @bank: Bank number | ||
103 | * @bitpos: Bit position | ||
104 | * @data: Data causing the error | ||
105 | */ | ||
106 | struct ecc_error_info { | ||
107 | u32 row; | ||
108 | u32 col; | ||
109 | u32 bank; | ||
110 | u32 bitpos; | ||
111 | u32 data; | ||
112 | }; | ||
113 | |||
114 | /** | ||
115 | * struct synps_ecc_status - ECC status information to report | ||
116 | * @ce_cnt: Correctable error count | ||
117 | * @ue_cnt: Uncorrectable error count | ||
118 | * @ceinfo: Correctable error log information | ||
119 | * @ueinfo: Uncorrectable error log information | ||
120 | */ | ||
121 | struct synps_ecc_status { | ||
122 | u32 ce_cnt; | ||
123 | u32 ue_cnt; | ||
124 | struct ecc_error_info ceinfo; | ||
125 | struct ecc_error_info ueinfo; | ||
126 | }; | ||
127 | |||
128 | /** | ||
129 | * struct synps_edac_priv - DDR memory controller private instance data | ||
130 | * @baseaddr: Base address of the DDR controller | ||
131 | * @message: Buffer for framing the event specific info | ||
132 | * @stat: ECC status information | ||
133 | * @ce_cnt: Correctable Error count | ||
134 | * @ue_cnt: Uncorrectable Error count | ||
135 | */ | ||
136 | struct synps_edac_priv { | ||
137 | void __iomem *baseaddr; | ||
138 | char message[SYNPS_EDAC_MSG_SIZE]; | ||
139 | struct synps_ecc_status stat; | ||
140 | u32 ce_cnt; | ||
141 | u32 ue_cnt; | ||
142 | }; | ||
143 | |||
144 | /** | ||
145 | * synps_edac_geterror_info - Get the current ecc error info | ||
146 | * @base: Pointer to the base address of the ddr memory controller | ||
147 | * @p: Pointer to the synopsys ecc status structure | ||
148 | * | ||
149 | * Determines there is any ecc error or not | ||
150 | * | ||
151 | * Return: one if there is no error otherwise returns zero | ||
152 | */ | ||
153 | static int synps_edac_geterror_info(void __iomem *base, | ||
154 | struct synps_ecc_status *p) | ||
155 | { | ||
156 | u32 regval, clearval = 0; | ||
157 | |||
158 | regval = readl(base + STAT_OFST); | ||
159 | if (!regval) | ||
160 | return 1; | ||
161 | |||
162 | p->ce_cnt = (regval & STAT_CECNT_MASK) >> STAT_CECNT_SHIFT; | ||
163 | p->ue_cnt = regval & STAT_UECNT_MASK; | ||
164 | |||
165 | regval = readl(base + CE_LOG_OFST); | ||
166 | if (!(p->ce_cnt && (regval & LOG_VALID))) | ||
167 | goto ue_err; | ||
168 | |||
169 | p->ceinfo.bitpos = (regval & CE_LOG_BITPOS_MASK) >> CE_LOG_BITPOS_SHIFT; | ||
170 | regval = readl(base + CE_ADDR_OFST); | ||
171 | p->ceinfo.row = (regval & ADDR_ROW_MASK) >> ADDR_ROW_SHIFT; | ||
172 | p->ceinfo.col = regval & ADDR_COL_MASK; | ||
173 | p->ceinfo.bank = (regval & ADDR_BANK_MASK) >> ADDR_BANK_SHIFT; | ||
174 | p->ceinfo.data = readl(base + CE_DATA_31_0_OFST); | ||
175 | edac_dbg(3, "ce bit position: %d data: %d\n", p->ceinfo.bitpos, | ||
176 | p->ceinfo.data); | ||
177 | clearval = ECC_CTRL_CLR_CE_ERR; | ||
178 | |||
179 | ue_err: | ||
180 | regval = readl(base + UE_LOG_OFST); | ||
181 | if (!(p->ue_cnt && (regval & LOG_VALID))) | ||
182 | goto out; | ||
183 | |||
184 | regval = readl(base + UE_ADDR_OFST); | ||
185 | p->ueinfo.row = (regval & ADDR_ROW_MASK) >> ADDR_ROW_SHIFT; | ||
186 | p->ueinfo.col = regval & ADDR_COL_MASK; | ||
187 | p->ueinfo.bank = (regval & ADDR_BANK_MASK) >> ADDR_BANK_SHIFT; | ||
188 | p->ueinfo.data = readl(base + UE_DATA_31_0_OFST); | ||
189 | clearval |= ECC_CTRL_CLR_UE_ERR; | ||
190 | |||
191 | out: | ||
192 | writel(clearval, base + ECC_CTRL_OFST); | ||
193 | writel(0x0, base + ECC_CTRL_OFST); | ||
194 | |||
195 | return 0; | ||
196 | } | ||
197 | |||
198 | /** | ||
199 | * synps_edac_handle_error - Handle controller error types CE and UE | ||
200 | * @mci: Pointer to the edac memory controller instance | ||
201 | * @p: Pointer to the synopsys ecc status structure | ||
202 | * | ||
203 | * Handles the controller ECC correctable and un correctable error. | ||
204 | */ | ||
205 | static void synps_edac_handle_error(struct mem_ctl_info *mci, | ||
206 | struct synps_ecc_status *p) | ||
207 | { | ||
208 | struct synps_edac_priv *priv = mci->pvt_info; | ||
209 | struct ecc_error_info *pinf; | ||
210 | |||
211 | if (p->ce_cnt) { | ||
212 | pinf = &p->ceinfo; | ||
213 | snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, | ||
214 | "DDR ECC error type :%s Row %d Bank %d Col %d ", | ||
215 | "CE", pinf->row, pinf->bank, pinf->col); | ||
216 | edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, | ||
217 | p->ce_cnt, 0, 0, 0, 0, 0, -1, | ||
218 | priv->message, ""); | ||
219 | } | ||
220 | |||
221 | if (p->ue_cnt) { | ||
222 | pinf = &p->ueinfo; | ||
223 | snprintf(priv->message, SYNPS_EDAC_MSG_SIZE, | ||
224 | "DDR ECC error type :%s Row %d Bank %d Col %d ", | ||
225 | "UE", pinf->row, pinf->bank, pinf->col); | ||
226 | edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, | ||
227 | p->ue_cnt, 0, 0, 0, 0, 0, -1, | ||
228 | priv->message, ""); | ||
229 | } | ||
230 | |||
231 | memset(p, 0, sizeof(*p)); | ||
232 | } | ||
233 | |||
234 | /** | ||
235 | * synps_edac_check - Check controller for ECC errors | ||
236 | * @mci: Pointer to the edac memory controller instance | ||
237 | * | ||
238 | * Used to check and post ECC errors. Called by the polling thread | ||
239 | */ | ||
240 | static void synps_edac_check(struct mem_ctl_info *mci) | ||
241 | { | ||
242 | struct synps_edac_priv *priv = mci->pvt_info; | ||
243 | int status; | ||
244 | |||
245 | status = synps_edac_geterror_info(priv->baseaddr, &priv->stat); | ||
246 | if (status) | ||
247 | return; | ||
248 | |||
249 | priv->ce_cnt += priv->stat.ce_cnt; | ||
250 | priv->ue_cnt += priv->stat.ue_cnt; | ||
251 | synps_edac_handle_error(mci, &priv->stat); | ||
252 | |||
253 | edac_dbg(3, "Total error count ce %d ue %d\n", | ||
254 | priv->ce_cnt, priv->ue_cnt); | ||
255 | } | ||
256 | |||
257 | /** | ||
258 | * synps_edac_get_dtype - Return the controller memory width | ||
259 | * @base: Pointer to the ddr memory controller base address | ||
260 | * | ||
261 | * Get the EDAC device type width appropriate for the current controller | ||
262 | * configuration. | ||
263 | * | ||
264 | * Return: a device type width enumeration. | ||
265 | */ | ||
266 | static enum dev_type synps_edac_get_dtype(const void __iomem *base) | ||
267 | { | ||
268 | enum dev_type dt; | ||
269 | u32 width; | ||
270 | |||
271 | width = readl(base + CTRL_OFST); | ||
272 | width = (width & CTRL_BW_MASK) >> CTRL_BW_SHIFT; | ||
273 | |||
274 | switch (width) { | ||
275 | case DDRCTL_WDTH_16: | ||
276 | dt = DEV_X2; | ||
277 | break; | ||
278 | case DDRCTL_WDTH_32: | ||
279 | dt = DEV_X4; | ||
280 | break; | ||
281 | default: | ||
282 | dt = DEV_UNKNOWN; | ||
283 | } | ||
284 | |||
285 | return dt; | ||
286 | } | ||
287 | |||
288 | /** | ||
289 | * synps_edac_get_eccstate - Return the controller ecc enable/disable status | ||
290 | * @base: Pointer to the ddr memory controller base address | ||
291 | * | ||
292 | * Get the ECC enable/disable status for the controller | ||
293 | * | ||
294 | * Return: a ecc status boolean i.e true/false - enabled/disabled. | ||
295 | */ | ||
296 | static bool synps_edac_get_eccstate(void __iomem *base) | ||
297 | { | ||
298 | enum dev_type dt; | ||
299 | u32 ecctype; | ||
300 | bool state = false; | ||
301 | |||
302 | dt = synps_edac_get_dtype(base); | ||
303 | if (dt == DEV_UNKNOWN) | ||
304 | return state; | ||
305 | |||
306 | ecctype = readl(base + SCRUB_OFST) & SCRUB_MODE_MASK; | ||
307 | if ((ecctype == SCRUB_MODE_SECDED) && (dt == DEV_X2)) | ||
308 | state = true; | ||
309 | |||
310 | return state; | ||
311 | } | ||
312 | |||
313 | /** | ||
314 | * synps_edac_get_memsize - reads the size of the attached memory device | ||
315 | * | ||
316 | * Return: the memory size in bytes | ||
317 | */ | ||
318 | static u32 synps_edac_get_memsize(void) | ||
319 | { | ||
320 | struct sysinfo inf; | ||
321 | |||
322 | si_meminfo(&inf); | ||
323 | |||
324 | return inf.totalram * inf.mem_unit; | ||
325 | } | ||
326 | |||
327 | /** | ||
328 | * synps_edac_get_mtype - Returns controller memory type | ||
329 | * @base: pointer to the synopsys ecc status structure | ||
330 | * | ||
331 | * Get the EDAC memory type appropriate for the current controller | ||
332 | * configuration. | ||
333 | * | ||
334 | * Return: a memory type enumeration. | ||
335 | */ | ||
336 | static enum mem_type synps_edac_get_mtype(const void __iomem *base) | ||
337 | { | ||
338 | enum mem_type mt; | ||
339 | u32 memtype; | ||
340 | |||
341 | memtype = readl(base + T_ZQ_OFST); | ||
342 | |||
343 | if (memtype & T_ZQ_DDRMODE_MASK) | ||
344 | mt = MEM_DDR3; | ||
345 | else | ||
346 | mt = MEM_DDR2; | ||
347 | |||
348 | return mt; | ||
349 | } | ||
350 | |||
351 | /** | ||
352 | * synps_edac_init_csrows - Initialize the cs row data | ||
353 | * @mci: Pointer to the edac memory controller instance | ||
354 | * | ||
355 | * Initializes the chip select rows associated with the EDAC memory | ||
356 | * controller instance | ||
357 | * | ||
358 | * Return: Unconditionally 0. | ||
359 | */ | ||
360 | static int synps_edac_init_csrows(struct mem_ctl_info *mci) | ||
361 | { | ||
362 | struct csrow_info *csi; | ||
363 | struct dimm_info *dimm; | ||
364 | struct synps_edac_priv *priv = mci->pvt_info; | ||
365 | u32 size; | ||
366 | int row, j; | ||
367 | |||
368 | for (row = 0; row < mci->nr_csrows; row++) { | ||
369 | csi = mci->csrows[row]; | ||
370 | size = synps_edac_get_memsize(); | ||
371 | |||
372 | for (j = 0; j < csi->nr_channels; j++) { | ||
373 | dimm = csi->channels[j]->dimm; | ||
374 | dimm->edac_mode = EDAC_FLAG_SECDED; | ||
375 | dimm->mtype = synps_edac_get_mtype(priv->baseaddr); | ||
376 | dimm->nr_pages = (size >> PAGE_SHIFT) / csi->nr_channels; | ||
377 | dimm->grain = SYNPS_EDAC_ERR_GRAIN; | ||
378 | dimm->dtype = synps_edac_get_dtype(priv->baseaddr); | ||
379 | } | ||
380 | } | ||
381 | |||
382 | return 0; | ||
383 | } | ||
384 | |||
385 | /** | ||
386 | * synps_edac_mc_init - Initialize driver instance | ||
387 | * @mci: Pointer to the edac memory controller instance | ||
388 | * @pdev: Pointer to the platform_device struct | ||
389 | * | ||
390 | * Performs initialization of the EDAC memory controller instance and | ||
391 | * related driver-private data associated with the memory controller the | ||
392 | * instance is bound to. | ||
393 | * | ||
394 | * Return: Always zero. | ||
395 | */ | ||
396 | static int synps_edac_mc_init(struct mem_ctl_info *mci, | ||
397 | struct platform_device *pdev) | ||
398 | { | ||
399 | int status; | ||
400 | struct synps_edac_priv *priv; | ||
401 | |||
402 | mci->pdev = &pdev->dev; | ||
403 | priv = mci->pvt_info; | ||
404 | platform_set_drvdata(pdev, mci); | ||
405 | |||
406 | /* Initialize controller capabilities and configuration */ | ||
407 | mci->mtype_cap = MEM_FLAG_DDR3 | MEM_FLAG_DDR2; | ||
408 | mci->edac_ctl_cap = EDAC_FLAG_NONE | EDAC_FLAG_SECDED; | ||
409 | mci->scrub_cap = SCRUB_HW_SRC; | ||
410 | mci->scrub_mode = SCRUB_NONE; | ||
411 | |||
412 | mci->edac_cap = EDAC_FLAG_SECDED; | ||
413 | mci->ctl_name = "synps_ddr_controller"; | ||
414 | mci->dev_name = SYNPS_EDAC_MOD_STRING; | ||
415 | mci->mod_name = SYNPS_EDAC_MOD_VER; | ||
416 | mci->mod_ver = "1"; | ||
417 | |||
418 | edac_op_state = EDAC_OPSTATE_POLL; | ||
419 | mci->edac_check = synps_edac_check; | ||
420 | mci->ctl_page_to_phys = NULL; | ||
421 | |||
422 | status = synps_edac_init_csrows(mci); | ||
423 | |||
424 | return status; | ||
425 | } | ||
426 | |||
427 | /** | ||
428 | * synps_edac_mc_probe - Check controller and bind driver | ||
429 | * @pdev: Pointer to the platform_device struct | ||
430 | * | ||
431 | * Probes a specific controller instance for binding with the driver. | ||
432 | * | ||
433 | * Return: 0 if the controller instance was successfully bound to the | ||
434 | * driver; otherwise, < 0 on error. | ||
435 | */ | ||
436 | static int synps_edac_mc_probe(struct platform_device *pdev) | ||
437 | { | ||
438 | struct mem_ctl_info *mci; | ||
439 | struct edac_mc_layer layers[2]; | ||
440 | struct synps_edac_priv *priv; | ||
441 | int rc; | ||
442 | struct resource *res; | ||
443 | void __iomem *baseaddr; | ||
444 | |||
445 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
446 | baseaddr = devm_ioremap_resource(&pdev->dev, res); | ||
447 | if (IS_ERR(baseaddr)) | ||
448 | return PTR_ERR(baseaddr); | ||
449 | |||
450 | if (!synps_edac_get_eccstate(baseaddr)) { | ||
451 | edac_printk(KERN_INFO, EDAC_MC, "ECC not enabled\n"); | ||
452 | return -ENXIO; | ||
453 | } | ||
454 | |||
455 | layers[0].type = EDAC_MC_LAYER_CHIP_SELECT; | ||
456 | layers[0].size = SYNPS_EDAC_NR_CSROWS; | ||
457 | layers[0].is_virt_csrow = true; | ||
458 | layers[1].type = EDAC_MC_LAYER_CHANNEL; | ||
459 | layers[1].size = SYNPS_EDAC_NR_CHANS; | ||
460 | layers[1].is_virt_csrow = false; | ||
461 | |||
462 | mci = edac_mc_alloc(0, ARRAY_SIZE(layers), layers, | ||
463 | sizeof(struct synps_edac_priv)); | ||
464 | if (!mci) { | ||
465 | edac_printk(KERN_ERR, EDAC_MC, | ||
466 | "Failed memory allocation for mc instance\n"); | ||
467 | return -ENOMEM; | ||
468 | } | ||
469 | |||
470 | priv = mci->pvt_info; | ||
471 | priv->baseaddr = baseaddr; | ||
472 | rc = synps_edac_mc_init(mci, pdev); | ||
473 | if (rc) { | ||
474 | edac_printk(KERN_ERR, EDAC_MC, | ||
475 | "Failed to initialize instance\n"); | ||
476 | goto free_edac_mc; | ||
477 | } | ||
478 | |||
479 | rc = edac_mc_add_mc(mci); | ||
480 | if (rc) { | ||
481 | edac_printk(KERN_ERR, EDAC_MC, | ||
482 | "Failed to register with EDAC core\n"); | ||
483 | goto free_edac_mc; | ||
484 | } | ||
485 | |||
486 | /* | ||
487 | * Start capturing the correctable and uncorrectable errors. A write of | ||
488 | * 0 starts the counters. | ||
489 | */ | ||
490 | writel(0x0, baseaddr + ECC_CTRL_OFST); | ||
491 | return rc; | ||
492 | |||
493 | free_edac_mc: | ||
494 | edac_mc_free(mci); | ||
495 | |||
496 | return rc; | ||
497 | } | ||
498 | |||
499 | /** | ||
500 | * synps_edac_mc_remove - Unbind driver from controller | ||
501 | * @pdev: Pointer to the platform_device struct | ||
502 | * | ||
503 | * Return: Unconditionally 0 | ||
504 | */ | ||
505 | static int synps_edac_mc_remove(struct platform_device *pdev) | ||
506 | { | ||
507 | struct mem_ctl_info *mci = platform_get_drvdata(pdev); | ||
508 | |||
509 | edac_mc_del_mc(&pdev->dev); | ||
510 | edac_mc_free(mci); | ||
511 | |||
512 | return 0; | ||
513 | } | ||
514 | |||
515 | static struct of_device_id synps_edac_match[] = { | ||
516 | { .compatible = "xlnx,zynq-ddrc-a05", }, | ||
517 | { /* end of table */ } | ||
518 | }; | ||
519 | |||
520 | MODULE_DEVICE_TABLE(of, synps_edac_match); | ||
521 | |||
522 | static struct platform_driver synps_edac_mc_driver = { | ||
523 | .driver = { | ||
524 | .name = "synopsys-edac", | ||
525 | .of_match_table = synps_edac_match, | ||
526 | }, | ||
527 | .probe = synps_edac_mc_probe, | ||
528 | .remove = synps_edac_mc_remove, | ||
529 | }; | ||
530 | |||
531 | module_platform_driver(synps_edac_mc_driver); | ||
532 | |||
533 | MODULE_AUTHOR("Xilinx Inc"); | ||
534 | MODULE_DESCRIPTION("Synopsys DDR ECC driver"); | ||
535 | MODULE_LICENSE("GPL v2"); | ||