xref: /aosp_15_r20/external/coreboot/src/cpu/x86/lapic/apic_timer.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
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