blob: ec2e71c39e63988a45e10f4970578778ff000db1 [file] [log] [blame]
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +00001/*
2 * This file is part of the coreboot project.
3 *
4 * Copyright (C) 2007 Advanced Micro Devices, Inc.
5 * Copyright (C) 2009 coresystems GmbH
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; version 2 of 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.
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +000015 */
16
17#include <stdint.h>
Aaron Durbinfd8291c2013-04-29 17:18:49 -050018#include <console/console.h>
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +000019#include <delay.h>
Aaron Durbin38c326d2013-05-06 12:22:23 -050020#include <thread.h>
Patrick Georgie135ac52012-11-20 11:53:47 +010021#include <arch/io.h>
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070022#include <arch/cpu.h>
Stefan Reinauerfd4f4132013-06-19 12:25:44 -070023#include <arch/early_variables.h>
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +000024#include <cpu/x86/msr.h>
25#include <cpu/x86/lapic.h>
Patrick Georgi8cc84682013-02-09 15:56:04 +010026#include <cpu/intel/speedstep.h>
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +000027
28/* NOTE: This code uses global variables, so it can not be used during
29 * memory init.
30 */
31
Martin Roth0fa92b32017-06-24 13:53:20 -060032#if CONFIG_UDELAY_LAPIC_FIXED_FSB != 0
Aaron Durbin905bfb02014-03-28 08:40:59 -050033static inline u32 get_timer_fsb(void)
34{
35 return CONFIG_UDELAY_LAPIC_FIXED_FSB;
36}
Patrick Georgie135ac52012-11-20 11:53:47 +010037
38static int set_timer_fsb(void)
39{
40 return 0;
41}
42#else
Aaron Durbin905bfb02014-03-28 08:40:59 -050043static u32 g_timer_fsb CAR_GLOBAL;
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +000044
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070045static int set_timer_fsb(void)
46{
47 struct cpuinfo_x86 c;
48 int core_fsb[8] = { -1, 133, -1, 166, -1, 100, -1, -1 };
Sven Schnelle0860e722012-06-10 19:03:36 +020049 int core2_fsb[8] = { 266, 133, 200, 166, 333, 100, -1, -1 };
Arthur Heymansf3018f92016-12-13 15:21:24 +010050 int f2x_fsb[8] = { 100, 133, 200, 166, -1, -1, -1, -1 };
Elyes HAOUASf6bbc602017-11-26 15:34:20 +010051 msr_t msr;
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070052
53 get_fms(&c, cpuid_eax(1));
Arthur Heymansf3018f92016-12-13 15:21:24 +010054 switch (c.x86) {
55 case 0x6:
56 switch (c.x86_model) {
57 case 0xe: /* Core Solo/Duo */
58 case 0x1c: /* Atom */
59 car_set_var(g_timer_fsb,
60 core_fsb[rdmsr(MSR_FSB_FREQ).lo & 7]);
61 return 0;
62 case 0xf: /* Core 2 or Xeon */
63 case 0x17: /* Enhanced Core */
64 car_set_var(g_timer_fsb,
65 core2_fsb[rdmsr(MSR_FSB_FREQ).lo & 7]);
66 return 0;
67 case 0x2a: /* SandyBridge BCLK fixed at 100MHz*/
68 case 0x3a: /* IvyBridge BCLK fixed at 100MHz*/
69 case 0x3c: /* Haswell BCLK fixed at 100MHz */
70 case 0x45: /* Haswell-ULT BCLK fixed at 100MHz */
71 car_set_var(g_timer_fsb, 100);
72 return 0;
73 default:
74 car_set_var(g_timer_fsb, 200);
75 return 0;
76 }
77 case 0xf: /* Netburst */
Elyes HAOUASf6bbc602017-11-26 15:34:20 +010078 msr = rdmsr(MSR_EBC_FREQUENCY_ID);
Arthur Heymansf3018f92016-12-13 15:21:24 +010079 switch (c.x86_model) {
80 case 0x2:
81 car_set_var(g_timer_fsb,
Elyes HAOUASf6bbc602017-11-26 15:34:20 +010082 f2x_fsb[(msr.lo >> 16) & 7]);
Arthur Heymansf3018f92016-12-13 15:21:24 +010083 return 0;
84 case 0x3:
85 case 0x4:
86 case 0x6:
87 car_set_var(g_timer_fsb,
Elyes HAOUASf6bbc602017-11-26 15:34:20 +010088 core2_fsb[(msr.lo >> 16) & 7]);
Arthur Heymansf3018f92016-12-13 15:21:24 +010089 return 0;
90 } /* default: fallthrough */
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070091 default:
Arthur Heymansf3018f92016-12-13 15:21:24 +010092 return -1;
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070093 }
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070094}
Aaron Durbin905bfb02014-03-28 08:40:59 -050095
96static inline u32 get_timer_fsb(void)
97{
98 return car_get_var(g_timer_fsb);
99}
Patrick Georgie135ac52012-11-20 11:53:47 +0100100#endif
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +0000101
102void init_timer(void)
103{
Elyes HAOUASd6e96862016-08-21 10:12:15 +0200104 /* Set the APIC timer to no interrupts and periodic mode */
Vikram Narayanan6649d972011-05-10 21:47:57 +0000105 lapic_write(LAPIC_LVTT, (LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED));
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +0000106
107 /* Set the divider to 1, no divider */
108 lapic_write(LAPIC_TDCR, LAPIC_TDR_DIV_1);
109
110 /* Set the initial counter to 0xffffffff */
111 lapic_write(LAPIC_TMICT, 0xffffffff);
112
113 /* Set FSB frequency to a reasonable value */
Stefan Reinauer5b6404e2012-04-03 16:11:02 -0700114 set_timer_fsb();
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +0000115}
116
117void udelay(u32 usecs)
118{
Aaron Durbin905bfb02014-03-28 08:40:59 -0500119 u32 start, value, ticks, timer_fsb;
Stefan Reinauer5b6404e2012-04-03 16:11:02 -0700120
Aaron Durbin38c326d2013-05-06 12:22:23 -0500121 if (!thread_yield_microseconds(usecs))
122 return;
123
Aaron Durbin905bfb02014-03-28 08:40:59 -0500124 timer_fsb = get_timer_fsb();
Stefan Reinauerac2ec342012-07-10 15:19:23 -0700125 if (!timer_fsb || (lapic_read(LAPIC_LVTT) &
126 (LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED)) !=
Aaron Durbin905bfb02014-03-28 08:40:59 -0500127 (LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED)) {
Stefan Reinauer5b6404e2012-04-03 16:11:02 -0700128 init_timer();
Aaron Durbin905bfb02014-03-28 08:40:59 -0500129 timer_fsb = get_timer_fsb();
130 }
Stefan Reinauer5b6404e2012-04-03 16:11:02 -0700131
Lee Leahyc5917072017-03-15 16:38:51 -0700132 /* Calculate the number of ticks to run, our FSB runs at timer_fsb Mhz
133 */
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +0000134 ticks = usecs * timer_fsb;
135 start = lapic_read(LAPIC_TMCCT);
136 do {
137 value = lapic_read(LAPIC_TMCCT);
Elyes HAOUAScbe7464c2016-08-23 21:07:28 +0200138 } while ((start - value) < ticks);
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +0000139}
Aaron Durbinfd8291c2013-04-29 17:18:49 -0500140
Martin Roth0fa92b32017-06-24 13:53:20 -0600141#if IS_ENABLED(CONFIG_LAPIC_MONOTONIC_TIMER) && !defined(__PRE_RAM__)
Aaron Durbinfd8291c2013-04-29 17:18:49 -0500142#include <timer.h>
143
144static struct monotonic_counter {
145 int initialized;
146 struct mono_time time;
147 uint32_t last_value;
148} mono_counter;
149
150void timer_monotonic_get(struct mono_time *mt)
151{
152 uint32_t current_tick;
153 uint32_t usecs_elapsed;
Aaron Durbin905bfb02014-03-28 08:40:59 -0500154 uint32_t timer_fsb;
Aaron Durbinfd8291c2013-04-29 17:18:49 -0500155
156 if (!mono_counter.initialized) {
157 init_timer();
Aaron Durbin905bfb02014-03-28 08:40:59 -0500158 timer_fsb = get_timer_fsb();
Aaron Durbinfd8291c2013-04-29 17:18:49 -0500159 /* An FSB frequency of 200Mhz provides a 20 second polling
160 * interval between timer_monotonic_get() calls before wrap
161 * around occurs. */
162 if (timer_fsb > 200)
163 printk(BIOS_WARNING,
164 "apic timer freq (%d) may be too fast.\n",
165 timer_fsb);
166 mono_counter.last_value = lapic_read(LAPIC_TMCCT);
167 mono_counter.initialized = 1;
168 }
169
Aaron Durbin905bfb02014-03-28 08:40:59 -0500170 timer_fsb = get_timer_fsb();
Aaron Durbinfd8291c2013-04-29 17:18:49 -0500171 current_tick = lapic_read(LAPIC_TMCCT);
172 /* Note that the APIC timer counts down. */
173 usecs_elapsed = (mono_counter.last_value - current_tick) / timer_fsb;
174
175 /* Update current time and tick values only if a full tick occurred. */
176 if (usecs_elapsed) {
177 mono_time_add_usecs(&mono_counter.time, usecs_elapsed);
178 mono_counter.last_value = current_tick;
179 }
180
181 /* Save result. */
182 *mt = mono_counter.time;
183}
184#endif