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