|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | #include <linux/init.h> | 
|  | #include <linux/io.h> | 
|  | #include <linux/mm.h> | 
|  |  | 
|  | #include <asm/processor-cyrix.h> | 
|  | #include <asm/processor-flags.h> | 
|  | #include <asm/mtrr.h> | 
|  | #include <asm/msr.h> | 
|  |  | 
|  | #include "mtrr.h" | 
|  |  | 
|  | static void | 
|  | cyrix_get_arr(unsigned int reg, unsigned long *base, | 
|  | unsigned long *size, mtrr_type * type) | 
|  | { | 
|  | unsigned char arr, ccr3, rcr, shift; | 
|  | unsigned long flags; | 
|  |  | 
|  | arr = CX86_ARR_BASE + (reg << 1) + reg;	/* avoid multiplication by 3 */ | 
|  |  | 
|  | local_irq_save(flags); | 
|  |  | 
|  | ccr3 = getCx86(CX86_CCR3); | 
|  | setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10);	/* enable MAPEN */ | 
|  | ((unsigned char *)base)[3] = getCx86(arr); | 
|  | ((unsigned char *)base)[2] = getCx86(arr + 1); | 
|  | ((unsigned char *)base)[1] = getCx86(arr + 2); | 
|  | rcr = getCx86(CX86_RCR_BASE + reg); | 
|  | setCx86(CX86_CCR3, ccr3);			/* disable MAPEN */ | 
|  |  | 
|  | local_irq_restore(flags); | 
|  |  | 
|  | shift = ((unsigned char *) base)[1] & 0x0f; | 
|  | *base >>= PAGE_SHIFT; | 
|  |  | 
|  | /* | 
|  | * Power of two, at least 4K on ARR0-ARR6, 256K on ARR7 | 
|  | * Note: shift==0xf means 4G, this is unsupported. | 
|  | */ | 
|  | if (shift) | 
|  | *size = (reg < 7 ? 0x1UL : 0x40UL) << (shift - 1); | 
|  | else | 
|  | *size = 0; | 
|  |  | 
|  | /* Bit 0 is Cache Enable on ARR7, Cache Disable on ARR0-ARR6 */ | 
|  | if (reg < 7) { | 
|  | switch (rcr) { | 
|  | case 1: | 
|  | *type = MTRR_TYPE_UNCACHABLE; | 
|  | break; | 
|  | case 8: | 
|  | *type = MTRR_TYPE_WRBACK; | 
|  | break; | 
|  | case 9: | 
|  | *type = MTRR_TYPE_WRCOMB; | 
|  | break; | 
|  | case 24: | 
|  | default: | 
|  | *type = MTRR_TYPE_WRTHROUGH; | 
|  | break; | 
|  | } | 
|  | } else { | 
|  | switch (rcr) { | 
|  | case 0: | 
|  | *type = MTRR_TYPE_UNCACHABLE; | 
|  | break; | 
|  | case 8: | 
|  | *type = MTRR_TYPE_WRCOMB; | 
|  | break; | 
|  | case 9: | 
|  | *type = MTRR_TYPE_WRBACK; | 
|  | break; | 
|  | case 25: | 
|  | default: | 
|  | *type = MTRR_TYPE_WRTHROUGH; | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | * cyrix_get_free_region - get a free ARR. | 
|  | * | 
|  | * @base: the starting (base) address of the region. | 
|  | * @size: the size (in bytes) of the region. | 
|  | * | 
|  | * Returns: the index of the region on success, else -1 on error. | 
|  | */ | 
|  | static int | 
|  | cyrix_get_free_region(unsigned long base, unsigned long size, int replace_reg) | 
|  | { | 
|  | unsigned long lbase, lsize; | 
|  | mtrr_type ltype; | 
|  | int i; | 
|  |  | 
|  | switch (replace_reg) { | 
|  | case 7: | 
|  | if (size < 0x40) | 
|  | break; | 
|  | fallthrough; | 
|  | case 6: | 
|  | case 5: | 
|  | case 4: | 
|  | return replace_reg; | 
|  | case 3: | 
|  | case 2: | 
|  | case 1: | 
|  | case 0: | 
|  | return replace_reg; | 
|  | } | 
|  | /* If we are to set up a region >32M then look at ARR7 immediately */ | 
|  | if (size > 0x2000) { | 
|  | cyrix_get_arr(7, &lbase, &lsize, <ype); | 
|  | if (lsize == 0) | 
|  | return 7; | 
|  | /* Else try ARR0-ARR6 first  */ | 
|  | } else { | 
|  | for (i = 0; i < 7; i++) { | 
|  | cyrix_get_arr(i, &lbase, &lsize, <ype); | 
|  | if (lsize == 0) | 
|  | return i; | 
|  | } | 
|  | /* | 
|  | * ARR0-ARR6 isn't free | 
|  | * try ARR7 but its size must be at least 256K | 
|  | */ | 
|  | cyrix_get_arr(i, &lbase, &lsize, <ype); | 
|  | if ((lsize == 0) && (size >= 0x40)) | 
|  | return i; | 
|  | } | 
|  | return -ENOSPC; | 
|  | } | 
|  |  | 
|  | static u32 cr4, ccr3; | 
|  |  | 
|  | static void prepare_set(void) | 
|  | { | 
|  | u32 cr0; | 
|  |  | 
|  | /*  Save value of CR4 and clear Page Global Enable (bit 7)  */ | 
|  | if (boot_cpu_has(X86_FEATURE_PGE)) { | 
|  | cr4 = __read_cr4(); | 
|  | __write_cr4(cr4 & ~X86_CR4_PGE); | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Disable and flush caches. | 
|  | * Note that wbinvd flushes the TLBs as a side-effect | 
|  | */ | 
|  | cr0 = read_cr0() | X86_CR0_CD; | 
|  | wbinvd(); | 
|  | write_cr0(cr0); | 
|  | wbinvd(); | 
|  |  | 
|  | /* Cyrix ARRs - everything else was excluded at the top */ | 
|  | ccr3 = getCx86(CX86_CCR3); | 
|  |  | 
|  | /* Cyrix ARRs - everything else was excluded at the top */ | 
|  | setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); | 
|  | } | 
|  |  | 
|  | static void post_set(void) | 
|  | { | 
|  | /* Flush caches and TLBs */ | 
|  | wbinvd(); | 
|  |  | 
|  | /* Cyrix ARRs - everything else was excluded at the top */ | 
|  | setCx86(CX86_CCR3, ccr3); | 
|  |  | 
|  | /* Enable caches */ | 
|  | write_cr0(read_cr0() & ~X86_CR0_CD); | 
|  |  | 
|  | /* Restore value of CR4 */ | 
|  | if (boot_cpu_has(X86_FEATURE_PGE)) | 
|  | __write_cr4(cr4); | 
|  | } | 
|  |  | 
|  | static void cyrix_set_arr(unsigned int reg, unsigned long base, | 
|  | unsigned long size, mtrr_type type) | 
|  | { | 
|  | unsigned char arr, arr_type, arr_size; | 
|  |  | 
|  | arr = CX86_ARR_BASE + (reg << 1) + reg;	/* avoid multiplication by 3 */ | 
|  |  | 
|  | /* count down from 32M (ARR0-ARR6) or from 2G (ARR7) */ | 
|  | if (reg >= 7) | 
|  | size >>= 6; | 
|  |  | 
|  | size &= 0x7fff;		/* make sure arr_size <= 14 */ | 
|  | for (arr_size = 0; size; arr_size++, size >>= 1) | 
|  | ; | 
|  |  | 
|  | if (reg < 7) { | 
|  | switch (type) { | 
|  | case MTRR_TYPE_UNCACHABLE: | 
|  | arr_type = 1; | 
|  | break; | 
|  | case MTRR_TYPE_WRCOMB: | 
|  | arr_type = 9; | 
|  | break; | 
|  | case MTRR_TYPE_WRTHROUGH: | 
|  | arr_type = 24; | 
|  | break; | 
|  | default: | 
|  | arr_type = 8; | 
|  | break; | 
|  | } | 
|  | } else { | 
|  | switch (type) { | 
|  | case MTRR_TYPE_UNCACHABLE: | 
|  | arr_type = 0; | 
|  | break; | 
|  | case MTRR_TYPE_WRCOMB: | 
|  | arr_type = 8; | 
|  | break; | 
|  | case MTRR_TYPE_WRTHROUGH: | 
|  | arr_type = 25; | 
|  | break; | 
|  | default: | 
|  | arr_type = 9; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | prepare_set(); | 
|  |  | 
|  | base <<= PAGE_SHIFT; | 
|  | setCx86(arr + 0,  ((unsigned char *)&base)[3]); | 
|  | setCx86(arr + 1,  ((unsigned char *)&base)[2]); | 
|  | setCx86(arr + 2, (((unsigned char *)&base)[1]) | arr_size); | 
|  | setCx86(CX86_RCR_BASE + reg, arr_type); | 
|  |  | 
|  | post_set(); | 
|  | } | 
|  |  | 
|  | typedef struct { | 
|  | unsigned long	base; | 
|  | unsigned long	size; | 
|  | mtrr_type	type; | 
|  | } arr_state_t; | 
|  |  | 
|  | static arr_state_t arr_state[8] = { | 
|  | {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, | 
|  | {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL} | 
|  | }; | 
|  |  | 
|  | static unsigned char ccr_state[7] = { 0, 0, 0, 0, 0, 0, 0 }; | 
|  |  | 
|  | static void cyrix_set_all(void) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | prepare_set(); | 
|  |  | 
|  | /* the CCRs are not contiguous */ | 
|  | for (i = 0; i < 4; i++) | 
|  | setCx86(CX86_CCR0 + i, ccr_state[i]); | 
|  | for (; i < 7; i++) | 
|  | setCx86(CX86_CCR4 + i, ccr_state[i]); | 
|  |  | 
|  | for (i = 0; i < 8; i++) { | 
|  | cyrix_set_arr(i, arr_state[i].base, | 
|  | arr_state[i].size, arr_state[i].type); | 
|  | } | 
|  |  | 
|  | post_set(); | 
|  | } | 
|  |  | 
|  | static const struct mtrr_ops cyrix_mtrr_ops = { | 
|  | .vendor            = X86_VENDOR_CYRIX, | 
|  | .set_all	   = cyrix_set_all, | 
|  | .set               = cyrix_set_arr, | 
|  | .get               = cyrix_get_arr, | 
|  | .get_free_region   = cyrix_get_free_region, | 
|  | .validate_add_page = generic_validate_add_page, | 
|  | .have_wrcomb       = positive_have_wrcomb, | 
|  | }; | 
|  |  | 
|  | int __init cyrix_init_mtrr(void) | 
|  | { | 
|  | set_mtrr_ops(&cyrix_mtrr_ops); | 
|  | return 0; | 
|  | } |