]>
Commit | Line | Data |
---|---|---|
bb80be39 SG |
1 | /* |
2 | * From Coreboot file of same name | |
3 | * | |
4 | * Copyright (C) 2007-2009 coresystems GmbH | |
5 | * Copyright (C) 2011 The Chromium Authors | |
6 | * | |
7 | * SPDX-License-Identifier: GPL-2.0 | |
8 | */ | |
9 | ||
10 | #include <common.h> | |
bba22a97 SG |
11 | #include <cpu.h> |
12 | #include <dm.h> | |
bb80be39 SG |
13 | #include <fdtdec.h> |
14 | #include <malloc.h> | |
15 | #include <asm/acpi.h> | |
16 | #include <asm/cpu.h> | |
bba22a97 | 17 | #include <asm/cpu_x86.h> |
bb80be39 | 18 | #include <asm/lapic.h> |
bb80be39 SG |
19 | #include <asm/msr.h> |
20 | #include <asm/mtrr.h> | |
21 | #include <asm/processor.h> | |
22 | #include <asm/speedstep.h> | |
23 | #include <asm/turbo.h> | |
63d54a67 | 24 | #include <asm/arch/bd82x6x.h> |
bb80be39 SG |
25 | #include <asm/arch/model_206ax.h> |
26 | ||
27 | static void enable_vmx(void) | |
28 | { | |
29 | struct cpuid_result regs; | |
30 | #ifdef CONFIG_ENABLE_VMX | |
31 | int enable = true; | |
32 | #else | |
33 | int enable = false; | |
34 | #endif | |
35 | msr_t msr; | |
36 | ||
37 | regs = cpuid(1); | |
38 | /* Check that the VMX is supported before reading or writing the MSR. */ | |
39 | if (!((regs.ecx & CPUID_VMX) || (regs.ecx & CPUID_SMX))) | |
40 | return; | |
41 | ||
42 | msr = msr_read(MSR_IA32_FEATURE_CONTROL); | |
43 | ||
44 | if (msr.lo & (1 << 0)) { | |
45 | debug("VMX is locked, so %s will do nothing\n", __func__); | |
46 | /* VMX locked. If we set it again we get an illegal | |
47 | * instruction | |
48 | */ | |
49 | return; | |
50 | } | |
51 | ||
52 | /* The IA32_FEATURE_CONTROL MSR may initialize with random values. | |
53 | * It must be cleared regardless of VMX config setting. | |
54 | */ | |
55 | msr.hi = 0; | |
56 | msr.lo = 0; | |
57 | ||
58 | debug("%s VMX\n", enable ? "Enabling" : "Disabling"); | |
59 | ||
60 | /* | |
61 | * Even though the Intel manual says you must set the lock bit in | |
62 | * addition to the VMX bit in order for VMX to work, it is incorrect. | |
63 | * Thus we leave it unlocked for the OS to manage things itself. | |
64 | * This is good for a few reasons: | |
65 | * - No need to reflash the bios just to toggle the lock bit. | |
66 | * - The VMX bits really really should match each other across cores, | |
67 | * so hard locking it on one while another has the opposite setting | |
68 | * can easily lead to crashes as code using VMX migrates between | |
69 | * them. | |
70 | * - Vendors that want to "upsell" from a bios that disables+locks to | |
71 | * one that doesn't is sleazy. | |
72 | * By leaving this to the OS (e.g. Linux), people can do exactly what | |
73 | * they want on the fly, and do it correctly (e.g. across multiple | |
74 | * cores). | |
75 | */ | |
76 | if (enable) { | |
77 | msr.lo |= (1 << 2); | |
78 | if (regs.ecx & CPUID_SMX) | |
79 | msr.lo |= (1 << 1); | |
80 | } | |
81 | ||
82 | msr_write(MSR_IA32_FEATURE_CONTROL, msr); | |
83 | } | |
84 | ||
85 | /* Convert time in seconds to POWER_LIMIT_1_TIME MSR value */ | |
86 | static const u8 power_limit_time_sec_to_msr[] = { | |
87 | [0] = 0x00, | |
88 | [1] = 0x0a, | |
89 | [2] = 0x0b, | |
90 | [3] = 0x4b, | |
91 | [4] = 0x0c, | |
92 | [5] = 0x2c, | |
93 | [6] = 0x4c, | |
94 | [7] = 0x6c, | |
95 | [8] = 0x0d, | |
96 | [10] = 0x2d, | |
97 | [12] = 0x4d, | |
98 | [14] = 0x6d, | |
99 | [16] = 0x0e, | |
100 | [20] = 0x2e, | |
101 | [24] = 0x4e, | |
102 | [28] = 0x6e, | |
103 | [32] = 0x0f, | |
104 | [40] = 0x2f, | |
105 | [48] = 0x4f, | |
106 | [56] = 0x6f, | |
107 | [64] = 0x10, | |
108 | [80] = 0x30, | |
109 | [96] = 0x50, | |
110 | [112] = 0x70, | |
111 | [128] = 0x11, | |
112 | }; | |
113 | ||
114 | /* Convert POWER_LIMIT_1_TIME MSR value to seconds */ | |
115 | static const u8 power_limit_time_msr_to_sec[] = { | |
116 | [0x00] = 0, | |
117 | [0x0a] = 1, | |
118 | [0x0b] = 2, | |
119 | [0x4b] = 3, | |
120 | [0x0c] = 4, | |
121 | [0x2c] = 5, | |
122 | [0x4c] = 6, | |
123 | [0x6c] = 7, | |
124 | [0x0d] = 8, | |
125 | [0x2d] = 10, | |
126 | [0x4d] = 12, | |
127 | [0x6d] = 14, | |
128 | [0x0e] = 16, | |
129 | [0x2e] = 20, | |
130 | [0x4e] = 24, | |
131 | [0x6e] = 28, | |
132 | [0x0f] = 32, | |
133 | [0x2f] = 40, | |
134 | [0x4f] = 48, | |
135 | [0x6f] = 56, | |
136 | [0x10] = 64, | |
137 | [0x30] = 80, | |
138 | [0x50] = 96, | |
139 | [0x70] = 112, | |
140 | [0x11] = 128, | |
141 | }; | |
142 | ||
143 | int cpu_config_tdp_levels(void) | |
144 | { | |
145 | struct cpuid_result result; | |
146 | msr_t platform_info; | |
147 | ||
148 | /* Minimum CPU revision */ | |
149 | result = cpuid(1); | |
150 | if (result.eax < IVB_CONFIG_TDP_MIN_CPUID) | |
151 | return 0; | |
152 | ||
153 | /* Bits 34:33 indicate how many levels supported */ | |
154 | platform_info = msr_read(MSR_PLATFORM_INFO); | |
155 | return (platform_info.hi >> 1) & 3; | |
156 | } | |
157 | ||
158 | /* | |
159 | * Configure processor power limits if possible | |
160 | * This must be done AFTER set of BIOS_RESET_CPL | |
161 | */ | |
162 | void set_power_limits(u8 power_limit_1_time) | |
163 | { | |
164 | msr_t msr = msr_read(MSR_PLATFORM_INFO); | |
165 | msr_t limit; | |
166 | unsigned power_unit; | |
167 | unsigned tdp, min_power, max_power, max_time; | |
168 | u8 power_limit_1_val; | |
169 | ||
170 | if (power_limit_1_time > ARRAY_SIZE(power_limit_time_sec_to_msr)) | |
171 | return; | |
172 | ||
173 | if (!(msr.lo & PLATFORM_INFO_SET_TDP)) | |
174 | return; | |
175 | ||
176 | /* Get units */ | |
177 | msr = msr_read(MSR_PKG_POWER_SKU_UNIT); | |
178 | power_unit = 2 << ((msr.lo & 0xf) - 1); | |
179 | ||
180 | /* Get power defaults for this SKU */ | |
181 | msr = msr_read(MSR_PKG_POWER_SKU); | |
182 | tdp = msr.lo & 0x7fff; | |
183 | min_power = (msr.lo >> 16) & 0x7fff; | |
184 | max_power = msr.hi & 0x7fff; | |
185 | max_time = (msr.hi >> 16) & 0x7f; | |
186 | ||
187 | debug("CPU TDP: %u Watts\n", tdp / power_unit); | |
188 | ||
189 | if (power_limit_time_msr_to_sec[max_time] > power_limit_1_time) | |
190 | power_limit_1_time = power_limit_time_msr_to_sec[max_time]; | |
191 | ||
192 | if (min_power > 0 && tdp < min_power) | |
193 | tdp = min_power; | |
194 | ||
195 | if (max_power > 0 && tdp > max_power) | |
196 | tdp = max_power; | |
197 | ||
198 | power_limit_1_val = power_limit_time_sec_to_msr[power_limit_1_time]; | |
199 | ||
200 | /* Set long term power limit to TDP */ | |
201 | limit.lo = 0; | |
202 | limit.lo |= tdp & PKG_POWER_LIMIT_MASK; | |
203 | limit.lo |= PKG_POWER_LIMIT_EN; | |
204 | limit.lo |= (power_limit_1_val & PKG_POWER_LIMIT_TIME_MASK) << | |
205 | PKG_POWER_LIMIT_TIME_SHIFT; | |
206 | ||
207 | /* Set short term power limit to 1.25 * TDP */ | |
208 | limit.hi = 0; | |
209 | limit.hi |= ((tdp * 125) / 100) & PKG_POWER_LIMIT_MASK; | |
210 | limit.hi |= PKG_POWER_LIMIT_EN; | |
211 | /* Power limit 2 time is only programmable on SNB EP/EX */ | |
212 | ||
213 | msr_write(MSR_PKG_POWER_LIMIT, limit); | |
214 | ||
215 | /* Use nominal TDP values for CPUs with configurable TDP */ | |
216 | if (cpu_config_tdp_levels()) { | |
217 | msr = msr_read(MSR_CONFIG_TDP_NOMINAL); | |
218 | limit.hi = 0; | |
219 | limit.lo = msr.lo & 0xff; | |
220 | msr_write(MSR_TURBO_ACTIVATION_RATIO, limit); | |
221 | } | |
222 | } | |
223 | ||
224 | static void configure_c_states(void) | |
225 | { | |
226 | struct cpuid_result result; | |
227 | msr_t msr; | |
228 | ||
229 | msr = msr_read(MSR_PMG_CST_CONFIG_CTL); | |
230 | msr.lo |= (1 << 28); /* C1 Auto Undemotion Enable */ | |
231 | msr.lo |= (1 << 27); /* C3 Auto Undemotion Enable */ | |
232 | msr.lo |= (1 << 26); /* C1 Auto Demotion Enable */ | |
233 | msr.lo |= (1 << 25); /* C3 Auto Demotion Enable */ | |
234 | msr.lo &= ~(1 << 10); /* Disable IO MWAIT redirection */ | |
235 | msr.lo |= 7; /* No package C-state limit */ | |
236 | msr_write(MSR_PMG_CST_CONFIG_CTL, msr); | |
237 | ||
238 | msr = msr_read(MSR_PMG_IO_CAPTURE_ADR); | |
239 | msr.lo &= ~0x7ffff; | |
240 | msr.lo |= (PMB0_BASE + 4); /* LVL_2 base address */ | |
241 | msr.lo |= (2 << 16); /* CST Range: C7 is max C-state */ | |
242 | msr_write(MSR_PMG_IO_CAPTURE_ADR, msr); | |
243 | ||
244 | msr = msr_read(MSR_MISC_PWR_MGMT); | |
245 | msr.lo &= ~(1 << 0); /* Enable P-state HW_ALL coordination */ | |
246 | msr_write(MSR_MISC_PWR_MGMT, msr); | |
247 | ||
248 | msr = msr_read(MSR_POWER_CTL); | |
249 | msr.lo |= (1 << 18); /* Enable Energy Perf Bias MSR 0x1b0 */ | |
250 | msr.lo |= (1 << 1); /* C1E Enable */ | |
251 | msr.lo |= (1 << 0); /* Bi-directional PROCHOT# */ | |
252 | msr_write(MSR_POWER_CTL, msr); | |
253 | ||
254 | /* C3 Interrupt Response Time Limit */ | |
255 | msr.hi = 0; | |
256 | msr.lo = IRTL_VALID | IRTL_1024_NS | 0x50; | |
257 | msr_write(MSR_PKGC3_IRTL, msr); | |
258 | ||
259 | /* C6 Interrupt Response Time Limit */ | |
260 | msr.hi = 0; | |
261 | msr.lo = IRTL_VALID | IRTL_1024_NS | 0x68; | |
262 | msr_write(MSR_PKGC6_IRTL, msr); | |
263 | ||
264 | /* C7 Interrupt Response Time Limit */ | |
265 | msr.hi = 0; | |
266 | msr.lo = IRTL_VALID | IRTL_1024_NS | 0x6D; | |
267 | msr_write(MSR_PKGC7_IRTL, msr); | |
268 | ||
269 | /* Primary Plane Current Limit */ | |
270 | msr = msr_read(MSR_PP0_CURRENT_CONFIG); | |
271 | msr.lo &= ~0x1fff; | |
272 | msr.lo |= PP0_CURRENT_LIMIT; | |
273 | msr_write(MSR_PP0_CURRENT_CONFIG, msr); | |
274 | ||
275 | /* Secondary Plane Current Limit */ | |
276 | msr = msr_read(MSR_PP1_CURRENT_CONFIG); | |
277 | msr.lo &= ~0x1fff; | |
278 | result = cpuid(1); | |
279 | if (result.eax >= 0x30600) | |
280 | msr.lo |= PP1_CURRENT_LIMIT_IVB; | |
281 | else | |
282 | msr.lo |= PP1_CURRENT_LIMIT_SNB; | |
283 | msr_write(MSR_PP1_CURRENT_CONFIG, msr); | |
284 | } | |
285 | ||
286 | static int configure_thermal_target(void) | |
287 | { | |
288 | int tcc_offset; | |
289 | msr_t msr; | |
290 | int node; | |
291 | ||
292 | /* Find pointer to CPU configuration */ | |
293 | node = fdtdec_next_compatible(gd->fdt_blob, 0, | |
294 | COMPAT_INTEL_MODEL_206AX); | |
295 | if (node < 0) | |
296 | return -ENOENT; | |
297 | tcc_offset = fdtdec_get_int(gd->fdt_blob, node, "tcc-offset", 0); | |
298 | ||
299 | /* Set TCC activaiton offset if supported */ | |
300 | msr = msr_read(MSR_PLATFORM_INFO); | |
301 | if ((msr.lo & (1 << 30)) && tcc_offset) { | |
302 | msr = msr_read(MSR_TEMPERATURE_TARGET); | |
303 | msr.lo &= ~(0xf << 24); /* Bits 27:24 */ | |
304 | msr.lo |= (tcc_offset & 0xf) << 24; | |
305 | msr_write(MSR_TEMPERATURE_TARGET, msr); | |
306 | } | |
307 | ||
308 | return 0; | |
309 | } | |
310 | ||
311 | static void configure_misc(void) | |
312 | { | |
313 | msr_t msr; | |
314 | ||
315 | msr = msr_read(IA32_MISC_ENABLE); | |
316 | msr.lo |= (1 << 0); /* Fast String enable */ | |
317 | msr.lo |= (1 << 3); /* TM1/TM2/EMTTM enable */ | |
318 | msr.lo |= (1 << 16); /* Enhanced SpeedStep Enable */ | |
319 | msr_write(IA32_MISC_ENABLE, msr); | |
320 | ||
321 | /* Disable Thermal interrupts */ | |
322 | msr.lo = 0; | |
323 | msr.hi = 0; | |
324 | msr_write(IA32_THERM_INTERRUPT, msr); | |
325 | ||
326 | /* Enable package critical interrupt only */ | |
327 | msr.lo = 1 << 4; | |
328 | msr.hi = 0; | |
329 | msr_write(IA32_PACKAGE_THERM_INTERRUPT, msr); | |
330 | } | |
331 | ||
332 | static void enable_lapic_tpr(void) | |
333 | { | |
334 | msr_t msr; | |
335 | ||
336 | msr = msr_read(MSR_PIC_MSG_CONTROL); | |
337 | msr.lo &= ~(1 << 10); /* Enable APIC TPR updates */ | |
338 | msr_write(MSR_PIC_MSG_CONTROL, msr); | |
339 | } | |
340 | ||
341 | static void configure_dca_cap(void) | |
342 | { | |
343 | struct cpuid_result cpuid_regs; | |
344 | msr_t msr; | |
345 | ||
346 | /* Check feature flag in CPUID.(EAX=1):ECX[18]==1 */ | |
347 | cpuid_regs = cpuid(1); | |
348 | if (cpuid_regs.ecx & (1 << 18)) { | |
349 | msr = msr_read(IA32_PLATFORM_DCA_CAP); | |
350 | msr.lo |= 1; | |
351 | msr_write(IA32_PLATFORM_DCA_CAP, msr); | |
352 | } | |
353 | } | |
354 | ||
355 | static void set_max_ratio(void) | |
356 | { | |
357 | msr_t msr, perf_ctl; | |
358 | ||
359 | perf_ctl.hi = 0; | |
360 | ||
361 | /* Check for configurable TDP option */ | |
362 | if (cpu_config_tdp_levels()) { | |
363 | /* Set to nominal TDP ratio */ | |
364 | msr = msr_read(MSR_CONFIG_TDP_NOMINAL); | |
365 | perf_ctl.lo = (msr.lo & 0xff) << 8; | |
366 | } else { | |
367 | /* Platform Info bits 15:8 give max ratio */ | |
368 | msr = msr_read(MSR_PLATFORM_INFO); | |
369 | perf_ctl.lo = msr.lo & 0xff00; | |
370 | } | |
371 | msr_write(IA32_PERF_CTL, perf_ctl); | |
372 | ||
373 | debug("model_x06ax: frequency set to %d\n", | |
374 | ((perf_ctl.lo >> 8) & 0xff) * SANDYBRIDGE_BCLK); | |
375 | } | |
376 | ||
377 | static void set_energy_perf_bias(u8 policy) | |
378 | { | |
379 | msr_t msr; | |
380 | ||
381 | /* Energy Policy is bits 3:0 */ | |
382 | msr = msr_read(IA32_ENERGY_PERFORMANCE_BIAS); | |
383 | msr.lo &= ~0xf; | |
384 | msr.lo |= policy & 0xf; | |
385 | msr_write(IA32_ENERGY_PERFORMANCE_BIAS, msr); | |
386 | ||
387 | debug("model_x06ax: energy policy set to %u\n", policy); | |
388 | } | |
389 | ||
390 | static void configure_mca(void) | |
391 | { | |
392 | msr_t msr; | |
393 | int i; | |
394 | ||
395 | msr.lo = 0; | |
396 | msr.hi = 0; | |
397 | /* This should only be done on a cold boot */ | |
398 | for (i = 0; i < 7; i++) | |
399 | msr_write(IA32_MC0_STATUS + (i * 4), msr); | |
400 | } | |
401 | ||
402 | #if CONFIG_USBDEBUG | |
403 | static unsigned ehci_debug_addr; | |
404 | #endif | |
405 | ||
bb80be39 SG |
406 | int model_206ax_init(struct x86_cpu_priv *cpu) |
407 | { | |
408 | int ret; | |
409 | ||
410 | /* Clear out pending MCEs */ | |
411 | configure_mca(); | |
412 | ||
413 | #if CONFIG_USBDEBUG | |
414 | /* Is this caution really needed? */ | |
415 | if (!ehci_debug_addr) | |
416 | ehci_debug_addr = get_ehci_debug(); | |
417 | set_ehci_debug(0); | |
418 | #endif | |
419 | ||
420 | /* Setup MTRRs based on physical address size */ | |
421 | #if 0 /* TODO: Implement this */ | |
422 | struct cpuid_result cpuid_regs; | |
423 | ||
424 | cpuid_regs = cpuid(0x80000008); | |
425 | x86_setup_fixed_mtrrs(); | |
426 | x86_setup_var_mtrrs(cpuid_regs.eax & 0xff, 2); | |
427 | x86_mtrr_check(); | |
428 | #endif | |
429 | ||
430 | #if CONFIG_USBDEBUG | |
431 | set_ehci_debug(ehci_debug_addr); | |
432 | #endif | |
433 | ||
434 | /* Enable the local cpu apics */ | |
435 | enable_lapic_tpr(); | |
436 | lapic_setup(); | |
437 | ||
438 | /* Enable virtualization if enabled in CMOS */ | |
439 | enable_vmx(); | |
440 | ||
441 | /* Configure C States */ | |
442 | configure_c_states(); | |
443 | ||
444 | /* Configure Enhanced SpeedStep and Thermal Sensors */ | |
445 | configure_misc(); | |
446 | ||
447 | /* Thermal throttle activation offset */ | |
448 | ret = configure_thermal_target(); | |
bba22a97 SG |
449 | if (ret) { |
450 | debug("Cannot set thermal target\n"); | |
bb80be39 | 451 | return ret; |
bba22a97 | 452 | } |
bb80be39 SG |
453 | |
454 | /* Enable Direct Cache Access */ | |
455 | configure_dca_cap(); | |
456 | ||
457 | /* Set energy policy */ | |
458 | set_energy_perf_bias(ENERGY_POLICY_NORMAL); | |
459 | ||
460 | /* Set Max Ratio */ | |
461 | set_max_ratio(); | |
462 | ||
463 | /* Enable Turbo */ | |
464 | turbo_enable(); | |
465 | ||
bba22a97 SG |
466 | return 0; |
467 | } | |
468 | ||
469 | static int model_206ax_get_info(struct udevice *dev, struct cpu_info *info) | |
470 | { | |
471 | info->features = 1 << CPU_FEAT_L1_CACHE | 1 << CPU_FEAT_MMU; | |
472 | ||
473 | return 0; | |
474 | } | |
475 | ||
476 | static int model_206ax_get_count(struct udevice *dev) | |
477 | { | |
478 | return 4; | |
479 | } | |
bb80be39 | 480 | |
bba22a97 SG |
481 | static int cpu_x86_model_206ax_probe(struct udevice *dev) |
482 | { | |
bb80be39 SG |
483 | return 0; |
484 | } | |
bba22a97 SG |
485 | |
486 | static const struct cpu_ops cpu_x86_model_206ax_ops = { | |
487 | .get_desc = cpu_x86_get_desc, | |
488 | .get_info = model_206ax_get_info, | |
489 | .get_count = model_206ax_get_count, | |
490 | }; | |
491 | ||
492 | static const struct udevice_id cpu_x86_model_206ax_ids[] = { | |
493 | { .compatible = "intel,core-gen3" }, | |
494 | { } | |
495 | }; | |
496 | ||
497 | U_BOOT_DRIVER(cpu_x86_model_206ax_drv) = { | |
498 | .name = "cpu_x86_model_206ax", | |
499 | .id = UCLASS_CPU, | |
500 | .of_match = cpu_x86_model_206ax_ids, | |
501 | .bind = cpu_x86_bind, | |
502 | .probe = cpu_x86_model_206ax_probe, | |
503 | .ops = &cpu_x86_model_206ax_ops, | |
504 | }; |