diff options
author | David Daney <david.daney@cavium.com> | 2012-11-15 16:58:59 -0500 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2012-12-13 12:15:26 -0500 |
commit | e1ced09797776dfd4a2a7b04b9ee7e97ab1e64be (patch) | |
tree | 473934ca424e0e10f235bcd9ae97781349af5495 /drivers/edac/octeon_edac-pc.c | |
parent | abe105a4d8c5ee2aa2acef33c5d163e5d187598f (diff) |
MIPS/EDAC: Improve OCTEON EDAC support.
Some initialization errors are reported with the existing OCTEON EDAC
support patch. Also some parts have more than one memory controller.
Fix the errors and add multiple controllers if present.
Signed-off-by: David Daney <david.daney@cavium.com>
Diffstat (limited to 'drivers/edac/octeon_edac-pc.c')
-rw-r--r-- | drivers/edac/octeon_edac-pc.c | 137 |
1 files changed, 70 insertions, 67 deletions
diff --git a/drivers/edac/octeon_edac-pc.c b/drivers/edac/octeon_edac-pc.c index 9d13061744e4..14a5e57f2b32 100644 --- a/drivers/edac/octeon_edac-pc.c +++ b/drivers/edac/octeon_edac-pc.c | |||
@@ -3,6 +3,8 @@ | |||
3 | * License. See the file "COPYING" in the main directory of this archive | 3 | * License. See the file "COPYING" in the main directory of this archive |
4 | * for more details. | 4 | * for more details. |
5 | * | 5 | * |
6 | * Copyright (C) 2012 Cavium, Inc. | ||
7 | * | ||
6 | * Copyright (C) 2009 Wind River Systems, | 8 | * Copyright (C) 2009 Wind River Systems, |
7 | * written by Ralf Baechle <ralf@linux-mips.org> | 9 | * written by Ralf Baechle <ralf@linux-mips.org> |
8 | */ | 10 | */ |
@@ -19,93 +21,112 @@ | |||
19 | #include <asm/octeon/cvmx.h> | 21 | #include <asm/octeon/cvmx.h> |
20 | #include <asm/mipsregs.h> | 22 | #include <asm/mipsregs.h> |
21 | 23 | ||
22 | #define EDAC_MOD_STR "octeon" | ||
23 | |||
24 | extern int register_co_cache_error_notifier(struct notifier_block *nb); | 24 | extern int register_co_cache_error_notifier(struct notifier_block *nb); |
25 | extern int unregister_co_cache_error_notifier(struct notifier_block *nb); | 25 | extern int unregister_co_cache_error_notifier(struct notifier_block *nb); |
26 | 26 | ||
27 | extern unsigned long long cache_err_dcache[NR_CPUS]; | 27 | extern unsigned long long cache_err_dcache[NR_CPUS]; |
28 | 28 | ||
29 | static struct edac_device_ctl_info *ed_cavium; | 29 | struct co_cache_error { |
30 | struct notifier_block notifier; | ||
31 | struct edac_device_ctl_info *ed; | ||
32 | }; | ||
30 | 33 | ||
31 | /* | 34 | /** |
32 | * EDAC CPU cache error callback | 35 | * EDAC CPU cache error callback |
33 | * | 36 | * |
37 | * @event: non-zero if unrecoverable. | ||
34 | */ | 38 | */ |
35 | |||
36 | static int co_cache_error_event(struct notifier_block *this, | 39 | static int co_cache_error_event(struct notifier_block *this, |
37 | unsigned long event, void *ptr) | 40 | unsigned long event, void *ptr) |
38 | { | 41 | { |
42 | struct co_cache_error *p = container_of(this, struct co_cache_error, | ||
43 | notifier); | ||
44 | |||
39 | unsigned int core = cvmx_get_core_num(); | 45 | unsigned int core = cvmx_get_core_num(); |
40 | unsigned int cpu = smp_processor_id(); | 46 | unsigned int cpu = smp_processor_id(); |
41 | uint64_t icache_err = read_octeon_c0_icacheerr(); | 47 | u64 icache_err = read_octeon_c0_icacheerr(); |
42 | struct edac_device_ctl_info *ed = ed_cavium; | 48 | u64 dcache_err; |
43 | 49 | ||
44 | edac_device_printk(ed, KERN_ERR, | 50 | if (event) { |
45 | "Cache error exception on core %d / processor %d:\n", | 51 | dcache_err = cache_err_dcache[core]; |
46 | core, cpu); | 52 | cache_err_dcache[core] = 0; |
47 | edac_device_printk(ed, KERN_ERR, | 53 | } else { |
48 | "cp0_errorepc == %lx\n", read_c0_errorepc()); | 54 | dcache_err = read_octeon_c0_dcacheerr(); |
55 | } | ||
56 | |||
49 | if (icache_err & 1) { | 57 | if (icache_err & 1) { |
50 | edac_device_printk(ed, KERN_ERR, "CacheErr (Icache) == %llx\n", | 58 | edac_device_printk(p->ed, KERN_ERR, |
51 | (unsigned long long)icache_err); | 59 | "CacheErr (Icache):%llx, core %d/cpu %d, cp0_errorepc == %lx\n", |
60 | (unsigned long long)icache_err, core, cpu, | ||
61 | read_c0_errorepc()); | ||
52 | write_octeon_c0_icacheerr(0); | 62 | write_octeon_c0_icacheerr(0); |
53 | edac_device_handle_ce(ed, 0, 0, ed->ctl_name); | 63 | edac_device_handle_ce(p->ed, cpu, 1, "icache"); |
54 | } | 64 | } |
55 | if (cache_err_dcache[core] & 1) { | 65 | if (dcache_err & 1) { |
56 | edac_device_printk(ed, KERN_ERR, "CacheErr (Dcache) == %llx\n", | 66 | edac_device_printk(p->ed, KERN_ERR, |
57 | (unsigned long long)cache_err_dcache[core]); | 67 | "CacheErr (Dcache):%llx, core %d/cpu %d, cp0_errorepc == %lx\n", |
58 | cache_err_dcache[core] = 0; | 68 | (unsigned long long)dcache_err, core, cpu, |
59 | edac_device_handle_ue(ed, 0, 0, ed->ctl_name); | 69 | read_c0_errorepc()); |
70 | if (event) | ||
71 | edac_device_handle_ue(p->ed, cpu, 0, "dcache"); | ||
72 | else | ||
73 | edac_device_handle_ce(p->ed, cpu, 0, "dcache"); | ||
74 | |||
75 | /* Clear the error indication */ | ||
76 | if (OCTEON_IS_MODEL(OCTEON_FAM_2)) | ||
77 | write_octeon_c0_dcacheerr(1); | ||
78 | else | ||
79 | write_octeon_c0_dcacheerr(0); | ||
60 | } | 80 | } |
61 | 81 | ||
62 | return NOTIFY_DONE; | 82 | return NOTIFY_STOP; |
63 | } | 83 | } |
64 | 84 | ||
65 | static struct notifier_block co_cache_error_notifier = { | ||
66 | .notifier_call = co_cache_error_event, | ||
67 | }; | ||
68 | |||
69 | static int __devinit co_cache_error_probe(struct platform_device *pdev) | 85 | static int __devinit co_cache_error_probe(struct platform_device *pdev) |
70 | { | 86 | { |
71 | struct edac_device_ctl_info *ed; | 87 | struct co_cache_error *p = devm_kzalloc(&pdev->dev, sizeof(*p), |
72 | int res = 0; | 88 | GFP_KERNEL); |
89 | if (!p) | ||
90 | return -ENOMEM; | ||
91 | |||
92 | p->notifier.notifier_call = co_cache_error_event; | ||
93 | platform_set_drvdata(pdev, p); | ||
94 | |||
95 | p->ed = edac_device_alloc_ctl_info(0, "cpu", num_possible_cpus(), | ||
96 | "cache", 2, 0, NULL, 0, | ||
97 | edac_device_alloc_index()); | ||
98 | if (!p->ed) | ||
99 | goto err; | ||
73 | 100 | ||
74 | ed = edac_device_alloc_ctl_info(0, "cpu", 1, NULL, 0, 0, NULL, 0, | 101 | p->ed->dev = &pdev->dev; |
75 | edac_device_alloc_index()); | ||
76 | 102 | ||
77 | ed->dev = &pdev->dev; | 103 | p->ed->dev_name = dev_name(&pdev->dev); |
78 | platform_set_drvdata(pdev, ed); | ||
79 | ed->dev_name = dev_name(&pdev->dev); | ||
80 | 104 | ||
81 | ed->mod_name = "octeon-cpu"; | 105 | p->ed->mod_name = "octeon-cpu"; |
82 | ed->ctl_name = "co_cpu_err"; | 106 | p->ed->ctl_name = "cache"; |
83 | 107 | ||
84 | if (edac_device_add_device(ed) > 0) { | 108 | if (edac_device_add_device(p->ed)) { |
85 | pr_err("%s: edac_device_add_device() failed\n", __func__); | 109 | pr_err("%s: edac_device_add_device() failed\n", __func__); |
86 | goto err; | 110 | goto err1; |
87 | } | 111 | } |
88 | 112 | ||
89 | register_co_cache_error_notifier(&co_cache_error_notifier); | 113 | register_co_cache_error_notifier(&p->notifier); |
90 | ed_cavium = ed; | ||
91 | 114 | ||
92 | return 0; | 115 | return 0; |
93 | 116 | ||
117 | err1: | ||
118 | edac_device_free_ctl_info(p->ed); | ||
94 | err: | 119 | err: |
95 | edac_device_free_ctl_info(ed); | 120 | return -ENXIO; |
96 | |||
97 | return res; | ||
98 | } | 121 | } |
99 | 122 | ||
100 | static int co_cache_error_remove(struct platform_device *pdev) | 123 | static int co_cache_error_remove(struct platform_device *pdev) |
101 | { | 124 | { |
102 | struct edac_device_ctl_info *ed = platform_get_drvdata(pdev); | 125 | struct co_cache_error *p = platform_get_drvdata(pdev); |
103 | 126 | ||
104 | unregister_co_cache_error_notifier(&co_cache_error_notifier); | 127 | unregister_co_cache_error_notifier(&p->notifier); |
105 | ed_cavium = NULL; | ||
106 | edac_device_del_device(&pdev->dev); | 128 | edac_device_del_device(&pdev->dev); |
107 | edac_device_free_ctl_info(ed); | 129 | edac_device_free_ctl_info(p->ed); |
108 | |||
109 | return 0; | 130 | return 0; |
110 | } | 131 | } |
111 | 132 | ||
@@ -113,28 +134,10 @@ static struct platform_driver co_cache_error_driver = { | |||
113 | .probe = co_cache_error_probe, | 134 | .probe = co_cache_error_probe, |
114 | .remove = co_cache_error_remove, | 135 | .remove = co_cache_error_remove, |
115 | .driver = { | 136 | .driver = { |
116 | .name = "co_pc_edac", | 137 | .name = "octeon_pc_edac", |
117 | } | 138 | } |
118 | }; | 139 | }; |
119 | 140 | module_platform_driver(co_cache_error_driver); | |
120 | static int __init co_edac_init(void) | ||
121 | { | ||
122 | int ret; | ||
123 | |||
124 | ret = platform_driver_register(&co_cache_error_driver); | ||
125 | if (ret) | ||
126 | pr_warning(EDAC_MOD_STR "CPU err failed to register\n"); | ||
127 | |||
128 | return ret; | ||
129 | } | ||
130 | |||
131 | static void __exit co_edac_exit(void) | ||
132 | { | ||
133 | platform_driver_unregister(&co_cache_error_driver); | ||
134 | } | ||
135 | |||
136 | module_init(co_edac_init); | ||
137 | module_exit(co_edac_exit); | ||
138 | 141 | ||
139 | MODULE_LICENSE("GPL"); | 142 | MODULE_LICENSE("GPL"); |
140 | MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>"); | 143 | MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>"); |