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
cyrix_get_arr(unsigned int reg,unsigned long * base,unsigned long * size,mtrr_type * type)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
cyrix_get_free_region(unsigned long base,unsigned long size,int replace_reg)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;
101df561f66SGustavo 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
prepare_set(void)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
post_set(void)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
cyrix_set_arr(unsigned int reg,unsigned long base,unsigned long size,mtrr_type type)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
237*f8bd9f25SJuergen Gross const struct mtrr_ops cyrix_mtrr_ops = {
2382ec1df41SThomas Gleixner .var_regs = 8,
2392ec1df41SThomas Gleixner .set = cyrix_set_arr,
2402ec1df41SThomas Gleixner .get = cyrix_get_arr,
2412ec1df41SThomas Gleixner .get_free_region = cyrix_get_free_region,
2422ec1df41SThomas Gleixner .validate_add_page = generic_validate_add_page,
2432ec1df41SThomas Gleixner .have_wrcomb = positive_have_wrcomb,
2442ec1df41SThomas Gleixner };
245