/ src / cpu / x86 / lapic / apic_timer.c
apic_timer.c
 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  
 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  
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  
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