blob: 71f74ad2be071d496485506b0d06ee19b50e2208 [file] [log] [blame]
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -07001/* Copyright (c) 2016, The Linux Foundation. All rights reserved.
2 *
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License version 2 and
5 * only version 2 as published by the Free Software Foundation.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 */
12
13#include <linux/kernel.h>
14#include <linux/edac.h>
15#include <linux/of_device.h>
16#include <linux/platform_device.h>
17#include <linux/smp.h>
18#include <linux/spinlock.h>
19#include <linux/mfd/syscon.h>
20#include <linux/regmap.h>
21#include "edac_core.h"
22
23#ifdef CONFIG_EDAC_QCOM_LLCC_PANIC_ON_CE
24#define LLCC_ERP_PANIC_ON_CE 1
25#else
26#define LLCC_ERP_PANIC_ON_CE 0
27#endif
28
29#ifdef CONFIG_EDAC_QCOM_LLCC_PANIC_ON_UE
30#define LLCC_ERP_PANIC_ON_UE 1
31#else
32#define LLCC_ERP_PANIC_ON_UE 0
33#endif
34
35#define EDAC_LLCC "qcom_llcc"
36
37#define TRP_SYN_REG_CNT 6
38
39#define DRP_SYN_REG_CNT 8
40
41/* single & Double Bit syndrome register offsets */
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -070042#define TRP_ECC_SB_ERR_SYN0 0x0002304C
43#define TRP_ECC_DB_ERR_SYN0 0x00020370
44#define DRP_ECC_SB_ERR_SYN0 0x0004204C
45#define DRP_ECC_DB_ERR_SYN0 0x00042070
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070046
47/* Error register offsets */
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -070048#define TRP_ECC_ERROR_STATUS1 0x00020348
49#define TRP_ECC_ERROR_STATUS0 0x00020344
50#define DRP_ECC_ERROR_STATUS1 0x00042048
51#define DRP_ECC_ERROR_STATUS0 0x00042044
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070052
53/* TRP, DRP interrupt register offsets */
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -070054#define DRP_INTERRUPT_STATUS 0x00041000
55#define TRP_INTERRUPT_0_STATUS 0x00020480
56#define DRP_INTERRUPT_CLEAR 0x00041008
57#define DRP_ECC_ERROR_CNTR_CLEAR 0x00040004
58#define TRP_INTERRUPT_0_CLEAR 0x00020484
59#define TRP_ECC_ERROR_CNTR_CLEAR 0x00020440
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070060
61/* Mask and shift macros */
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -070062#define ECC_DB_ERR_COUNT_MASK 0x0000001f
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070063#define ECC_DB_ERR_WAYS_MASK 0xffff0000
64#define ECC_DB_ERR_WAYS_SHIFT 16
65
66#define ECC_SB_ERR_COUNT_MASK 0x00ff0000
67#define ECC_SB_ERR_COUNT_SHIFT 16
68#define ECC_SB_ERR_WAYS_MASK 0x0000ffff
69
70#define SB_ECC_ERROR 0x1
71#define DB_ECC_ERROR 0x2
72
73#define DRP_TRP_INT_CLEAR 0x3
74#define DRP_TRP_CNT_CLEAR 0x3
75
76static int poll_msec = 5000;
77module_param(poll_msec, int, 0444);
78
79enum {
80 LLCC_DRAM_CE = 0,
81 LLCC_DRAM_UE,
82 LLCC_TRAM_CE,
83 LLCC_TRAM_UE,
84};
85
86struct errors_edac {
87 const char *msg;
88 void (*func)(struct edac_device_ctl_info *edev_ctl,
89 int inst_nr, int block_nr, const char *msg);
90};
91
92struct erp_drvdata {
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070093 struct regmap *llcc_map;
94};
95
96static const struct errors_edac errors[] = {
97 {"LLCC Data RAM correctable Error", edac_device_handle_ce},
98 {"LLCC Data RAM uncorrectable Error", edac_device_handle_ue},
99 {"LLCC Tag RAM correctable Error", edac_device_handle_ce},
100 {"LLCC Tag RAM uncorrectable Error", edac_device_handle_ue},
101};
102
103/* Clear the error interrupt and counter registers */
104static void qcom_llcc_clear_errors(int err_type, struct regmap *llcc_map)
105{
106 switch (err_type) {
107 case LLCC_DRAM_CE:
108 case LLCC_DRAM_UE:
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700109 /* Clear the interrupt */
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700110 regmap_write(llcc_map, DRP_INTERRUPT_CLEAR, DRP_TRP_INT_CLEAR);
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700111 /* Clear the counters */
112 regmap_write(llcc_map, DRP_ECC_ERROR_CNTR_CLEAR,
113 DRP_TRP_CNT_CLEAR);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700114 break;
115 case LLCC_TRAM_CE:
116 case LLCC_TRAM_UE:
117 regmap_write(llcc_map, TRP_INTERRUPT_0_CLEAR,
118 DRP_TRP_INT_CLEAR);
119 regmap_write(llcc_map, TRP_ECC_ERROR_CNTR_CLEAR,
120 DRP_TRP_CNT_CLEAR);
121 break;
122 }
123}
124
125/* Dump syndrome registers for tag Ram Double bit errors */
126static void dump_trp_db_syn_reg(struct regmap *llcc_map)
127{
128 int i;
129 int db_err_cnt;
130 int db_err_ways;
131 u32 synd_reg;
132 u32 synd_val;
133
134 for (i = 0; i < TRP_SYN_REG_CNT; i++) {
135 synd_reg = TRP_ECC_DB_ERR_SYN0 + (i * 4);
136 regmap_read(llcc_map, synd_reg, &synd_val);
137 edac_printk(KERN_CRIT, EDAC_LLCC, "TRP_ECC_SYN%d: 0x%8x\n",
138 i, synd_val);
139 }
140
141 regmap_read(llcc_map, TRP_ECC_ERROR_STATUS1, &db_err_cnt);
142 db_err_cnt = (db_err_cnt & ECC_DB_ERR_COUNT_MASK);
143 edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error count: 0x%4x\n",
144 db_err_cnt);
145
146 regmap_read(llcc_map, TRP_ECC_ERROR_STATUS0, &db_err_ways);
147 db_err_ways = (db_err_ways & ECC_DB_ERR_WAYS_MASK);
148 db_err_ways >>= ECC_DB_ERR_WAYS_SHIFT;
149
150 edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error ways: 0x%4x\n",
151 db_err_ways);
152}
153
154/* Dump syndrome register for tag Ram Single Bit Errors */
155static void dump_trp_sb_syn_reg(struct regmap *llcc_map)
156{
157 int i;
158 int sb_err_cnt;
159 int sb_err_ways;
160 u32 synd_reg;
161 u32 synd_val;
162
163 for (i = 0; i < TRP_SYN_REG_CNT; i++) {
164 synd_reg = TRP_ECC_SB_ERR_SYN0 + (i * 4);
165 regmap_read(llcc_map, synd_reg, &synd_val);
166 edac_printk(KERN_CRIT, EDAC_LLCC, "TRP_ECC_SYN%d: 0x%8x\n",
167 i, synd_val);
168 }
169
170 regmap_read(llcc_map, TRP_ECC_ERROR_STATUS1, &sb_err_cnt);
171 sb_err_cnt = (sb_err_cnt & ECC_SB_ERR_COUNT_MASK);
172 sb_err_cnt >>= ECC_SB_ERR_COUNT_SHIFT;
173 edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error count: 0x%4x\n",
174 sb_err_cnt);
175
176 regmap_read(llcc_map, TRP_ECC_ERROR_STATUS0, &sb_err_ways);
177 sb_err_ways = sb_err_ways & ECC_SB_ERR_WAYS_MASK;
178
179 edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error ways: 0x%4x\n",
180 sb_err_ways);
181}
182
183/* Dump syndrome registers for Data Ram Double bit errors */
184static void dump_drp_db_syn_reg(struct regmap *llcc_map)
185{
186 int i;
187 int db_err_cnt;
188 int db_err_ways;
189 u32 synd_reg;
190 u32 synd_val;
191
192 for (i = 0; i < DRP_SYN_REG_CNT; i++) {
193 synd_reg = DRP_ECC_DB_ERR_SYN0 + (i * 4);
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700194 regmap_read(llcc_map, synd_reg, &synd_val);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700195 edac_printk(KERN_CRIT, EDAC_LLCC, "DRP_ECC_SYN%d: 0x%8x\n",
196 i, synd_val);
197 }
198
199 regmap_read(llcc_map, DRP_ECC_ERROR_STATUS1, &db_err_cnt);
200 db_err_cnt = (db_err_cnt & ECC_DB_ERR_COUNT_MASK);
201 edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error count: 0x%4x\n",
202 db_err_cnt);
203
204 regmap_read(llcc_map, DRP_ECC_ERROR_STATUS0, &db_err_ways);
205 db_err_ways &= ECC_DB_ERR_WAYS_MASK;
206 db_err_ways >>= ECC_DB_ERR_WAYS_SHIFT;
207 edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error ways: 0x%4x\n",
208 db_err_ways);
209}
210
211/* Dump Syndrome registers for Data Ram Single bit errors*/
212static void dump_drp_sb_syn_reg(struct regmap *llcc_map)
213{
214 int i;
215 int sb_err_cnt;
216 int sb_err_ways;
217 u32 synd_reg;
218 u32 synd_val;
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700219 u32 synd_reg_offset;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700220
221 for (i = 0; i < DRP_SYN_REG_CNT; i++) {
222 synd_reg_offset = DRP_ECC_SB_ERR_SYN0 + (i * 4);
223 regmap_read(llcc_map, synd_reg, &synd_val);
224 edac_printk(KERN_CRIT, EDAC_LLCC, "DRP_ECC_SYN%d: 0x%8x\n",
225 i, synd_val);
226 }
227
228 regmap_read(llcc_map, DRP_ECC_ERROR_STATUS1, &sb_err_cnt);
229 sb_err_cnt &= ECC_SB_ERR_COUNT_MASK;
230 sb_err_cnt >>= ECC_SB_ERR_COUNT_SHIFT;
231 edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error count: 0x%4x\n",
232 sb_err_cnt);
233
234 regmap_read(llcc_map, DRP_ECC_ERROR_STATUS0, &sb_err_ways);
235 sb_err_ways = sb_err_ways & ECC_SB_ERR_WAYS_MASK;
236
237 edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error ways: 0x%4x\n",
238 sb_err_ways);
239}
240
241
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700242static void dump_syn_reg(struct edac_device_ctl_info *edev_ctl,
243 int err_type, struct regmap *llcc_map)
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700244{
245 switch (err_type) {
246 case LLCC_DRAM_CE:
247 dump_drp_sb_syn_reg(llcc_map);
248 break;
249 case LLCC_DRAM_UE:
250 dump_drp_db_syn_reg(llcc_map);
251 break;
252 case LLCC_TRAM_CE:
253 dump_trp_sb_syn_reg(llcc_map);
254 break;
255 case LLCC_TRAM_UE:
256 dump_trp_db_syn_reg(llcc_map);
257 break;
258 }
259
260 qcom_llcc_clear_errors(err_type, llcc_map);
261
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700262 errors[err_type].func(edev_ctl, 0, 0, errors[err_type].msg);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700263}
264
265static void qcom_llcc_poll_cache_errors
266 (struct edac_device_ctl_info *edev_ctl)
267{
268 u32 drp_error;
269 u32 trp_error;
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700270 struct erp_drvdata *drv = edev_ctl->pvt_info;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700271
272 /* Look for Data RAM errors */
273 regmap_read(drv->llcc_map, DRP_INTERRUPT_STATUS, &drp_error);
274
275 if (drp_error & SB_ECC_ERROR) {
276 edac_printk(KERN_CRIT, EDAC_LLCC,
277 "Single Bit Error detected in Data Ram\n");
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700278 dump_syn_reg(edev_ctl, LLCC_DRAM_CE, drv->llcc_map);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700279 } else if (drp_error & DB_ECC_ERROR) {
280 edac_printk(KERN_CRIT, EDAC_LLCC,
281 "Double Bit Error detected in Data Ram\n");
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700282 dump_syn_reg(edev_ctl, LLCC_DRAM_UE, drv->llcc_map);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700283 }
284
285 /* Look for Tag RAM errors */
286 regmap_read(drv->llcc_map, TRP_INTERRUPT_0_STATUS, &trp_error);
287 if (trp_error & SB_ECC_ERROR) {
288 edac_printk(KERN_CRIT, EDAC_LLCC,
289 "Single Bit Error detected in Tag Ram\n");
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700290 dump_syn_reg(edev_ctl, LLCC_TRAM_CE, drv->llcc_map);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700291 } else if (trp_error & DB_ECC_ERROR) {
292 edac_printk(KERN_CRIT, EDAC_LLCC,
293 "Double Bit Error detected in Tag Ram\n");
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700294 dump_syn_reg(edev_ctl, LLCC_TRAM_UE, drv->llcc_map);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700295 }
296}
297
298static int qcom_llcc_erp_probe(struct platform_device *pdev)
299{
300 int rc = 0;
301 struct erp_drvdata *drv;
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700302 struct edac_device_ctl_info *edev_ctl;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700303 struct device *dev = &pdev->dev;
304
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700305 /* Allocate edac control info */
306 edev_ctl = edac_device_alloc_ctl_info(sizeof(*drv), "qcom-llcc", 1,
307 NULL, 1, 1, NULL, 0, edac_device_alloc_index());
308
309 edev_ctl->dev = dev;
310 edev_ctl->mod_name = dev_name(dev);
311 edev_ctl->dev_name = dev_name(dev);
312 edev_ctl->ctl_name = "llcc";
313 edev_ctl->poll_msec = poll_msec;
314 edev_ctl->edac_check = qcom_llcc_poll_cache_errors;
315 edev_ctl->defer_work = 1;
316 edev_ctl->panic_on_ce = LLCC_ERP_PANIC_ON_CE;
317 edev_ctl->panic_on_ue = LLCC_ERP_PANIC_ON_UE;
318
319 drv = edev_ctl->pvt_info;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700320
321 drv->llcc_map = syscon_node_to_regmap(dev->parent->of_node);
322 if (IS_ERR(drv->llcc_map)) {
323 dev_err(dev, "no regmap for syscon llcc parent\n");
324 return -ENOMEM;
325 }
326
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700327 platform_set_drvdata(pdev, edev_ctl);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700328
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700329 rc = edac_device_add_device(edev_ctl);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700330 if (rc)
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700331 edac_device_free_ctl_info(edev_ctl);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700332
333 return rc;
334}
335
336static int qcom_llcc_erp_remove(struct platform_device *pdev)
337{
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700338 struct edac_device_ctl_info *edev_ctl = dev_get_drvdata(&pdev->dev);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700339
340 edac_device_del_device(edev_ctl->dev);
341 edac_device_free_ctl_info(edev_ctl);
342
343 return 0;
344}
345
346static const struct of_device_id qcom_llcc_erp_match_table[] = {
347 { .compatible = "qcom,llcc-erp" },
348 { },
349};
350
351static struct platform_driver qcom_llcc_erp_driver = {
352 .probe = qcom_llcc_erp_probe,
353 .remove = qcom_llcc_erp_remove,
354 .driver = {
355 .name = "qcom_llcc_erp",
356 .owner = THIS_MODULE,
357 .of_match_table = qcom_llcc_erp_match_table,
358 },
359};
360
361static int __init qcom_llcc_erp_init(void)
362{
363 return platform_driver_register(&qcom_llcc_erp_driver);
364}
365module_init(qcom_llcc_erp_init);
366
367static void __exit qcom_llcc_erp_exit(void)
368{
369 platform_driver_unregister(&qcom_llcc_erp_driver);
370}
371module_exit(qcom_llcc_erp_exit);
372
373MODULE_DESCRIPTION("QCOM LLCC Error Reporting");
374MODULE_LICENSE("GPL v2");