blob: 749fef03c4d2869b88ea7c0ed45f9d74d1565965 [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.
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
Paul Menzela46a7122013-02-23 18:37:27 +010018 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +000019 */
20
21#include <stdint.h>
Aaron Durbinfd8291c2013-04-29 17:18:49 -050022#include <console/console.h>
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +000023#include <delay.h>
Patrick Georgie135ac52012-11-20 11:53:47 +010024#include <arch/io.h>
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070025#include <arch/cpu.h>
Patrick Georgie135ac52012-11-20 11:53:47 +010026#include <cpu/x86/car.h>
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +000027#include <cpu/x86/msr.h>
28#include <cpu/x86/lapic.h>
Patrick Georgi8cc84682013-02-09 15:56:04 +010029#include <cpu/intel/speedstep.h>
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +000030
31/* NOTE: This code uses global variables, so it can not be used during
32 * memory init.
33 */
34
Patrick Georgie135ac52012-11-20 11:53:47 +010035#if CONFIG_UDELAY_LAPIC_FIXED_FSB
36static const u32 timer_fsb = CONFIG_UDELAY_LAPIC_FIXED_FSB;
37
38static int set_timer_fsb(void)
39{
40 return 0;
41}
42#else
43static u32 timer_fsb CAR_GLOBAL = 0;
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 };
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070050
51 get_fms(&c, cpuid_eax(1));
52 if (c.x86 != 6)
53 return -1;
54
55 switch (c.x86_model) {
56 case 0xe: /* Core Solo/Duo */
57 case 0x1c: /* Atom */
Patrick Georgi8cc84682013-02-09 15:56:04 +010058 timer_fsb = core_fsb[rdmsr(MSR_FSB_FREQ).lo & 7];
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070059 break;
Sven Schnelle0860e722012-06-10 19:03:36 +020060 case 0xf: /* Core 2 or Xeon */
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070061 case 0x17: /* Enhanced Core */
Patrick Georgi8cc84682013-02-09 15:56:04 +010062 timer_fsb = core2_fsb[rdmsr(MSR_FSB_FREQ).lo & 7];
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070063 break;
64 case 0x2a: /* SandyBridge BCLK fixed at 100MHz*/
Stefan Reinauer8d32b892012-07-10 13:24:29 -070065 case 0x3a: /* IvyBridge BCLK fixed at 100MHz*/
Aaron Durbin8e345d42013-01-15 15:34:08 -060066 case 0x3c: /* Haswell BCLK fixed at 100MHz */
67 case 0x45: /* Haswell-ULT BCLK fixed at 100MHz */
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070068 timer_fsb = 100;
69 break;
70 default:
71 timer_fsb = 200;
72 break;
73 }
74
75 return 0;
76}
Patrick Georgie135ac52012-11-20 11:53:47 +010077#endif
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +000078
79void init_timer(void)
80{
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +000081 /* Set the apic timer to no interrupts and periodic mode */
Vikram Narayanan6649d972011-05-10 21:47:57 +000082 lapic_write(LAPIC_LVTT, (LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED));
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +000083
84 /* Set the divider to 1, no divider */
85 lapic_write(LAPIC_TDCR, LAPIC_TDR_DIV_1);
86
87 /* Set the initial counter to 0xffffffff */
88 lapic_write(LAPIC_TMICT, 0xffffffff);
89
90 /* Set FSB frequency to a reasonable value */
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070091 set_timer_fsb();
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +000092}
93
94void udelay(u32 usecs)
95{
96 u32 start, value, ticks;
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070097
Stefan Reinauerac2ec342012-07-10 15:19:23 -070098 if (!timer_fsb || (lapic_read(LAPIC_LVTT) &
99 (LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED)) !=
100 (LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED))
Stefan Reinauer5b6404e2012-04-03 16:11:02 -0700101 init_timer();
102
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +0000103 /* Calculate the number of ticks to run, our FSB runs at timer_fsb Mhz */
104 ticks = usecs * timer_fsb;
105 start = lapic_read(LAPIC_TMCCT);
106 do {
107 value = lapic_read(LAPIC_TMCCT);
108 } while((start - value) < ticks);
109}
Aaron Durbinfd8291c2013-04-29 17:18:49 -0500110
111#if CONFIG_LAPIC_MONOTONIC_TIMER && !defined(__PRE_RAM__)
112#include <timer.h>
113
114static struct monotonic_counter {
115 int initialized;
116 struct mono_time time;
117 uint32_t last_value;
118} mono_counter;
119
120void timer_monotonic_get(struct mono_time *mt)
121{
122 uint32_t current_tick;
123 uint32_t usecs_elapsed;
124
125 if (!mono_counter.initialized) {
126 init_timer();
127 /* An FSB frequency of 200Mhz provides a 20 second polling
128 * interval between timer_monotonic_get() calls before wrap
129 * around occurs. */
130 if (timer_fsb > 200)
131 printk(BIOS_WARNING,
132 "apic timer freq (%d) may be too fast.\n",
133 timer_fsb);
134 mono_counter.last_value = lapic_read(LAPIC_TMCCT);
135 mono_counter.initialized = 1;
136 }
137
138 current_tick = lapic_read(LAPIC_TMCCT);
139 /* Note that the APIC timer counts down. */
140 usecs_elapsed = (mono_counter.last_value - current_tick) / timer_fsb;
141
142 /* Update current time and tick values only if a full tick occurred. */
143 if (usecs_elapsed) {
144 mono_time_add_usecs(&mono_counter.time, usecs_elapsed);
145 mono_counter.last_value = current_tick;
146 }
147
148 /* Save result. */
149 *mt = mono_counter.time;
150}
151#endif