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) 2012 Cavium, Inc. |
7 | * |
8 | * Copyright (C) 2009 Wind River Systems, |
9 | * written by Ralf Baechle <ralf@linux-mips.org> |
10 | */ |
11 | #include <linux/module.h> |
12 | #include <linux/init.h> |
13 | #include <linux/slab.h> |
14 | #include <linux/io.h> |
15 | #include <linux/edac.h> |
16 | |
17 | #include <asm/octeon/cvmx.h> |
18 | |
19 | #include "edac_module.h" |
20 | |
21 | #define EDAC_MOD_STR "octeon-l2c" |
22 | |
23 | static void octeon_l2c_poll_oct1(struct edac_device_ctl_info *l2c) |
24 | { |
25 | union cvmx_l2t_err l2t_err, l2t_err_reset; |
26 | union cvmx_l2d_err l2d_err, l2d_err_reset; |
27 | |
28 | l2t_err_reset.u64 = 0; |
29 | l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR); |
30 | if (l2t_err.s.sec_err) { |
31 | edac_device_handle_ce(edac_dev: l2c, inst_nr: 0, block_nr: 0, |
32 | msg: "Tag Single bit error (corrected)" ); |
33 | l2t_err_reset.s.sec_err = 1; |
34 | } |
35 | if (l2t_err.s.ded_err) { |
36 | edac_device_handle_ue(edac_dev: l2c, inst_nr: 0, block_nr: 0, |
37 | msg: "Tag Double bit error (detected)" ); |
38 | l2t_err_reset.s.ded_err = 1; |
39 | } |
40 | if (l2t_err_reset.u64) |
41 | cvmx_write_csr(CVMX_L2T_ERR, l2t_err_reset.u64); |
42 | |
43 | l2d_err_reset.u64 = 0; |
44 | l2d_err.u64 = cvmx_read_csr(CVMX_L2D_ERR); |
45 | if (l2d_err.s.sec_err) { |
46 | edac_device_handle_ce(edac_dev: l2c, inst_nr: 0, block_nr: 1, |
47 | msg: "Data Single bit error (corrected)" ); |
48 | l2d_err_reset.s.sec_err = 1; |
49 | } |
50 | if (l2d_err.s.ded_err) { |
51 | edac_device_handle_ue(edac_dev: l2c, inst_nr: 0, block_nr: 1, |
52 | msg: "Data Double bit error (detected)" ); |
53 | l2d_err_reset.s.ded_err = 1; |
54 | } |
55 | if (l2d_err_reset.u64) |
56 | cvmx_write_csr(CVMX_L2D_ERR, l2d_err_reset.u64); |
57 | |
58 | } |
59 | |
60 | static void _octeon_l2c_poll_oct2(struct edac_device_ctl_info *l2c, int tad) |
61 | { |
62 | union cvmx_l2c_err_tdtx err_tdtx, err_tdtx_reset; |
63 | union cvmx_l2c_err_ttgx err_ttgx, err_ttgx_reset; |
64 | char buf1[64]; |
65 | char buf2[80]; |
66 | |
67 | err_tdtx_reset.u64 = 0; |
68 | err_tdtx.u64 = cvmx_read_csr(CVMX_L2C_ERR_TDTX(tad)); |
69 | if (err_tdtx.s.dbe || err_tdtx.s.sbe || |
70 | err_tdtx.s.vdbe || err_tdtx.s.vsbe) |
71 | snprintf(buf: buf1, size: sizeof(buf1), |
72 | fmt: "type:%d, syn:0x%x, way:%d" , |
73 | err_tdtx.s.type, err_tdtx.s.syn, err_tdtx.s.wayidx); |
74 | |
75 | if (err_tdtx.s.dbe) { |
76 | snprintf(buf: buf2, size: sizeof(buf2), |
77 | fmt: "L2D Double bit error (detected):%s" , buf1); |
78 | err_tdtx_reset.s.dbe = 1; |
79 | edac_device_handle_ue(edac_dev: l2c, inst_nr: tad, block_nr: 1, msg: buf2); |
80 | } |
81 | if (err_tdtx.s.sbe) { |
82 | snprintf(buf: buf2, size: sizeof(buf2), |
83 | fmt: "L2D Single bit error (corrected):%s" , buf1); |
84 | err_tdtx_reset.s.sbe = 1; |
85 | edac_device_handle_ce(edac_dev: l2c, inst_nr: tad, block_nr: 1, msg: buf2); |
86 | } |
87 | if (err_tdtx.s.vdbe) { |
88 | snprintf(buf: buf2, size: sizeof(buf2), |
89 | fmt: "VBF Double bit error (detected):%s" , buf1); |
90 | err_tdtx_reset.s.vdbe = 1; |
91 | edac_device_handle_ue(edac_dev: l2c, inst_nr: tad, block_nr: 1, msg: buf2); |
92 | } |
93 | if (err_tdtx.s.vsbe) { |
94 | snprintf(buf: buf2, size: sizeof(buf2), |
95 | fmt: "VBF Single bit error (corrected):%s" , buf1); |
96 | err_tdtx_reset.s.vsbe = 1; |
97 | edac_device_handle_ce(edac_dev: l2c, inst_nr: tad, block_nr: 1, msg: buf2); |
98 | } |
99 | if (err_tdtx_reset.u64) |
100 | cvmx_write_csr(CVMX_L2C_ERR_TDTX(tad), err_tdtx_reset.u64); |
101 | |
102 | err_ttgx_reset.u64 = 0; |
103 | err_ttgx.u64 = cvmx_read_csr(CVMX_L2C_ERR_TTGX(tad)); |
104 | |
105 | if (err_ttgx.s.dbe || err_ttgx.s.sbe) |
106 | snprintf(buf: buf1, size: sizeof(buf1), |
107 | fmt: "type:%d, syn:0x%x, way:%d" , |
108 | err_ttgx.s.type, err_ttgx.s.syn, err_ttgx.s.wayidx); |
109 | |
110 | if (err_ttgx.s.dbe) { |
111 | snprintf(buf: buf2, size: sizeof(buf2), |
112 | fmt: "Tag Double bit error (detected):%s" , buf1); |
113 | err_ttgx_reset.s.dbe = 1; |
114 | edac_device_handle_ue(edac_dev: l2c, inst_nr: tad, block_nr: 0, msg: buf2); |
115 | } |
116 | if (err_ttgx.s.sbe) { |
117 | snprintf(buf: buf2, size: sizeof(buf2), |
118 | fmt: "Tag Single bit error (corrected):%s" , buf1); |
119 | err_ttgx_reset.s.sbe = 1; |
120 | edac_device_handle_ce(edac_dev: l2c, inst_nr: tad, block_nr: 0, msg: buf2); |
121 | } |
122 | if (err_ttgx_reset.u64) |
123 | cvmx_write_csr(CVMX_L2C_ERR_TTGX(tad), err_ttgx_reset.u64); |
124 | } |
125 | |
126 | static void octeon_l2c_poll_oct2(struct edac_device_ctl_info *l2c) |
127 | { |
128 | int i; |
129 | for (i = 0; i < l2c->nr_instances; i++) |
130 | _octeon_l2c_poll_oct2(l2c, tad: i); |
131 | } |
132 | |
133 | static int octeon_l2c_probe(struct platform_device *pdev) |
134 | { |
135 | struct edac_device_ctl_info *l2c; |
136 | |
137 | int num_tads = OCTEON_IS_MODEL(OCTEON_CN68XX) ? 4 : 1; |
138 | |
139 | /* 'Tags' are block 0, 'Data' is block 1*/ |
140 | l2c = edac_device_alloc_ctl_info(sizeof_private: 0, edac_device_name: "l2c" , nr_instances: num_tads, edac_block_name: "l2c" , nr_blocks: 2, offset_value: 0, |
141 | NULL, nr_attribs: 0, device_index: edac_device_alloc_index()); |
142 | if (!l2c) |
143 | return -ENOMEM; |
144 | |
145 | l2c->dev = &pdev->dev; |
146 | platform_set_drvdata(pdev, data: l2c); |
147 | l2c->dev_name = dev_name(dev: &pdev->dev); |
148 | |
149 | l2c->mod_name = "octeon-l2c" ; |
150 | l2c->ctl_name = "octeon_l2c_err" ; |
151 | |
152 | |
153 | if (OCTEON_IS_OCTEON1PLUS()) { |
154 | union cvmx_l2t_err l2t_err; |
155 | union cvmx_l2d_err l2d_err; |
156 | |
157 | l2t_err.u64 = cvmx_read_csr(CVMX_L2T_ERR); |
158 | l2t_err.s.sec_intena = 0; /* We poll */ |
159 | l2t_err.s.ded_intena = 0; |
160 | cvmx_write_csr(CVMX_L2T_ERR, l2t_err.u64); |
161 | |
162 | l2d_err.u64 = cvmx_read_csr(CVMX_L2D_ERR); |
163 | l2d_err.s.sec_intena = 0; /* We poll */ |
164 | l2d_err.s.ded_intena = 0; |
165 | cvmx_write_csr(CVMX_L2T_ERR, l2d_err.u64); |
166 | |
167 | l2c->edac_check = octeon_l2c_poll_oct1; |
168 | } else { |
169 | /* OCTEON II */ |
170 | l2c->edac_check = octeon_l2c_poll_oct2; |
171 | } |
172 | |
173 | if (edac_device_add_device(edac_dev: l2c) > 0) { |
174 | pr_err("%s: edac_device_add_device() failed\n" , __func__); |
175 | goto err; |
176 | } |
177 | |
178 | |
179 | return 0; |
180 | |
181 | err: |
182 | edac_device_free_ctl_info(ctl_info: l2c); |
183 | |
184 | return -ENXIO; |
185 | } |
186 | |
187 | static void octeon_l2c_remove(struct platform_device *pdev) |
188 | { |
189 | struct edac_device_ctl_info *l2c = platform_get_drvdata(pdev); |
190 | |
191 | edac_device_del_device(dev: &pdev->dev); |
192 | edac_device_free_ctl_info(ctl_info: l2c); |
193 | } |
194 | |
195 | static struct platform_driver octeon_l2c_driver = { |
196 | .probe = octeon_l2c_probe, |
197 | .remove_new = octeon_l2c_remove, |
198 | .driver = { |
199 | .name = "octeon_l2c_edac" , |
200 | } |
201 | }; |
202 | module_platform_driver(octeon_l2c_driver); |
203 | |
204 | MODULE_LICENSE("GPL" ); |
205 | MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>" ); |
206 | |