1 | // SPDX-License-Identifier: GPL-2.0 |
2 | #include <linux/init.h> |
3 | #include <linux/io.h> |
4 | #include <linux/mm.h> |
5 | |
6 | #include <asm/processor-cyrix.h> |
7 | #include <asm/processor-flags.h> |
8 | #include <asm/mtrr.h> |
9 | #include <asm/msr.h> |
10 | |
11 | #include "mtrr.h" |
12 | |
13 | static void |
14 | cyrix_get_arr(unsigned int reg, unsigned long *base, |
15 | unsigned long *size, mtrr_type * type) |
16 | { |
17 | unsigned char arr, ccr3, rcr, shift; |
18 | unsigned long flags; |
19 | |
20 | arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ |
21 | |
22 | local_irq_save(flags); |
23 | |
24 | ccr3 = getCx86(CX86_CCR3); |
25 | setCx86(CX86_CCR3, data: (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ |
26 | ((unsigned char *)base)[3] = getCx86(reg: arr); |
27 | ((unsigned char *)base)[2] = getCx86(reg: arr + 1); |
28 | ((unsigned char *)base)[1] = getCx86(reg: arr + 2); |
29 | rcr = getCx86(CX86_RCR_BASE + reg); |
30 | setCx86(CX86_CCR3, data: ccr3); /* disable MAPEN */ |
31 | |
32 | local_irq_restore(flags); |
33 | |
34 | shift = ((unsigned char *) base)[1] & 0x0f; |
35 | *base >>= PAGE_SHIFT; |
36 | |
37 | /* |
38 | * Power of two, at least 4K on ARR0-ARR6, 256K on ARR7 |
39 | * Note: shift==0xf means 4G, this is unsupported. |
40 | */ |
41 | if (shift) |
42 | *size = (reg < 7 ? 0x1UL : 0x40UL) << (shift - 1); |
43 | else |
44 | *size = 0; |
45 | |
46 | /* Bit 0 is Cache Enable on ARR7, Cache Disable on ARR0-ARR6 */ |
47 | if (reg < 7) { |
48 | switch (rcr) { |
49 | case 1: |
50 | *type = MTRR_TYPE_UNCACHABLE; |
51 | break; |
52 | case 8: |
53 | *type = MTRR_TYPE_WRBACK; |
54 | break; |
55 | case 9: |
56 | *type = MTRR_TYPE_WRCOMB; |
57 | break; |
58 | case 24: |
59 | default: |
60 | *type = MTRR_TYPE_WRTHROUGH; |
61 | break; |
62 | } |
63 | } else { |
64 | switch (rcr) { |
65 | case 0: |
66 | *type = MTRR_TYPE_UNCACHABLE; |
67 | break; |
68 | case 8: |
69 | *type = MTRR_TYPE_WRCOMB; |
70 | break; |
71 | case 9: |
72 | *type = MTRR_TYPE_WRBACK; |
73 | break; |
74 | case 25: |
75 | default: |
76 | *type = MTRR_TYPE_WRTHROUGH; |
77 | break; |
78 | } |
79 | } |
80 | } |
81 | |
82 | /* |
83 | * cyrix_get_free_region - get a free ARR. |
84 | * |
85 | * @base: the starting (base) address of the region. |
86 | * @size: the size (in bytes) of the region. |
87 | * |
88 | * Returns: the index of the region on success, else -1 on error. |
89 | */ |
90 | static int |
91 | cyrix_get_free_region(unsigned long base, unsigned long size, int replace_reg) |
92 | { |
93 | unsigned long lbase, lsize; |
94 | mtrr_type ltype; |
95 | int i; |
96 | |
97 | switch (replace_reg) { |
98 | case 7: |
99 | if (size < 0x40) |
100 | break; |
101 | fallthrough; |
102 | case 6: |
103 | case 5: |
104 | case 4: |
105 | return replace_reg; |
106 | case 3: |
107 | case 2: |
108 | case 1: |
109 | case 0: |
110 | return replace_reg; |
111 | } |
112 | /* If we are to set up a region >32M then look at ARR7 immediately */ |
113 | if (size > 0x2000) { |
114 | cyrix_get_arr(reg: 7, base: &lbase, size: &lsize, type: <ype); |
115 | if (lsize == 0) |
116 | return 7; |
117 | /* Else try ARR0-ARR6 first */ |
118 | } else { |
119 | for (i = 0; i < 7; i++) { |
120 | cyrix_get_arr(reg: i, base: &lbase, size: &lsize, type: <ype); |
121 | if (lsize == 0) |
122 | return i; |
123 | } |
124 | /* |
125 | * ARR0-ARR6 isn't free |
126 | * try ARR7 but its size must be at least 256K |
127 | */ |
128 | cyrix_get_arr(reg: i, base: &lbase, size: &lsize, type: <ype); |
129 | if ((lsize == 0) && (size >= 0x40)) |
130 | return i; |
131 | } |
132 | return -ENOSPC; |
133 | } |
134 | |
135 | static u32 cr4, ccr3; |
136 | |
137 | static void prepare_set(void) |
138 | { |
139 | u32 cr0; |
140 | |
141 | /* Save value of CR4 and clear Page Global Enable (bit 7) */ |
142 | if (boot_cpu_has(X86_FEATURE_PGE)) { |
143 | cr4 = __read_cr4(); |
144 | __write_cr4(x: cr4 & ~X86_CR4_PGE); |
145 | } |
146 | |
147 | /* |
148 | * Disable and flush caches. |
149 | * Note that wbinvd flushes the TLBs as a side-effect |
150 | */ |
151 | cr0 = read_cr0() | X86_CR0_CD; |
152 | wbinvd(); |
153 | write_cr0(x: cr0); |
154 | wbinvd(); |
155 | |
156 | /* Cyrix ARRs - everything else was excluded at the top */ |
157 | ccr3 = getCx86(CX86_CCR3); |
158 | |
159 | /* Cyrix ARRs - everything else was excluded at the top */ |
160 | setCx86(CX86_CCR3, data: (ccr3 & 0x0f) | 0x10); |
161 | } |
162 | |
163 | static void post_set(void) |
164 | { |
165 | /* Flush caches and TLBs */ |
166 | wbinvd(); |
167 | |
168 | /* Cyrix ARRs - everything else was excluded at the top */ |
169 | setCx86(CX86_CCR3, data: ccr3); |
170 | |
171 | /* Enable caches */ |
172 | write_cr0(x: read_cr0() & ~X86_CR0_CD); |
173 | |
174 | /* Restore value of CR4 */ |
175 | if (boot_cpu_has(X86_FEATURE_PGE)) |
176 | __write_cr4(x: cr4); |
177 | } |
178 | |
179 | static void cyrix_set_arr(unsigned int reg, unsigned long base, |
180 | unsigned long size, mtrr_type type) |
181 | { |
182 | unsigned char arr, arr_type, arr_size; |
183 | |
184 | arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ |
185 | |
186 | /* count down from 32M (ARR0-ARR6) or from 2G (ARR7) */ |
187 | if (reg >= 7) |
188 | size >>= 6; |
189 | |
190 | size &= 0x7fff; /* make sure arr_size <= 14 */ |
191 | for (arr_size = 0; size; arr_size++, size >>= 1) |
192 | ; |
193 | |
194 | if (reg < 7) { |
195 | switch (type) { |
196 | case MTRR_TYPE_UNCACHABLE: |
197 | arr_type = 1; |
198 | break; |
199 | case MTRR_TYPE_WRCOMB: |
200 | arr_type = 9; |
201 | break; |
202 | case MTRR_TYPE_WRTHROUGH: |
203 | arr_type = 24; |
204 | break; |
205 | default: |
206 | arr_type = 8; |
207 | break; |
208 | } |
209 | } else { |
210 | switch (type) { |
211 | case MTRR_TYPE_UNCACHABLE: |
212 | arr_type = 0; |
213 | break; |
214 | case MTRR_TYPE_WRCOMB: |
215 | arr_type = 8; |
216 | break; |
217 | case MTRR_TYPE_WRTHROUGH: |
218 | arr_type = 25; |
219 | break; |
220 | default: |
221 | arr_type = 9; |
222 | break; |
223 | } |
224 | } |
225 | |
226 | prepare_set(); |
227 | |
228 | base <<= PAGE_SHIFT; |
229 | setCx86(reg: arr + 0, data: ((unsigned char *)&base)[3]); |
230 | setCx86(reg: arr + 1, data: ((unsigned char *)&base)[2]); |
231 | setCx86(reg: arr + 2, data: (((unsigned char *)&base)[1]) | arr_size); |
232 | setCx86(CX86_RCR_BASE + reg, data: arr_type); |
233 | |
234 | post_set(); |
235 | } |
236 | |
237 | const struct mtrr_ops cyrix_mtrr_ops = { |
238 | .var_regs = 8, |
239 | .set = cyrix_set_arr, |
240 | .get = cyrix_get_arr, |
241 | .get_free_region = cyrix_get_free_region, |
242 | .validate_add_page = generic_validate_add_page, |
243 | .have_wrcomb = positive_have_wrcomb, |
244 | }; |
245 | |