diff options
author | Ralf Baechle <ralf@linux-mips.org> | 2012-10-16 18:39:09 -0400 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2012-12-12 10:48:49 -0500 |
commit | f65aad41772f6a0022e9763fe06f47604449964c (patch) | |
tree | 701d6ea74ac0c41f008c5554d876945fe4caf512 /arch/mips/mm | |
parent | aa1762f49c81a14d0453e4f67f922e4f155510a3 (diff) |
MIPS: Cavium: Add EDAC support.
Drivers for EDAC on Cavium. Supported subsystems are:
o CPU primary caches. These are parity protected only, so only error
reporting.
o Second level cache - ECC protected, provides SECDED.
o Memory: ECC / SECDEC if used with suitable DRAM modules. The driver will
will only initialize if ECC is enabled on a system so is safe to run on
non-ECC memory.
o PCI: Parity error reporting
Since it is very hard to test this sort of code the implementation is very
conservative and uses polling where possible for now.
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Reviewed-by: Borislav Petkov <borislav.petkov@amd.com>
Diffstat (limited to 'arch/mips/mm')
-rw-r--r-- | arch/mips/mm/c-octeon.c | 46 |
1 files changed, 24 insertions, 22 deletions
diff --git a/arch/mips/mm/c-octeon.c b/arch/mips/mm/c-octeon.c index 44e69e7a4519..9f67553762d5 100644 --- a/arch/mips/mm/c-octeon.c +++ b/arch/mips/mm/c-octeon.c | |||
@@ -5,6 +5,7 @@ | |||
5 | * | 5 | * |
6 | * Copyright (C) 2005-2007 Cavium Networks | 6 | * Copyright (C) 2005-2007 Cavium Networks |
7 | */ | 7 | */ |
8 | #include <linux/export.h> | ||
8 | #include <linux/init.h> | 9 | #include <linux/init.h> |
9 | #include <linux/kernel.h> | 10 | #include <linux/kernel.h> |
10 | #include <linux/sched.h> | 11 | #include <linux/sched.h> |
@@ -28,6 +29,7 @@ | |||
28 | #include <asm/octeon/octeon.h> | 29 | #include <asm/octeon/octeon.h> |
29 | 30 | ||
30 | unsigned long long cache_err_dcache[NR_CPUS]; | 31 | unsigned long long cache_err_dcache[NR_CPUS]; |
32 | EXPORT_SYMBOL_GPL(cache_err_dcache); | ||
31 | 33 | ||
32 | /** | 34 | /** |
33 | * Octeon automatically flushes the dcache on tlb changes, so | 35 | * Octeon automatically flushes the dcache on tlb changes, so |
@@ -288,42 +290,42 @@ void __cpuinit octeon_cache_init(void) | |||
288 | * Handle a cache error exception | 290 | * Handle a cache error exception |
289 | */ | 291 | */ |
290 | 292 | ||
291 | static void cache_parity_error_octeon(int non_recoverable) | 293 | static RAW_NOTIFIER_HEAD(co_cache_error_chain); |
294 | |||
295 | int register_co_cache_error_notifier(struct notifier_block *nb) | ||
292 | { | 296 | { |
293 | unsigned long coreid = cvmx_get_core_num(); | 297 | return raw_notifier_chain_register(&co_cache_error_chain, nb); |
294 | uint64_t icache_err = read_octeon_c0_icacheerr(); | 298 | } |
295 | 299 | EXPORT_SYMBOL_GPL(register_co_cache_error_notifier); | |
296 | pr_err("Cache error exception:\n"); | ||
297 | pr_err("cp0_errorepc == %lx\n", read_c0_errorepc()); | ||
298 | if (icache_err & 1) { | ||
299 | pr_err("CacheErr (Icache) == %llx\n", | ||
300 | (unsigned long long)icache_err); | ||
301 | write_octeon_c0_icacheerr(0); | ||
302 | } | ||
303 | if (cache_err_dcache[coreid] & 1) { | ||
304 | pr_err("CacheErr (Dcache) == %llx\n", | ||
305 | (unsigned long long)cache_err_dcache[coreid]); | ||
306 | cache_err_dcache[coreid] = 0; | ||
307 | } | ||
308 | 300 | ||
309 | if (non_recoverable) | 301 | int unregister_co_cache_error_notifier(struct notifier_block *nb) |
310 | panic("Can't handle cache error: nested exception"); | 302 | { |
303 | return raw_notifier_chain_unregister(&co_cache_error_chain, nb); | ||
304 | } | ||
305 | EXPORT_SYMBOL_GPL(unregister_co_cache_error_notifier); | ||
306 | |||
307 | static inline int co_cache_error_call_notifiers(unsigned long val) | ||
308 | { | ||
309 | return raw_notifier_call_chain(&co_cache_error_chain, val, NULL); | ||
311 | } | 310 | } |
312 | 311 | ||
313 | /** | 312 | /** |
314 | * Called when the the exception is recoverable | 313 | * Called when the the exception is recoverable |
315 | */ | 314 | */ |
316 | |||
317 | asmlinkage void cache_parity_error_octeon_recoverable(void) | 315 | asmlinkage void cache_parity_error_octeon_recoverable(void) |
318 | { | 316 | { |
319 | cache_parity_error_octeon(0); | 317 | co_cache_error_call_notifiers(0); |
320 | } | 318 | } |
321 | 319 | ||
322 | /** | 320 | /** |
323 | * Called when the the exception is not recoverable | 321 | * Called when the the exception is not recoverable |
322 | * | ||
323 | * The issue not that the cache error exception itself was non-recoverable | ||
324 | * but that due to nesting of exception may have lost some state so can't | ||
325 | * continue. | ||
324 | */ | 326 | */ |
325 | |||
326 | asmlinkage void cache_parity_error_octeon_non_recoverable(void) | 327 | asmlinkage void cache_parity_error_octeon_non_recoverable(void) |
327 | { | 328 | { |
328 | cache_parity_error_octeon(1); | 329 | co_cache_error_call_notifiers(1); |
330 | panic("Can't handle cache error: nested exception"); | ||
329 | } | 331 | } |