Merge branches 'release', 'asus', 'sony-laptop' and 'thinkpad' into release
[pandora-kernel.git] / arch / x86 / kernel / cpu / mtrr / cyrix.c
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);