xref: /openbmc/linux/arch/x86/kernel/cpu/mtrr/cyrix.c (revision 1a931707ad4a46e79d4ecfee56d8f6e8cc8d4f28)
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, &ltype);
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, &ltype);
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, &ltype);
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