Path: blob/master/arch/x86/kernel/cpu/mtrr/cyrix.c
10775 views
#include <linux/init.h>1#include <linux/io.h>2#include <linux/mm.h>34#include <asm/processor-cyrix.h>5#include <asm/processor-flags.h>6#include <asm/mtrr.h>7#include <asm/msr.h>89#include "mtrr.h"1011static void12cyrix_get_arr(unsigned int reg, unsigned long *base,13unsigned long *size, mtrr_type * type)14{15unsigned char arr, ccr3, rcr, shift;16unsigned long flags;1718arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */1920local_irq_save(flags);2122ccr3 = getCx86(CX86_CCR3);23setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */24((unsigned char *)base)[3] = getCx86(arr);25((unsigned char *)base)[2] = getCx86(arr + 1);26((unsigned char *)base)[1] = getCx86(arr + 2);27rcr = getCx86(CX86_RCR_BASE + reg);28setCx86(CX86_CCR3, ccr3); /* disable MAPEN */2930local_irq_restore(flags);3132shift = ((unsigned char *) base)[1] & 0x0f;33*base >>= PAGE_SHIFT;3435/*36* Power of two, at least 4K on ARR0-ARR6, 256K on ARR737* Note: shift==0xf means 4G, this is unsupported.38*/39if (shift)40*size = (reg < 7 ? 0x1UL : 0x40UL) << (shift - 1);41else42*size = 0;4344/* Bit 0 is Cache Enable on ARR7, Cache Disable on ARR0-ARR6 */45if (reg < 7) {46switch (rcr) {47case 1:48*type = MTRR_TYPE_UNCACHABLE;49break;50case 8:51*type = MTRR_TYPE_WRBACK;52break;53case 9:54*type = MTRR_TYPE_WRCOMB;55break;56case 24:57default:58*type = MTRR_TYPE_WRTHROUGH;59break;60}61} else {62switch (rcr) {63case 0:64*type = MTRR_TYPE_UNCACHABLE;65break;66case 8:67*type = MTRR_TYPE_WRCOMB;68break;69case 9:70*type = MTRR_TYPE_WRBACK;71break;72case 25:73default:74*type = MTRR_TYPE_WRTHROUGH;75break;76}77}78}7980/*81* cyrix_get_free_region - get a free ARR.82*83* @base: the starting (base) address of the region.84* @size: the size (in bytes) of the region.85*86* Returns: the index of the region on success, else -1 on error.87*/88static int89cyrix_get_free_region(unsigned long base, unsigned long size, int replace_reg)90{91unsigned long lbase, lsize;92mtrr_type ltype;93int i;9495switch (replace_reg) {96case 7:97if (size < 0x40)98break;99case 6:100case 5:101case 4:102return replace_reg;103case 3:104case 2:105case 1:106case 0:107return replace_reg;108}109/* If we are to set up a region >32M then look at ARR7 immediately */110if (size > 0x2000) {111cyrix_get_arr(7, &lbase, &lsize, <ype);112if (lsize == 0)113return 7;114/* Else try ARR0-ARR6 first */115} else {116for (i = 0; i < 7; i++) {117cyrix_get_arr(i, &lbase, &lsize, <ype);118if (lsize == 0)119return i;120}121/*122* ARR0-ARR6 isn't free123* try ARR7 but its size must be at least 256K124*/125cyrix_get_arr(i, &lbase, &lsize, <ype);126if ((lsize == 0) && (size >= 0x40))127return i;128}129return -ENOSPC;130}131132static u32 cr4, ccr3;133134static void prepare_set(void)135{136u32 cr0;137138/* Save value of CR4 and clear Page Global Enable (bit 7) */139if (cpu_has_pge) {140cr4 = read_cr4();141write_cr4(cr4 & ~X86_CR4_PGE);142}143144/*145* Disable and flush caches.146* Note that wbinvd flushes the TLBs as a side-effect147*/148cr0 = read_cr0() | X86_CR0_CD;149wbinvd();150write_cr0(cr0);151wbinvd();152153/* Cyrix ARRs - everything else was excluded at the top */154ccr3 = getCx86(CX86_CCR3);155156/* Cyrix ARRs - everything else was excluded at the top */157setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10);158}159160static void post_set(void)161{162/* Flush caches and TLBs */163wbinvd();164165/* Cyrix ARRs - everything else was excluded at the top */166setCx86(CX86_CCR3, ccr3);167168/* Enable caches */169write_cr0(read_cr0() & 0xbfffffff);170171/* Restore value of CR4 */172if (cpu_has_pge)173write_cr4(cr4);174}175176static void cyrix_set_arr(unsigned int reg, unsigned long base,177unsigned long size, mtrr_type type)178{179unsigned char arr, arr_type, arr_size;180181arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */182183/* count down from 32M (ARR0-ARR6) or from 2G (ARR7) */184if (reg >= 7)185size >>= 6;186187size &= 0x7fff; /* make sure arr_size <= 14 */188for (arr_size = 0; size; arr_size++, size >>= 1)189;190191if (reg < 7) {192switch (type) {193case MTRR_TYPE_UNCACHABLE:194arr_type = 1;195break;196case MTRR_TYPE_WRCOMB:197arr_type = 9;198break;199case MTRR_TYPE_WRTHROUGH:200arr_type = 24;201break;202default:203arr_type = 8;204break;205}206} else {207switch (type) {208case MTRR_TYPE_UNCACHABLE:209arr_type = 0;210break;211case MTRR_TYPE_WRCOMB:212arr_type = 8;213break;214case MTRR_TYPE_WRTHROUGH:215arr_type = 25;216break;217default:218arr_type = 9;219break;220}221}222223prepare_set();224225base <<= PAGE_SHIFT;226setCx86(arr + 0, ((unsigned char *)&base)[3]);227setCx86(arr + 1, ((unsigned char *)&base)[2]);228setCx86(arr + 2, (((unsigned char *)&base)[1]) | arr_size);229setCx86(CX86_RCR_BASE + reg, arr_type);230231post_set();232}233234typedef struct {235unsigned long base;236unsigned long size;237mtrr_type type;238} arr_state_t;239240static arr_state_t arr_state[8] = {241{0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL},242{0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}243};244245static unsigned char ccr_state[7] = { 0, 0, 0, 0, 0, 0, 0 };246247static void cyrix_set_all(void)248{249int i;250251prepare_set();252253/* the CCRs are not contiguous */254for (i = 0; i < 4; i++)255setCx86(CX86_CCR0 + i, ccr_state[i]);256for (; i < 7; i++)257setCx86(CX86_CCR4 + i, ccr_state[i]);258259for (i = 0; i < 8; i++) {260cyrix_set_arr(i, arr_state[i].base,261arr_state[i].size, arr_state[i].type);262}263264post_set();265}266267static const struct mtrr_ops cyrix_mtrr_ops = {268.vendor = X86_VENDOR_CYRIX,269.set_all = cyrix_set_all,270.set = cyrix_set_arr,271.get = cyrix_get_arr,272.get_free_region = cyrix_get_free_region,273.validate_add_page = generic_validate_add_page,274.have_wrcomb = positive_have_wrcomb,275};276277int __init cyrix_init_mtrr(void)278{279set_mtrr_ops(&cyrix_mtrr_ops);280return 0;281}282283284