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