5474b8d6c788766697683592b12f16de61cef778
[coreboot.git] / src / cpu / via / model_c7 / model_c7_init.c
1 /*
2  * This file is part of the coreboot project.
3  *
4  * (C) 2007-2009 coresystems GmbH
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; version 2 of
9  * the License.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
19  * MA 02110-1301 USA
20  */
21
22 #include <device/device.h>
23 #include <console/console.h>
24 #include <delay.h>
25 #include <stdlib.h>
26
27 #include <cpu/cpu.h>
28 #include <cpu/x86/mtrr.h>
29 #include <cpu/x86/msr.h>
30 #include <cpu/x86/lapic.h>
31 #include <cpu/x86/cache.h>
32 #include <cpu/x86/mtrr.h>
33
34 #define MSR_IA32_PERF_STATUS    0x00000198
35 #define MSR_IA32_PERF_CTL       0x00000199
36 #define MSR_IA32_MISC_ENABLE    0x000001a0
37
38 static int c7a_speed_translation[] = {
39 //      LFM     HFM
40         0x0409, 0x0f13,         // 400MHz, 844mV --> 1500MHz, 1.004V    C7-M
41         0x0409, 0x1018,         // 400MHz, 844mV --> 1600MHz, 1.084V
42         0x0409, 0x0c18,         // 533MHz, 844mV --> 1600MHz, 1.084V
43         0x0409, 0x121c,         // 400MHz, 844mV --> 1800MHz, 1.148V
44         0x0409, 0x0e1c,         // 533MHz, 844mV --> 1860MHz, 1.148V
45         0x0409, 0x141f,         // 400MHz, 844mV --> 2000MHz, 1.196V
46         0x0409, 0x0f1f,         // 533MHz, 844mV --> 2000MHz, 1.196V
47         0x0406, 0x0a06,         // 400MHz, 796mV --> 1000MHz, 796mV     C7-M ULV
48         0x0406, 0x0a09,         // 400MHz, 796mV --> 1000MHz, 844mV
49         0x0406, 0x0c09,         // 400MHz, 796mV --> 1200MHz, 844mV
50         0x0406, 0x0f10,         // 400MHz, 796mV --> 1500MHz, 956mV
51 };
52
53 static int c7d_speed_translation[] = {
54 //      LFM     HFM
55         0x0409, 0x1018,         // 400MHz, 844mV --> 1600MHz, 1.084V    C7-M
56         0x0409, 0x121f,         // 400MHz, 844mV --> 1800MHz, 1.196V
57         0x0809, 0x121f,         // 800MHz, 844mV --> 1800MHz, 1.196V
58         0x0409, 0x141f,         // 400MHz, 844mV --> 2000MHz, 1.196V
59         0x0809, 0x141f,         // 800MHz, 844mV --> 2000MHz, 1.196V
60         0x0406, 0x0806,         // 400MHz, 796mV --> 800MHz, 796mV      C7-M ULV
61         0x0406, 0x0a06,         // 400MHz, 796mV --> 1000MHz, 796mV
62         0x0406, 0x0c09,         // 400MHz, 796mV --> 1200MHz, 844mV
63         0x0806, 0x0c09,         // 800MHz, 796mV --> 1200MHz, 844mV
64         0x0406, 0x0f10,         // 400MHz, 796mV --> 1500MHz, 956mV
65         0x0806, 0x1010,         // 800MHz, 796mV --> 1600MHz, 956mV
66 };
67
68 static void set_c7_speed(int model) {
69         int cnt, current, new, i;
70         msr_t msr;
71         printk(BIOS_DEBUG, "Enabling improved C7 clock and voltage.\n");
72
73         // Enable Speedstep
74         msr = rdmsr(MSR_IA32_MISC_ENABLE);
75         msr.lo |= (1 << 16);
76         wrmsr(MSR_IA32_MISC_ENABLE, msr);
77
78         msr = rdmsr(MSR_IA32_PERF_STATUS);
79
80         printk(BIOS_INFO, "Voltage: %dmV (min %dmV; max %dmV)\n",
81                     ((int)(msr.lo & 0xff) * 16 + 700),
82                     ((int)((msr.hi >> 16) & 0xff) * 16 + 700),
83                     ((int)(msr.hi & 0xff) * 16 + 700));
84
85         printk(BIOS_INFO, "CPU multiplier: %dx (min %dx; max %dx)\n",
86                     (int)((msr.lo >> 8) & 0xff),
87                     (int)((msr.hi >> 24) & 0xff), (int)((msr.hi >> 8) & 0xff));
88
89         printk(BIOS_DEBUG, " msr.lo = %x\n", msr.lo);
90
91         /* Wait while CPU is busy */
92         cnt = 0;
93         while (msr.lo & ((1 << 16) | (1 << 17))) {
94                 udelay(16);
95                 msr = rdmsr(MSR_IA32_PERF_STATUS);
96                 cnt++;
97                 if (cnt > 128) {
98                         printk(BIOS_WARNING, "Could not update multiplier and voltage.\n");
99                         return;
100                 }
101         }
102
103         current = msr.lo & 0xffff;
104
105         // Start out with no change.
106         new = current;
107         switch (model) {
108         case 10:                // model A
109                 for (i = 0; i < ARRAY_SIZE(c7a_speed_translation); i += 2) {
110                         if ((c7a_speed_translation[i] == current) &&
111                             ((c7a_speed_translation[i + 1] & 0xff00) ==
112                              (msr.hi & 0xff00))) {
113                                 new = c7a_speed_translation[i + 1];
114                         }
115                 }
116                 break;
117         case 13:                // model D
118                 for (i = 0; i < ARRAY_SIZE(c7d_speed_translation); i += 2) {
119                         if ((c7d_speed_translation[i] == current) &&
120                             ((c7d_speed_translation[i + 1] & 0xff00) ==
121                              (msr.hi & 0xff00))) {
122                                 new = c7d_speed_translation[i + 1];
123                         }
124                 }
125                 break;
126         default:
127                 print_info("CPU type not known, multiplier unchanged.\n");
128         }
129
130         msr.lo = new;
131         msr.hi = 0;
132         printk(BIOS_DEBUG, " new msr.lo = %x\n", msr.lo);
133
134         wrmsr(MSR_IA32_PERF_CTL, msr);
135
136         /* Wait until the power transition ends */
137         cnt = 0;
138         do {
139                 udelay(16);
140                 msr = rdmsr(MSR_IA32_PERF_STATUS);
141                 cnt++;
142                 if (cnt > 128) {
143                         printk(BIOS_WARNING, "Error while updating multiplier and voltage\n");
144                         break;
145                 }
146         } while (msr.lo & ((1 << 16) | (1 << 17)));
147
148         printk(BIOS_INFO, "Current voltage: %dmV\n", ((int)(msr.lo & 0xff) * 16 + 700));
149         printk(BIOS_INFO, "Current CPU multiplier: %dx\n", (int)((msr.lo >> 8) & 0xff));
150 }
151
152 static void model_c7_init(device_t dev)
153 {
154         u8 brand;
155         struct cpuinfo_x86 c;
156         msr_t msr;
157
158         get_fms(&c, dev->device);
159
160         printk(BIOS_INFO, "Detected VIA ");
161
162         switch (c.x86_model) {
163         case 10:
164                 msr = rdmsr(0x1153);
165                 brand = (((msr.lo >> 2) ^ msr.lo) >> 18) & 3;
166                 printk(BIOS_INFO, "Model A ");
167                 break;
168         case 13:
169                 msr = rdmsr(0x1154);
170                 brand = (((msr.lo >> 4) ^ (msr.lo >> 2))) & 0x000000ff;
171                 printk(BIOS_INFO, "Model D ");
172                 break;
173         default:
174                 printk(BIOS_INFO, "Model Unknown ");
175                 brand = 0xff;
176         }
177
178         switch (brand) {
179         case 0:
180                 printk(BIOS_INFO, "C7-M\n");
181                 break;
182         case 1:
183                 printk(BIOS_INFO, "C7\n");
184                 break;
185         case 2:
186                 printk(BIOS_INFO, "Eden\n");
187                 break;
188         case 3:
189                 printk(BIOS_INFO, "C7-D\n");
190                 break;
191         default:
192                 printk(BIOS_INFO, "%02x (please report)\n", brand);
193         }
194
195         /* Gear up */
196         set_c7_speed(c.x86_model);
197
198         /* Enable APIC */
199         msr = rdmsr(0x1107);
200         msr.lo |= 1<<24;
201         wrmsr(0x1107, msr);
202
203         /* Turn on cache */
204         x86_enable_cache();
205
206         /* Set up Memory Type Range Registers */
207         x86_setup_mtrrs(36);
208         x86_mtrr_check();
209
210         /* Enable the local cpu apics */
211         setup_lapic();
212 };
213
214 static struct device_operations cpu_dev_ops = {
215         .init = model_c7_init,
216 };
217
218 /* Look in arch/i386/lib/cpu.c:cpu_initialize. If there is no CPU with an exact
219  * ID, the cpu mask (stepping) is masked out and the check is repeated. This
220  * allows us to keep the table significantly smaller.
221  */
222
223 static struct cpu_device_id cpu_table[] = {
224         {X86_VENDOR_CENTAUR, 0x06A0},   // VIA C7 Esther
225         {X86_VENDOR_CENTAUR, 0x06A9},   // VIA C7 Esther
226         {X86_VENDOR_CENTAUR, 0x06D0},   // VIA C7-M
227         {0, 0},
228 };
229
230 static const struct cpu_driver driver __cpu_driver = {
231         .ops = &cpu_dev_ops,
232         .id_table = cpu_table,
233 };