Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | #include <linux/init.h> |
| 2 | #include <linux/mm.h> |
| 3 | #include <asm/mtrr.h> |
| 4 | #include <asm/msr.h> |
| 5 | #include "mtrr.h" |
| 6 | |
| 7 | static struct { |
| 8 | unsigned long high; |
| 9 | unsigned long low; |
| 10 | } centaur_mcr[8]; |
| 11 | |
| 12 | static u8 centaur_mcr_reserved; |
| 13 | static u8 centaur_mcr_type; /* 0 for winchip, 1 for winchip2 */ |
| 14 | |
| 15 | /* |
| 16 | * Report boot time MCR setups |
| 17 | */ |
| 18 | |
| 19 | static int |
Jan Beulich | 365bff8 | 2006-12-07 02:14:09 +0100 | [diff] [blame] | 20 | centaur_get_free_region(unsigned long base, unsigned long size, int replace_reg) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 21 | /* [SUMMARY] Get a free MTRR. |
| 22 | <base> The starting (base) address of the region. |
| 23 | <size> The size (in bytes) of the region. |
| 24 | [RETURNS] The index of the region on success, else -1 on error. |
| 25 | */ |
| 26 | { |
| 27 | int i, max; |
| 28 | mtrr_type ltype; |
Jan Beulich | 365bff8 | 2006-12-07 02:14:09 +0100 | [diff] [blame] | 29 | unsigned long lbase, lsize; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 30 | |
| 31 | max = num_var_ranges; |
Jan Beulich | 365bff8 | 2006-12-07 02:14:09 +0100 | [diff] [blame] | 32 | if (replace_reg >= 0 && replace_reg < max) |
| 33 | return replace_reg; |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 34 | for (i = 0; i < max; ++i) { |
| 35 | if (centaur_mcr_reserved & (1 << i)) |
| 36 | continue; |
| 37 | mtrr_if->get(i, &lbase, &lsize, <ype); |
| 38 | if (lsize == 0) |
| 39 | return i; |
| 40 | } |
| 41 | return -ENOSPC; |
| 42 | } |
| 43 | |
| 44 | void |
| 45 | mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi) |
| 46 | { |
| 47 | centaur_mcr[mcr].low = lo; |
| 48 | centaur_mcr[mcr].high = hi; |
| 49 | } |
| 50 | |
| 51 | static void |
| 52 | centaur_get_mcr(unsigned int reg, unsigned long *base, |
Jan Beulich | 365bff8 | 2006-12-07 02:14:09 +0100 | [diff] [blame] | 53 | unsigned long *size, mtrr_type * type) |
Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 54 | { |
| 55 | *base = centaur_mcr[reg].high >> PAGE_SHIFT; |
| 56 | *size = -(centaur_mcr[reg].low & 0xfffff000) >> PAGE_SHIFT; |
| 57 | *type = MTRR_TYPE_WRCOMB; /* If it is there, it is write-combining */ |
| 58 | if (centaur_mcr_type == 1 && ((centaur_mcr[reg].low & 31) & 2)) |
| 59 | *type = MTRR_TYPE_UNCACHABLE; |
| 60 | if (centaur_mcr_type == 1 && (centaur_mcr[reg].low & 31) == 25) |
| 61 | *type = MTRR_TYPE_WRBACK; |
| 62 | if (centaur_mcr_type == 0 && (centaur_mcr[reg].low & 31) == 31) |
| 63 | *type = MTRR_TYPE_WRBACK; |
| 64 | |
| 65 | } |
| 66 | |
| 67 | static void centaur_set_mcr(unsigned int reg, unsigned long base, |
| 68 | unsigned long size, mtrr_type type) |
| 69 | { |
| 70 | unsigned long low, high; |
| 71 | |
| 72 | if (size == 0) { |
| 73 | /* Disable */ |
| 74 | high = low = 0; |
| 75 | } else { |
| 76 | high = base << PAGE_SHIFT; |
| 77 | if (centaur_mcr_type == 0) |
| 78 | low = -size << PAGE_SHIFT | 0x1f; /* only support write-combining... */ |
| 79 | else { |
| 80 | if (type == MTRR_TYPE_UNCACHABLE) |
| 81 | low = -size << PAGE_SHIFT | 0x02; /* NC */ |
| 82 | else |
| 83 | low = -size << PAGE_SHIFT | 0x09; /* WWO,WC */ |
| 84 | } |
| 85 | } |
| 86 | centaur_mcr[reg].high = high; |
| 87 | centaur_mcr[reg].low = low; |
| 88 | wrmsr(MSR_IDT_MCR0 + reg, low, high); |
| 89 | } |
| 90 | |
| 91 | #if 0 |
| 92 | /* |
| 93 | * Initialise the later (saner) Winchip MCR variant. In this version |
| 94 | * the BIOS can pass us the registers it has used (but not their values) |
| 95 | * and the control register is read/write |
| 96 | */ |
| 97 | |
| 98 | static void __init |
| 99 | centaur_mcr1_init(void) |
| 100 | { |
| 101 | unsigned i; |
| 102 | u32 lo, hi; |
| 103 | |
| 104 | /* Unfortunately, MCR's are read-only, so there is no way to |
| 105 | * find out what the bios might have done. |
| 106 | */ |
| 107 | |
| 108 | rdmsr(MSR_IDT_MCR_CTRL, lo, hi); |
| 109 | if (((lo >> 17) & 7) == 1) { /* Type 1 Winchip2 MCR */ |
| 110 | lo &= ~0x1C0; /* clear key */ |
| 111 | lo |= 0x040; /* set key to 1 */ |
| 112 | wrmsr(MSR_IDT_MCR_CTRL, lo, hi); /* unlock MCR */ |
| 113 | } |
| 114 | |
| 115 | centaur_mcr_type = 1; |
| 116 | |
| 117 | /* |
| 118 | * Clear any unconfigured MCR's. |
| 119 | */ |
| 120 | |
| 121 | for (i = 0; i < 8; ++i) { |
| 122 | if (centaur_mcr[i].high == 0 && centaur_mcr[i].low == 0) { |
| 123 | if (!(lo & (1 << (9 + i)))) |
| 124 | wrmsr(MSR_IDT_MCR0 + i, 0, 0); |
| 125 | else |
| 126 | /* |
| 127 | * If the BIOS set up an MCR we cannot see it |
| 128 | * but we don't wish to obliterate it |
| 129 | */ |
| 130 | centaur_mcr_reserved |= (1 << i); |
| 131 | } |
| 132 | } |
| 133 | /* |
| 134 | * Throw the main write-combining switch... |
| 135 | * However if OOSTORE is enabled then people have already done far |
| 136 | * cleverer things and we should behave. |
| 137 | */ |
| 138 | |
| 139 | lo |= 15; /* Write combine enables */ |
| 140 | wrmsr(MSR_IDT_MCR_CTRL, lo, hi); |
| 141 | } |
| 142 | |
| 143 | /* |
| 144 | * Initialise the original winchip with read only MCR registers |
| 145 | * no used bitmask for the BIOS to pass on and write only control |
| 146 | */ |
| 147 | |
| 148 | static void __init |
| 149 | centaur_mcr0_init(void) |
| 150 | { |
| 151 | unsigned i; |
| 152 | |
| 153 | /* Unfortunately, MCR's are read-only, so there is no way to |
| 154 | * find out what the bios might have done. |
| 155 | */ |
| 156 | |
| 157 | /* Clear any unconfigured MCR's. |
| 158 | * This way we are sure that the centaur_mcr array contains the actual |
| 159 | * values. The disadvantage is that any BIOS tweaks are thus undone. |
| 160 | * |
| 161 | */ |
| 162 | for (i = 0; i < 8; ++i) { |
| 163 | if (centaur_mcr[i].high == 0 && centaur_mcr[i].low == 0) |
| 164 | wrmsr(MSR_IDT_MCR0 + i, 0, 0); |
| 165 | } |
| 166 | |
| 167 | wrmsr(MSR_IDT_MCR_CTRL, 0x01F0001F, 0); /* Write only */ |
| 168 | } |
| 169 | |
| 170 | /* |
| 171 | * Initialise Winchip series MCR registers |
| 172 | */ |
| 173 | |
| 174 | static void __init |
| 175 | centaur_mcr_init(void) |
| 176 | { |
| 177 | struct set_mtrr_context ctxt; |
| 178 | |
| 179 | set_mtrr_prepare_save(&ctxt); |
| 180 | set_mtrr_cache_disable(&ctxt); |
| 181 | |
| 182 | if (boot_cpu_data.x86_model == 4) |
| 183 | centaur_mcr0_init(); |
| 184 | else if (boot_cpu_data.x86_model == 8 || boot_cpu_data.x86_model == 9) |
| 185 | centaur_mcr1_init(); |
| 186 | |
| 187 | set_mtrr_done(&ctxt); |
| 188 | } |
| 189 | #endif |
| 190 | |
| 191 | static int centaur_validate_add_page(unsigned long base, |
| 192 | unsigned long size, unsigned int type) |
| 193 | { |
| 194 | /* |
| 195 | * FIXME: Winchip2 supports uncached |
| 196 | */ |
| 197 | if (type != MTRR_TYPE_WRCOMB && |
| 198 | (centaur_mcr_type == 0 || type != MTRR_TYPE_UNCACHABLE)) { |
| 199 | printk(KERN_WARNING |
| 200 | "mtrr: only write-combining%s supported\n", |
| 201 | centaur_mcr_type ? " and uncacheable are" |
| 202 | : " is"); |
| 203 | return -EINVAL; |
| 204 | } |
| 205 | return 0; |
| 206 | } |
| 207 | |
| 208 | static struct mtrr_ops centaur_mtrr_ops = { |
| 209 | .vendor = X86_VENDOR_CENTAUR, |
| 210 | // .init = centaur_mcr_init, |
| 211 | .set = centaur_set_mcr, |
| 212 | .get = centaur_get_mcr, |
| 213 | .get_free_region = centaur_get_free_region, |
| 214 | .validate_add_page = centaur_validate_add_page, |
| 215 | .have_wrcomb = positive_have_wrcomb, |
| 216 | }; |
| 217 | |
| 218 | int __init centaur_init_mtrr(void) |
| 219 | { |
| 220 | set_mtrr_ops(¢aur_mtrr_ops); |
| 221 | return 0; |
| 222 | } |
| 223 | |
| 224 | //arch_initcall(centaur_init_mtrr); |