1 // SPDX-License-Identifier: GPL-2.0 2 #include <linux/init.h> 3 #include <linux/io.h> 4 #include <linux/mm.h> 5 6 #include <asm/processor-cyrix.h> 7 #include <asm/processor-flags.h> 8 #include <asm/mtrr.h> 9 #include <asm/msr.h> 10 11 #include "mtrr.h" 12 13 static void 14 cyrix_get_arr(unsigned int reg, unsigned long *base, 15 unsigned long *size, mtrr_type * type) 16 { 17 unsigned char arr, ccr3, rcr, shift; 18 unsigned long flags; 19 20 arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ 21 22 local_irq_save(flags); 23 24 ccr3 = getCx86(CX86_CCR3); 25 setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ 26 ((unsigned char *)base)[3] = getCx86(arr); 27 ((unsigned char *)base)[2] = getCx86(arr + 1); 28 ((unsigned char *)base)[1] = getCx86(arr + 2); 29 rcr = getCx86(CX86_RCR_BASE + reg); 30 setCx86(CX86_CCR3, ccr3); /* disable MAPEN */ 31 32 local_irq_restore(flags); 33 34 shift = ((unsigned char *) base)[1] & 0x0f; 35 *base >>= PAGE_SHIFT; 36 37 /* 38 * Power of two, at least 4K on ARR0-ARR6, 256K on ARR7 39 * Note: shift==0xf means 4G, this is unsupported. 40 */ 41 if (shift) 42 *size = (reg < 7 ? 0x1UL : 0x40UL) << (shift - 1); 43 else 44 *size = 0; 45 46 /* Bit 0 is Cache Enable on ARR7, Cache Disable on ARR0-ARR6 */ 47 if (reg < 7) { 48 switch (rcr) { 49 case 1: 50 *type = MTRR_TYPE_UNCACHABLE; 51 break; 52 case 8: 53 *type = MTRR_TYPE_WRBACK; 54 break; 55 case 9: 56 *type = MTRR_TYPE_WRCOMB; 57 break; 58 case 24: 59 default: 60 *type = MTRR_TYPE_WRTHROUGH; 61 break; 62 } 63 } else { 64 switch (rcr) { 65 case 0: 66 *type = MTRR_TYPE_UNCACHABLE; 67 break; 68 case 8: 69 *type = MTRR_TYPE_WRCOMB; 70 break; 71 case 9: 72 *type = MTRR_TYPE_WRBACK; 73 break; 74 case 25: 75 default: 76 *type = MTRR_TYPE_WRTHROUGH; 77 break; 78 } 79 } 80 } 81 82 /* 83 * cyrix_get_free_region - get a free ARR. 84 * 85 * @base: the starting (base) address of the region. 86 * @size: the size (in bytes) of the region. 87 * 88 * Returns: the index of the region on success, else -1 on error. 89 */ 90 static int 91 cyrix_get_free_region(unsigned long base, unsigned long size, int replace_reg) 92 { 93 unsigned long lbase, lsize; 94 mtrr_type ltype; 95 int i; 96 97 switch (replace_reg) { 98 case 7: 99 if (size < 0x40) 100 break; 101 case 6: 102 case 5: 103 case 4: 104 return replace_reg; 105 case 3: 106 case 2: 107 case 1: 108 case 0: 109 return replace_reg; 110 } 111 /* If we are to set up a region >32M then look at ARR7 immediately */ 112 if (size > 0x2000) { 113 cyrix_get_arr(7, &lbase, &lsize, <ype); 114 if (lsize == 0) 115 return 7; 116 /* Else try ARR0-ARR6 first */ 117 } else { 118 for (i = 0; i < 7; i++) { 119 cyrix_get_arr(i, &lbase, &lsize, <ype); 120 if (lsize == 0) 121 return i; 122 } 123 /* 124 * ARR0-ARR6 isn't free 125 * try ARR7 but its size must be at least 256K 126 */ 127 cyrix_get_arr(i, &lbase, &lsize, <ype); 128 if ((lsize == 0) && (size >= 0x40)) 129 return i; 130 } 131 return -ENOSPC; 132 } 133 134 static u32 cr4, ccr3; 135 136 static void prepare_set(void) 137 { 138 u32 cr0; 139 140 /* Save value of CR4 and clear Page Global Enable (bit 7) */ 141 if (boot_cpu_has(X86_FEATURE_PGE)) { 142 cr4 = __read_cr4(); 143 __write_cr4(cr4 & ~X86_CR4_PGE); 144 } 145 146 /* 147 * Disable and flush caches. 148 * Note that wbinvd flushes the TLBs as a side-effect 149 */ 150 cr0 = read_cr0() | X86_CR0_CD; 151 wbinvd(); 152 write_cr0(cr0); 153 wbinvd(); 154 155 /* Cyrix ARRs - everything else was excluded at the top */ 156 ccr3 = getCx86(CX86_CCR3); 157 158 /* Cyrix ARRs - everything else was excluded at the top */ 159 setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); 160 } 161 162 static void post_set(void) 163 { 164 /* Flush caches and TLBs */ 165 wbinvd(); 166 167 /* Cyrix ARRs - everything else was excluded at the top */ 168 setCx86(CX86_CCR3, ccr3); 169 170 /* Enable caches */ 171 write_cr0(read_cr0() & ~X86_CR0_CD); 172 173 /* Restore value of CR4 */ 174 if (boot_cpu_has(X86_FEATURE_PGE)) 175 __write_cr4(cr4); 176 } 177 178 static void cyrix_set_arr(unsigned int reg, unsigned long base, 179 unsigned long size, mtrr_type type) 180 { 181 unsigned char arr, arr_type, arr_size; 182 183 arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ 184 185 /* count down from 32M (ARR0-ARR6) or from 2G (ARR7) */ 186 if (reg >= 7) 187 size >>= 6; 188 189 size &= 0x7fff; /* make sure arr_size <= 14 */ 190 for (arr_size = 0; size; arr_size++, size >>= 1) 191 ; 192 193 if (reg < 7) { 194 switch (type) { 195 case MTRR_TYPE_UNCACHABLE: 196 arr_type = 1; 197 break; 198 case MTRR_TYPE_WRCOMB: 199 arr_type = 9; 200 break; 201 case MTRR_TYPE_WRTHROUGH: 202 arr_type = 24; 203 break; 204 default: 205 arr_type = 8; 206 break; 207 } 208 } else { 209 switch (type) { 210 case MTRR_TYPE_UNCACHABLE: 211 arr_type = 0; 212 break; 213 case MTRR_TYPE_WRCOMB: 214 arr_type = 8; 215 break; 216 case MTRR_TYPE_WRTHROUGH: 217 arr_type = 25; 218 break; 219 default: 220 arr_type = 9; 221 break; 222 } 223 } 224 225 prepare_set(); 226 227 base <<= PAGE_SHIFT; 228 setCx86(arr + 0, ((unsigned char *)&base)[3]); 229 setCx86(arr + 1, ((unsigned char *)&base)[2]); 230 setCx86(arr + 2, (((unsigned char *)&base)[1]) | arr_size); 231 setCx86(CX86_RCR_BASE + reg, arr_type); 232 233 post_set(); 234 } 235 236 typedef struct { 237 unsigned long base; 238 unsigned long size; 239 mtrr_type type; 240 } arr_state_t; 241 242 static arr_state_t arr_state[8] = { 243 {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, 244 {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL} 245 }; 246 247 static unsigned char ccr_state[7] = { 0, 0, 0, 0, 0, 0, 0 }; 248 249 static void cyrix_set_all(void) 250 { 251 int i; 252 253 prepare_set(); 254 255 /* the CCRs are not contiguous */ 256 for (i = 0; i < 4; i++) 257 setCx86(CX86_CCR0 + i, ccr_state[i]); 258 for (; i < 7; i++) 259 setCx86(CX86_CCR4 + i, ccr_state[i]); 260 261 for (i = 0; i < 8; i++) { 262 cyrix_set_arr(i, arr_state[i].base, 263 arr_state[i].size, arr_state[i].type); 264 } 265 266 post_set(); 267 } 268 269 static const struct mtrr_ops cyrix_mtrr_ops = { 270 .vendor = X86_VENDOR_CYRIX, 271 .set_all = cyrix_set_all, 272 .set = cyrix_set_arr, 273 .get = cyrix_get_arr, 274 .get_free_region = cyrix_get_free_region, 275 .validate_add_page = generic_validate_add_page, 276 .have_wrcomb = positive_have_wrcomb, 277 }; 278 279 int __init cyrix_init_mtrr(void) 280 { 281 set_mtrr_ops(&cyrix_mtrr_ops); 282 return 0; 283 } 284