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