Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 | 2 | #include <linux/init.h> |
23110377 | 3 | #include <linux/io.h> |
1da177e4 | 4 | #include <linux/mm.h> |
23110377 | 5 | |
f25f64ed | 6 | #include <asm/processor-cyrix.h> |
7ebad705 | 7 | #include <asm/processor-flags.h> |
23110377 JSR |
8 | #include <asm/mtrr.h> |
9 | #include <asm/msr.h> | |
10 | ||
1da177e4 LT |
11 | #include "mtrr.h" |
12 | ||
1da177e4 LT |
13 | static void |
14 | cyrix_get_arr(unsigned int reg, unsigned long *base, | |
365bff80 | 15 | unsigned long *size, mtrr_type * type) |
1da177e4 | 16 | { |
1da177e4 | 17 | unsigned char arr, ccr3, rcr, shift; |
23110377 | 18 | unsigned long flags; |
1da177e4 LT |
19 | |
20 | arr = CX86_ARR_BASE + (reg << 1) + reg; /* avoid multiplication by 3 */ | |
21 | ||
1da177e4 LT |
22 | local_irq_save(flags); |
23 | ||
24 | ccr3 = getCx86(CX86_CCR3); | |
25 | setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */ | |
23110377 JSR |
26 | ((unsigned char *)base)[3] = getCx86(arr); |
27 | ((unsigned char *)base)[2] = getCx86(arr + 1); | |
28 | ((unsigned char *)base)[1] = getCx86(arr + 2); | |
1da177e4 | 29 | rcr = getCx86(CX86_RCR_BASE + reg); |
23110377 | 30 | setCx86(CX86_CCR3, ccr3); /* disable MAPEN */ |
1da177e4 | 31 | |
1da177e4 | 32 | local_irq_restore(flags); |
23110377 | 33 | |
1da177e4 LT |
34 | shift = ((unsigned char *) base)[1] & 0x0f; |
35 | *base >>= PAGE_SHIFT; | |
36 | ||
23110377 JSR |
37 | /* |
38 | * Power of two, at least 4K on ARR0-ARR6, 256K on ARR7 | |
1da177e4 LT |
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 | ||
23110377 JSR |
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 | */ | |
1da177e4 | 90 | static int |
365bff80 | 91 | cyrix_get_free_region(unsigned long base, unsigned long size, int replace_reg) |
1da177e4 | 92 | { |
365bff80 | 93 | unsigned long lbase, lsize; |
23110377 JSR |
94 | mtrr_type ltype; |
95 | int i; | |
1da177e4 | 96 | |
365bff80 JB |
97 | switch (replace_reg) { |
98 | case 7: | |
99 | if (size < 0x40) | |
100 | break; | |
7468a4ea | 101 | /* Else, fall through */ |
365bff80 JB |
102 | case 6: |
103 | case 5: | |
104 | case 4: | |
105 | return replace_reg; | |
106 | case 3: | |
365bff80 JB |
107 | case 2: |
108 | case 1: | |
109 | case 0: | |
110 | return replace_reg; | |
111 | } | |
1da177e4 LT |
112 | /* If we are to set up a region >32M then look at ARR7 immediately */ |
113 | if (size > 0x2000) { | |
114 | cyrix_get_arr(7, &lbase, &lsize, <ype); | |
115 | if (lsize == 0) | |
116 | return 7; | |
23110377 | 117 | /* Else try ARR0-ARR6 first */ |
1da177e4 LT |
118 | } else { |
119 | for (i = 0; i < 7; i++) { | |
120 | cyrix_get_arr(i, &lbase, &lsize, <ype); | |
1da177e4 LT |
121 | if (lsize == 0) |
122 | return i; | |
123 | } | |
23110377 JSR |
124 | /* |
125 | * ARR0-ARR6 isn't free | |
126 | * try ARR7 but its size must be at least 256K | |
127 | */ | |
1da177e4 LT |
128 | cyrix_get_arr(i, &lbase, &lsize, <ype); |
129 | if ((lsize == 0) && (size >= 0x40)) | |
130 | return i; | |
131 | } | |
132 | return -ENOSPC; | |
133 | } | |
134 | ||
23110377 | 135 | static u32 cr4, ccr3; |
1da177e4 LT |
136 | |
137 | static void prepare_set(void) | |
138 | { | |
139 | u32 cr0; | |
140 | ||
141 | /* Save value of CR4 and clear Page Global Enable (bit 7) */ | |
c109bf95 | 142 | if (boot_cpu_has(X86_FEATURE_PGE)) { |
1e02ce4c AL |
143 | cr4 = __read_cr4(); |
144 | __write_cr4(cr4 & ~X86_CR4_PGE); | |
1da177e4 LT |
145 | } |
146 | ||
23110377 JSR |
147 | /* |
148 | * Disable and flush caches. | |
149 | * Note that wbinvd flushes the TLBs as a side-effect | |
150 | */ | |
7ebad705 | 151 | cr0 = read_cr0() | X86_CR0_CD; |
1da177e4 LT |
152 | wbinvd(); |
153 | write_cr0(cr0); | |
154 | wbinvd(); | |
155 | ||
27b46d76 | 156 | /* Cyrix ARRs - everything else was excluded at the top */ |
1da177e4 LT |
157 | ccr3 = getCx86(CX86_CCR3); |
158 | ||
27b46d76 | 159 | /* Cyrix ARRs - everything else was excluded at the top */ |
1da177e4 | 160 | setCx86(CX86_CCR3, (ccr3 & 0x0f) | 0x10); |
1da177e4 LT |
161 | } |
162 | ||
163 | static void post_set(void) | |
164 | { | |
23110377 | 165 | /* Flush caches and TLBs */ |
1da177e4 LT |
166 | wbinvd(); |
167 | ||
168 | /* Cyrix ARRs - everything else was excluded at the top */ | |
169 | setCx86(CX86_CCR3, ccr3); | |
23110377 JSR |
170 | |
171 | /* Enable caches */ | |
a3d7b7dd | 172 | write_cr0(read_cr0() & ~X86_CR0_CD); |
1da177e4 | 173 | |
23110377 | 174 | /* Restore value of CR4 */ |
c109bf95 | 175 | if (boot_cpu_has(X86_FEATURE_PGE)) |
1e02ce4c | 176 | __write_cr4(cr4); |
1da177e4 LT |
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 */ | |
23110377 JSR |
191 | for (arr_size = 0; size; arr_size++, size >>= 1) |
192 | ; | |
1da177e4 LT |
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; | |
23110377 JSR |
229 | setCx86(arr + 0, ((unsigned char *)&base)[3]); |
230 | setCx86(arr + 1, ((unsigned char *)&base)[2]); | |
231 | setCx86(arr + 2, (((unsigned char *)&base)[1]) | arr_size); | |
1da177e4 LT |
232 | setCx86(CX86_RCR_BASE + reg, arr_type); |
233 | ||
234 | post_set(); | |
235 | } | |
236 | ||
237 | typedef struct { | |
23110377 JSR |
238 | unsigned long base; |
239 | unsigned long size; | |
240 | mtrr_type type; | |
1da177e4 LT |
241 | } arr_state_t; |
242 | ||
80581c43 | 243 | static arr_state_t arr_state[8] = { |
1da177e4 LT |
244 | {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, |
245 | {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL}, {0UL, 0UL, 0UL} | |
246 | }; | |
247 | ||
80581c43 | 248 | static unsigned char ccr_state[7] = { 0, 0, 0, 0, 0, 0, 0 }; |
1da177e4 LT |
249 | |
250 | static void cyrix_set_all(void) | |
251 | { | |
252 | int i; | |
253 | ||
254 | prepare_set(); | |
255 | ||
256 | /* the CCRs are not contiguous */ | |
257 | for (i = 0; i < 4; i++) | |
258 | setCx86(CX86_CCR0 + i, ccr_state[i]); | |
259 | for (; i < 7; i++) | |
260 | setCx86(CX86_CCR4 + i, ccr_state[i]); | |
23110377 JSR |
261 | |
262 | for (i = 0; i < 8; i++) { | |
263 | cyrix_set_arr(i, arr_state[i].base, | |
1da177e4 | 264 | arr_state[i].size, arr_state[i].type); |
23110377 | 265 | } |
1da177e4 LT |
266 | |
267 | post_set(); | |
268 | } | |
269 | ||
3b9cfc0a | 270 | static const struct mtrr_ops cyrix_mtrr_ops = { |
1da177e4 | 271 | .vendor = X86_VENDOR_CYRIX, |
1da177e4 LT |
272 | .set_all = cyrix_set_all, |
273 | .set = cyrix_set_arr, | |
274 | .get = cyrix_get_arr, | |
275 | .get_free_region = cyrix_get_free_region, | |
276 | .validate_add_page = generic_validate_add_page, | |
277 | .have_wrcomb = positive_have_wrcomb, | |
278 | }; | |
279 | ||
280 | int __init cyrix_init_mtrr(void) | |
281 | { | |
282 | set_mtrr_ops(&cyrix_mtrr_ops); | |
283 | return 0; | |
284 | } |