xref: /openbmc/linux/arch/x86/kernel/cpu/mtrr/cyrix.c (revision 1e02ce4cccdcb9688386e5b8d2c9fa4660b45389)
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, &ltype);
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, &ltype);
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, &ltype);
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)  */
1402ec1df41SThomas Gleixner 	if (cpu_has_pge) {
141*1e02ce4cSAndy Lutomirski 		cr4 = __read_cr4();
142*1e02ce4cSAndy 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 */
1732ec1df41SThomas Gleixner 	if (cpu_has_pge)
174*1e02ce4cSAndy 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