1 /* SPDX-License-Identifier: GPL-2.0-only */
2
3 #include <stdint.h>
4 #include <console/console.h>
5 #include <delay.h>
6 #include <thread.h>
7 #include <cpu/x86/lapic.h>
8
init_timer(void)9 void init_timer(void)
10 {
11 /* Set the APIC timer to no interrupts and periodic mode */
12 lapic_write(LAPIC_LVTT, (LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED));
13
14 /* Set the divider to 1, no divider */
15 lapic_write(LAPIC_TDCR, LAPIC_TDR_DIV_1);
16
17 /* Set the initial counter to 0xffffffff */
18 lapic_write(LAPIC_TMICT, 0xffffffff);
19 }
20
udelay(u32 usecs)21 void udelay(u32 usecs)
22 {
23 u32 start, value, ticks, timer_fsb;
24
25 if (!thread_yield_microseconds(usecs))
26 return;
27
28 timer_fsb = get_timer_fsb();
29 if (!timer_fsb || (lapic_read(LAPIC_LVTT) &
30 (LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED)) !=
31 (LAPIC_LVT_TIMER_PERIODIC | LAPIC_LVT_MASKED)) {
32 init_timer();
33 timer_fsb = get_timer_fsb();
34 }
35
36 /* Calculate the number of ticks to run, our FSB runs at timer_fsb MHz
37 */
38 ticks = usecs * timer_fsb;
39 start = lapic_read(LAPIC_TMCCT);
40 do {
41 value = lapic_read(LAPIC_TMCCT);
42 } while ((start - value) < ticks);
43 }
44
45 #if CONFIG(LAPIC_MONOTONIC_TIMER)
46 #include <timer.h>
47
48 static struct monotonic_counter {
49 int initialized;
50 struct mono_time time;
51 uint32_t last_value;
52 } mono_counter;
53
timer_monotonic_get(struct mono_time * mt)54 void timer_monotonic_get(struct mono_time *mt)
55 {
56 uint32_t current_tick;
57 uint32_t usecs_elapsed;
58 uint32_t timer_fsb;
59
60 if (!mono_counter.initialized) {
61 init_timer();
62 timer_fsb = get_timer_fsb();
63 /* An FSB frequency of 200Mhz provides a 20 second polling
64 * interval between timer_monotonic_get() calls before wrap
65 * around occurs. */
66 if (timer_fsb > 200)
67 printk(BIOS_WARNING,
68 "apic timer freq (%d) may be too fast.\n",
69 timer_fsb);
70 mono_counter.last_value = lapic_read(LAPIC_TMCCT);
71 mono_counter.initialized = 1;
72 }
73
74 timer_fsb = get_timer_fsb();
75 current_tick = lapic_read(LAPIC_TMCCT);
76 /* Note that the APIC timer counts down. */
77 usecs_elapsed = (mono_counter.last_value - current_tick) / timer_fsb;
78
79 /* Update current time and tick values only if a full tick occurred. */
80 if (usecs_elapsed) {
81 mono_time_add_usecs(&mono_counter.time, usecs_elapsed);
82 mono_counter.last_value = current_tick;
83 }
84
85 /* Save result. */
86 *mt = mono_counter.time;
87 }
88 #endif
89