blob: 18b2da7cad6465517702a7dc3e6dc422b5d95f59 [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>
Channagoud Kadabic26a8912016-11-21 13:57:20 -080021#include <linux/interrupt.h>
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070022#include "edac_core.h"
23
24#ifdef CONFIG_EDAC_QCOM_LLCC_PANIC_ON_CE
25#define LLCC_ERP_PANIC_ON_CE 1
26#else
27#define LLCC_ERP_PANIC_ON_CE 0
28#endif
29
30#ifdef CONFIG_EDAC_QCOM_LLCC_PANIC_ON_UE
31#define LLCC_ERP_PANIC_ON_UE 1
32#else
33#define LLCC_ERP_PANIC_ON_UE 0
34#endif
35
36#define EDAC_LLCC "qcom_llcc"
37
38#define TRP_SYN_REG_CNT 6
39
40#define DRP_SYN_REG_CNT 8
41
42/* single & Double Bit syndrome register offsets */
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -070043#define TRP_ECC_SB_ERR_SYN0 0x0002304C
44#define TRP_ECC_DB_ERR_SYN0 0x00020370
45#define DRP_ECC_SB_ERR_SYN0 0x0004204C
46#define DRP_ECC_DB_ERR_SYN0 0x00042070
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070047
48/* Error register offsets */
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -070049#define TRP_ECC_ERROR_STATUS1 0x00020348
50#define TRP_ECC_ERROR_STATUS0 0x00020344
51#define DRP_ECC_ERROR_STATUS1 0x00042048
52#define DRP_ECC_ERROR_STATUS0 0x00042044
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070053
54/* TRP, DRP interrupt register offsets */
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -070055#define DRP_INTERRUPT_STATUS 0x00041000
56#define TRP_INTERRUPT_0_STATUS 0x00020480
57#define DRP_INTERRUPT_CLEAR 0x00041008
58#define DRP_ECC_ERROR_CNTR_CLEAR 0x00040004
59#define TRP_INTERRUPT_0_CLEAR 0x00020484
60#define TRP_ECC_ERROR_CNTR_CLEAR 0x00020440
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070061
62/* Mask and shift macros */
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -070063#define ECC_DB_ERR_COUNT_MASK 0x0000001f
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070064#define ECC_DB_ERR_WAYS_MASK 0xffff0000
65#define ECC_DB_ERR_WAYS_SHIFT 16
66
67#define ECC_SB_ERR_COUNT_MASK 0x00ff0000
68#define ECC_SB_ERR_COUNT_SHIFT 16
69#define ECC_SB_ERR_WAYS_MASK 0x0000ffff
70
71#define SB_ECC_ERROR 0x1
72#define DB_ECC_ERROR 0x2
73
74#define DRP_TRP_INT_CLEAR 0x3
75#define DRP_TRP_CNT_CLEAR 0x3
76
77static int poll_msec = 5000;
78module_param(poll_msec, int, 0444);
79
Channagoud Kadabic26a8912016-11-21 13:57:20 -080080static int interrupt_mode;
81module_param(interrupt_mode, int, 0444);
82MODULE_PARM_DESC(interrupt_mode,
83 "Controls whether to use interrupt or poll mode");
84
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070085enum {
86 LLCC_DRAM_CE = 0,
87 LLCC_DRAM_UE,
88 LLCC_TRAM_CE,
89 LLCC_TRAM_UE,
90};
91
92struct errors_edac {
93 const char *msg;
94 void (*func)(struct edac_device_ctl_info *edev_ctl,
95 int inst_nr, int block_nr, const char *msg);
96};
97
98struct erp_drvdata {
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -070099 struct regmap *llcc_map;
Channagoud Kadabic26a8912016-11-21 13:57:20 -0800100 u32 ecc_irq;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700101};
102
103static const struct errors_edac errors[] = {
104 {"LLCC Data RAM correctable Error", edac_device_handle_ce},
105 {"LLCC Data RAM uncorrectable Error", edac_device_handle_ue},
106 {"LLCC Tag RAM correctable Error", edac_device_handle_ce},
107 {"LLCC Tag RAM uncorrectable Error", edac_device_handle_ue},
108};
109
110/* Clear the error interrupt and counter registers */
111static void qcom_llcc_clear_errors(int err_type, struct regmap *llcc_map)
112{
113 switch (err_type) {
114 case LLCC_DRAM_CE:
115 case LLCC_DRAM_UE:
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700116 /* Clear the interrupt */
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700117 regmap_write(llcc_map, DRP_INTERRUPT_CLEAR, DRP_TRP_INT_CLEAR);
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700118 /* Clear the counters */
119 regmap_write(llcc_map, DRP_ECC_ERROR_CNTR_CLEAR,
120 DRP_TRP_CNT_CLEAR);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700121 break;
122 case LLCC_TRAM_CE:
123 case LLCC_TRAM_UE:
124 regmap_write(llcc_map, TRP_INTERRUPT_0_CLEAR,
125 DRP_TRP_INT_CLEAR);
126 regmap_write(llcc_map, TRP_ECC_ERROR_CNTR_CLEAR,
127 DRP_TRP_CNT_CLEAR);
128 break;
129 }
130}
131
132/* Dump syndrome registers for tag Ram Double bit errors */
133static void dump_trp_db_syn_reg(struct regmap *llcc_map)
134{
135 int i;
136 int db_err_cnt;
137 int db_err_ways;
138 u32 synd_reg;
139 u32 synd_val;
140
141 for (i = 0; i < TRP_SYN_REG_CNT; i++) {
142 synd_reg = TRP_ECC_DB_ERR_SYN0 + (i * 4);
143 regmap_read(llcc_map, synd_reg, &synd_val);
144 edac_printk(KERN_CRIT, EDAC_LLCC, "TRP_ECC_SYN%d: 0x%8x\n",
145 i, synd_val);
146 }
147
148 regmap_read(llcc_map, TRP_ECC_ERROR_STATUS1, &db_err_cnt);
149 db_err_cnt = (db_err_cnt & ECC_DB_ERR_COUNT_MASK);
150 edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error count: 0x%4x\n",
151 db_err_cnt);
152
153 regmap_read(llcc_map, TRP_ECC_ERROR_STATUS0, &db_err_ways);
154 db_err_ways = (db_err_ways & ECC_DB_ERR_WAYS_MASK);
155 db_err_ways >>= ECC_DB_ERR_WAYS_SHIFT;
156
157 edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error ways: 0x%4x\n",
158 db_err_ways);
159}
160
161/* Dump syndrome register for tag Ram Single Bit Errors */
162static void dump_trp_sb_syn_reg(struct regmap *llcc_map)
163{
164 int i;
165 int sb_err_cnt;
166 int sb_err_ways;
167 u32 synd_reg;
168 u32 synd_val;
169
170 for (i = 0; i < TRP_SYN_REG_CNT; i++) {
171 synd_reg = TRP_ECC_SB_ERR_SYN0 + (i * 4);
172 regmap_read(llcc_map, synd_reg, &synd_val);
173 edac_printk(KERN_CRIT, EDAC_LLCC, "TRP_ECC_SYN%d: 0x%8x\n",
174 i, synd_val);
175 }
176
177 regmap_read(llcc_map, TRP_ECC_ERROR_STATUS1, &sb_err_cnt);
178 sb_err_cnt = (sb_err_cnt & ECC_SB_ERR_COUNT_MASK);
179 sb_err_cnt >>= ECC_SB_ERR_COUNT_SHIFT;
180 edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error count: 0x%4x\n",
181 sb_err_cnt);
182
183 regmap_read(llcc_map, TRP_ECC_ERROR_STATUS0, &sb_err_ways);
184 sb_err_ways = sb_err_ways & ECC_SB_ERR_WAYS_MASK;
185
186 edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error ways: 0x%4x\n",
187 sb_err_ways);
188}
189
190/* Dump syndrome registers for Data Ram Double bit errors */
191static void dump_drp_db_syn_reg(struct regmap *llcc_map)
192{
193 int i;
194 int db_err_cnt;
195 int db_err_ways;
196 u32 synd_reg;
197 u32 synd_val;
198
199 for (i = 0; i < DRP_SYN_REG_CNT; i++) {
200 synd_reg = DRP_ECC_DB_ERR_SYN0 + (i * 4);
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700201 regmap_read(llcc_map, synd_reg, &synd_val);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700202 edac_printk(KERN_CRIT, EDAC_LLCC, "DRP_ECC_SYN%d: 0x%8x\n",
203 i, synd_val);
204 }
205
206 regmap_read(llcc_map, DRP_ECC_ERROR_STATUS1, &db_err_cnt);
207 db_err_cnt = (db_err_cnt & ECC_DB_ERR_COUNT_MASK);
208 edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error count: 0x%4x\n",
209 db_err_cnt);
210
211 regmap_read(llcc_map, DRP_ECC_ERROR_STATUS0, &db_err_ways);
212 db_err_ways &= ECC_DB_ERR_WAYS_MASK;
213 db_err_ways >>= ECC_DB_ERR_WAYS_SHIFT;
214 edac_printk(KERN_CRIT, EDAC_LLCC, "Double-Bit error ways: 0x%4x\n",
215 db_err_ways);
216}
217
218/* Dump Syndrome registers for Data Ram Single bit errors*/
219static void dump_drp_sb_syn_reg(struct regmap *llcc_map)
220{
221 int i;
222 int sb_err_cnt;
223 int sb_err_ways;
224 u32 synd_reg;
225 u32 synd_val;
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700226 u32 synd_reg_offset;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700227
228 for (i = 0; i < DRP_SYN_REG_CNT; i++) {
229 synd_reg_offset = DRP_ECC_SB_ERR_SYN0 + (i * 4);
230 regmap_read(llcc_map, synd_reg, &synd_val);
231 edac_printk(KERN_CRIT, EDAC_LLCC, "DRP_ECC_SYN%d: 0x%8x\n",
232 i, synd_val);
233 }
234
235 regmap_read(llcc_map, DRP_ECC_ERROR_STATUS1, &sb_err_cnt);
236 sb_err_cnt &= ECC_SB_ERR_COUNT_MASK;
237 sb_err_cnt >>= ECC_SB_ERR_COUNT_SHIFT;
238 edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error count: 0x%4x\n",
239 sb_err_cnt);
240
241 regmap_read(llcc_map, DRP_ECC_ERROR_STATUS0, &sb_err_ways);
242 sb_err_ways = sb_err_ways & ECC_SB_ERR_WAYS_MASK;
243
244 edac_printk(KERN_CRIT, EDAC_LLCC, "Single-Bit error ways: 0x%4x\n",
245 sb_err_ways);
246}
247
248
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700249static void dump_syn_reg(struct edac_device_ctl_info *edev_ctl,
250 int err_type, struct regmap *llcc_map)
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700251{
252 switch (err_type) {
253 case LLCC_DRAM_CE:
254 dump_drp_sb_syn_reg(llcc_map);
255 break;
256 case LLCC_DRAM_UE:
257 dump_drp_db_syn_reg(llcc_map);
258 break;
259 case LLCC_TRAM_CE:
260 dump_trp_sb_syn_reg(llcc_map);
261 break;
262 case LLCC_TRAM_UE:
263 dump_trp_db_syn_reg(llcc_map);
264 break;
265 }
266
267 qcom_llcc_clear_errors(err_type, llcc_map);
268
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700269 errors[err_type].func(edev_ctl, 0, 0, errors[err_type].msg);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700270}
271
Channagoud Kadabic26a8912016-11-21 13:57:20 -0800272static void qcom_llcc_check_cache_errors
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700273 (struct edac_device_ctl_info *edev_ctl)
274{
275 u32 drp_error;
276 u32 trp_error;
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700277 struct erp_drvdata *drv = edev_ctl->pvt_info;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700278
279 /* Look for Data RAM errors */
280 regmap_read(drv->llcc_map, DRP_INTERRUPT_STATUS, &drp_error);
281
282 if (drp_error & SB_ECC_ERROR) {
283 edac_printk(KERN_CRIT, EDAC_LLCC,
284 "Single Bit Error detected in Data Ram\n");
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700285 dump_syn_reg(edev_ctl, LLCC_DRAM_CE, drv->llcc_map);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700286 } else if (drp_error & DB_ECC_ERROR) {
287 edac_printk(KERN_CRIT, EDAC_LLCC,
288 "Double Bit Error detected in Data Ram\n");
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700289 dump_syn_reg(edev_ctl, LLCC_DRAM_UE, drv->llcc_map);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700290 }
291
292 /* Look for Tag RAM errors */
293 regmap_read(drv->llcc_map, TRP_INTERRUPT_0_STATUS, &trp_error);
294 if (trp_error & SB_ECC_ERROR) {
295 edac_printk(KERN_CRIT, EDAC_LLCC,
296 "Single Bit Error detected in Tag Ram\n");
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700297 dump_syn_reg(edev_ctl, LLCC_TRAM_CE, drv->llcc_map);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700298 } else if (trp_error & DB_ECC_ERROR) {
299 edac_printk(KERN_CRIT, EDAC_LLCC,
300 "Double Bit Error detected in Tag Ram\n");
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700301 dump_syn_reg(edev_ctl, LLCC_TRAM_UE, drv->llcc_map);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700302 }
303}
304
Channagoud Kadabic26a8912016-11-21 13:57:20 -0800305static void qcom_llcc_poll_cache_errors(struct edac_device_ctl_info *edev_ctl)
306{
307 qcom_llcc_check_cache_errors(edev_ctl);
308}
309
310static irqreturn_t llcc_ecc_irq_handler
311 (int irq, void *edev_ctl)
312{
313 qcom_llcc_check_cache_errors(edev_ctl);
314 return IRQ_HANDLED;
315}
316
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700317static int qcom_llcc_erp_probe(struct platform_device *pdev)
318{
319 int rc = 0;
320 struct erp_drvdata *drv;
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700321 struct edac_device_ctl_info *edev_ctl;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700322 struct device *dev = &pdev->dev;
323
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700324 /* Allocate edac control info */
325 edev_ctl = edac_device_alloc_ctl_info(sizeof(*drv), "qcom-llcc", 1,
326 NULL, 1, 1, NULL, 0, edac_device_alloc_index());
327
328 edev_ctl->dev = dev;
329 edev_ctl->mod_name = dev_name(dev);
330 edev_ctl->dev_name = dev_name(dev);
331 edev_ctl->ctl_name = "llcc";
332 edev_ctl->poll_msec = poll_msec;
333 edev_ctl->edac_check = qcom_llcc_poll_cache_errors;
334 edev_ctl->defer_work = 1;
335 edev_ctl->panic_on_ce = LLCC_ERP_PANIC_ON_CE;
336 edev_ctl->panic_on_ue = LLCC_ERP_PANIC_ON_UE;
337
338 drv = edev_ctl->pvt_info;
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700339
340 drv->llcc_map = syscon_node_to_regmap(dev->parent->of_node);
341 if (IS_ERR(drv->llcc_map)) {
342 dev_err(dev, "no regmap for syscon llcc parent\n");
Channagoud Kadabic26a8912016-11-21 13:57:20 -0800343 rc = -ENOMEM;
344 goto out;
345 }
346
347 if (interrupt_mode) {
348 drv->ecc_irq = platform_get_irq_byname(pdev, "ecc_irq");
349 if (!drv->ecc_irq) {
350 rc = -ENODEV;
351 goto out;
352 }
353
354 rc = devm_request_irq(dev, drv->ecc_irq, llcc_ecc_irq_handler,
355 IRQF_TRIGGER_RISING, "llcc_ecc", edev_ctl);
356 if (rc) {
357 dev_err(dev, "failed to request ecc irq\n");
358 goto out;
359 }
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700360 }
361
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700362 platform_set_drvdata(pdev, edev_ctl);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700363
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700364 rc = edac_device_add_device(edev_ctl);
Channagoud Kadabic26a8912016-11-21 13:57:20 -0800365out:
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700366 if (rc)
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700367 edac_device_free_ctl_info(edev_ctl);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700368
369 return rc;
370}
371
372static int qcom_llcc_erp_remove(struct platform_device *pdev)
373{
Channagoud Kadabi5c9de9a2016-10-03 23:03:07 -0700374 struct edac_device_ctl_info *edev_ctl = dev_get_drvdata(&pdev->dev);
Channagoud Kadabi845ae7c2016-06-23 18:55:38 -0700375
376 edac_device_del_device(edev_ctl->dev);
377 edac_device_free_ctl_info(edev_ctl);
378
379 return 0;
380}
381
382static const struct of_device_id qcom_llcc_erp_match_table[] = {
383 { .compatible = "qcom,llcc-erp" },
384 { },
385};
386
387static struct platform_driver qcom_llcc_erp_driver = {
388 .probe = qcom_llcc_erp_probe,
389 .remove = qcom_llcc_erp_remove,
390 .driver = {
391 .name = "qcom_llcc_erp",
392 .owner = THIS_MODULE,
393 .of_match_table = qcom_llcc_erp_match_table,
394 },
395};
396
397static int __init qcom_llcc_erp_init(void)
398{
399 return platform_driver_register(&qcom_llcc_erp_driver);
400}
401module_init(qcom_llcc_erp_init);
402
403static void __exit qcom_llcc_erp_exit(void)
404{
405 platform_driver_unregister(&qcom_llcc_erp_driver);
406}
407module_exit(qcom_llcc_erp_exit);
408
409MODULE_DESCRIPTION("QCOM LLCC Error Reporting");
410MODULE_LICENSE("GPL v2");