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 fallthrough; 102 case 6: 103 case 5: 104 case 4: 105 return replace_reg; 106 case 3: 107 case 2: 108 case 1: 109 case 0: 110 return replace_reg; 111 } 112 /* If we are to set up a region >32M then look at ARR7 immediately */ 113 if (size > 0x2000) { 114 cyrix_get_arr(7, &lbase, &lsize, <ype); 115 if (lsize == 0) 116 return 7; 117 /* Else try ARR0-ARR6 first */ 118 } else { 119 for (i = 0; i < 7; i++) { 120 cyrix_get_arr(i, &lbase, &lsize, <ype); 121 if (lsize == 0) 122 return i; 123 } 124 /* 125 * ARR0-ARR6 isn't free 126 * try ARR7 but its size must be at least 256K 127 */ 128 cyrix_get_arr(i, &lbase, &lsize, <ype); 129 if ((lsize == 0) && (size >= 0x40)) 130 return i; 131 } 132 return -ENOSPC; 133 } 134 135 static u32 cr4, ccr3; 136 137 static void prepare_set(void) 138 { 139 u32 cr0; 140 141 /* Save value of CR4 and clear Page Global Enable (bit 7) */ 142 if (boot_cpu_has(X86_FEATURE_PGE)) { 143 cr4 = __read_cr4(); 144 __write_cr4(cr4 & ~X86_CR4_PGE); 145 } 146 147 /* 148 * Disable and flush caches. 149 * Note that wbinvd flushes the TLBs as a side-effect 150 */ 151 cr0 = read_cr0() | X86_CR0_CD; 152 wbinvd(); 153 write_cr0(cr0); 154 wbinvd(); 155 156 /* Cyrix ARRs - everything else was excluded at the top */ 157 ccr3 = getCx86(CX86_CCR3); 158 159 /* Cyrix ARRs - everything else was excluded at the top */ 160 setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); 161 } 162 163 static void post_set(void) 164 { 165 /* Flush caches and TLBs */ 166 wbinvd(); 167 168 /* Cyrix ARRs - everything else was excluded at the top */ 169 setCx86(CX86_CCR3, ccr3); 170 171 /* Enable caches */ 172 write_cr0(read_cr0() & ~X86_CR0_CD); 173 174 /* Restore value of CR4 */ 175 if (boot_cpu_has(X86_FEATURE_PGE)) 176 __write_cr4(cr4); 177 } 178 179 static void cyrix_set_arr(unsigned int reg, unsigned long base, 180 unsigned long size, mtrr_type type) 181 { 182 unsigned char arr, arr_type, arr_size; 183 184 arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ 185 186 /* count down from 32M (ARR0-ARR6) or from 2G (ARR7) */ 187 if (reg >= 7) 188 size >>= 6; 189 190 size &= 0x7fff; /* make sure arr_size <= 14 */ 191 for (arr_size = 0; size; arr_size++, size >>= 1) 192 ; 193 194 if (reg < 7) { 195 switch (type) { 196 case MTRR_TYPE_UNCACHABLE: 197 arr_type = 1; 198 break; 199 case MTRR_TYPE_WRCOMB: 200 arr_type = 9; 201 break; 202 case MTRR_TYPE_WRTHROUGH: 203 arr_type = 24; 204 break; 205 default: 206 arr_type = 8; 207 break; 208 } 209 } else { 210 switch (type) { 211 case MTRR_TYPE_UNCACHABLE: 212 arr_type = 0; 213 break; 214 case MTRR_TYPE_WRCOMB: 215 arr_type = 8; 216 break; 217 case MTRR_TYPE_WRTHROUGH: 218 arr_type = 25; 219 break; 220 default: 221 arr_type = 9; 222 break; 223 } 224 } 225 226 prepare_set(); 227 228 base <<= PAGE_SHIFT; 229 setCx86(arr + 0, ((unsigned char *)&base)[3]); 230 setCx86(arr + 1, ((unsigned char *)&base)[2]); 231 setCx86(arr + 2, (((unsigned char *)&base)[1]) | arr_size); 232 setCx86(CX86_RCR_BASE + reg, arr_type); 233 234 post_set(); 235 } 236 237 const struct mtrr_ops cyrix_mtrr_ops = { 238 .var_regs = 8, 239 .set = cyrix_set_arr, 240 .get = cyrix_get_arr, 241 .get_free_region = cyrix_get_free_region, 242 .validate_add_page = generic_validate_add_page, 243 .have_wrcomb = positive_have_wrcomb, 244 }; 245