blob: 9a721e4c83a1ae3e9cc36e9cc27610db4a3abd5e [file] [log] [blame]
Stepan Moskovchenko07552e12012-02-29 20:09:32 -08001/* Copyright (c) 2012, Code Aurora Forum. 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/interrupt.h>
14#include <linux/irq.h>
15#include <linux/platform_device.h>
16#include <linux/module.h>
17#include <linux/errno.h>
18#include <linux/proc_fs.h>
19#include <linux/cpu.h>
20#include <mach/msm-krait-l2-accessors.h>
21
22#define CESR_DCTPE BIT(0)
23#define CESR_DCDPE BIT(1)
24#define CESR_ICTPE BIT(2)
25#define CESR_ICDPE BIT(3)
26#define CESR_DCTE (BIT(4) | BIT(5))
27#define CESR_ICTE (BIT(6) | BIT(7))
28#define CESR_TLBMH BIT(16)
29#define CESR_I_MASK 0x000000CC
30
Stepan Moskovchenkobddae7c2012-04-25 14:48:21 -070031/* Print a message for everything but TLB MH events */
32#define CESR_PRINT_MASK 0x000000FF
33
Stepan Moskovchenko07552e12012-02-29 20:09:32 -080034#define L2ESR_IND_ADDR 0x204
35#define L2ESYNR0_IND_ADDR 0x208
36#define L2ESYNR1_IND_ADDR 0x209
37#define L2EAR0_IND_ADDR 0x20C
38#define L2EAR1_IND_ADDR 0x20D
39
40#define L2ESR_MPDCD BIT(0)
41#define L2ESR_MPSLV BIT(1)
42#define L2ESR_TSESB BIT(2)
43#define L2ESR_TSEDB BIT(3)
44#define L2ESR_DSESB BIT(4)
45#define L2ESR_DSEDB BIT(5)
46#define L2ESR_MSE BIT(6)
47#define L2ESR_MPLDREXNOK BIT(8)
48
Stepan Moskovchenkoe9a5dc12012-04-03 20:25:49 -070049#define L2ESR_ACCESS_ERR_MASK 0xFFFC
50
Stepan Moskovchenko07552e12012-02-29 20:09:32 -080051#define L2ESR_CPU_MASK 0x0F
52#define L2ESR_CPU_SHIFT 16
53
54#ifdef CONFIG_MSM_L1_ERR_PANIC
55#define ERP_L1_ERR(a) panic(a)
56#else
57#define ERP_L1_ERR(a) do { } while (0)
58#endif
59
60#ifdef CONFIG_MSM_L2_ERP_PORT_PANIC
61#define ERP_PORT_ERR(a) panic(a)
62#else
63#define ERP_PORT_ERR(a) WARN(1, a)
64#endif
65
66#ifdef CONFIG_MSM_L2_ERP_1BIT_PANIC
67#define ERP_1BIT_ERR(a) panic(a)
68#else
69#define ERP_1BIT_ERR(a) do { } while (0)
70#endif
71
Stepan Moskovchenkoe9a5dc12012-04-03 20:25:49 -070072#ifdef CONFIG_MSM_L2_ERP_PRINT_ACCESS_ERRORS
73#define print_access_errors() 1
74#else
75#define print_access_errors() 0
76#endif
77
Stepan Moskovchenko07552e12012-02-29 20:09:32 -080078#ifdef CONFIG_MSM_L2_ERP_2BIT_PANIC
79#define ERP_2BIT_ERR(a) panic(a)
80#else
81#define ERP_2BIT_ERR(a) do { } while (0)
82#endif
83
84#define MODULE_NAME "msm_cache_erp"
85
86struct msm_l1_err_stats {
87 unsigned int dctpe;
88 unsigned int dcdpe;
89 unsigned int ictpe;
90 unsigned int icdpe;
91 unsigned int dcte;
92 unsigned int icte;
93 unsigned int tlbmh;
94};
95
96struct msm_l2_err_stats {
97 unsigned int mpdcd;
98 unsigned int mpslv;
99 unsigned int tsesb;
100 unsigned int tsedb;
101 unsigned int dsesb;
102 unsigned int dsedb;
103 unsigned int mse;
104 unsigned int mplxrexnok;
105};
106
107static DEFINE_PER_CPU(struct msm_l1_err_stats, msm_l1_erp_stats);
108static struct msm_l2_err_stats msm_l2_erp_stats;
109
110static int l1_erp_irq, l2_erp_irq;
111static struct proc_dir_entry *procfs_entry;
112
113static inline unsigned int read_cesr(void)
114{
115 unsigned int cesr;
116 asm volatile ("mrc p15, 7, %0, c15, c0, 1" : "=r" (cesr));
117 return cesr;
118}
119
120static inline void write_cesr(unsigned int cesr)
121{
122 asm volatile ("mcr p15, 7, %[cesr], c15, c0, 1" : : [cesr]"r" (cesr));
123}
124
125static inline unsigned int read_cesynr(void)
126{
127 unsigned int cesynr;
128 asm volatile ("mrc p15, 7, %0, c15, c0, 3" : "=r" (cesynr));
129 return cesynr;
130}
131
132static int proc_read_status(char *page, char **start, off_t off, int count,
133 int *eof, void *data)
134{
135 struct msm_l1_err_stats *l1_stats;
136 char *p = page;
137 int len, cpu, ret, bytes_left = PAGE_SIZE;
138
139 for_each_present_cpu(cpu) {
140 l1_stats = &per_cpu(msm_l1_erp_stats, cpu);
141
142 ret = snprintf(p, bytes_left,
143 "CPU %d:\n" \
144 "\tD-cache tag parity errors:\t%u\n" \
145 "\tD-cache data parity errors:\t%u\n" \
146 "\tI-cache tag parity errors:\t%u\n" \
147 "\tI-cache data parity errors:\t%u\n" \
148 "\tD-cache timing errors:\t\t%u\n" \
149 "\tI-cache timing errors:\t\t%u\n" \
150 "\tTLB multi-hit errors:\t\t%u\n\n", \
151 cpu,
152 l1_stats->dctpe,
153 l1_stats->dcdpe,
154 l1_stats->ictpe,
155 l1_stats->icdpe,
156 l1_stats->dcte,
157 l1_stats->icte,
158 l1_stats->tlbmh);
159 p += ret;
160 bytes_left -= ret;
161 }
162
163 p += snprintf(p, bytes_left,
164 "L2 master port decode errors:\t\t%u\n" \
165 "L2 master port slave errors:\t\t%u\n" \
166 "L2 tag soft errors, single-bit:\t\t%u\n" \
167 "L2 tag soft errors, double-bit:\t\t%u\n" \
168 "L2 data soft errors, single-bit:\t%u\n" \
169 "L2 data soft errors, double-bit:\t%u\n" \
170 "L2 modified soft errors:\t\t%u\n" \
171 "L2 master port LDREX NOK errors:\t%u\n",
172 msm_l2_erp_stats.mpdcd,
173 msm_l2_erp_stats.mpslv,
174 msm_l2_erp_stats.tsesb,
175 msm_l2_erp_stats.tsedb,
176 msm_l2_erp_stats.dsesb,
177 msm_l2_erp_stats.dsedb,
178 msm_l2_erp_stats.mse,
179 msm_l2_erp_stats.mplxrexnok);
180
181 len = (p - page) - off;
182 if (len < 0)
183 len = 0;
184
185 *eof = (len <= count) ? 1 : 0;
186 *start = page + off;
187
188 return len;
189}
190
191static irqreturn_t msm_l1_erp_irq(int irq, void *dev_id)
192{
193 struct msm_l1_err_stats *l1_stats = dev_id;
194 unsigned int cesr = read_cesr();
195 unsigned int i_cesynr, d_cesynr;
Stepan Moskovchenkobddae7c2012-04-25 14:48:21 -0700196 int print_regs = cesr & CESR_PRINT_MASK;
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800197
Stepan Moskovchenkobddae7c2012-04-25 14:48:21 -0700198 if (print_regs) {
199 pr_alert("L1 Error detected on CPU %d!\n", smp_processor_id());
200 pr_alert("\tCESR = 0x%08x\n", cesr);
201 }
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800202
203 if (cesr & CESR_DCTPE) {
204 pr_alert("D-cache tag parity error\n");
205 l1_stats->dctpe++;
206 }
207
208 if (cesr & CESR_DCDPE) {
209 pr_alert("D-cache data parity error\n");
210 l1_stats->dcdpe++;
211 }
212
213 if (cesr & CESR_ICTPE) {
214 pr_alert("I-cache tag parity error\n");
215 l1_stats->ictpe++;
216 }
217
218 if (cesr & CESR_ICDPE) {
219 pr_alert("I-cache data parity error\n");
220 l1_stats->icdpe++;
221 }
222
223 if (cesr & CESR_DCTE) {
224 pr_alert("D-cache timing error\n");
225 l1_stats->dcte++;
226 }
227
228 if (cesr & CESR_ICTE) {
229 pr_alert("I-cache timing error\n");
230 l1_stats->icte++;
231 }
232
233 if (cesr & CESR_TLBMH) {
Stepan Moskovchenkobddae7c2012-04-25 14:48:21 -0700234 asm ("mcr p15, 0, r0, c8, c7, 0");
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800235 l1_stats->tlbmh++;
236 }
237
238 if (cesr & (CESR_ICTPE | CESR_ICDPE | CESR_ICTE)) {
239 i_cesynr = read_cesynr();
240 pr_alert("I-side CESYNR = 0x%08x\n", i_cesynr);
241 write_cesr(CESR_I_MASK);
242
243 /*
244 * Clear the I-side bits from the captured CESR value so that we
245 * don't accidentally clear any new I-side errors when we do
246 * the CESR write-clear operation.
247 */
248 cesr &= ~CESR_I_MASK;
249 }
250
251 if (cesr & (CESR_DCTPE | CESR_DCDPE | CESR_DCTE)) {
252 d_cesynr = read_cesynr();
253 pr_alert("D-side CESYNR = 0x%08x\n", d_cesynr);
254 }
255
256 /* Clear the interrupt bits we processed */
257 write_cesr(cesr);
258
Stepan Moskovchenkobddae7c2012-04-25 14:48:21 -0700259 if (print_regs)
260 ERP_L1_ERR("L1 cache error detected");
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800261
262 return IRQ_HANDLED;
263}
264
265static irqreturn_t msm_l2_erp_irq(int irq, void *dev_id)
266{
267 unsigned int l2esr;
268 unsigned int l2esynr0;
269 unsigned int l2esynr1;
270 unsigned int l2ear0;
271 unsigned int l2ear1;
272 int soft_error = 0;
273 int port_error = 0;
274 int unrecoverable = 0;
Stepan Moskovchenkoe9a5dc12012-04-03 20:25:49 -0700275 int print_alert;
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800276
277 l2esr = get_l2_indirect_reg(L2ESR_IND_ADDR);
278 l2esynr0 = get_l2_indirect_reg(L2ESYNR0_IND_ADDR);
279 l2esynr1 = get_l2_indirect_reg(L2ESYNR1_IND_ADDR);
280 l2ear0 = get_l2_indirect_reg(L2EAR0_IND_ADDR);
281 l2ear1 = get_l2_indirect_reg(L2EAR1_IND_ADDR);
282
Stepan Moskovchenkoe9a5dc12012-04-03 20:25:49 -0700283 print_alert = print_access_errors() || (l2esr & L2ESR_ACCESS_ERR_MASK);
284
285 if (print_alert) {
286 pr_alert("L2 Error detected!\n");
287 pr_alert("\tL2ESR = 0x%08x\n", l2esr);
288 pr_alert("\tL2ESYNR0 = 0x%08x\n", l2esynr0);
289 pr_alert("\tL2ESYNR1 = 0x%08x\n", l2esynr1);
290 pr_alert("\tL2EAR0 = 0x%08x\n", l2ear0);
291 pr_alert("\tL2EAR1 = 0x%08x\n", l2ear1);
292 pr_alert("\tCPU bitmap = 0x%x\n", (l2esr >> L2ESR_CPU_SHIFT) &
293 L2ESR_CPU_MASK);
294 }
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800295
296 if (l2esr & L2ESR_MPDCD) {
Stepan Moskovchenkoe9a5dc12012-04-03 20:25:49 -0700297 if (print_alert)
298 pr_alert("L2 master port decode error\n");
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800299 port_error++;
300 msm_l2_erp_stats.mpdcd++;
301 }
302
303 if (l2esr & L2ESR_MPSLV) {
Stepan Moskovchenkoe9a5dc12012-04-03 20:25:49 -0700304 if (print_alert)
305 pr_alert("L2 master port slave error\n");
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800306 port_error++;
307 msm_l2_erp_stats.mpslv++;
308 }
309
310 if (l2esr & L2ESR_TSESB) {
311 pr_alert("L2 tag soft error, single-bit\n");
312 soft_error++;
313 msm_l2_erp_stats.tsesb++;
314 }
315
316 if (l2esr & L2ESR_TSEDB) {
317 pr_alert("L2 tag soft error, double-bit\n");
318 soft_error++;
319 unrecoverable++;
320 msm_l2_erp_stats.tsedb++;
321 }
322
323 if (l2esr & L2ESR_DSESB) {
324 pr_alert("L2 data soft error, single-bit\n");
325 soft_error++;
326 msm_l2_erp_stats.dsesb++;
327 }
328
329 if (l2esr & L2ESR_DSEDB) {
330 pr_alert("L2 data soft error, double-bit\n");
331 soft_error++;
332 unrecoverable++;
333 msm_l2_erp_stats.dsedb++;
334 }
335
336 if (l2esr & L2ESR_MSE) {
337 pr_alert("L2 modified soft error\n");
338 soft_error++;
339 msm_l2_erp_stats.mse++;
340 }
341
342 if (l2esr & L2ESR_MPLDREXNOK) {
343 pr_alert("L2 master port LDREX received Normal OK response\n");
344 port_error++;
345 msm_l2_erp_stats.mplxrexnok++;
346 }
347
Stepan Moskovchenkoe9a5dc12012-04-03 20:25:49 -0700348 if (port_error && print_alert)
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800349 ERP_PORT_ERR("L2 master port error detected");
350
351 if (soft_error && !unrecoverable)
352 ERP_1BIT_ERR("L2 single-bit error detected");
353
354 if (unrecoverable)
355 ERP_2BIT_ERR("L2 double-bit error detected, trouble ahead");
356
357 set_l2_indirect_reg(L2ESR_IND_ADDR, l2esr);
358 return IRQ_HANDLED;
359}
360
361static void enable_erp_irq_callback(void *info)
362{
363 enable_percpu_irq(l1_erp_irq, IRQ_TYPE_LEVEL_HIGH);
364}
365
366static void disable_erp_irq_callback(void *info)
367{
368 disable_percpu_irq(l1_erp_irq);
369}
370
371static int cache_erp_cpu_callback(struct notifier_block *nfb,
372 unsigned long action, void *hcpu)
373{
374 switch (action & (~CPU_TASKS_FROZEN)) {
375 case CPU_STARTING:
376 enable_erp_irq_callback(NULL);
377 break;
378
379 case CPU_DYING:
380 disable_erp_irq_callback(NULL);
381 break;
382 }
383 return NOTIFY_OK;
384}
385
386static struct notifier_block cache_erp_cpu_notifier = {
387 .notifier_call = cache_erp_cpu_callback,
388};
389
390static int msm_cache_erp_probe(struct platform_device *pdev)
391{
392 struct resource *r;
393 int ret, cpu;
394
395 r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "l1_irq");
396
397 if (!r) {
398 pr_err("Could not get L1 resource\n");
399 ret = -ENODEV;
400 goto fail;
401 }
402
403 l1_erp_irq = r->start;
404
405 ret = request_percpu_irq(l1_erp_irq, msm_l1_erp_irq, "MSM_L1",
406 &msm_l1_erp_stats);
407
408 if (ret) {
409 pr_err("Failed to request the L1 cache error interrupt\n");
410 goto fail;
411 }
412
413 r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "l2_irq");
414
415 if (!r) {
416 pr_err("Could not get L2 resource\n");
417 ret = -ENODEV;
418 goto fail_l1;
419 }
420
421 l2_erp_irq = r->start;
422 ret = request_irq(l2_erp_irq, msm_l2_erp_irq, 0, "MSM_L2", NULL);
423
424 if (ret) {
425 pr_err("Failed to request the L2 cache error interrupt\n");
426 goto fail_l1;
427 }
428
429 procfs_entry = create_proc_entry("cpu/msm_cache_erp", S_IRUGO, NULL);
430
431 if (!procfs_entry) {
432 pr_err("Failed to create procfs node for cache error reporting\n");
433 ret = -ENODEV;
434 goto fail_l2;
435 }
436
437 get_online_cpus();
438 register_hotcpu_notifier(&cache_erp_cpu_notifier);
439 for_each_cpu(cpu, cpu_online_mask)
440 smp_call_function_single(cpu, enable_erp_irq_callback, NULL, 1);
441 put_online_cpus();
442
443 procfs_entry->read_proc = proc_read_status;
444 return 0;
445
446fail_l2:
447 free_irq(l2_erp_irq, NULL);
448fail_l1:
449 free_percpu_irq(l1_erp_irq, NULL);
450fail:
451 return ret;
452}
453
454static int msm_cache_erp_remove(struct platform_device *pdev)
455{
456 int cpu;
457 if (procfs_entry)
458 remove_proc_entry("cpu/msm_cache_erp", NULL);
459
460 get_online_cpus();
461 unregister_hotcpu_notifier(&cache_erp_cpu_notifier);
462 for_each_cpu(cpu, cpu_online_mask)
463 smp_call_function_single(cpu, disable_erp_irq_callback, NULL,
464 1);
465 put_online_cpus();
466
467 free_percpu_irq(l1_erp_irq, NULL);
468
469 disable_irq(l2_erp_irq);
470 free_irq(l2_erp_irq, NULL);
471 return 0;
472}
473
474static struct platform_driver msm_cache_erp_driver = {
475 .probe = msm_cache_erp_probe,
476 .remove = msm_cache_erp_remove,
477 .driver = {
478 .name = MODULE_NAME,
479 .owner = THIS_MODULE,
480 },
481};
482
483static int __init msm_cache_erp_init(void)
484{
485 return platform_driver_register(&msm_cache_erp_driver);
486}
487
488static void __exit msm_cache_erp_exit(void)
489{
490 platform_driver_unregister(&msm_cache_erp_driver);
491}
492
493
494module_init(msm_cache_erp_init);
495module_exit(msm_cache_erp_exit);
496MODULE_LICENSE("GPL v2");
497MODULE_DESCRIPTION("MSM cache error reporting driver");