mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
284 lines
5.8 KiB
284 lines
5.8 KiB
// 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; |
|
}
|
|
|