blob: ea10fd0554e9d4de00808f605a0dbd0114b74a58 [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>
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070021#include <arch/cpu.h>
Stefan Reinauerfd4f4132013-06-19 12:25:44 -070022#include <arch/early_variables.h>
Elyes HAOUASd731a242018-12-07 14:49:20 +010023#include <cpu/intel/fsb.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{
Elyes HAOUASd731a242018-12-07 14:49:20 +010047 int ia32_fsb = get_ia32_fsb();
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070048
Elyes HAOUASd731a242018-12-07 14:49:20 +010049 if (ia32_fsb > 0) {
50 car_set_var(g_timer_fsb, ia32_fsb);
51 return 0;
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070052 }
Elyes HAOUASd731a242018-12-07 14:49:20 +010053 return -1;
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070054}
Aaron Durbin905bfb02014-03-28 08:40:59 -050055
56static inline u32 get_timer_fsb(void)
57{
58 return car_get_var(g_timer_fsb);
59}
Patrick Georgie135ac52012-11-20 11:53:47 +010060#endif
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +000061
62void init_timer(void)
63{
Elyes HAOUASd6e96862016-08-21 10:12:15 +020064 /* Set the APIC timer to no interrupts and periodic mode */
Vikram Narayanan6649d972011-05-10 21:47:57 +000065 lapic_write(LAPIC_LVTT, (LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED));
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +000066
67 /* Set the divider to 1, no divider */
68 lapic_write(LAPIC_TDCR, LAPIC_TDR_DIV_1);
69
70 /* Set the initial counter to 0xffffffff */
71 lapic_write(LAPIC_TMICT, 0xffffffff);
72
73 /* Set FSB frequency to a reasonable value */
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070074 set_timer_fsb();
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +000075}
76
77void udelay(u32 usecs)
78{
Aaron Durbin905bfb02014-03-28 08:40:59 -050079 u32 start, value, ticks, timer_fsb;
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070080
Aaron Durbin38c326d2013-05-06 12:22:23 -050081 if (!thread_yield_microseconds(usecs))
82 return;
83
Aaron Durbin905bfb02014-03-28 08:40:59 -050084 timer_fsb = get_timer_fsb();
Stefan Reinauerac2ec342012-07-10 15:19:23 -070085 if (!timer_fsb || (lapic_read(LAPIC_LVTT) &
86 (LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED)) !=
Aaron Durbin905bfb02014-03-28 08:40:59 -050087 (LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED)) {
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070088 init_timer();
Aaron Durbin905bfb02014-03-28 08:40:59 -050089 timer_fsb = get_timer_fsb();
90 }
Stefan Reinauer5b6404e2012-04-03 16:11:02 -070091
Lee Leahyc5917072017-03-15 16:38:51 -070092 /* Calculate the number of ticks to run, our FSB runs at timer_fsb Mhz
93 */
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +000094 ticks = usecs * timer_fsb;
95 start = lapic_read(LAPIC_TMCCT);
96 do {
97 value = lapic_read(LAPIC_TMCCT);
Elyes HAOUAScbe7464c2016-08-23 21:07:28 +020098 } while ((start - value) < ticks);
Stefan Reinauer4fbefdd2009-07-21 21:19:06 +000099}
Aaron Durbinfd8291c2013-04-29 17:18:49 -0500100
Arthur Heymansb3587a62018-08-02 13:21:57 +0200101#if IS_ENABLED(CONFIG_LAPIC_MONOTONIC_TIMER)
Aaron Durbinfd8291c2013-04-29 17:18:49 -0500102#include <timer.h>
103
104static struct monotonic_counter {
105 int initialized;
106 struct mono_time time;
107 uint32_t last_value;
Arthur Heymansb3587a62018-08-02 13:21:57 +0200108} mono_counter_g CAR_GLOBAL;
Aaron Durbinfd8291c2013-04-29 17:18:49 -0500109
110void timer_monotonic_get(struct mono_time *mt)
111{
112 uint32_t current_tick;
113 uint32_t usecs_elapsed;
Aaron Durbin905bfb02014-03-28 08:40:59 -0500114 uint32_t timer_fsb;
Arthur Heymansb3587a62018-08-02 13:21:57 +0200115 struct monotonic_counter *mono_counter;
Aaron Durbinfd8291c2013-04-29 17:18:49 -0500116
Arthur Heymansb3587a62018-08-02 13:21:57 +0200117 mono_counter = car_get_var_ptr(&mono_counter_g);
118
119 if (!mono_counter->initialized) {
Aaron Durbinfd8291c2013-04-29 17:18:49 -0500120 init_timer();
Aaron Durbin905bfb02014-03-28 08:40:59 -0500121 timer_fsb = get_timer_fsb();
Aaron Durbinfd8291c2013-04-29 17:18:49 -0500122 /* An FSB frequency of 200Mhz provides a 20 second polling
123 * interval between timer_monotonic_get() calls before wrap
124 * around occurs. */
125 if (timer_fsb > 200)
126 printk(BIOS_WARNING,
127 "apic timer freq (%d) may be too fast.\n",
128 timer_fsb);
Arthur Heymansb3587a62018-08-02 13:21:57 +0200129 mono_counter->last_value = lapic_read(LAPIC_TMCCT);
130 mono_counter->initialized = 1;
Aaron Durbinfd8291c2013-04-29 17:18:49 -0500131 }
132
Aaron Durbin905bfb02014-03-28 08:40:59 -0500133 timer_fsb = get_timer_fsb();
Aaron Durbinfd8291c2013-04-29 17:18:49 -0500134 current_tick = lapic_read(LAPIC_TMCCT);
135 /* Note that the APIC timer counts down. */
Arthur Heymansb3587a62018-08-02 13:21:57 +0200136 usecs_elapsed = (mono_counter->last_value - current_tick) / timer_fsb;
Aaron Durbinfd8291c2013-04-29 17:18:49 -0500137
138 /* Update current time and tick values only if a full tick occurred. */
139 if (usecs_elapsed) {
Arthur Heymansb3587a62018-08-02 13:21:57 +0200140 mono_time_add_usecs(&mono_counter->time, usecs_elapsed);
141 mono_counter->last_value = current_tick;
Aaron Durbinfd8291c2013-04-29 17:18:49 -0500142 }
143
144 /* Save result. */
Arthur Heymansb3587a62018-08-02 13:21:57 +0200145 *mt = mono_counter->time;
Aaron Durbinfd8291c2013-04-29 17:18:49 -0500146}
147#endif