Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
cyrix.c
Go to the documentation of this file.
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 
12 static void
13 cyrix_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 */
89 static int
90 cyrix_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 
133 static u32 cr4, ccr3;
134 
135 static 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 
161 static 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 
177 static 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) {
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) {
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 
235 typedef struct {
236  unsigned long base;
237  unsigned long size;
239 } arr_state_t;
240 
241 static 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 
246 static unsigned char ccr_state[7] = { 0, 0, 0, 0, 0, 0, 0 };
247 
248 static 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 
268 static 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 
279 {
280  set_mtrr_ops(&cyrix_mtrr_ops);
281  return 0;
282 }