1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0 22ec1df41SThomas Gleixner #include <linux/init.h> 323110377SJaswinder Singh Rajput #include <linux/io.h> 42ec1df41SThomas Gleixner #include <linux/mm.h> 523110377SJaswinder Singh Rajput 62ec1df41SThomas Gleixner #include <asm/processor-cyrix.h> 77ebad705SDave Jones #include <asm/processor-flags.h> 823110377SJaswinder Singh Rajput #include <asm/mtrr.h> 923110377SJaswinder Singh Rajput #include <asm/msr.h> 1023110377SJaswinder Singh Rajput 112ec1df41SThomas Gleixner #include "mtrr.h" 122ec1df41SThomas Gleixner 132ec1df41SThomas Gleixner static void 142ec1df41SThomas Gleixner cyrix_get_arr(unsigned int reg, unsigned long *base, 152ec1df41SThomas Gleixner unsigned long *size, mtrr_type * type) 162ec1df41SThomas Gleixner { 172ec1df41SThomas Gleixner unsigned char arr, ccr3, rcr, shift; 1823110377SJaswinder Singh Rajput unsigned long flags; 192ec1df41SThomas Gleixner 202ec1df41SThomas Gleixner arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ 212ec1df41SThomas Gleixner 222ec1df41SThomas Gleixner local_irq_save(flags); 232ec1df41SThomas Gleixner 242ec1df41SThomas Gleixner ccr3 = getCx86(CX86_CCR3); 252ec1df41SThomas Gleixner setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ 262ec1df41SThomas Gleixner ((unsigned char *)base)[3] = getCx86(arr); 272ec1df41SThomas Gleixner ((unsigned char *)base)[2] = getCx86(arr + 1); 282ec1df41SThomas Gleixner ((unsigned char *)base)[1] = getCx86(arr + 2); 292ec1df41SThomas Gleixner rcr = getCx86(CX86_RCR_BASE + reg); 302ec1df41SThomas Gleixner setCx86(CX86_CCR3, ccr3); /* disable MAPEN */ 312ec1df41SThomas Gleixner 322ec1df41SThomas Gleixner local_irq_restore(flags); 3323110377SJaswinder Singh Rajput 342ec1df41SThomas Gleixner shift = ((unsigned char *) base)[1] & 0x0f; 352ec1df41SThomas Gleixner *base >>= PAGE_SHIFT; 362ec1df41SThomas Gleixner 3723110377SJaswinder Singh Rajput /* 3823110377SJaswinder Singh Rajput * Power of two, at least 4K on ARR0-ARR6, 256K on ARR7 392ec1df41SThomas Gleixner * Note: shift==0xf means 4G, this is unsupported. 402ec1df41SThomas Gleixner */ 412ec1df41SThomas Gleixner if (shift) 422ec1df41SThomas Gleixner *size = (reg < 7 ? 0x1UL : 0x40UL) << (shift - 1); 432ec1df41SThomas Gleixner else 442ec1df41SThomas Gleixner *size = 0; 452ec1df41SThomas Gleixner 462ec1df41SThomas Gleixner /* Bit 0 is Cache Enable on ARR7, Cache Disable on ARR0-ARR6 */ 472ec1df41SThomas Gleixner if (reg < 7) { 482ec1df41SThomas Gleixner switch (rcr) { 492ec1df41SThomas Gleixner case 1: 502ec1df41SThomas Gleixner *type = MTRR_TYPE_UNCACHABLE; 512ec1df41SThomas Gleixner break; 522ec1df41SThomas Gleixner case 8: 532ec1df41SThomas Gleixner *type = MTRR_TYPE_WRBACK; 542ec1df41SThomas Gleixner break; 552ec1df41SThomas Gleixner case 9: 562ec1df41SThomas Gleixner *type = MTRR_TYPE_WRCOMB; 572ec1df41SThomas Gleixner break; 582ec1df41SThomas Gleixner case 24: 592ec1df41SThomas Gleixner default: 602ec1df41SThomas Gleixner *type = MTRR_TYPE_WRTHROUGH; 612ec1df41SThomas Gleixner break; 622ec1df41SThomas Gleixner } 632ec1df41SThomas Gleixner } else { 642ec1df41SThomas Gleixner switch (rcr) { 652ec1df41SThomas Gleixner case 0: 662ec1df41SThomas Gleixner *type = MTRR_TYPE_UNCACHABLE; 672ec1df41SThomas Gleixner break; 682ec1df41SThomas Gleixner case 8: 692ec1df41SThomas Gleixner *type = MTRR_TYPE_WRCOMB; 702ec1df41SThomas Gleixner break; 712ec1df41SThomas Gleixner case 9: 722ec1df41SThomas Gleixner *type = MTRR_TYPE_WRBACK; 732ec1df41SThomas Gleixner break; 742ec1df41SThomas Gleixner case 25: 752ec1df41SThomas Gleixner default: 762ec1df41SThomas Gleixner *type = MTRR_TYPE_WRTHROUGH; 772ec1df41SThomas Gleixner break; 782ec1df41SThomas Gleixner } 792ec1df41SThomas Gleixner } 802ec1df41SThomas Gleixner } 812ec1df41SThomas Gleixner 8223110377SJaswinder Singh Rajput /* 8323110377SJaswinder Singh Rajput * cyrix_get_free_region - get a free ARR. 8423110377SJaswinder Singh Rajput * 8523110377SJaswinder Singh Rajput * @base: the starting (base) address of the region. 8623110377SJaswinder Singh Rajput * @size: the size (in bytes) of the region. 8723110377SJaswinder Singh Rajput * 8823110377SJaswinder Singh Rajput * Returns: the index of the region on success, else -1 on error. 8923110377SJaswinder Singh Rajput */ 902ec1df41SThomas Gleixner static int 912ec1df41SThomas Gleixner cyrix_get_free_region(unsigned long base, unsigned long size, int replace_reg) 922ec1df41SThomas Gleixner { 932ec1df41SThomas Gleixner unsigned long lbase, lsize; 9423110377SJaswinder Singh Rajput mtrr_type ltype; 9523110377SJaswinder Singh Rajput int i; 962ec1df41SThomas Gleixner 972ec1df41SThomas Gleixner switch (replace_reg) { 982ec1df41SThomas Gleixner case 7: 992ec1df41SThomas Gleixner if (size < 0x40) 1002ec1df41SThomas Gleixner break; 101*df561f66SGustavo A. R. Silva fallthrough; 1022ec1df41SThomas Gleixner case 6: 1032ec1df41SThomas Gleixner case 5: 1042ec1df41SThomas Gleixner case 4: 1052ec1df41SThomas Gleixner return replace_reg; 1062ec1df41SThomas Gleixner case 3: 1072ec1df41SThomas Gleixner case 2: 1082ec1df41SThomas Gleixner case 1: 1092ec1df41SThomas Gleixner case 0: 1102ec1df41SThomas Gleixner return replace_reg; 1112ec1df41SThomas Gleixner } 1122ec1df41SThomas Gleixner /* If we are to set up a region >32M then look at ARR7 immediately */ 1132ec1df41SThomas Gleixner if (size > 0x2000) { 1142ec1df41SThomas Gleixner cyrix_get_arr(7, &lbase, &lsize, <ype); 1152ec1df41SThomas Gleixner if (lsize == 0) 1162ec1df41SThomas Gleixner return 7; 1172ec1df41SThomas Gleixner /* Else try ARR0-ARR6 first */ 1182ec1df41SThomas Gleixner } else { 1192ec1df41SThomas Gleixner for (i = 0; i < 7; i++) { 1202ec1df41SThomas Gleixner cyrix_get_arr(i, &lbase, &lsize, <ype); 1212ec1df41SThomas Gleixner if (lsize == 0) 1222ec1df41SThomas Gleixner return i; 1232ec1df41SThomas Gleixner } 12423110377SJaswinder Singh Rajput /* 12523110377SJaswinder Singh Rajput * ARR0-ARR6 isn't free 12623110377SJaswinder Singh Rajput * try ARR7 but its size must be at least 256K 12723110377SJaswinder Singh Rajput */ 1282ec1df41SThomas Gleixner cyrix_get_arr(i, &lbase, &lsize, <ype); 1292ec1df41SThomas Gleixner if ((lsize == 0) && (size >= 0x40)) 1302ec1df41SThomas Gleixner return i; 1312ec1df41SThomas Gleixner } 1322ec1df41SThomas Gleixner return -ENOSPC; 1332ec1df41SThomas Gleixner } 1342ec1df41SThomas Gleixner 13523110377SJaswinder Singh Rajput static u32 cr4, ccr3; 1362ec1df41SThomas Gleixner 1372ec1df41SThomas Gleixner static void prepare_set(void) 1382ec1df41SThomas Gleixner { 1392ec1df41SThomas Gleixner u32 cr0; 1402ec1df41SThomas Gleixner 1412ec1df41SThomas Gleixner /* Save value of CR4 and clear Page Global Enable (bit 7) */ 142c109bf95SBorislav Petkov if (boot_cpu_has(X86_FEATURE_PGE)) { 1431e02ce4cSAndy Lutomirski cr4 = __read_cr4(); 1441e02ce4cSAndy Lutomirski __write_cr4(cr4 & ~X86_CR4_PGE); 1452ec1df41SThomas Gleixner } 1462ec1df41SThomas Gleixner 14723110377SJaswinder Singh Rajput /* 14823110377SJaswinder Singh Rajput * Disable and flush caches. 14923110377SJaswinder Singh Rajput * Note that wbinvd flushes the TLBs as a side-effect 15023110377SJaswinder Singh Rajput */ 1517ebad705SDave Jones cr0 = read_cr0() | X86_CR0_CD; 1522ec1df41SThomas Gleixner wbinvd(); 1532ec1df41SThomas Gleixner write_cr0(cr0); 1542ec1df41SThomas Gleixner wbinvd(); 1552ec1df41SThomas Gleixner 15627b46d76SSimon Arlott /* Cyrix ARRs - everything else was excluded at the top */ 1572ec1df41SThomas Gleixner ccr3 = getCx86(CX86_CCR3); 1582ec1df41SThomas Gleixner 15927b46d76SSimon Arlott /* Cyrix ARRs - everything else was excluded at the top */ 1602ec1df41SThomas Gleixner setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); 1612ec1df41SThomas Gleixner } 1622ec1df41SThomas Gleixner 1632ec1df41SThomas Gleixner static void post_set(void) 1642ec1df41SThomas Gleixner { 1652ec1df41SThomas Gleixner /* Flush caches and TLBs */ 1662ec1df41SThomas Gleixner wbinvd(); 1672ec1df41SThomas Gleixner 1682ec1df41SThomas Gleixner /* Cyrix ARRs - everything else was excluded at the top */ 1692ec1df41SThomas Gleixner setCx86(CX86_CCR3, ccr3); 1702ec1df41SThomas Gleixner 1712ec1df41SThomas Gleixner /* Enable caches */ 172a3d7b7ddSH. Peter Anvin write_cr0(read_cr0() & ~X86_CR0_CD); 1732ec1df41SThomas Gleixner 1742ec1df41SThomas Gleixner /* Restore value of CR4 */ 175c109bf95SBorislav Petkov if (boot_cpu_has(X86_FEATURE_PGE)) 1761e02ce4cSAndy Lutomirski __write_cr4(cr4); 1772ec1df41SThomas Gleixner } 1782ec1df41SThomas Gleixner 1792ec1df41SThomas Gleixner static void cyrix_set_arr(unsigned int reg, unsigned long base, 1802ec1df41SThomas Gleixner unsigned long size, mtrr_type type) 1812ec1df41SThomas Gleixner { 1822ec1df41SThomas Gleixner unsigned char arr, arr_type, arr_size; 1832ec1df41SThomas Gleixner 1842ec1df41SThomas Gleixner arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ 1852ec1df41SThomas Gleixner 1862ec1df41SThomas Gleixner /* count down from 32M (ARR0-ARR6) or from 2G (ARR7) */ 1872ec1df41SThomas Gleixner if (reg >= 7) 1882ec1df41SThomas Gleixner size >>= 6; 1892ec1df41SThomas Gleixner 1902ec1df41SThomas Gleixner size &= 0x7fff; /* make sure arr_size <= 14 */ 19123110377SJaswinder Singh Rajput for (arr_size = 0; size; arr_size++, size >>= 1) 19223110377SJaswinder Singh Rajput ; 1932ec1df41SThomas Gleixner 1942ec1df41SThomas Gleixner if (reg < 7) { 1952ec1df41SThomas Gleixner switch (type) { 1962ec1df41SThomas Gleixner case MTRR_TYPE_UNCACHABLE: 1972ec1df41SThomas Gleixner arr_type = 1; 1982ec1df41SThomas Gleixner break; 1992ec1df41SThomas Gleixner case MTRR_TYPE_WRCOMB: 2002ec1df41SThomas Gleixner arr_type = 9; 2012ec1df41SThomas Gleixner break; 2022ec1df41SThomas Gleixner case MTRR_TYPE_WRTHROUGH: 2032ec1df41SThomas Gleixner arr_type = 24; 2042ec1df41SThomas Gleixner break; 2052ec1df41SThomas Gleixner default: 2062ec1df41SThomas Gleixner arr_type = 8; 2072ec1df41SThomas Gleixner break; 2082ec1df41SThomas Gleixner } 2092ec1df41SThomas Gleixner } else { 2102ec1df41SThomas Gleixner switch (type) { 2112ec1df41SThomas Gleixner case MTRR_TYPE_UNCACHABLE: 2122ec1df41SThomas Gleixner arr_type = 0; 2132ec1df41SThomas Gleixner break; 2142ec1df41SThomas Gleixner case MTRR_TYPE_WRCOMB: 2152ec1df41SThomas Gleixner arr_type = 8; 2162ec1df41SThomas Gleixner break; 2172ec1df41SThomas Gleixner case MTRR_TYPE_WRTHROUGH: 2182ec1df41SThomas Gleixner arr_type = 25; 2192ec1df41SThomas Gleixner break; 2202ec1df41SThomas Gleixner default: 2212ec1df41SThomas Gleixner arr_type = 9; 2222ec1df41SThomas Gleixner break; 2232ec1df41SThomas Gleixner } 2242ec1df41SThomas Gleixner } 2252ec1df41SThomas Gleixner 2262ec1df41SThomas Gleixner prepare_set(); 2272ec1df41SThomas Gleixner 2282ec1df41SThomas Gleixner base <<= PAGE_SHIFT; 22923110377SJaswinder Singh Rajput setCx86(arr + 0, ((unsigned char *)&base)[3]); 2302ec1df41SThomas Gleixner setCx86(arr + 1, ((unsigned char *)&base)[2]); 2312ec1df41SThomas Gleixner setCx86(arr + 2, (((unsigned char *)&base)[1]) | arr_size); 2322ec1df41SThomas Gleixner setCx86(CX86_RCR_BASE + reg, arr_type); 2332ec1df41SThomas Gleixner 2342ec1df41SThomas Gleixner post_set(); 2352ec1df41SThomas Gleixner } 2362ec1df41SThomas Gleixner 2372ec1df41SThomas Gleixner typedef struct { 2382ec1df41SThomas Gleixner unsigned long base; 2392ec1df41SThomas Gleixner unsigned long size; 2402ec1df41SThomas Gleixner mtrr_type type; 2412ec1df41SThomas Gleixner } arr_state_t; 2422ec1df41SThomas Gleixner 2432ec1df41SThomas Gleixner static arr_state_t arr_state[8] = { 2442ec1df41SThomas Gleixner {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, 2452ec1df41SThomas Gleixner {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL} 2462ec1df41SThomas Gleixner }; 2472ec1df41SThomas Gleixner 2482ec1df41SThomas Gleixner static unsigned char ccr_state[7] = { 0, 0, 0, 0, 0, 0, 0 }; 2492ec1df41SThomas Gleixner 2502ec1df41SThomas Gleixner static void cyrix_set_all(void) 2512ec1df41SThomas Gleixner { 2522ec1df41SThomas Gleixner int i; 2532ec1df41SThomas Gleixner 2542ec1df41SThomas Gleixner prepare_set(); 2552ec1df41SThomas Gleixner 2562ec1df41SThomas Gleixner /* the CCRs are not contiguous */ 2572ec1df41SThomas Gleixner for (i = 0; i < 4; i++) 2582ec1df41SThomas Gleixner setCx86(CX86_CCR0 + i, ccr_state[i]); 2592ec1df41SThomas Gleixner for (; i < 7; i++) 2602ec1df41SThomas Gleixner setCx86(CX86_CCR4 + i, ccr_state[i]); 26123110377SJaswinder Singh Rajput 26223110377SJaswinder Singh Rajput for (i = 0; i < 8; i++) { 2632ec1df41SThomas Gleixner cyrix_set_arr(i, arr_state[i].base, 2642ec1df41SThomas Gleixner arr_state[i].size, arr_state[i].type); 26523110377SJaswinder Singh Rajput } 2662ec1df41SThomas Gleixner 2672ec1df41SThomas Gleixner post_set(); 2682ec1df41SThomas Gleixner } 2692ec1df41SThomas Gleixner 2703b9cfc0aSEmese Revfy static const struct mtrr_ops cyrix_mtrr_ops = { 2712ec1df41SThomas Gleixner .vendor = X86_VENDOR_CYRIX, 2722ec1df41SThomas Gleixner .set_all = cyrix_set_all, 2732ec1df41SThomas Gleixner .set = cyrix_set_arr, 2742ec1df41SThomas Gleixner .get = cyrix_get_arr, 2752ec1df41SThomas Gleixner .get_free_region = cyrix_get_free_region, 2762ec1df41SThomas Gleixner .validate_add_page = generic_validate_add_page, 2772ec1df41SThomas Gleixner .have_wrcomb = positive_have_wrcomb, 2782ec1df41SThomas Gleixner }; 2792ec1df41SThomas Gleixner 2802ec1df41SThomas Gleixner int __init cyrix_init_mtrr(void) 2812ec1df41SThomas Gleixner { 2822ec1df41SThomas Gleixner set_mtrr_ops(&cyrix_mtrr_ops); 2832ec1df41SThomas Gleixner return 0; 2842ec1df41SThomas Gleixner } 285