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