aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2012-10-16 18:39:09 -0400
committerRalf Baechle <ralf@linux-mips.org>2012-12-12 10:48:49 -0500
commitf65aad41772f6a0022e9763fe06f47604449964c (patch)
tree701d6ea74ac0c41f008c5554d876945fe4caf512 /drivers
parentaa1762f49c81a14d0453e4f67f922e4f155510a3 (diff)
MIPS: Cavium: Add EDAC support.
Drivers for EDAC on Cavium. Supported subsystems are: o CPU primary caches. These are parity protected only, so only error reporting. o Second level cache - ECC protected, provides SECDED. o Memory: ECC / SECDEC if used with suitable DRAM modules. The driver will will only initialize if ECC is enabled on a system so is safe to run on non-ECC memory. o PCI: Parity error reporting Since it is very hard to test this sort of code the implementation is very conservative and uses polling where possible for now. Signed-off-by: Ralf Baechle <ralf@linux-mips.org> Reviewed-by: Borislav Petkov <borislav.petkov@amd.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/edac/Kconfig33
-rw-r--r--drivers/edac/Makefile5
-rw-r--r--drivers/edac/octeon_edac-l2c.c118
-rw-r--r--drivers/edac/octeon_edac-lmc.c150
-rw-r--r--drivers/edac/octeon_edac-lmc.h78
-rw-r--r--drivers/edac/octeon_edac-pc.c140
-rw-r--r--drivers/edac/octeon_edac-pci.c135
7 files changed, 658 insertions, 1 deletions
diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig
index 409b92b8d346..a9db20815a39 100644
--- a/drivers/edac/Kconfig
+++ b/drivers/edac/Kconfig
@@ -7,7 +7,7 @@
7menuconfig EDAC 7menuconfig EDAC
8 bool "EDAC (Error Detection And Correction) reporting" 8 bool "EDAC (Error Detection And Correction) reporting"
9 depends on HAS_IOMEM 9 depends on HAS_IOMEM
10 depends on X86 || PPC || TILE || ARM 10 depends on X86 || PPC || TILE || ARM || EDAC_SUPPORT
11 help 11 help
12 EDAC is designed to report errors in the core system. 12 EDAC is designed to report errors in the core system.
13 These are low-level errors that are reported in the CPU or 13 These are low-level errors that are reported in the CPU or
@@ -27,6 +27,9 @@ menuconfig EDAC
27 There is also a mailing list for the EDAC project, which can 27 There is also a mailing list for the EDAC project, which can
28 be found via the sourceforge page. 28 be found via the sourceforge page.
29 29
30config EDAC_SUPPORT
31 bool
32
30if EDAC 33if EDAC
31 34
32comment "Reporting subsystems" 35comment "Reporting subsystems"
@@ -316,4 +319,32 @@ config EDAC_HIGHBANK_L2
316 Support for error detection and correction on the 319 Support for error detection and correction on the
317 Calxeda Highbank memory controller. 320 Calxeda Highbank memory controller.
318 321
322config EDAC_OCTEON_PC
323 tristate "Cavium Octeon Primary Caches"
324 depends on EDAC_MM_EDAC && CPU_CAVIUM_OCTEON
325 help
326 Support for error detection and correction on the primary caches of
327 the cnMIPS cores of Cavium Octeon family SOCs.
328
329config EDAC_OCTEON_L2C
330 tristate "Cavium Octeon Secondary Caches (L2C)"
331 depends on EDAC_MM_EDAC && CPU_CAVIUM_OCTEON
332 help
333 Support for error detection and correction on the
334 Cavium Octeon family of SOCs.
335
336config EDAC_OCTEON_LMC
337 tristate "Cavium Octeon DRAM Memory Controller (LMC)"
338 depends on EDAC_MM_EDAC && CPU_CAVIUM_OCTEON
339 help
340 Support for error detection and correction on the
341 Cavium Octeon family of SOCs.
342
343config EDAC_OCTEON_PCI
344 tristate "Cavium Octeon PCI Controller"
345 depends on EDAC_MM_EDAC && PCI && CPU_CAVIUM_OCTEON
346 help
347 Support for error detection and correction on the
348 Cavium Octeon family of SOCs.
349
319endif # EDAC 350endif # EDAC
diff --git a/drivers/edac/Makefile b/drivers/edac/Makefile
index 7e5129a733f8..5608a9ba61b7 100644
--- a/drivers/edac/Makefile
+++ b/drivers/edac/Makefile
@@ -58,3 +58,8 @@ obj-$(CONFIG_EDAC_TILE) += tile_edac.o
58 58
59obj-$(CONFIG_EDAC_HIGHBANK_MC) += highbank_mc_edac.o 59obj-$(CONFIG_EDAC_HIGHBANK_MC) += highbank_mc_edac.o
60obj-$(CONFIG_EDAC_HIGHBANK_L2) += highbank_l2_edac.o 60obj-$(CONFIG_EDAC_HIGHBANK_L2) += highbank_l2_edac.o
61
62obj-$(CONFIG_EDAC_OCTEON_PC) += octeon_edac-pc.o
63obj-$(CONFIG_EDAC_OCTEON_L2C) += octeon_edac-l2c.o
64obj-$(CONFIG_EDAC_OCTEON_LMC) += octeon_edac-lmc.o
65obj-$(CONFIG_EDAC_OCTEON_PCI) += octeon_edac-pci.o
diff --git a/drivers/edac/octeon_edac-l2c.c b/drivers/edac/octeon_edac-l2c.c
new file mode 100644
index 000000000000..5f459aa451bf
--- /dev/null
+++ b/drivers/edac/octeon_edac-l2c.c
@@ -0,0 +1,118 @@
1/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 2009 Wind River Systems,
7 * written by Ralf Baechle <ralf@linux-mips.org>
8 */
9#include <linux/module.h>
10#include <linux/init.h>
11#include <linux/slab.h>
12#include <linux/io.h>
13#include <linux/edac.h>
14
15#include <asm/octeon/cvmx.h>
16
17#include "edac_core.h"
18#include "edac_module.h"
19
20#define EDAC_MOD_STR "octeon-l2c"
21
22static void co_l2c_poll(struct edac_device_ctl_info *l2c)
23{
24 union cvmx_l2t_err l2t_err;
25
26 l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR);
27 if (l2t_err.s.sec_err) {
28 edac_device_handle_ce(l2c, 0, 0,
29 "Single bit error (corrected)");
30 l2t_err.s.sec_err = 1; /* Reset */
31 cvmx_write_csr(CVMX_L2T_ERR, l2t_err.u64);
32 }
33 if (l2t_err.s.ded_err) {
34 edac_device_handle_ue(l2c, 0, 0,
35 "Double bit error (corrected)");
36 l2t_err.s.ded_err = 1; /* Reset */
37 cvmx_write_csr(CVMX_L2T_ERR, l2t_err.u64);
38 }
39}
40
41static int __devinit co_l2c_probe(struct platform_device *pdev)
42{
43 struct edac_device_ctl_info *l2c;
44 union cvmx_l2t_err l2t_err;
45 int res = 0;
46
47 l2c = edac_device_alloc_ctl_info(0, "l2c", 1, NULL, 0, 0,
48 NULL, 0, edac_device_alloc_index());
49 if (!l2c)
50 return -ENOMEM;
51
52 l2c->dev = &pdev->dev;
53 platform_set_drvdata(pdev, l2c);
54 l2c->dev_name = dev_name(&pdev->dev);
55
56 l2c->mod_name = "octeon-l2c";
57 l2c->ctl_name = "octeon_l2c_err";
58 l2c->edac_check = co_l2c_poll;
59
60 if (edac_device_add_device(l2c) > 0) {
61 pr_err("%s: edac_device_add_device() failed\n", __func__);
62 goto err;
63 }
64
65 l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR);
66 l2t_err.s.sec_intena = 0; /* We poll */
67 l2t_err.s.ded_intena = 0;
68 l2t_err.s.sec_err = 1; /* Clear, just in case */
69 l2t_err.s.ded_err = 1;
70 cvmx_write_csr(CVMX_L2T_ERR, l2t_err.u64);
71
72 return 0;
73
74err:
75 edac_device_free_ctl_info(l2c);
76
77 return res;
78}
79
80static int co_l2c_remove(struct platform_device *pdev)
81{
82 struct edac_device_ctl_info *l2c = platform_get_drvdata(pdev);
83
84 edac_device_del_device(&pdev->dev);
85 edac_device_free_ctl_info(l2c);
86
87 return 0;
88}
89
90static struct platform_driver co_l2c_driver = {
91 .probe = co_l2c_probe,
92 .remove = co_l2c_remove,
93 .driver = {
94 .name = "co_l2c_edac",
95 }
96};
97
98static int __init co_edac_init(void)
99{
100 int ret;
101
102 ret = platform_driver_register(&co_l2c_driver);
103 if (ret)
104 pr_warning(EDAC_MOD_STR " EDAC failed to register\n");
105
106 return ret;
107}
108
109static void __exit co_edac_exit(void)
110{
111 platform_driver_unregister(&co_l2c_driver);
112}
113
114module_init(co_edac_init);
115module_exit(co_edac_exit);
116
117MODULE_LICENSE("GPL");
118MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
diff --git a/drivers/edac/octeon_edac-lmc.c b/drivers/edac/octeon_edac-lmc.c
new file mode 100644
index 000000000000..e0c1e44187bc
--- /dev/null
+++ b/drivers/edac/octeon_edac-lmc.c
@@ -0,0 +1,150 @@
1/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 2009 Wind River Systems,
7 * written by Ralf Baechle <ralf@linux-mips.org>
8 */
9#include <linux/module.h>
10#include <linux/init.h>
11#include <linux/slab.h>
12#include <linux/io.h>
13#include <linux/edac.h>
14
15#include <asm/octeon/cvmx.h>
16
17#include "edac_core.h"
18#include "edac_module.h"
19#include "octeon_edac-lmc.h"
20
21#define EDAC_MOD_STR "octeon"
22
23static struct mem_ctl_info *mc_cavium;
24static void *lmc_base;
25
26static void co_lmc_poll(struct mem_ctl_info *mci)
27{
28 union lmc_mem_cfg0 cfg0;
29 union lmc_fadr fadr;
30 char msg[64];
31
32 fadr.u64 = readq(lmc_base + LMC_FADR);
33 cfg0.u64 = readq(lmc_base + LMC_MEM_CFG0);
34 snprintf(msg, sizeof(msg), "DIMM %d rank %d bank %d row %d col %d",
35 fadr.fdimm, fadr.fbunk, fadr.fbank, fadr.frow, fadr.fcol);
36
37 if (cfg0.sec_err) {
38 edac_mc_handle_error(HW_EVENT_ERR_CORRECTED, mci, 1, 0, 0, 0, -1, -1, -1,
39 msg, "");
40
41 cfg0.intr_sec_ena = -1; /* Done, re-arm */
42 }
43
44 if (cfg0.ded_err) {
45 edac_mc_handle_error(HW_EVENT_ERR_UNCORRECTED, mci, 1, 0, 0, 0, -1, -1, -1,
46 msg, "");
47 cfg0.intr_ded_ena = -1; /* Done, re-arm */
48 }
49
50 writeq(cfg0.u64, lmc_base + LMC_MEM_CFG0);
51}
52
53static int __devinit co_lmc_probe(struct platform_device *pdev)
54{
55 struct mem_ctl_info *mci;
56 union lmc_mem_cfg0 cfg0;
57 int res = 0;
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
67 mci->mod_name = "octeon-lmc";
68 mci->ctl_name = "co_lmc_err";
69 mci->edac_check = co_lmc_poll;
70
71 if (edac_mc_add_mc(mci) > 0) {
72 pr_err("%s: edac_mc_add_mc() failed\n", __func__);
73 goto err;
74 }
75
76 cfg0.u64 = readq(lmc_base + LMC_MEM_CFG0); /* We poll */
77 cfg0.intr_ded_ena = 0;
78 cfg0.intr_sec_ena = 0;
79 writeq(cfg0.u64, lmc_base + LMC_MEM_CFG0);
80
81 mc_cavium = mci;
82
83 return 0;
84
85err:
86 edac_mc_free(mci);
87
88 return res;
89}
90
91static int co_lmc_remove(struct platform_device *pdev)
92{
93 struct mem_ctl_info *mci = platform_get_drvdata(pdev);
94
95 mc_cavium = NULL;
96 edac_mc_del_mc(&pdev->dev);
97 edac_mc_free(mci);
98
99 return 0;
100}
101
102static struct platform_driver co_lmc_driver = {
103 .probe = co_lmc_probe,
104 .remove = co_lmc_remove,
105 .driver = {
106 .name = "co_lmc_edac",
107 }
108};
109
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
149MODULE_LICENSE("GPL");
150MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
diff --git a/drivers/edac/octeon_edac-lmc.h b/drivers/edac/octeon_edac-lmc.h
new file mode 100644
index 000000000000..246dc525bc10
--- /dev/null
+++ b/drivers/edac/octeon_edac-lmc.h
@@ -0,0 +1,78 @@
1/*
2 * LMC Registers, see chapter 2.5
3 *
4 * These are RSL Type registers and are accessed indirectly across the
5 * I/O bus, so accesses are slowish. Not that it matters. Any size load is
6 * ok but stores must be 64-bit.
7 */
8#define LMC_BASE 0x0001180088000000
9#define LMC_SIZE 0xb8
10
11#define LMC_MEM_CFG0 0x0000000000000000
12#define LMC_MEM_CFG1 0x0000000000000008
13#define LMC_CTL 0x0000000000000010
14#define LMC_DDR2_CTL 0x0000000000000018
15#define LMC_FADR 0x0000000000000020
16#define LMC_FADR_FDIMM
17#define LMC_FADR_FBUNK
18#define LMC_FADR_FBANK
19#define LMC_FADR_FROW
20#define LMC_FADR_FCOL
21#define LMC_COMP_CTL 0x0000000000000028
22#define LMC_WODT_CTL 0x0000000000000030
23#define LMC_ECC_SYND 0x0000000000000038
24#define LMC_IFB_CNT_LO 0x0000000000000048
25#define LMC_IFB_CNT_HI 0x0000000000000050
26#define LMC_OPS_CNT_LO 0x0000000000000058
27#define LMC_OPS_CNT_HI 0x0000000000000060
28#define LMC_DCLK_CNT_LO 0x0000000000000068
29#define LMC_DCLK_CNT_HI 0x0000000000000070
30#define LMC_DELAY_CFG 0x0000000000000088
31#define LMC_CTL1 0x0000000000000090
32#define LMC_DUAL_MEM_CONFIG 0x0000000000000098
33#define LMC_RODT_COMP_CTL 0x00000000000000A0
34#define LMC_PLL_CTL 0x00000000000000A8
35#define LMC_PLL_STATUS 0x00000000000000B0
36
37union lmc_mem_cfg0 {
38 uint64_t u64;
39 struct {
40 uint64_t reserved_32_63:32;
41 uint64_t reset:1;
42 uint64_t silo_qc:1;
43 uint64_t bunk_ena:1;
44 uint64_t ded_err:4;
45 uint64_t sec_err:4;
46 uint64_t intr_ded_ena:1;
47 uint64_t intr_sec_ena:1;
48 uint64_t reserved_15_18:4;
49 uint64_t ref_int:5;
50 uint64_t pbank_lsb:4;
51 uint64_t row_lsb:3;
52 uint64_t ecc_ena:1;
53 uint64_t init_start:1;
54 };
55};
56
57union lmc_fadr {
58 uint64_t u64;
59 struct {
60 uint64_t reserved_32_63:32;
61 uint64_t fdimm:2;
62 uint64_t fbunk:1;
63 uint64_t fbank:3;
64 uint64_t frow:14;
65 uint64_t fcol:12;
66 };
67};
68
69union lmc_ecc_synd {
70 uint64_t u64;
71 struct {
72 uint64_t reserved_32_63:32;
73 uint64_t mrdsyn3:8;
74 uint64_t mrdsyn2:8;
75 uint64_t mrdsyn1:8;
76 uint64_t mrdsyn0:8;
77 };
78};
diff --git a/drivers/edac/octeon_edac-pc.c b/drivers/edac/octeon_edac-pc.c
new file mode 100644
index 000000000000..9d13061744e4
--- /dev/null
+++ b/drivers/edac/octeon_edac-pc.c
@@ -0,0 +1,140 @@
1/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 2009 Wind River Systems,
7 * written by Ralf Baechle <ralf@linux-mips.org>
8 */
9#include <linux/module.h>
10#include <linux/init.h>
11#include <linux/slab.h>
12#include <linux/interrupt.h>
13#include <linux/io.h>
14#include <linux/edac.h>
15
16#include "edac_core.h"
17#include "edac_module.h"
18
19#include <asm/octeon/cvmx.h>
20#include <asm/mipsregs.h>
21
22#define EDAC_MOD_STR "octeon"
23
24extern int register_co_cache_error_notifier(struct notifier_block *nb);
25extern int unregister_co_cache_error_notifier(struct notifier_block *nb);
26
27extern unsigned long long cache_err_dcache[NR_CPUS];
28
29static struct edac_device_ctl_info *ed_cavium;
30
31/*
32 * EDAC CPU cache error callback
33 *
34 */
35
36static int co_cache_error_event(struct notifier_block *this,
37 unsigned long event, void *ptr)
38{
39 unsigned int core = cvmx_get_core_num();
40 unsigned int cpu = smp_processor_id();
41 uint64_t icache_err = read_octeon_c0_icacheerr();
42 struct edac_device_ctl_info *ed = ed_cavium;
43
44 edac_device_printk(ed, KERN_ERR,
45 "Cache error exception on core %d / processor %d:\n",
46 core, cpu);
47 edac_device_printk(ed, KERN_ERR,
48 "cp0_errorepc == %lx\n", read_c0_errorepc());
49 if (icache_err & 1) {
50 edac_device_printk(ed, KERN_ERR, "CacheErr (Icache) == %llx\n",
51 (unsigned long long)icache_err);
52 write_octeon_c0_icacheerr(0);
53 edac_device_handle_ce(ed, 0, 0, ed->ctl_name);
54 }
55 if (cache_err_dcache[core] & 1) {
56 edac_device_printk(ed, KERN_ERR, "CacheErr (Dcache) == %llx\n",
57 (unsigned long long)cache_err_dcache[core]);
58 cache_err_dcache[core] = 0;
59 edac_device_handle_ue(ed, 0, 0, ed->ctl_name);
60 }
61
62 return NOTIFY_DONE;
63}
64
65static struct notifier_block co_cache_error_notifier = {
66 .notifier_call = co_cache_error_event,
67};
68
69static int __devinit co_cache_error_probe(struct platform_device *pdev)
70{
71 struct edac_device_ctl_info *ed;
72 int res = 0;
73
74 ed = edac_device_alloc_ctl_info(0, "cpu", 1, NULL, 0, 0, NULL, 0,
75 edac_device_alloc_index());
76
77 ed->dev = &pdev->dev;
78 platform_set_drvdata(pdev, ed);
79 ed->dev_name = dev_name(&pdev->dev);
80
81 ed->mod_name = "octeon-cpu";
82 ed->ctl_name = "co_cpu_err";
83
84 if (edac_device_add_device(ed) > 0) {
85 pr_err("%s: edac_device_add_device() failed\n", __func__);
86 goto err;
87 }
88
89 register_co_cache_error_notifier(&co_cache_error_notifier);
90 ed_cavium = ed;
91
92 return 0;
93
94err:
95 edac_device_free_ctl_info(ed);
96
97 return res;
98}
99
100static int co_cache_error_remove(struct platform_device *pdev)
101{
102 struct edac_device_ctl_info *ed = platform_get_drvdata(pdev);
103
104 unregister_co_cache_error_notifier(&co_cache_error_notifier);
105 ed_cavium = NULL;
106 edac_device_del_device(&pdev->dev);
107 edac_device_free_ctl_info(ed);
108
109 return 0;
110}
111
112static struct platform_driver co_cache_error_driver = {
113 .probe = co_cache_error_probe,
114 .remove = co_cache_error_remove,
115 .driver = {
116 .name = "co_pc_edac",
117 }
118};
119
120static int __init co_edac_init(void)
121{
122 int ret;
123
124 ret = platform_driver_register(&co_cache_error_driver);
125 if (ret)
126 pr_warning(EDAC_MOD_STR "CPU err failed to register\n");
127
128 return ret;
129}
130
131static void __exit co_edac_exit(void)
132{
133 platform_driver_unregister(&co_cache_error_driver);
134}
135
136module_init(co_edac_init);
137module_exit(co_edac_exit);
138
139MODULE_LICENSE("GPL");
140MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
diff --git a/drivers/edac/octeon_edac-pci.c b/drivers/edac/octeon_edac-pci.c
new file mode 100644
index 000000000000..e72b96e3e4e0
--- /dev/null
+++ b/drivers/edac/octeon_edac-pci.c
@@ -0,0 +1,135 @@
1/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 2009 Wind River Systems,
7 * written by Ralf Baechle <ralf@linux-mips.org>
8 */
9#include <linux/module.h>
10#include <linux/init.h>
11#include <linux/slab.h>
12#include <linux/io.h>
13#include <linux/edac.h>
14
15#include <asm/octeon/cvmx.h>
16#include <asm/octeon/cvmx-npi-defs.h>
17#include <asm/octeon/cvmx-pci-defs.h>
18#include <asm/octeon/octeon.h>
19
20#include "edac_core.h"
21#include "edac_module.h"
22
23#define EDAC_MOD_STR "octeon"
24
25static void co_pci_poll(struct edac_pci_ctl_info *pci)
26{
27 union cvmx_pci_cfg01 cfg01;
28
29 cfg01.u32 = octeon_npi_read32(CVMX_NPI_PCI_CFG01);
30 if (cfg01.s.dpe) { /* Detected parity error */
31 edac_pci_handle_pe(pci, pci->ctl_name);
32 cfg01.s.dpe = 1; /* Reset */
33 octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32);
34 }
35 if (cfg01.s.sse) {
36 edac_pci_handle_npe(pci, "Signaled System Error");
37 cfg01.s.sse = 1; /* Reset */
38 octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32);
39 }
40 if (cfg01.s.rma) {
41 edac_pci_handle_npe(pci, "Received Master Abort");
42 cfg01.s.rma = 1; /* Reset */
43 octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32);
44 }
45 if (cfg01.s.rta) {
46 edac_pci_handle_npe(pci, "Received Target Abort");
47 cfg01.s.rta = 1; /* Reset */
48 octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32);
49 }
50 if (cfg01.s.sta) {
51 edac_pci_handle_npe(pci, "Signaled Target Abort");
52 cfg01.s.sta = 1; /* Reset */
53 octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32);
54 }
55 if (cfg01.s.mdpe) {
56 edac_pci_handle_npe(pci, "Master Data Parity Error");
57 cfg01.s.mdpe = 1; /* Reset */
58 octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32);
59 }
60 if (cfg01.s.mdpe) {
61 edac_pci_handle_npe(pci, "Master Data Parity Error");
62 cfg01.s.mdpe = 1; /* Reset */
63 octeon_npi_write32(CVMX_NPI_PCI_CFG01, cfg01.u32);
64 }
65}
66
67static int __devinit co_pci_probe(struct platform_device *pdev)
68{
69 struct edac_pci_ctl_info *pci;
70 int res = 0;
71
72 pci = edac_pci_alloc_ctl_info(0, "octeon_pci_err");
73 if (!pci)
74 return -ENOMEM;
75
76 pci->dev = &pdev->dev;
77 platform_set_drvdata(pdev, pci);
78 pci->dev_name = dev_name(&pdev->dev);
79
80 pci->mod_name = "octeon-pci";
81 pci->ctl_name = "octeon_pci_err";
82 pci->edac_check = co_pci_poll;
83
84 if (edac_pci_add_device(pci, 0) > 0) {
85 pr_err("%s: edac_pci_add_device() failed\n", __func__);
86 goto err;
87 }
88
89 return 0;
90
91err:
92 edac_pci_free_ctl_info(pci);
93
94 return res;
95}
96
97static int co_pci_remove(struct platform_device *pdev)
98{
99 struct edac_pci_ctl_info *pci = platform_get_drvdata(pdev);
100
101 edac_pci_del_device(&pdev->dev);
102 edac_pci_free_ctl_info(pci);
103
104 return 0;
105}
106
107static struct platform_driver co_pci_driver = {
108 .probe = co_pci_probe,
109 .remove = co_pci_remove,
110 .driver = {
111 .name = "co_pci_edac",
112 }
113};
114
115static int __init co_edac_init(void)
116{
117 int ret;
118
119 ret = platform_driver_register(&co_pci_driver);
120 if (ret)
121 pr_warning(EDAC_MOD_STR " PCI EDAC failed to register\n");
122
123 return ret;
124}
125
126static void __exit co_edac_exit(void)
127{
128 platform_driver_unregister(&co_pci_driver);
129}
130
131module_init(co_edac_init);
132module_exit(co_edac_exit);
133
134MODULE_LICENSE("GPL");
135MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");