12ec1df41SThomas Gleixner #include <linux/init.h> 223110377SJaswinder Singh Rajput #include <linux/io.h> 32ec1df41SThomas Gleixner #include <linux/mm.h> 423110377SJaswinder Singh Rajput 52ec1df41SThomas Gleixner #include <asm/processor-cyrix.h> 67ebad705SDave Jones #include <asm/processor-flags.h> 723110377SJaswinder Singh Rajput #include <asm/mtrr.h> 823110377SJaswinder Singh Rajput #include <asm/msr.h> 923110377SJaswinder Singh Rajput 102ec1df41SThomas Gleixner #include "mtrr.h" 112ec1df41SThomas Gleixner 122ec1df41SThomas Gleixner static void 132ec1df41SThomas Gleixner cyrix_get_arr(unsigned int reg, unsigned long *base, 142ec1df41SThomas Gleixner unsigned long *size, mtrr_type * type) 152ec1df41SThomas Gleixner { 162ec1df41SThomas Gleixner unsigned char arr, ccr3, rcr, shift; 1723110377SJaswinder Singh Rajput unsigned long flags; 182ec1df41SThomas Gleixner 192ec1df41SThomas Gleixner arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ 202ec1df41SThomas Gleixner 212ec1df41SThomas Gleixner local_irq_save(flags); 222ec1df41SThomas Gleixner 232ec1df41SThomas Gleixner ccr3 = getCx86(CX86_CCR3); 242ec1df41SThomas Gleixner setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ 252ec1df41SThomas Gleixner ((unsigned char *)base)[3] = getCx86(arr); 262ec1df41SThomas Gleixner ((unsigned char *)base)[2] = getCx86(arr + 1); 272ec1df41SThomas Gleixner ((unsigned char *)base)[1] = getCx86(arr + 2); 282ec1df41SThomas Gleixner rcr = getCx86(CX86_RCR_BASE + reg); 292ec1df41SThomas Gleixner setCx86(CX86_CCR3, ccr3); /* disable MAPEN */ 302ec1df41SThomas Gleixner 312ec1df41SThomas Gleixner local_irq_restore(flags); 3223110377SJaswinder Singh Rajput 332ec1df41SThomas Gleixner shift = ((unsigned char *) base)[1] & 0x0f; 342ec1df41SThomas Gleixner *base >>= PAGE_SHIFT; 352ec1df41SThomas Gleixner 3623110377SJaswinder Singh Rajput /* 3723110377SJaswinder Singh Rajput * Power of two, at least 4K on ARR0-ARR6, 256K on ARR7 382ec1df41SThomas Gleixner * Note: shift==0xf means 4G, this is unsupported. 392ec1df41SThomas Gleixner */ 402ec1df41SThomas Gleixner if (shift) 412ec1df41SThomas Gleixner *size = (reg < 7 ? 0x1UL : 0x40UL) << (shift - 1); 422ec1df41SThomas Gleixner else 432ec1df41SThomas Gleixner *size = 0; 442ec1df41SThomas Gleixner 452ec1df41SThomas Gleixner /* Bit 0 is Cache Enable on ARR7, Cache Disable on ARR0-ARR6 */ 462ec1df41SThomas Gleixner if (reg < 7) { 472ec1df41SThomas Gleixner switch (rcr) { 482ec1df41SThomas Gleixner case 1: 492ec1df41SThomas Gleixner *type = MTRR_TYPE_UNCACHABLE; 502ec1df41SThomas Gleixner break; 512ec1df41SThomas Gleixner case 8: 522ec1df41SThomas Gleixner *type = MTRR_TYPE_WRBACK; 532ec1df41SThomas Gleixner break; 542ec1df41SThomas Gleixner case 9: 552ec1df41SThomas Gleixner *type = MTRR_TYPE_WRCOMB; 562ec1df41SThomas Gleixner break; 572ec1df41SThomas Gleixner case 24: 582ec1df41SThomas Gleixner default: 592ec1df41SThomas Gleixner *type = MTRR_TYPE_WRTHROUGH; 602ec1df41SThomas Gleixner break; 612ec1df41SThomas Gleixner } 622ec1df41SThomas Gleixner } else { 632ec1df41SThomas Gleixner switch (rcr) { 642ec1df41SThomas Gleixner case 0: 652ec1df41SThomas Gleixner *type = MTRR_TYPE_UNCACHABLE; 662ec1df41SThomas Gleixner break; 672ec1df41SThomas Gleixner case 8: 682ec1df41SThomas Gleixner *type = MTRR_TYPE_WRCOMB; 692ec1df41SThomas Gleixner break; 702ec1df41SThomas Gleixner case 9: 712ec1df41SThomas Gleixner *type = MTRR_TYPE_WRBACK; 722ec1df41SThomas Gleixner break; 732ec1df41SThomas Gleixner case 25: 742ec1df41SThomas Gleixner default: 752ec1df41SThomas Gleixner *type = MTRR_TYPE_WRTHROUGH; 762ec1df41SThomas Gleixner break; 772ec1df41SThomas Gleixner } 782ec1df41SThomas Gleixner } 792ec1df41SThomas Gleixner } 802ec1df41SThomas Gleixner 8123110377SJaswinder Singh Rajput /* 8223110377SJaswinder Singh Rajput * cyrix_get_free_region - get a free ARR. 8323110377SJaswinder Singh Rajput * 8423110377SJaswinder Singh Rajput * @base: the starting (base) address of the region. 8523110377SJaswinder Singh Rajput * @size: the size (in bytes) of the region. 8623110377SJaswinder Singh Rajput * 8723110377SJaswinder Singh Rajput * Returns: the index of the region on success, else -1 on error. 8823110377SJaswinder Singh Rajput */ 892ec1df41SThomas Gleixner static int 902ec1df41SThomas Gleixner cyrix_get_free_region(unsigned long base, unsigned long size, int replace_reg) 912ec1df41SThomas Gleixner { 922ec1df41SThomas Gleixner unsigned long lbase, lsize; 9323110377SJaswinder Singh Rajput mtrr_type ltype; 9423110377SJaswinder Singh Rajput int i; 952ec1df41SThomas Gleixner 962ec1df41SThomas Gleixner switch (replace_reg) { 972ec1df41SThomas Gleixner case 7: 982ec1df41SThomas Gleixner if (size < 0x40) 992ec1df41SThomas Gleixner break; 1002ec1df41SThomas Gleixner case 6: 1012ec1df41SThomas Gleixner case 5: 1022ec1df41SThomas Gleixner case 4: 1032ec1df41SThomas Gleixner return replace_reg; 1042ec1df41SThomas Gleixner case 3: 1052ec1df41SThomas Gleixner case 2: 1062ec1df41SThomas Gleixner case 1: 1072ec1df41SThomas Gleixner case 0: 1082ec1df41SThomas Gleixner return replace_reg; 1092ec1df41SThomas Gleixner } 1102ec1df41SThomas Gleixner /* If we are to set up a region >32M then look at ARR7 immediately */ 1112ec1df41SThomas Gleixner if (size > 0x2000) { 1122ec1df41SThomas Gleixner cyrix_get_arr(7, &lbase, &lsize, <ype); 1132ec1df41SThomas Gleixner if (lsize == 0) 1142ec1df41SThomas Gleixner return 7; 1152ec1df41SThomas Gleixner /* Else try ARR0-ARR6 first */ 1162ec1df41SThomas Gleixner } else { 1172ec1df41SThomas Gleixner for (i = 0; i < 7; i++) { 1182ec1df41SThomas Gleixner cyrix_get_arr(i, &lbase, &lsize, <ype); 1192ec1df41SThomas Gleixner if (lsize == 0) 1202ec1df41SThomas Gleixner return i; 1212ec1df41SThomas Gleixner } 12223110377SJaswinder Singh Rajput /* 12323110377SJaswinder Singh Rajput * ARR0-ARR6 isn't free 12423110377SJaswinder Singh Rajput * try ARR7 but its size must be at least 256K 12523110377SJaswinder Singh Rajput */ 1262ec1df41SThomas Gleixner cyrix_get_arr(i, &lbase, &lsize, <ype); 1272ec1df41SThomas Gleixner if ((lsize == 0) && (size >= 0x40)) 1282ec1df41SThomas Gleixner return i; 1292ec1df41SThomas Gleixner } 1302ec1df41SThomas Gleixner return -ENOSPC; 1312ec1df41SThomas Gleixner } 1322ec1df41SThomas Gleixner 13323110377SJaswinder Singh Rajput static u32 cr4, ccr3; 1342ec1df41SThomas Gleixner 1352ec1df41SThomas Gleixner static void prepare_set(void) 1362ec1df41SThomas Gleixner { 1372ec1df41SThomas Gleixner u32 cr0; 1382ec1df41SThomas Gleixner 1392ec1df41SThomas Gleixner /* Save value of CR4 and clear Page Global Enable (bit 7) */ 140*c109bf95SBorislav Petkov if (boot_cpu_has(X86_FEATURE_PGE)) { 1411e02ce4cSAndy Lutomirski cr4 = __read_cr4(); 1421e02ce4cSAndy Lutomirski __write_cr4(cr4 & ~X86_CR4_PGE); 1432ec1df41SThomas Gleixner } 1442ec1df41SThomas Gleixner 14523110377SJaswinder Singh Rajput /* 14623110377SJaswinder Singh Rajput * Disable and flush caches. 14723110377SJaswinder Singh Rajput * Note that wbinvd flushes the TLBs as a side-effect 14823110377SJaswinder Singh Rajput */ 1497ebad705SDave Jones cr0 = read_cr0() | X86_CR0_CD; 1502ec1df41SThomas Gleixner wbinvd(); 1512ec1df41SThomas Gleixner write_cr0(cr0); 1522ec1df41SThomas Gleixner wbinvd(); 1532ec1df41SThomas Gleixner 15427b46d76SSimon Arlott /* Cyrix ARRs - everything else was excluded at the top */ 1552ec1df41SThomas Gleixner ccr3 = getCx86(CX86_CCR3); 1562ec1df41SThomas Gleixner 15727b46d76SSimon Arlott /* Cyrix ARRs - everything else was excluded at the top */ 1582ec1df41SThomas Gleixner setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); 1592ec1df41SThomas Gleixner } 1602ec1df41SThomas Gleixner 1612ec1df41SThomas Gleixner static void post_set(void) 1622ec1df41SThomas Gleixner { 1632ec1df41SThomas Gleixner /* Flush caches and TLBs */ 1642ec1df41SThomas Gleixner wbinvd(); 1652ec1df41SThomas Gleixner 1662ec1df41SThomas Gleixner /* Cyrix ARRs - everything else was excluded at the top */ 1672ec1df41SThomas Gleixner setCx86(CX86_CCR3, ccr3); 1682ec1df41SThomas Gleixner 1692ec1df41SThomas Gleixner /* Enable caches */ 170a3d7b7ddSH. Peter Anvin write_cr0(read_cr0() & ~X86_CR0_CD); 1712ec1df41SThomas Gleixner 1722ec1df41SThomas Gleixner /* Restore value of CR4 */ 173*c109bf95SBorislav Petkov if (boot_cpu_has(X86_FEATURE_PGE)) 1741e02ce4cSAndy Lutomirski __write_cr4(cr4); 1752ec1df41SThomas Gleixner } 1762ec1df41SThomas Gleixner 1772ec1df41SThomas Gleixner static void cyrix_set_arr(unsigned int reg, unsigned long base, 1782ec1df41SThomas Gleixner unsigned long size, mtrr_type type) 1792ec1df41SThomas Gleixner { 1802ec1df41SThomas Gleixner unsigned char arr, arr_type, arr_size; 1812ec1df41SThomas Gleixner 1822ec1df41SThomas Gleixner arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ 1832ec1df41SThomas Gleixner 1842ec1df41SThomas Gleixner /* count down from 32M (ARR0-ARR6) or from 2G (ARR7) */ 1852ec1df41SThomas Gleixner if (reg >= 7) 1862ec1df41SThomas Gleixner size >>= 6; 1872ec1df41SThomas Gleixner 1882ec1df41SThomas Gleixner size &= 0x7fff; /* make sure arr_size <= 14 */ 18923110377SJaswinder Singh Rajput for (arr_size = 0; size; arr_size++, size >>= 1) 19023110377SJaswinder Singh Rajput ; 1912ec1df41SThomas Gleixner 1922ec1df41SThomas Gleixner if (reg < 7) { 1932ec1df41SThomas Gleixner switch (type) { 1942ec1df41SThomas Gleixner case MTRR_TYPE_UNCACHABLE: 1952ec1df41SThomas Gleixner arr_type = 1; 1962ec1df41SThomas Gleixner break; 1972ec1df41SThomas Gleixner case MTRR_TYPE_WRCOMB: 1982ec1df41SThomas Gleixner arr_type = 9; 1992ec1df41SThomas Gleixner break; 2002ec1df41SThomas Gleixner case MTRR_TYPE_WRTHROUGH: 2012ec1df41SThomas Gleixner arr_type = 24; 2022ec1df41SThomas Gleixner break; 2032ec1df41SThomas Gleixner default: 2042ec1df41SThomas Gleixner arr_type = 8; 2052ec1df41SThomas Gleixner break; 2062ec1df41SThomas Gleixner } 2072ec1df41SThomas Gleixner } else { 2082ec1df41SThomas Gleixner switch (type) { 2092ec1df41SThomas Gleixner case MTRR_TYPE_UNCACHABLE: 2102ec1df41SThomas Gleixner arr_type = 0; 2112ec1df41SThomas Gleixner break; 2122ec1df41SThomas Gleixner case MTRR_TYPE_WRCOMB: 2132ec1df41SThomas Gleixner arr_type = 8; 2142ec1df41SThomas Gleixner break; 2152ec1df41SThomas Gleixner case MTRR_TYPE_WRTHROUGH: 2162ec1df41SThomas Gleixner arr_type = 25; 2172ec1df41SThomas Gleixner break; 2182ec1df41SThomas Gleixner default: 2192ec1df41SThomas Gleixner arr_type = 9; 2202ec1df41SThomas Gleixner break; 2212ec1df41SThomas Gleixner } 2222ec1df41SThomas Gleixner } 2232ec1df41SThomas Gleixner 2242ec1df41SThomas Gleixner prepare_set(); 2252ec1df41SThomas Gleixner 2262ec1df41SThomas Gleixner base <<= PAGE_SHIFT; 22723110377SJaswinder Singh Rajput setCx86(arr + 0, ((unsigned char *)&base)[3]); 2282ec1df41SThomas Gleixner setCx86(arr + 1, ((unsigned char *)&base)[2]); 2292ec1df41SThomas Gleixner setCx86(arr + 2, (((unsigned char *)&base)[1]) | arr_size); 2302ec1df41SThomas Gleixner setCx86(CX86_RCR_BASE + reg, arr_type); 2312ec1df41SThomas Gleixner 2322ec1df41SThomas Gleixner post_set(); 2332ec1df41SThomas Gleixner } 2342ec1df41SThomas Gleixner 2352ec1df41SThomas Gleixner typedef struct { 2362ec1df41SThomas Gleixner unsigned long base; 2372ec1df41SThomas Gleixner unsigned long size; 2382ec1df41SThomas Gleixner mtrr_type type; 2392ec1df41SThomas Gleixner } arr_state_t; 2402ec1df41SThomas Gleixner 2412ec1df41SThomas Gleixner static arr_state_t arr_state[8] = { 2422ec1df41SThomas Gleixner {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, 2432ec1df41SThomas Gleixner {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL} 2442ec1df41SThomas Gleixner }; 2452ec1df41SThomas Gleixner 2462ec1df41SThomas Gleixner static unsigned char ccr_state[7] = { 0, 0, 0, 0, 0, 0, 0 }; 2472ec1df41SThomas Gleixner 2482ec1df41SThomas Gleixner static void cyrix_set_all(void) 2492ec1df41SThomas Gleixner { 2502ec1df41SThomas Gleixner int i; 2512ec1df41SThomas Gleixner 2522ec1df41SThomas Gleixner prepare_set(); 2532ec1df41SThomas Gleixner 2542ec1df41SThomas Gleixner /* the CCRs are not contiguous */ 2552ec1df41SThomas Gleixner for (i = 0; i < 4; i++) 2562ec1df41SThomas Gleixner setCx86(CX86_CCR0 + i, ccr_state[i]); 2572ec1df41SThomas Gleixner for (; i < 7; i++) 2582ec1df41SThomas Gleixner setCx86(CX86_CCR4 + i, ccr_state[i]); 25923110377SJaswinder Singh Rajput 26023110377SJaswinder Singh Rajput for (i = 0; i < 8; i++) { 2612ec1df41SThomas Gleixner cyrix_set_arr(i, arr_state[i].base, 2622ec1df41SThomas Gleixner arr_state[i].size, arr_state[i].type); 26323110377SJaswinder Singh Rajput } 2642ec1df41SThomas Gleixner 2652ec1df41SThomas Gleixner post_set(); 2662ec1df41SThomas Gleixner } 2672ec1df41SThomas Gleixner 2683b9cfc0aSEmese Revfy static const struct mtrr_ops cyrix_mtrr_ops = { 2692ec1df41SThomas Gleixner .vendor = X86_VENDOR_CYRIX, 2702ec1df41SThomas Gleixner .set_all = cyrix_set_all, 2712ec1df41SThomas Gleixner .set = cyrix_set_arr, 2722ec1df41SThomas Gleixner .get = cyrix_get_arr, 2732ec1df41SThomas Gleixner .get_free_region = cyrix_get_free_region, 2742ec1df41SThomas Gleixner .validate_add_page = generic_validate_add_page, 2752ec1df41SThomas Gleixner .have_wrcomb = positive_have_wrcomb, 2762ec1df41SThomas Gleixner }; 2772ec1df41SThomas Gleixner 2782ec1df41SThomas Gleixner int __init cyrix_init_mtrr(void) 2792ec1df41SThomas Gleixner { 2802ec1df41SThomas Gleixner set_mtrr_ops(&cyrix_mtrr_ops); 2812ec1df41SThomas Gleixner return 0; 2822ec1df41SThomas Gleixner } 283