xref: /OK3568_Linux_fs/kernel/drivers/edac/octeon_edac-pc.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  * This file is subject to the terms and conditions of the GNU General Public
3*4882a593Smuzhiyun  * License.  See the file "COPYING" in the main directory of this archive
4*4882a593Smuzhiyun  * for more details.
5*4882a593Smuzhiyun  *
6*4882a593Smuzhiyun  * Copyright (C) 2012 Cavium, Inc.
7*4882a593Smuzhiyun  *
8*4882a593Smuzhiyun  * Copyright (C) 2009 Wind River Systems,
9*4882a593Smuzhiyun  *   written by Ralf Baechle <ralf@linux-mips.org>
10*4882a593Smuzhiyun  */
11*4882a593Smuzhiyun #include <linux/module.h>
12*4882a593Smuzhiyun #include <linux/init.h>
13*4882a593Smuzhiyun #include <linux/slab.h>
14*4882a593Smuzhiyun #include <linux/interrupt.h>
15*4882a593Smuzhiyun #include <linux/io.h>
16*4882a593Smuzhiyun #include <linux/edac.h>
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include "edac_module.h"
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #include <asm/octeon/cvmx.h>
21*4882a593Smuzhiyun #include <asm/mipsregs.h>
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun extern int register_co_cache_error_notifier(struct notifier_block *nb);
24*4882a593Smuzhiyun extern int unregister_co_cache_error_notifier(struct notifier_block *nb);
25*4882a593Smuzhiyun 
26*4882a593Smuzhiyun extern unsigned long long cache_err_dcache[NR_CPUS];
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun struct co_cache_error {
29*4882a593Smuzhiyun 	struct notifier_block notifier;
30*4882a593Smuzhiyun 	struct edac_device_ctl_info *ed;
31*4882a593Smuzhiyun };
32*4882a593Smuzhiyun 
33*4882a593Smuzhiyun /**
34*4882a593Smuzhiyun  * EDAC CPU cache error callback
35*4882a593Smuzhiyun  *
36*4882a593Smuzhiyun  * @event: non-zero if unrecoverable.
37*4882a593Smuzhiyun  */
co_cache_error_event(struct notifier_block * this,unsigned long event,void * ptr)38*4882a593Smuzhiyun static int  co_cache_error_event(struct notifier_block *this,
39*4882a593Smuzhiyun 	unsigned long event, void *ptr)
40*4882a593Smuzhiyun {
41*4882a593Smuzhiyun 	struct co_cache_error *p = container_of(this, struct co_cache_error,
42*4882a593Smuzhiyun 						notifier);
43*4882a593Smuzhiyun 
44*4882a593Smuzhiyun 	unsigned int core = cvmx_get_core_num();
45*4882a593Smuzhiyun 	unsigned int cpu = smp_processor_id();
46*4882a593Smuzhiyun 	u64 icache_err = read_octeon_c0_icacheerr();
47*4882a593Smuzhiyun 	u64 dcache_err;
48*4882a593Smuzhiyun 
49*4882a593Smuzhiyun 	if (event) {
50*4882a593Smuzhiyun 		dcache_err = cache_err_dcache[core];
51*4882a593Smuzhiyun 		cache_err_dcache[core] = 0;
52*4882a593Smuzhiyun 	} else {
53*4882a593Smuzhiyun 		dcache_err = read_octeon_c0_dcacheerr();
54*4882a593Smuzhiyun 	}
55*4882a593Smuzhiyun 
56*4882a593Smuzhiyun 	if (icache_err & 1) {
57*4882a593Smuzhiyun 		edac_device_printk(p->ed, KERN_ERR,
58*4882a593Smuzhiyun 				   "CacheErr (Icache):%llx, core %d/cpu %d, cp0_errorepc == %lx\n",
59*4882a593Smuzhiyun 				   (unsigned long long)icache_err, core, cpu,
60*4882a593Smuzhiyun 				   read_c0_errorepc());
61*4882a593Smuzhiyun 		write_octeon_c0_icacheerr(0);
62*4882a593Smuzhiyun 		edac_device_handle_ce(p->ed, cpu, 1, "icache");
63*4882a593Smuzhiyun 	}
64*4882a593Smuzhiyun 	if (dcache_err & 1) {
65*4882a593Smuzhiyun 		edac_device_printk(p->ed, KERN_ERR,
66*4882a593Smuzhiyun 				   "CacheErr (Dcache):%llx, core %d/cpu %d, cp0_errorepc == %lx\n",
67*4882a593Smuzhiyun 				   (unsigned long long)dcache_err, core, cpu,
68*4882a593Smuzhiyun 				   read_c0_errorepc());
69*4882a593Smuzhiyun 		if (event)
70*4882a593Smuzhiyun 			edac_device_handle_ue(p->ed, cpu, 0, "dcache");
71*4882a593Smuzhiyun 		else
72*4882a593Smuzhiyun 			edac_device_handle_ce(p->ed, cpu, 0, "dcache");
73*4882a593Smuzhiyun 
74*4882a593Smuzhiyun 		/* Clear the error indication */
75*4882a593Smuzhiyun 		if (OCTEON_IS_OCTEON2())
76*4882a593Smuzhiyun 			write_octeon_c0_dcacheerr(1);
77*4882a593Smuzhiyun 		else
78*4882a593Smuzhiyun 			write_octeon_c0_dcacheerr(0);
79*4882a593Smuzhiyun 	}
80*4882a593Smuzhiyun 
81*4882a593Smuzhiyun 	return NOTIFY_STOP;
82*4882a593Smuzhiyun }
83*4882a593Smuzhiyun 
co_cache_error_probe(struct platform_device * pdev)84*4882a593Smuzhiyun static int co_cache_error_probe(struct platform_device *pdev)
85*4882a593Smuzhiyun {
86*4882a593Smuzhiyun 	struct co_cache_error *p = devm_kzalloc(&pdev->dev, sizeof(*p),
87*4882a593Smuzhiyun 						GFP_KERNEL);
88*4882a593Smuzhiyun 	if (!p)
89*4882a593Smuzhiyun 		return -ENOMEM;
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun 	p->notifier.notifier_call = co_cache_error_event;
92*4882a593Smuzhiyun 	platform_set_drvdata(pdev, p);
93*4882a593Smuzhiyun 
94*4882a593Smuzhiyun 	p->ed = edac_device_alloc_ctl_info(0, "cpu", num_possible_cpus(),
95*4882a593Smuzhiyun 					   "cache", 2, 0, NULL, 0,
96*4882a593Smuzhiyun 					   edac_device_alloc_index());
97*4882a593Smuzhiyun 	if (!p->ed)
98*4882a593Smuzhiyun 		goto err;
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	p->ed->dev = &pdev->dev;
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	p->ed->dev_name = dev_name(&pdev->dev);
103*4882a593Smuzhiyun 
104*4882a593Smuzhiyun 	p->ed->mod_name = "octeon-cpu";
105*4882a593Smuzhiyun 	p->ed->ctl_name = "cache";
106*4882a593Smuzhiyun 
107*4882a593Smuzhiyun 	if (edac_device_add_device(p->ed)) {
108*4882a593Smuzhiyun 		pr_err("%s: edac_device_add_device() failed\n", __func__);
109*4882a593Smuzhiyun 		goto err1;
110*4882a593Smuzhiyun 	}
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	register_co_cache_error_notifier(&p->notifier);
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	return 0;
115*4882a593Smuzhiyun 
116*4882a593Smuzhiyun err1:
117*4882a593Smuzhiyun 	edac_device_free_ctl_info(p->ed);
118*4882a593Smuzhiyun err:
119*4882a593Smuzhiyun 	return -ENXIO;
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun 
co_cache_error_remove(struct platform_device * pdev)122*4882a593Smuzhiyun static int co_cache_error_remove(struct platform_device *pdev)
123*4882a593Smuzhiyun {
124*4882a593Smuzhiyun 	struct co_cache_error *p = platform_get_drvdata(pdev);
125*4882a593Smuzhiyun 
126*4882a593Smuzhiyun 	unregister_co_cache_error_notifier(&p->notifier);
127*4882a593Smuzhiyun 	edac_device_del_device(&pdev->dev);
128*4882a593Smuzhiyun 	edac_device_free_ctl_info(p->ed);
129*4882a593Smuzhiyun 	return 0;
130*4882a593Smuzhiyun }
131*4882a593Smuzhiyun 
132*4882a593Smuzhiyun static struct platform_driver co_cache_error_driver = {
133*4882a593Smuzhiyun 	.probe = co_cache_error_probe,
134*4882a593Smuzhiyun 	.remove = co_cache_error_remove,
135*4882a593Smuzhiyun 	.driver = {
136*4882a593Smuzhiyun 		   .name = "octeon_pc_edac",
137*4882a593Smuzhiyun 	}
138*4882a593Smuzhiyun };
139*4882a593Smuzhiyun module_platform_driver(co_cache_error_driver);
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun MODULE_LICENSE("GPL");
142*4882a593Smuzhiyun MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
143