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