1 // SPDX-License-Identifier: GPL-2.0 2 #include <linux/init.h> 3 #include <linux/mm.h> 4 5 #include <asm/mtrr.h> 6 #include <asm/msr.h> 7 8 #include "mtrr.h" 9 10 static struct { 11 unsigned long high; 12 unsigned long low; 13 } centaur_mcr[8]; 14 15 static u8 centaur_mcr_reserved; 16 static u8 centaur_mcr_type; /* 0 for winchip, 1 for winchip2 */ 17 18 /** 19 * centaur_get_free_region - Get a free MTRR. 20 * 21 * @base: The starting (base) address of the region. 22 * @size: The size (in bytes) of the region. 23 * 24 * Returns: the index of the region on success, else -1 on error. 25 */ 26 static int 27 centaur_get_free_region(unsigned long base, unsigned long size, int replace_reg) 28 { 29 unsigned long lbase, lsize; 30 mtrr_type ltype; 31 int i, max; 32 33 max = num_var_ranges; 34 if (replace_reg >= 0 && replace_reg < max) 35 return replace_reg; 36 37 for (i = 0; i < max; ++i) { 38 if (centaur_mcr_reserved & (1 << i)) 39 continue; 40 mtrr_if->get(i, &lbase, &lsize, <ype); 41 if (lsize == 0) 42 return i; 43 } 44 45 return -ENOSPC; 46 } 47 48 /* 49 * Report boot time MCR setups 50 */ 51 void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi) 52 { 53 centaur_mcr[mcr].low = lo; 54 centaur_mcr[mcr].high = hi; 55 } 56 57 static void 58 centaur_get_mcr(unsigned int reg, unsigned long *base, 59 unsigned long *size, mtrr_type * type) 60 { 61 *base = centaur_mcr[reg].high >> PAGE_SHIFT; 62 *size = -(centaur_mcr[reg].low & 0xfffff000) >> PAGE_SHIFT; 63 *type = MTRR_TYPE_WRCOMB; /* write-combining */ 64 65 if (centaur_mcr_type == 1 && ((centaur_mcr[reg].low & 31) & 2)) 66 *type = MTRR_TYPE_UNCACHABLE; 67 if (centaur_mcr_type == 1 && (centaur_mcr[reg].low & 31) == 25) 68 *type = MTRR_TYPE_WRBACK; 69 if (centaur_mcr_type == 0 && (centaur_mcr[reg].low & 31) == 31) 70 *type = MTRR_TYPE_WRBACK; 71 } 72 73 static void 74 centaur_set_mcr(unsigned int reg, unsigned long base, 75 unsigned long size, mtrr_type type) 76 { 77 unsigned long low, high; 78 79 if (size == 0) { 80 /* Disable */ 81 high = low = 0; 82 } else { 83 high = base << PAGE_SHIFT; 84 if (centaur_mcr_type == 0) { 85 /* Only support write-combining... */ 86 low = -size << PAGE_SHIFT | 0x1f; 87 } else { 88 if (type == MTRR_TYPE_UNCACHABLE) 89 low = -size << PAGE_SHIFT | 0x02; /* NC */ 90 else 91 low = -size << PAGE_SHIFT | 0x09; /* WWO, WC */ 92 } 93 } 94 centaur_mcr[reg].high = high; 95 centaur_mcr[reg].low = low; 96 wrmsr(MSR_IDT_MCR0 + reg, low, high); 97 } 98 99 static int 100 centaur_validate_add_page(unsigned long base, unsigned long size, unsigned int type) 101 { 102 /* 103 * FIXME: Winchip2 supports uncached 104 */ 105 if (type != MTRR_TYPE_WRCOMB && 106 (centaur_mcr_type == 0 || type != MTRR_TYPE_UNCACHABLE)) { 107 pr_warn("mtrr: only write-combining%s supported\n", 108 centaur_mcr_type ? " and uncacheable are" : " is"); 109 return -EINVAL; 110 } 111 return 0; 112 } 113 114 const struct mtrr_ops centaur_mtrr_ops = { 115 .vendor = X86_VENDOR_CENTAUR, 116 .set = centaur_set_mcr, 117 .get = centaur_get_mcr, 118 .get_free_region = centaur_get_free_region, 119 .validate_add_page = centaur_validate_add_page, 120 .have_wrcomb = positive_have_wrcomb, 121 }; 122