diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2012-10-16 18:39:09 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2012-12-12 10:48:49 -0500 |
commit | f65aad41772f6a0022e9763fe06f47604449964c (patch) | |
tree | 701d6ea74ac0c41f008c5554d876945fe4caf512 /drivers/edac/octeon_edac-l2c.c | |
parent | aa1762f49c81a14d0453e4f67f922e4f155510a3 (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/edac/octeon_edac-l2c.c')
-rw-r--r-- | drivers/edac/octeon_edac-l2c.c | 118 |
1 files changed, 118 insertions, 0 deletions
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 | |||
22 | static 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 | |||
41 | static 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 | |||
74 | err: | ||
75 | edac_device_free_ctl_info(l2c); | ||
76 | |||
77 | return res; | ||
78 | } | ||
79 | |||
80 | static 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 | |||
90 | static 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 | |||
98 | static 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 | |||
109 | static void __exit co_edac_exit(void) | ||
110 | { | ||
111 | platform_driver_unregister(&co_l2c_driver); | ||
112 | } | ||
113 | |||
114 | module_init(co_edac_init); | ||
115 | module_exit(co_edac_exit); | ||
116 | |||
117 | MODULE_LICENSE("GPL"); | ||
118 | MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>"); | ||