Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 1 | /* |
| 2 | * This file is part of the coreboot project. |
Stefan Reinauer | 14e2277 | 2010-04-27 06:56:47 +0000 | [diff] [blame] | 3 | * |
Stefan Reinauer | aeba92a | 2009-04-17 08:37:18 +0000 | [diff] [blame] | 4 | * (C) 2007-2009 coresystems GmbH |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 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 |
Stefan Reinauer | aeba92a | 2009-04-17 08:37:18 +0000 | [diff] [blame] | 8 | * published by the Free Software Foundation; version 2 of |
| 9 | * the License. |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 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. |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 15 | */ |
| 16 | |
| 17 | #include <device/device.h> |
| 18 | #include <console/console.h> |
| 19 | #include <delay.h> |
| 20 | #include <stdlib.h> |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 21 | #include <cpu/cpu.h> |
| 22 | #include <cpu/x86/mtrr.h> |
| 23 | #include <cpu/x86/msr.h> |
| 24 | #include <cpu/x86/lapic.h> |
| 25 | #include <cpu/x86/cache.h> |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 26 | |
| 27 | #define MSR_IA32_PERF_STATUS 0x00000198 |
| 28 | #define MSR_IA32_PERF_CTL 0x00000199 |
| 29 | #define MSR_IA32_MISC_ENABLE 0x000001a0 |
| 30 | |
| 31 | static int c7a_speed_translation[] = { |
Stefan Reinauer | 14e2277 | 2010-04-27 06:56:47 +0000 | [diff] [blame] | 32 | // LFM HFM |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 33 | 0x0409, 0x0f13, // 400MHz, 844mV --> 1500MHz, 1.004V C7-M |
| 34 | 0x0409, 0x1018, // 400MHz, 844mV --> 1600MHz, 1.084V |
| 35 | 0x0409, 0x0c18, // 533MHz, 844mV --> 1600MHz, 1.084V |
| 36 | 0x0409, 0x121c, // 400MHz, 844mV --> 1800MHz, 1.148V |
| 37 | 0x0409, 0x0e1c, // 533MHz, 844mV --> 1860MHz, 1.148V |
| 38 | 0x0409, 0x141f, // 400MHz, 844mV --> 2000MHz, 1.196V |
| 39 | 0x0409, 0x0f1f, // 533MHz, 844mV --> 2000MHz, 1.196V |
| 40 | 0x0406, 0x0a06, // 400MHz, 796mV --> 1000MHz, 796mV C7-M ULV |
| 41 | 0x0406, 0x0a09, // 400MHz, 796mV --> 1000MHz, 844mV |
| 42 | 0x0406, 0x0c09, // 400MHz, 796mV --> 1200MHz, 844mV |
| 43 | 0x0406, 0x0f10, // 400MHz, 796mV --> 1500MHz, 956mV |
| 44 | }; |
| 45 | |
| 46 | static int c7d_speed_translation[] = { |
Stefan Reinauer | 14e2277 | 2010-04-27 06:56:47 +0000 | [diff] [blame] | 47 | // LFM HFM |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 48 | 0x0409, 0x1018, // 400MHz, 844mV --> 1600MHz, 1.084V C7-M |
| 49 | 0x0409, 0x121f, // 400MHz, 844mV --> 1800MHz, 1.196V |
| 50 | 0x0809, 0x121f, // 800MHz, 844mV --> 1800MHz, 1.196V |
| 51 | 0x0409, 0x141f, // 400MHz, 844mV --> 2000MHz, 1.196V |
| 52 | 0x0809, 0x141f, // 800MHz, 844mV --> 2000MHz, 1.196V |
| 53 | 0x0406, 0x0806, // 400MHz, 796mV --> 800MHz, 796mV C7-M ULV |
| 54 | 0x0406, 0x0a06, // 400MHz, 796mV --> 1000MHz, 796mV |
| 55 | 0x0406, 0x0c09, // 400MHz, 796mV --> 1200MHz, 844mV |
| 56 | 0x0806, 0x0c09, // 800MHz, 796mV --> 1200MHz, 844mV |
| 57 | 0x0406, 0x0f10, // 400MHz, 796mV --> 1500MHz, 956mV |
| 58 | 0x0806, 0x1010, // 800MHz, 796mV --> 1600MHz, 956mV |
| 59 | }; |
| 60 | |
| 61 | static void set_c7_speed(int model) { |
| 62 | int cnt, current, new, i; |
| 63 | msr_t msr; |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 64 | printk(BIOS_DEBUG, "Enabling improved C7 clock and voltage.\n"); |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 65 | |
| 66 | // Enable Speedstep |
| 67 | msr = rdmsr(MSR_IA32_MISC_ENABLE); |
| 68 | msr.lo |= (1 << 16); |
| 69 | wrmsr(MSR_IA32_MISC_ENABLE, msr); |
| 70 | |
| 71 | msr = rdmsr(MSR_IA32_PERF_STATUS); |
| 72 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 73 | printk(BIOS_INFO, "Voltage: %dmV (min %dmV; max %dmV)\n", |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 74 | ((int)(msr.lo & 0xff) * 16 + 700), |
| 75 | ((int)((msr.hi >> 16) & 0xff) * 16 + 700), |
| 76 | ((int)(msr.hi & 0xff) * 16 + 700)); |
| 77 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 78 | printk(BIOS_INFO, "CPU multiplier: %dx (min %dx; max %dx)\n", |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 79 | (int)((msr.lo >> 8) & 0xff), |
| 80 | (int)((msr.hi >> 24) & 0xff), (int)((msr.hi >> 8) & 0xff)); |
| 81 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 82 | printk(BIOS_DEBUG, " msr.lo = %x\n", msr.lo); |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 83 | |
| 84 | /* Wait while CPU is busy */ |
| 85 | cnt = 0; |
| 86 | while (msr.lo & ((1 << 16) | (1 << 17))) { |
| 87 | udelay(16); |
| 88 | msr = rdmsr(MSR_IA32_PERF_STATUS); |
| 89 | cnt++; |
| 90 | if (cnt > 128) { |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 91 | printk(BIOS_WARNING, "Could not update multiplier and voltage.\n"); |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 92 | return; |
| 93 | } |
| 94 | } |
| 95 | |
| 96 | current = msr.lo & 0xffff; |
| 97 | |
| 98 | // Start out with no change. |
| 99 | new = current; |
| 100 | switch (model) { |
| 101 | case 10: // model A |
| 102 | for (i = 0; i < ARRAY_SIZE(c7a_speed_translation); i += 2) { |
| 103 | if ((c7a_speed_translation[i] == current) && |
| 104 | ((c7a_speed_translation[i + 1] & 0xff00) == |
| 105 | (msr.hi & 0xff00))) { |
| 106 | new = c7a_speed_translation[i + 1]; |
| 107 | } |
| 108 | } |
| 109 | break; |
| 110 | case 13: // model D |
| 111 | for (i = 0; i < ARRAY_SIZE(c7d_speed_translation); i += 2) { |
| 112 | if ((c7d_speed_translation[i] == current) && |
| 113 | ((c7d_speed_translation[i + 1] & 0xff00) == |
| 114 | (msr.hi & 0xff00))) { |
| 115 | new = c7d_speed_translation[i + 1]; |
| 116 | } |
| 117 | } |
| 118 | break; |
| 119 | default: |
Stefan Reinauer | 5491ca2 | 2015-01-05 13:07:39 -0800 | [diff] [blame] | 120 | printk(BIOS_INFO, "CPU type not known, multiplier unchanged.\n"); |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 121 | } |
| 122 | |
| 123 | msr.lo = new; |
| 124 | msr.hi = 0; |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 125 | printk(BIOS_DEBUG, " new msr.lo = %x\n", msr.lo); |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 126 | |
| 127 | wrmsr(MSR_IA32_PERF_CTL, msr); |
| 128 | |
| 129 | /* Wait until the power transition ends */ |
| 130 | cnt = 0; |
| 131 | do { |
| 132 | udelay(16); |
| 133 | msr = rdmsr(MSR_IA32_PERF_STATUS); |
| 134 | cnt++; |
| 135 | if (cnt > 128) { |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 136 | printk(BIOS_WARNING, "Error while updating multiplier and voltage\n"); |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 137 | break; |
| 138 | } |
| 139 | } while (msr.lo & ((1 << 16) | (1 << 17))); |
| 140 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 141 | printk(BIOS_INFO, "Current voltage: %dmV\n", ((int)(msr.lo & 0xff) * 16 + 700)); |
| 142 | printk(BIOS_INFO, "Current CPU multiplier: %dx\n", (int)((msr.lo >> 8) & 0xff)); |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 143 | } |
| 144 | |
Edward O'Callaghan | 2c9d2cf | 2014-10-27 23:29:29 +1100 | [diff] [blame] | 145 | static void c7_init(struct device *dev) |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 146 | { |
| 147 | u8 brand; |
| 148 | struct cpuinfo_x86 c; |
| 149 | msr_t msr; |
| 150 | |
| 151 | get_fms(&c, dev->device); |
| 152 | |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 153 | printk(BIOS_INFO, "Detected VIA "); |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 154 | |
| 155 | switch (c.x86_model) { |
| 156 | case 10: |
| 157 | msr = rdmsr(0x1153); |
| 158 | brand = (((msr.lo >> 2) ^ msr.lo) >> 18) & 3; |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 159 | printk(BIOS_INFO, "Model A "); |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 160 | break; |
| 161 | case 13: |
| 162 | msr = rdmsr(0x1154); |
| 163 | brand = (((msr.lo >> 4) ^ (msr.lo >> 2))) & 0x000000ff; |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 164 | printk(BIOS_INFO, "Model D "); |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 165 | break; |
| 166 | default: |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 167 | printk(BIOS_INFO, "Model Unknown "); |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 168 | brand = 0xff; |
| 169 | } |
| 170 | |
| 171 | switch (brand) { |
| 172 | case 0: |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 173 | printk(BIOS_INFO, "C7-M\n"); |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 174 | break; |
| 175 | case 1: |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 176 | printk(BIOS_INFO, "C7\n"); |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 177 | break; |
| 178 | case 2: |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 179 | printk(BIOS_INFO, "Eden\n"); |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 180 | break; |
| 181 | case 3: |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 182 | printk(BIOS_INFO, "C7-D\n"); |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 183 | break; |
| 184 | default: |
Stefan Reinauer | c02b4fc | 2010-03-22 11:42:32 +0000 | [diff] [blame] | 185 | printk(BIOS_INFO, "%02x (please report)\n", brand); |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 186 | } |
| 187 | |
| 188 | /* Gear up */ |
| 189 | set_c7_speed(c.x86_model); |
| 190 | |
Stefan Reinauer | aeba92a | 2009-04-17 08:37:18 +0000 | [diff] [blame] | 191 | /* Enable APIC */ |
| 192 | msr = rdmsr(0x1107); |
| 193 | msr.lo |= 1<<24; |
| 194 | wrmsr(0x1107, msr); |
| 195 | |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 196 | /* Turn on cache */ |
| 197 | x86_enable_cache(); |
| 198 | |
| 199 | /* Set up Memory Type Range Registers */ |
Sven Schnelle | adfbcb79 | 2012-01-10 12:01:43 +0100 | [diff] [blame] | 200 | x86_setup_mtrrs(); |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 201 | x86_mtrr_check(); |
| 202 | |
| 203 | /* Enable the local cpu apics */ |
| 204 | setup_lapic(); |
| 205 | }; |
| 206 | |
| 207 | static struct device_operations cpu_dev_ops = { |
Kyösti Mälkki | 12b7262 | 2012-02-09 16:51:38 +0200 | [diff] [blame] | 208 | .init = c7_init, |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 209 | }; |
| 210 | |
Stefan Reinauer | 8677a23 | 2010-12-11 20:33:41 +0000 | [diff] [blame] | 211 | /* Look in arch/x86/lib/cpu.c:cpu_initialize. If there is no CPU with an exact |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 212 | * ID, the cpu mask (stepping) is masked out and the check is repeated. This |
| 213 | * allows us to keep the table significantly smaller. |
| 214 | */ |
| 215 | |
| 216 | static struct cpu_device_id cpu_table[] = { |
| 217 | {X86_VENDOR_CENTAUR, 0x06A0}, // VIA C7 Esther |
Corey Osgood | 845a2eba | 2008-12-18 02:18:45 +0000 | [diff] [blame] | 218 | {X86_VENDOR_CENTAUR, 0x06A9}, // VIA C7 Esther |
Stefan Reinauer | cfcc9ca | 2008-03-18 23:10:24 +0000 | [diff] [blame] | 219 | {X86_VENDOR_CENTAUR, 0x06D0}, // VIA C7-M |
| 220 | {0, 0}, |
| 221 | }; |
| 222 | |
| 223 | static const struct cpu_driver driver __cpu_driver = { |
| 224 | .ops = &cpu_dev_ops, |
| 225 | .id_table = cpu_table, |
| 226 | }; |