blob: f52bc28ae9e662ee15dd5e13e4246c1cec325bba [file] [log] [blame]
Duy Truong790f06d2013-02-13 16:38:12 -08001/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
Stepan Moskovchenko07552e12012-02-29 20:09:32 -08002 *
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>
Stepan Moskovchenko6482c432012-04-30 13:34:23 -070020#include <linux/io.h>
Stepan Moskovchenko07552e12012-02-29 20:09:32 -080021#include <mach/msm-krait-l2-accessors.h>
Stepan Moskovchenko6482c432012-04-30 13:34:23 -070022#include <mach/msm_iomap.h>
Stepan Moskovchenkoe1aba3d2012-12-06 18:40:25 -080023#include <mach/socinfo.h>
Stepan Moskovchenko6482c432012-04-30 13:34:23 -070024#include <asm/cputype.h>
25#include "acpuclock.h"
Stepan Moskovchenko07552e12012-02-29 20:09:32 -080026
27#define CESR_DCTPE BIT(0)
28#define CESR_DCDPE BIT(1)
29#define CESR_ICTPE BIT(2)
30#define CESR_ICDPE BIT(3)
31#define CESR_DCTE (BIT(4) | BIT(5))
32#define CESR_ICTE (BIT(6) | BIT(7))
33#define CESR_TLBMH BIT(16)
34#define CESR_I_MASK 0x000000CC
35
Stepan Moskovchenkoe1aba3d2012-12-06 18:40:25 -080036#define CESR_VALID_MASK 0x000100FF
37
Stepan Moskovchenkobddae7c2012-04-25 14:48:21 -070038/* Print a message for everything but TLB MH events */
39#define CESR_PRINT_MASK 0x000000FF
40
Stepan Moskovchenko7035f652012-05-31 17:48:01 -070041/* Log everything but TLB MH events */
42#define CESR_LOG_EVENT_MASK 0x000000FF
43
Stepan Moskovchenko07552e12012-02-29 20:09:32 -080044#define L2ESR_IND_ADDR 0x204
45#define L2ESYNR0_IND_ADDR 0x208
46#define L2ESYNR1_IND_ADDR 0x209
47#define L2EAR0_IND_ADDR 0x20C
48#define L2EAR1_IND_ADDR 0x20D
49
50#define L2ESR_MPDCD BIT(0)
51#define L2ESR_MPSLV BIT(1)
52#define L2ESR_TSESB BIT(2)
53#define L2ESR_TSEDB BIT(3)
54#define L2ESR_DSESB BIT(4)
55#define L2ESR_DSEDB BIT(5)
56#define L2ESR_MSE BIT(6)
57#define L2ESR_MPLDREXNOK BIT(8)
58
Stepan Moskovchenkoe9a5dc12012-04-03 20:25:49 -070059#define L2ESR_ACCESS_ERR_MASK 0xFFFC
60
Stepan Moskovchenko07552e12012-02-29 20:09:32 -080061#define L2ESR_CPU_MASK 0x0F
62#define L2ESR_CPU_SHIFT 16
63
64#ifdef CONFIG_MSM_L1_ERR_PANIC
65#define ERP_L1_ERR(a) panic(a)
66#else
67#define ERP_L1_ERR(a) do { } while (0)
68#endif
69
Stepan Moskovchenkoe1aba3d2012-12-06 18:40:25 -080070#ifdef CONFIG_MSM_L1_RECOV_ERR_PANIC
71#define ERP_L1_RECOV_ERR(a) panic(a)
72#else
73#define ERP_L1_RECOV_ERR(a) do { } while (0)
74#endif
75
Stepan Moskovchenko07552e12012-02-29 20:09:32 -080076#ifdef CONFIG_MSM_L2_ERP_PORT_PANIC
77#define ERP_PORT_ERR(a) panic(a)
78#else
79#define ERP_PORT_ERR(a) WARN(1, a)
80#endif
81
82#ifdef CONFIG_MSM_L2_ERP_1BIT_PANIC
83#define ERP_1BIT_ERR(a) panic(a)
84#else
85#define ERP_1BIT_ERR(a) do { } while (0)
86#endif
87
Stepan Moskovchenkoe9a5dc12012-04-03 20:25:49 -070088#ifdef CONFIG_MSM_L2_ERP_PRINT_ACCESS_ERRORS
89#define print_access_errors() 1
90#else
91#define print_access_errors() 0
92#endif
93
Stepan Moskovchenko07552e12012-02-29 20:09:32 -080094#ifdef CONFIG_MSM_L2_ERP_2BIT_PANIC
95#define ERP_2BIT_ERR(a) panic(a)
96#else
97#define ERP_2BIT_ERR(a) do { } while (0)
98#endif
99
100#define MODULE_NAME "msm_cache_erp"
101
Stepan Moskovchenkoaa15fd12012-06-18 16:51:13 -0700102#define ERP_LOG_MAGIC_ADDR 0x6A4
Stepan Moskovchenko7035f652012-05-31 17:48:01 -0700103#define ERP_LOG_MAGIC 0x11C39893
104
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800105struct msm_l1_err_stats {
106 unsigned int dctpe;
107 unsigned int dcdpe;
108 unsigned int ictpe;
109 unsigned int icdpe;
110 unsigned int dcte;
111 unsigned int icte;
112 unsigned int tlbmh;
113};
114
115struct msm_l2_err_stats {
116 unsigned int mpdcd;
117 unsigned int mpslv;
118 unsigned int tsesb;
119 unsigned int tsedb;
120 unsigned int dsesb;
121 unsigned int dsedb;
122 unsigned int mse;
123 unsigned int mplxrexnok;
124};
125
Stepan Moskovchenkod383c0e2013-04-15 17:51:15 -0700126struct msm_erp_dump_region {
127 struct resource *res;
128 void __iomem *va;
129};
130
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800131static DEFINE_PER_CPU(struct msm_l1_err_stats, msm_l1_erp_stats);
132static struct msm_l2_err_stats msm_l2_erp_stats;
133
134static int l1_erp_irq, l2_erp_irq;
135static struct proc_dir_entry *procfs_entry;
Stepan Moskovchenkod383c0e2013-04-15 17:51:15 -0700136static int num_dump_regions;
137static struct msm_erp_dump_region *dump_regions;
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800138
Stepan Moskovchenko7035f652012-05-31 17:48:01 -0700139#ifdef CONFIG_MSM_L1_ERR_LOG
140static struct proc_dir_entry *procfs_log_entry;
141#endif
142
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800143static inline unsigned int read_cesr(void)
144{
145 unsigned int cesr;
146 asm volatile ("mrc p15, 7, %0, c15, c0, 1" : "=r" (cesr));
147 return cesr;
148}
149
150static inline void write_cesr(unsigned int cesr)
151{
152 asm volatile ("mcr p15, 7, %[cesr], c15, c0, 1" : : [cesr]"r" (cesr));
153}
154
155static inline unsigned int read_cesynr(void)
156{
157 unsigned int cesynr;
158 asm volatile ("mrc p15, 7, %0, c15, c0, 3" : "=r" (cesynr));
159 return cesynr;
160}
161
162static int proc_read_status(char *page, char **start, off_t off, int count,
163 int *eof, void *data)
164{
165 struct msm_l1_err_stats *l1_stats;
166 char *p = page;
167 int len, cpu, ret, bytes_left = PAGE_SIZE;
168
169 for_each_present_cpu(cpu) {
170 l1_stats = &per_cpu(msm_l1_erp_stats, cpu);
171
172 ret = snprintf(p, bytes_left,
173 "CPU %d:\n" \
174 "\tD-cache tag parity errors:\t%u\n" \
175 "\tD-cache data parity errors:\t%u\n" \
176 "\tI-cache tag parity errors:\t%u\n" \
177 "\tI-cache data parity errors:\t%u\n" \
178 "\tD-cache timing errors:\t\t%u\n" \
179 "\tI-cache timing errors:\t\t%u\n" \
180 "\tTLB multi-hit errors:\t\t%u\n\n", \
181 cpu,
182 l1_stats->dctpe,
183 l1_stats->dcdpe,
184 l1_stats->ictpe,
185 l1_stats->icdpe,
186 l1_stats->dcte,
187 l1_stats->icte,
188 l1_stats->tlbmh);
189 p += ret;
190 bytes_left -= ret;
191 }
192
193 p += snprintf(p, bytes_left,
194 "L2 master port decode errors:\t\t%u\n" \
195 "L2 master port slave errors:\t\t%u\n" \
196 "L2 tag soft errors, single-bit:\t\t%u\n" \
197 "L2 tag soft errors, double-bit:\t\t%u\n" \
198 "L2 data soft errors, single-bit:\t%u\n" \
199 "L2 data soft errors, double-bit:\t%u\n" \
200 "L2 modified soft errors:\t\t%u\n" \
201 "L2 master port LDREX NOK errors:\t%u\n",
202 msm_l2_erp_stats.mpdcd,
203 msm_l2_erp_stats.mpslv,
204 msm_l2_erp_stats.tsesb,
205 msm_l2_erp_stats.tsedb,
206 msm_l2_erp_stats.dsesb,
207 msm_l2_erp_stats.dsedb,
208 msm_l2_erp_stats.mse,
209 msm_l2_erp_stats.mplxrexnok);
210
211 len = (p - page) - off;
212 if (len < 0)
213 len = 0;
214
215 *eof = (len <= count) ? 1 : 0;
216 *start = page + off;
217
218 return len;
219}
220
Stepan Moskovchenkod383c0e2013-04-15 17:51:15 -0700221static int msm_erp_dump_regions(void)
222{
223 int i = 0;
224 struct msm_erp_dump_region *r;
225
226 for (i = 0; i < num_dump_regions; i++) {
227 r = &dump_regions[i];
228
229 pr_alert("%s %pR:\n", r->res->name, r->res);
230 print_hex_dump(KERN_ALERT, "", DUMP_PREFIX_OFFSET, 32, 4, r->va,
231 resource_size(r->res), 0);
232 }
233
234 return 0;
235}
236
Stepan Moskovchenko7035f652012-05-31 17:48:01 -0700237#ifdef CONFIG_MSM_L1_ERR_LOG
238static int proc_read_log(char *page, char **start, off_t off, int count,
239 int *eof, void *data)
240{
241 char *p = page;
242 int len, log_value;
243 log_value = __raw_readl(MSM_IMEM_BASE + ERP_LOG_MAGIC_ADDR) ==
244 ERP_LOG_MAGIC ? 1 : 0;
245
246 p += snprintf(p, PAGE_SIZE, "%d\n", log_value);
247
248 len = (p - page) - off;
249 if (len < 0)
250 len = 0;
251
252 *eof = (len <= count) ? 1 : 0;
253 *start = page + off;
254
255 return len;
256}
257
258static void log_cpu_event(void)
259{
260 __raw_writel(ERP_LOG_MAGIC, MSM_IMEM_BASE + ERP_LOG_MAGIC_ADDR);
261 mb();
262}
263
264static int procfs_event_log_init(void)
265{
266 procfs_log_entry = create_proc_entry("cpu/msm_erp_log", S_IRUGO, NULL);
267
268 if (!procfs_log_entry)
269 return -ENODEV;
270 procfs_log_entry->read_proc = proc_read_log;
271 return 0;
272}
273
274#else
275static inline void log_cpu_event(void) { }
276static inline int procfs_event_log_init(void) { return 0; }
277#endif
278
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800279static irqreturn_t msm_l1_erp_irq(int irq, void *dev_id)
280{
281 struct msm_l1_err_stats *l1_stats = dev_id;
282 unsigned int cesr = read_cesr();
283 unsigned int i_cesynr, d_cesynr;
Stepan Moskovchenko6482c432012-04-30 13:34:23 -0700284 unsigned int cpu = smp_processor_id();
Stepan Moskovchenkobddae7c2012-04-25 14:48:21 -0700285 int print_regs = cesr & CESR_PRINT_MASK;
Stepan Moskovchenko7035f652012-05-31 17:48:01 -0700286 int log_event = cesr & CESR_LOG_EVENT_MASK;
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800287
Stepan Moskovchenkobddae7c2012-04-25 14:48:21 -0700288 if (print_regs) {
Stepan Moskovchenko6482c432012-04-30 13:34:23 -0700289 pr_alert("L1 / TLB Error detected on CPU %d!\n", cpu);
290 pr_alert("\tCESR = 0x%08x\n", cesr);
291 pr_alert("\tCPU speed = %lu\n", acpuclk_get_rate(cpu));
292 pr_alert("\tMIDR = 0x%08x\n", read_cpuid_id());
Stepan Moskovchenkod383c0e2013-04-15 17:51:15 -0700293 msm_erp_dump_regions();
Stepan Moskovchenkobddae7c2012-04-25 14:48:21 -0700294 }
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800295
296 if (cesr & CESR_DCTPE) {
297 pr_alert("D-cache tag parity error\n");
298 l1_stats->dctpe++;
299 }
300
301 if (cesr & CESR_DCDPE) {
302 pr_alert("D-cache data parity error\n");
303 l1_stats->dcdpe++;
304 }
305
306 if (cesr & CESR_ICTPE) {
307 pr_alert("I-cache tag parity error\n");
308 l1_stats->ictpe++;
309 }
310
311 if (cesr & CESR_ICDPE) {
312 pr_alert("I-cache data parity error\n");
313 l1_stats->icdpe++;
314 }
315
316 if (cesr & CESR_DCTE) {
317 pr_alert("D-cache timing error\n");
318 l1_stats->dcte++;
319 }
320
321 if (cesr & CESR_ICTE) {
322 pr_alert("I-cache timing error\n");
323 l1_stats->icte++;
324 }
325
326 if (cesr & CESR_TLBMH) {
Stepan Moskovchenkobddae7c2012-04-25 14:48:21 -0700327 asm ("mcr p15, 0, r0, c8, c7, 0");
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800328 l1_stats->tlbmh++;
329 }
330
331 if (cesr & (CESR_ICTPE | CESR_ICDPE | CESR_ICTE)) {
332 i_cesynr = read_cesynr();
333 pr_alert("I-side CESYNR = 0x%08x\n", i_cesynr);
334 write_cesr(CESR_I_MASK);
335
336 /*
337 * Clear the I-side bits from the captured CESR value so that we
338 * don't accidentally clear any new I-side errors when we do
339 * the CESR write-clear operation.
340 */
341 cesr &= ~CESR_I_MASK;
342 }
343
344 if (cesr & (CESR_DCTPE | CESR_DCDPE | CESR_DCTE)) {
345 d_cesynr = read_cesynr();
346 pr_alert("D-side CESYNR = 0x%08x\n", d_cesynr);
347 }
348
Stepan Moskovchenko7035f652012-05-31 17:48:01 -0700349 if (log_event)
350 log_cpu_event();
351
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800352 /* Clear the interrupt bits we processed */
353 write_cesr(cesr);
354
Stepan Moskovchenkoe1aba3d2012-12-06 18:40:25 -0800355 if (print_regs) {
356 if ((cesr & (~CESR_I_MASK & CESR_VALID_MASK)) ||
357 cpu_is_krait_v1() || cpu_is_krait_v2())
358 ERP_L1_ERR("L1 nonrecoverable cache error detected");
359 else
360 ERP_L1_RECOV_ERR("L1 recoverable error detected\n");
361 }
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800362
363 return IRQ_HANDLED;
364}
365
366static irqreturn_t msm_l2_erp_irq(int irq, void *dev_id)
367{
368 unsigned int l2esr;
369 unsigned int l2esynr0;
370 unsigned int l2esynr1;
371 unsigned int l2ear0;
372 unsigned int l2ear1;
373 int soft_error = 0;
374 int port_error = 0;
375 int unrecoverable = 0;
Stepan Moskovchenkoe9a5dc12012-04-03 20:25:49 -0700376 int print_alert;
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800377
378 l2esr = get_l2_indirect_reg(L2ESR_IND_ADDR);
379 l2esynr0 = get_l2_indirect_reg(L2ESYNR0_IND_ADDR);
380 l2esynr1 = get_l2_indirect_reg(L2ESYNR1_IND_ADDR);
381 l2ear0 = get_l2_indirect_reg(L2EAR0_IND_ADDR);
382 l2ear1 = get_l2_indirect_reg(L2EAR1_IND_ADDR);
383
Stepan Moskovchenkoe9a5dc12012-04-03 20:25:49 -0700384 print_alert = print_access_errors() || (l2esr & L2ESR_ACCESS_ERR_MASK);
385
386 if (print_alert) {
387 pr_alert("L2 Error detected!\n");
388 pr_alert("\tL2ESR = 0x%08x\n", l2esr);
389 pr_alert("\tL2ESYNR0 = 0x%08x\n", l2esynr0);
390 pr_alert("\tL2ESYNR1 = 0x%08x\n", l2esynr1);
391 pr_alert("\tL2EAR0 = 0x%08x\n", l2ear0);
392 pr_alert("\tL2EAR1 = 0x%08x\n", l2ear1);
393 pr_alert("\tCPU bitmap = 0x%x\n", (l2esr >> L2ESR_CPU_SHIFT) &
394 L2ESR_CPU_MASK);
395 }
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800396
397 if (l2esr & L2ESR_MPDCD) {
Stepan Moskovchenkoe9a5dc12012-04-03 20:25:49 -0700398 if (print_alert)
399 pr_alert("L2 master port decode error\n");
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800400 port_error++;
401 msm_l2_erp_stats.mpdcd++;
402 }
403
404 if (l2esr & L2ESR_MPSLV) {
Stepan Moskovchenkoe9a5dc12012-04-03 20:25:49 -0700405 if (print_alert)
406 pr_alert("L2 master port slave error\n");
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800407 port_error++;
408 msm_l2_erp_stats.mpslv++;
409 }
410
411 if (l2esr & L2ESR_TSESB) {
412 pr_alert("L2 tag soft error, single-bit\n");
413 soft_error++;
414 msm_l2_erp_stats.tsesb++;
415 }
416
417 if (l2esr & L2ESR_TSEDB) {
418 pr_alert("L2 tag soft error, double-bit\n");
419 soft_error++;
420 unrecoverable++;
421 msm_l2_erp_stats.tsedb++;
422 }
423
424 if (l2esr & L2ESR_DSESB) {
425 pr_alert("L2 data soft error, single-bit\n");
426 soft_error++;
427 msm_l2_erp_stats.dsesb++;
428 }
429
430 if (l2esr & L2ESR_DSEDB) {
431 pr_alert("L2 data soft error, double-bit\n");
432 soft_error++;
433 unrecoverable++;
434 msm_l2_erp_stats.dsedb++;
435 }
436
437 if (l2esr & L2ESR_MSE) {
438 pr_alert("L2 modified soft error\n");
439 soft_error++;
440 msm_l2_erp_stats.mse++;
441 }
442
443 if (l2esr & L2ESR_MPLDREXNOK) {
444 pr_alert("L2 master port LDREX received Normal OK response\n");
445 port_error++;
446 msm_l2_erp_stats.mplxrexnok++;
447 }
448
Stepan Moskovchenkoe9a5dc12012-04-03 20:25:49 -0700449 if (port_error && print_alert)
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800450 ERP_PORT_ERR("L2 master port error detected");
451
Stepan Moskovchenkod383c0e2013-04-15 17:51:15 -0700452 if (soft_error && print_alert)
453 msm_erp_dump_regions();
454
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800455 if (soft_error && !unrecoverable)
456 ERP_1BIT_ERR("L2 single-bit error detected");
457
458 if (unrecoverable)
459 ERP_2BIT_ERR("L2 double-bit error detected, trouble ahead");
460
461 set_l2_indirect_reg(L2ESR_IND_ADDR, l2esr);
462 return IRQ_HANDLED;
463}
464
465static void enable_erp_irq_callback(void *info)
466{
467 enable_percpu_irq(l1_erp_irq, IRQ_TYPE_LEVEL_HIGH);
468}
469
470static void disable_erp_irq_callback(void *info)
471{
472 disable_percpu_irq(l1_erp_irq);
473}
474
475static int cache_erp_cpu_callback(struct notifier_block *nfb,
476 unsigned long action, void *hcpu)
477{
478 switch (action & (~CPU_TASKS_FROZEN)) {
479 case CPU_STARTING:
480 enable_erp_irq_callback(NULL);
481 break;
482
483 case CPU_DYING:
484 disable_erp_irq_callback(NULL);
485 break;
486 }
487 return NOTIFY_OK;
488}
489
490static struct notifier_block cache_erp_cpu_notifier = {
491 .notifier_call = cache_erp_cpu_callback,
492};
493
Stepan Moskovchenkod383c0e2013-04-15 17:51:15 -0700494static int msm_erp_read_dump_regions(struct platform_device *pdev)
495{
496 int i;
497 struct device_node *np = pdev->dev.of_node;
498 struct resource *res;
499
500 num_dump_regions = of_property_count_strings(np, "reg-names");
501
502 if (num_dump_regions <= 0) {
503 num_dump_regions = 0;
504 return 0; /* Not an error - this is an optional property */
505 }
506
507 dump_regions = devm_kzalloc(&pdev->dev,
508 sizeof(*dump_regions) * num_dump_regions,
509 GFP_KERNEL);
510 if (!dump_regions)
511 return -ENOMEM;
512
513 for (i = 0; i < num_dump_regions; i++) {
514 res = platform_get_resource(pdev, IORESOURCE_MEM, i);
515 dump_regions[i].res = res;
516 dump_regions[i].va = devm_ioremap(&pdev->dev, res->start,
517 resource_size(res));
518 if (!dump_regions[i].va)
519 return -ENOMEM;
520 }
521
522 return 0;
523}
524
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800525static int msm_cache_erp_probe(struct platform_device *pdev)
526{
527 struct resource *r;
528 int ret, cpu;
529
530 r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "l1_irq");
531
532 if (!r) {
533 pr_err("Could not get L1 resource\n");
534 ret = -ENODEV;
535 goto fail;
536 }
537
538 l1_erp_irq = r->start;
539
540 ret = request_percpu_irq(l1_erp_irq, msm_l1_erp_irq, "MSM_L1",
541 &msm_l1_erp_stats);
542
543 if (ret) {
544 pr_err("Failed to request the L1 cache error interrupt\n");
545 goto fail;
546 }
547
548 r = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "l2_irq");
549
550 if (!r) {
551 pr_err("Could not get L2 resource\n");
552 ret = -ENODEV;
553 goto fail_l1;
554 }
555
556 l2_erp_irq = r->start;
557 ret = request_irq(l2_erp_irq, msm_l2_erp_irq, 0, "MSM_L2", NULL);
558
559 if (ret) {
560 pr_err("Failed to request the L2 cache error interrupt\n");
561 goto fail_l1;
562 }
563
564 procfs_entry = create_proc_entry("cpu/msm_cache_erp", S_IRUGO, NULL);
565
566 if (!procfs_entry) {
567 pr_err("Failed to create procfs node for cache error reporting\n");
568 ret = -ENODEV;
569 goto fail_l2;
570 }
571
Stepan Moskovchenkod383c0e2013-04-15 17:51:15 -0700572 ret = msm_erp_read_dump_regions(pdev);
573
574 if (ret)
575 goto fail_l2;
576
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800577 get_online_cpus();
578 register_hotcpu_notifier(&cache_erp_cpu_notifier);
579 for_each_cpu(cpu, cpu_online_mask)
580 smp_call_function_single(cpu, enable_erp_irq_callback, NULL, 1);
581 put_online_cpus();
582
583 procfs_entry->read_proc = proc_read_status;
Stepan Moskovchenko7035f652012-05-31 17:48:01 -0700584
585 ret = procfs_event_log_init();
586 if (ret)
587 pr_err("Failed to create procfs node for ERP log access\n");
588
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800589 return 0;
590
591fail_l2:
592 free_irq(l2_erp_irq, NULL);
593fail_l1:
594 free_percpu_irq(l1_erp_irq, NULL);
595fail:
596 return ret;
597}
598
599static int msm_cache_erp_remove(struct platform_device *pdev)
600{
601 int cpu;
602 if (procfs_entry)
603 remove_proc_entry("cpu/msm_cache_erp", NULL);
604
605 get_online_cpus();
606 unregister_hotcpu_notifier(&cache_erp_cpu_notifier);
607 for_each_cpu(cpu, cpu_online_mask)
608 smp_call_function_single(cpu, disable_erp_irq_callback, NULL,
609 1);
610 put_online_cpus();
611
612 free_percpu_irq(l1_erp_irq, NULL);
613
614 disable_irq(l2_erp_irq);
615 free_irq(l2_erp_irq, NULL);
616 return 0;
617}
618
Stepan Moskovchenko72977ed2012-07-02 12:36:23 -0700619static struct of_device_id cache_erp_match_table[] = {
620 { .compatible = "qcom,cache_erp", },
621 {}
622};
623
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800624static struct platform_driver msm_cache_erp_driver = {
625 .probe = msm_cache_erp_probe,
626 .remove = msm_cache_erp_remove,
627 .driver = {
628 .name = MODULE_NAME,
629 .owner = THIS_MODULE,
Stepan Moskovchenko72977ed2012-07-02 12:36:23 -0700630 .of_match_table = cache_erp_match_table,
Stepan Moskovchenko07552e12012-02-29 20:09:32 -0800631 },
632};
633
634static int __init msm_cache_erp_init(void)
635{
636 return platform_driver_register(&msm_cache_erp_driver);
637}
638
639static void __exit msm_cache_erp_exit(void)
640{
641 platform_driver_unregister(&msm_cache_erp_driver);
642}
643
644
645module_init(msm_cache_erp_init);
646module_exit(msm_cache_erp_exit);
647MODULE_LICENSE("GPL v2");
648MODULE_DESCRIPTION("MSM cache error reporting driver");