Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/arch/x86/kernel/cpu/mtrr/cyrix.c
26516 views
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, (ccr3 & 0x0f) | 0x10); /* enable MAPEN */
26
((unsigned char *)base)[3] = getCx86(arr);
27
((unsigned char *)base)[2] = getCx86(arr + 1);
28
((unsigned char *)base)[1] = getCx86(arr + 2);
29
rcr = getCx86(CX86_RCR_BASE + reg);
30
setCx86(CX86_CCR3, 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(7, &lbase, &lsize, &ltype);
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(i, &lbase, &lsize, &ltype);
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(i, &lbase, &lsize, &ltype);
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(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(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, (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, ccr3);
170
171
/* Enable caches */
172
write_cr0(read_cr0() & ~X86_CR0_CD);
173
174
/* Restore value of CR4 */
175
if (boot_cpu_has(X86_FEATURE_PGE))
176
__write_cr4(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(arr + 0, ((unsigned char *)&base)[3]);
230
setCx86(arr + 1, ((unsigned char *)&base)[2]);
231
setCx86(arr + 2, (((unsigned char *)&base)[1]) | arr_size);
232
setCx86(CX86_RCR_BASE + reg, 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
246