Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2001,2002,2003 Broadcom Corporation |
| 3 | * |
| 4 | * This program is free software; you can redistribute it and/or |
| 5 | * modify it under the terms of the GNU General Public License |
| 6 | * as published by the Free Software Foundation; either version 2 |
| 7 | * of the License, or (at your option) any later version. |
| 8 | * |
| 9 | * This program is distributed in the hope that it will be useful, |
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | * GNU General Public License for more details. |
| 13 | * |
| 14 | * You should have received a copy of the GNU General Public License |
| 15 | * along with this program; if not, write to the Free Software |
| 16 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 17 | */ |
| 18 | #include <linux/init.h> |
| 19 | |
| 20 | #include <asm/asm.h> |
| 21 | #include <asm/regdef.h> |
| 22 | #include <asm/mipsregs.h> |
| 23 | #include <asm/stackframe.h> |
| 24 | #include <asm/cacheops.h> |
| 25 | #include <asm/sibyte/board.h> |
| 26 | |
| 27 | #define C0_ERRCTL $26 /* CP0: Error info */ |
| 28 | #define C0_CERR_I $27 /* CP0: Icache error */ |
| 29 | #define C0_CERR_D $27,1 /* CP0: Dcache error */ |
| 30 | |
| 31 | /* |
| 32 | * Based on SiByte sample software cache-err/cerr.S |
| 33 | * CVS revision 1.8. Only the 'unrecoverable' case |
| 34 | * is changed. |
| 35 | */ |
| 36 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 37 | .set mips64 |
| 38 | .set noreorder |
| 39 | .set noat |
| 40 | |
| 41 | /* |
| 42 | * sb1_cerr_vec: code to be copied to the Cache Error |
| 43 | * Exception vector. The code must be pushed out to memory |
| 44 | * (either by copying to Kseg0 and Kseg1 both, or by flushing |
| 45 | * the L1 and L2) since it is fetched as 0xa0000100. |
| 46 | * |
| 47 | * NOTE: Be sure this handler is at most 28 instructions long |
| 48 | * since the final 16 bytes of the exception vector memory |
| 49 | * (0x170-0x17f) are used to preserve k0, k1, and ra. |
| 50 | */ |
| 51 | |
Ralf Baechle | 234fcd1 | 2008-03-08 09:56:28 +0000 | [diff] [blame] | 52 | __CPUINIT |
| 53 | |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 54 | LEAF(except_vec2_sb1) |
| 55 | /* |
| 56 | * If this error is recoverable, we need to exit the handler |
| 57 | * without having dirtied any registers. To do this, |
| 58 | * save/restore k0 and k1 from low memory (Useg is direct |
| 59 | * mapped while ERL=1). Note that we can't save to a |
| 60 | * CPU-specific location without ruining a register in the |
| 61 | * process. This means we are vulnerable to data corruption |
| 62 | * whenever the handler is reentered by a second CPU. |
| 63 | */ |
| 64 | sd k0,0x170($0) |
| 65 | sd k1,0x178($0) |
| 66 | |
Ralf Baechle | 51939fb | 2005-11-10 16:35:03 +0000 | [diff] [blame] | 67 | #ifdef CONFIG_SB1_CEX_ALWAYS_FATAL |
Andrew Isaacson | a4b5bd9 | 2005-10-19 23:57:40 -0700 | [diff] [blame] | 68 | j handle_vec2_sb1 |
| 69 | nop |
| 70 | #else |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 71 | /* |
| 72 | * M_ERRCTL_RECOVERABLE is bit 31, which makes it easy to tell |
| 73 | * if we can fast-path out of here for a h/w-recovered error. |
| 74 | */ |
| 75 | mfc0 k1,C0_ERRCTL |
| 76 | bgtz k1,attempt_recovery |
| 77 | sll k0,k1,1 |
| 78 | |
| 79 | recovered_dcache: |
| 80 | /* |
| 81 | * Unlock CacheErr-D (which in turn unlocks CacheErr-DPA). |
Lucas De Marchi | 25985ed | 2011-03-30 22:57:33 -0300 | [diff] [blame] | 82 | * Ought to log the occurrence of this recovered dcache error. |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 83 | */ |
| 84 | b recovered |
| 85 | mtc0 $0,C0_CERR_D |
| 86 | |
| 87 | attempt_recovery: |
| 88 | /* |
| 89 | * k0 has C0_ERRCTL << 1, which puts 'DC' at bit 31. Any |
| 90 | * Dcache errors we can recover from will take more extensive |
| 91 | * processing. For now, they are considered "unrecoverable". |
| 92 | * Note that 'DC' becoming set (outside of ERL mode) will |
| 93 | * cause 'IC' to clear; so if there's an Icache error, we'll |
| 94 | * only find out about it if we recover from this error and |
| 95 | * continue executing. |
| 96 | */ |
| 97 | bltz k0,unrecoverable |
| 98 | sll k0,1 |
| 99 | |
| 100 | /* |
| 101 | * k0 has C0_ERRCTL << 2, which puts 'IC' at bit 31. If an |
| 102 | * Icache error isn't indicated, I'm not sure why we got here. |
| 103 | * Consider that case "unrecoverable" for now. |
| 104 | */ |
| 105 | bgez k0,unrecoverable |
| 106 | |
| 107 | attempt_icache_recovery: |
| 108 | /* |
| 109 | * External icache errors are due to uncorrectable ECC errors |
| 110 | * in the L2 cache or Memory Controller and cannot be |
| 111 | * recovered here. |
| 112 | */ |
| 113 | mfc0 k0,C0_CERR_I /* delay slot */ |
| 114 | li k1,1 << 26 /* ICACHE_EXTERNAL */ |
| 115 | and k1,k0 |
| 116 | bnez k1,unrecoverable |
| 117 | andi k0,0x1fe0 |
| 118 | |
| 119 | /* |
| 120 | * Since the error is internal, the 'IDX' field from |
| 121 | * CacheErr-I is valid and we can just invalidate all blocks |
| 122 | * in that set. |
| 123 | */ |
| 124 | cache Index_Invalidate_I,(0<<13)(k0) |
| 125 | cache Index_Invalidate_I,(1<<13)(k0) |
| 126 | cache Index_Invalidate_I,(2<<13)(k0) |
| 127 | cache Index_Invalidate_I,(3<<13)(k0) |
| 128 | |
| 129 | /* Ought to log this recovered icache error */ |
| 130 | |
| 131 | recovered: |
| 132 | /* Restore the saved registers */ |
| 133 | ld k0,0x170($0) |
| 134 | ld k1,0x178($0) |
| 135 | eret |
| 136 | |
| 137 | unrecoverable: |
| 138 | /* Unrecoverable Icache or Dcache error; log it and/or fail */ |
| 139 | j handle_vec2_sb1 |
| 140 | nop |
Andrew Isaacson | a4b5bd9 | 2005-10-19 23:57:40 -0700 | [diff] [blame] | 141 | #endif |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 142 | |
| 143 | END(except_vec2_sb1) |
| 144 | |
| 145 | __FINIT |
| 146 | |
| 147 | LEAF(handle_vec2_sb1) |
| 148 | mfc0 k0,CP0_CONFIG |
| 149 | li k1,~CONF_CM_CMASK |
| 150 | and k0,k0,k1 |
| 151 | ori k0,k0,CONF_CM_UNCACHED |
| 152 | mtc0 k0,CP0_CONFIG |
| 153 | |
| 154 | SSNOP |
| 155 | SSNOP |
| 156 | SSNOP |
| 157 | SSNOP |
| 158 | bnezl $0, 1f |
| 159 | 1: |
| 160 | mfc0 k0, CP0_STATUS |
| 161 | sll k0, k0, 3 # check CU0 (kernel?) |
| 162 | bltz k0, 2f |
| 163 | nop |
| 164 | |
| 165 | /* Get a valid Kseg0 stack pointer. Any task's stack pointer |
| 166 | * will do, although if we ever want to resume execution we |
| 167 | * better not have corrupted any state. */ |
| 168 | get_saved_sp |
| 169 | move sp, k1 |
| 170 | |
| 171 | 2: |
| 172 | j sb1_cache_error |
| 173 | nop |
| 174 | |
| 175 | END(handle_vec2_sb1) |