12ec1df41SThomas Gleixner #include <linux/init.h> 2*23110377SJaswinder Singh Rajput #include <linux/io.h> 32ec1df41SThomas Gleixner #include <linux/mm.h> 4*23110377SJaswinder Singh Rajput 52ec1df41SThomas Gleixner #include <asm/processor-cyrix.h> 67ebad705SDave Jones #include <asm/processor-flags.h> 7*23110377SJaswinder Singh Rajput #include <asm/mtrr.h> 8*23110377SJaswinder Singh Rajput #include <asm/msr.h> 9*23110377SJaswinder 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; 17*23110377SJaswinder 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); 32*23110377SJaswinder Singh Rajput 332ec1df41SThomas Gleixner shift = ((unsigned char *) base)[1] & 0x0f; 342ec1df41SThomas Gleixner *base >>= PAGE_SHIFT; 352ec1df41SThomas Gleixner 36*23110377SJaswinder Singh Rajput /* 37*23110377SJaswinder 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 81*23110377SJaswinder Singh Rajput /* 82*23110377SJaswinder Singh Rajput * cyrix_get_free_region - get a free ARR. 83*23110377SJaswinder Singh Rajput * 84*23110377SJaswinder Singh Rajput * @base: the starting (base) address of the region. 85*23110377SJaswinder Singh Rajput * @size: the size (in bytes) of the region. 86*23110377SJaswinder Singh Rajput * 87*23110377SJaswinder Singh Rajput * Returns: the index of the region on success, else -1 on error. 88*23110377SJaswinder 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; 93*23110377SJaswinder Singh Rajput mtrr_type ltype; 94*23110377SJaswinder 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 } 122*23110377SJaswinder Singh Rajput /* 123*23110377SJaswinder Singh Rajput * ARR0-ARR6 isn't free 124*23110377SJaswinder Singh Rajput * try ARR7 but its size must be at least 256K 125*23110377SJaswinder 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 133*23110377SJaswinder 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) */ 1402ec1df41SThomas Gleixner if (cpu_has_pge) { 1412ec1df41SThomas Gleixner cr4 = read_cr4(); 1422ec1df41SThomas Gleixner write_cr4(cr4 & ~X86_CR4_PGE); 1432ec1df41SThomas Gleixner } 1442ec1df41SThomas Gleixner 145*23110377SJaswinder Singh Rajput /* 146*23110377SJaswinder Singh Rajput * Disable and flush caches. 147*23110377SJaswinder Singh Rajput * Note that wbinvd flushes the TLBs as a side-effect 148*23110377SJaswinder 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 */ 1702ec1df41SThomas Gleixner write_cr0(read_cr0() & 0xbfffffff); 1712ec1df41SThomas Gleixner 1722ec1df41SThomas Gleixner /* Restore value of CR4 */ 1732ec1df41SThomas Gleixner if (cpu_has_pge) 1742ec1df41SThomas Gleixner 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 */ 189*23110377SJaswinder Singh Rajput for (arr_size = 0; size; arr_size++, size >>= 1) 190*23110377SJaswinder 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; 227*23110377SJaswinder 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]); 259*23110377SJaswinder Singh Rajput 260*23110377SJaswinder 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); 263*23110377SJaswinder Singh Rajput } 2642ec1df41SThomas Gleixner 2652ec1df41SThomas Gleixner post_set(); 2662ec1df41SThomas Gleixner } 2672ec1df41SThomas Gleixner 2682ec1df41SThomas Gleixner static 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