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 | static void |
49 | centaur_get_mcr(unsigned int reg, unsigned long *base, |
50 | unsigned long *size, mtrr_type * type) |
51 | { |
52 | *base = centaur_mcr[reg].high >> PAGE_SHIFT; |
53 | *size = -(centaur_mcr[reg].low & 0xfffff000) >> PAGE_SHIFT; |
54 | *type = MTRR_TYPE_WRCOMB; /* write-combining */ |
55 | |
56 | if (centaur_mcr_type == 1 && ((centaur_mcr[reg].low & 31) & 2)) |
57 | *type = MTRR_TYPE_UNCACHABLE; |
58 | if (centaur_mcr_type == 1 && (centaur_mcr[reg].low & 31) == 25) |
59 | *type = MTRR_TYPE_WRBACK; |
60 | if (centaur_mcr_type == 0 && (centaur_mcr[reg].low & 31) == 31) |
61 | *type = MTRR_TYPE_WRBACK; |
62 | } |
63 | |
64 | static void |
65 | centaur_set_mcr(unsigned int reg, unsigned long base, |
66 | unsigned long size, mtrr_type type) |
67 | { |
68 | unsigned long low, high; |
69 | |
70 | if (size == 0) { |
71 | /* Disable */ |
72 | high = low = 0; |
73 | } else { |
74 | high = base << PAGE_SHIFT; |
75 | if (centaur_mcr_type == 0) { |
76 | /* Only support write-combining... */ |
77 | low = -size << PAGE_SHIFT | 0x1f; |
78 | } else { |
79 | if (type == MTRR_TYPE_UNCACHABLE) |
80 | low = -size << PAGE_SHIFT | 0x02; /* NC */ |
81 | else |
82 | low = -size << PAGE_SHIFT | 0x09; /* WWO, WC */ |
83 | } |
84 | } |
85 | centaur_mcr[reg].high = high; |
86 | centaur_mcr[reg].low = low; |
87 | wrmsr(MSR_IDT_MCR0 + reg, low, high); |
88 | } |
89 | |
90 | static int |
91 | centaur_validate_add_page(unsigned long base, unsigned long size, unsigned int type) |
92 | { |
93 | /* |
94 | * FIXME: Winchip2 supports uncached |
95 | */ |
96 | if (type != MTRR_TYPE_WRCOMB && |
97 | (centaur_mcr_type == 0 || type != MTRR_TYPE_UNCACHABLE)) { |
98 | pr_warn("mtrr: only write-combining%s supported\n" , |
99 | centaur_mcr_type ? " and uncacheable are" : " is" ); |
100 | return -EINVAL; |
101 | } |
102 | return 0; |
103 | } |
104 | |
105 | const struct mtrr_ops centaur_mtrr_ops = { |
106 | .var_regs = 8, |
107 | .set = centaur_set_mcr, |
108 | .get = centaur_get_mcr, |
109 | .get_free_region = centaur_get_free_region, |
110 | .validate_add_page = centaur_validate_add_page, |
111 | .have_wrcomb = positive_have_wrcomb, |
112 | }; |
113 | |