|  | #include <linux/init.h> | 
|  | #include <linux/mm.h> | 
|  | #include <asm/mtrr.h> | 
|  | #include <asm/msr.h> | 
|  | #include "mtrr.h" | 
|  |  | 
|  | static struct { | 
|  | unsigned long high; | 
|  | unsigned long low; | 
|  | } centaur_mcr[8]; | 
|  |  | 
|  | static u8 centaur_mcr_reserved; | 
|  | static u8 centaur_mcr_type;	/* 0 for winchip, 1 for winchip2 */ | 
|  |  | 
|  | /* | 
|  | *	Report boot time MCR setups | 
|  | */ | 
|  |  | 
|  | static int | 
|  | centaur_get_free_region(unsigned long base, unsigned long size) | 
|  | /*  [SUMMARY] Get a free MTRR. | 
|  | <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. | 
|  | */ | 
|  | { | 
|  | int i, max; | 
|  | mtrr_type ltype; | 
|  | unsigned long lbase; | 
|  | unsigned int lsize; | 
|  |  | 
|  | max = num_var_ranges; | 
|  | for (i = 0; i < max; ++i) { | 
|  | if (centaur_mcr_reserved & (1 << i)) | 
|  | continue; | 
|  | mtrr_if->get(i, &lbase, &lsize, <ype); | 
|  | if (lsize == 0) | 
|  | return i; | 
|  | } | 
|  | return -ENOSPC; | 
|  | } | 
|  |  | 
|  | void | 
|  | mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi) | 
|  | { | 
|  | centaur_mcr[mcr].low = lo; | 
|  | centaur_mcr[mcr].high = hi; | 
|  | } | 
|  |  | 
|  | static void | 
|  | centaur_get_mcr(unsigned int reg, unsigned long *base, | 
|  | unsigned int *size, mtrr_type * type) | 
|  | { | 
|  | *base = centaur_mcr[reg].high >> PAGE_SHIFT; | 
|  | *size = -(centaur_mcr[reg].low & 0xfffff000) >> PAGE_SHIFT; | 
|  | *type = MTRR_TYPE_WRCOMB;	/*  If it is there, it is write-combining  */ | 
|  | if (centaur_mcr_type == 1 && ((centaur_mcr[reg].low & 31) & 2)) | 
|  | *type = MTRR_TYPE_UNCACHABLE; | 
|  | if (centaur_mcr_type == 1 && (centaur_mcr[reg].low & 31) == 25) | 
|  | *type = MTRR_TYPE_WRBACK; | 
|  | if (centaur_mcr_type == 0 && (centaur_mcr[reg].low & 31) == 31) | 
|  | *type = MTRR_TYPE_WRBACK; | 
|  |  | 
|  | } | 
|  |  | 
|  | static void centaur_set_mcr(unsigned int reg, unsigned long base, | 
|  | unsigned long size, mtrr_type type) | 
|  | { | 
|  | unsigned long low, high; | 
|  |  | 
|  | if (size == 0) { | 
|  | /*  Disable  */ | 
|  | high = low = 0; | 
|  | } else { | 
|  | high = base << PAGE_SHIFT; | 
|  | if (centaur_mcr_type == 0) | 
|  | low = -size << PAGE_SHIFT | 0x1f;	/* only support write-combining... */ | 
|  | else { | 
|  | if (type == MTRR_TYPE_UNCACHABLE) | 
|  | low = -size << PAGE_SHIFT | 0x02;	/* NC */ | 
|  | else | 
|  | low = -size << PAGE_SHIFT | 0x09;	/* WWO,WC */ | 
|  | } | 
|  | } | 
|  | centaur_mcr[reg].high = high; | 
|  | centaur_mcr[reg].low = low; | 
|  | wrmsr(MSR_IDT_MCR0 + reg, low, high); | 
|  | } | 
|  | /* | 
|  | *	Initialise the later (saner) Winchip MCR variant. In this version | 
|  | *	the BIOS can pass us the registers it has used (but not their values) | 
|  | *	and the control register is read/write | 
|  | */ | 
|  |  | 
|  | static void __init | 
|  | centaur_mcr1_init(void) | 
|  | { | 
|  | unsigned i; | 
|  | u32 lo, hi; | 
|  |  | 
|  | /* Unfortunately, MCR's are read-only, so there is no way to | 
|  | * find out what the bios might have done. | 
|  | */ | 
|  |  | 
|  | rdmsr(MSR_IDT_MCR_CTRL, lo, hi); | 
|  | if (((lo >> 17) & 7) == 1) {	/* Type 1 Winchip2 MCR */ | 
|  | lo &= ~0x1C0;	/* clear key */ | 
|  | lo |= 0x040;	/* set key to 1 */ | 
|  | wrmsr(MSR_IDT_MCR_CTRL, lo, hi);	/* unlock MCR */ | 
|  | } | 
|  |  | 
|  | centaur_mcr_type = 1; | 
|  |  | 
|  | /* | 
|  | *  Clear any unconfigured MCR's. | 
|  | */ | 
|  |  | 
|  | for (i = 0; i < 8; ++i) { | 
|  | if (centaur_mcr[i].high == 0 && centaur_mcr[i].low == 0) { | 
|  | if (!(lo & (1 << (9 + i)))) | 
|  | wrmsr(MSR_IDT_MCR0 + i, 0, 0); | 
|  | else | 
|  | /* | 
|  | *      If the BIOS set up an MCR we cannot see it | 
|  | *      but we don't wish to obliterate it | 
|  | */ | 
|  | centaur_mcr_reserved |= (1 << i); | 
|  | } | 
|  | } | 
|  | /* | 
|  | *  Throw the main write-combining switch... | 
|  | *  However if OOSTORE is enabled then people have already done far | 
|  | *  cleverer things and we should behave. | 
|  | */ | 
|  |  | 
|  | lo |= 15;		/* Write combine enables */ | 
|  | wrmsr(MSR_IDT_MCR_CTRL, lo, hi); | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	Initialise the original winchip with read only MCR registers | 
|  | *	no used bitmask for the BIOS to pass on and write only control | 
|  | */ | 
|  |  | 
|  | static void __init | 
|  | centaur_mcr0_init(void) | 
|  | { | 
|  | unsigned i; | 
|  |  | 
|  | /* Unfortunately, MCR's are read-only, so there is no way to | 
|  | * find out what the bios might have done. | 
|  | */ | 
|  |  | 
|  | /* Clear any unconfigured MCR's. | 
|  | * This way we are sure that the centaur_mcr array contains the actual | 
|  | * values. The disadvantage is that any BIOS tweaks are thus undone. | 
|  | * | 
|  | */ | 
|  | for (i = 0; i < 8; ++i) { | 
|  | if (centaur_mcr[i].high == 0 && centaur_mcr[i].low == 0) | 
|  | wrmsr(MSR_IDT_MCR0 + i, 0, 0); | 
|  | } | 
|  |  | 
|  | wrmsr(MSR_IDT_MCR_CTRL, 0x01F0001F, 0);	/* Write only */ | 
|  | } | 
|  |  | 
|  | /* | 
|  | *	Initialise Winchip series MCR registers | 
|  | */ | 
|  |  | 
|  | static void __init | 
|  | centaur_mcr_init(void) | 
|  | { | 
|  | struct set_mtrr_context ctxt; | 
|  |  | 
|  | set_mtrr_prepare_save(&ctxt); | 
|  | set_mtrr_cache_disable(&ctxt); | 
|  |  | 
|  | if (boot_cpu_data.x86_model == 4) | 
|  | centaur_mcr0_init(); | 
|  | else if (boot_cpu_data.x86_model == 8 || boot_cpu_data.x86_model == 9) | 
|  | centaur_mcr1_init(); | 
|  |  | 
|  | set_mtrr_done(&ctxt); | 
|  | } | 
|  |  | 
|  | static int centaur_validate_add_page(unsigned long base, | 
|  | unsigned long size, unsigned int type) | 
|  | { | 
|  | /* | 
|  | *  FIXME: Winchip2 supports uncached | 
|  | */ | 
|  | if (type != MTRR_TYPE_WRCOMB && | 
|  | (centaur_mcr_type == 0 || type != MTRR_TYPE_UNCACHABLE)) { | 
|  | printk(KERN_WARNING | 
|  | "mtrr: only write-combining%s supported\n", | 
|  | centaur_mcr_type ? " and uncacheable are" | 
|  | : " is"); | 
|  | return -EINVAL; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static struct mtrr_ops centaur_mtrr_ops = { | 
|  | .vendor            = X86_VENDOR_CENTAUR, | 
|  | .init              = centaur_mcr_init, | 
|  | .set               = centaur_set_mcr, | 
|  | .get               = centaur_get_mcr, | 
|  | .get_free_region   = centaur_get_free_region, | 
|  | .validate_add_page = centaur_validate_add_page, | 
|  | .have_wrcomb       = positive_have_wrcomb, | 
|  | }; | 
|  |  | 
|  | int __init centaur_init_mtrr(void) | 
|  | { | 
|  | set_mtrr_ops(¢aur_mtrr_ops); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | //arch_initcall(centaur_init_mtrr); |