diff --git a/drivers/timer/Kconfig.x86 b/drivers/timer/Kconfig.x86 index ea6d4a216b7d4..42973b0bcc299 100644 --- a/drivers/timer/Kconfig.x86 +++ b/drivers/timer/Kconfig.x86 @@ -28,6 +28,7 @@ config APIC_TIMER select LOAPIC select TICKLESS_CAPABLE select SYSTEM_CLOCK_LOCK_FREE_COUNT + select TIMER_HAS_64BIT_CYCLE_COUNTER help Use the x86 local APIC in one-shot mode as the system time source. NOTE: this probably isn't what you want except on @@ -67,13 +68,12 @@ config APIC_TIMER_IRQ a different mechanism. config APIC_TIMER_TSC - bool "Use invariant TSC for sys_clock_cycle_get_32()" - select TIMER_HAS_64BIT_CYCLE_COUNTER + bool "Use invariant TSC for Local APIC timer's cycle count" help If your CPU supports invariant TSC, and you know the ratio of the TSC frequency to CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC (the local APIC timer frequency), then enable this for a much faster and more - accurate sys_clock_cycle_get_32(). + accurate Local APIC timer's cycle count. if APIC_TIMER_TSC diff --git a/drivers/timer/apic_timer.c b/drivers/timer/apic_timer.c index cf28acb04d93e..ac39ef9120cb5 100644 --- a/drivers/timer/apic_timer.c +++ b/drivers/timer/apic_timer.c @@ -9,6 +9,7 @@ #include #include #include +#include BUILD_ASSERT(!IS_ENABLED(CONFIG_SMP), "APIC timer doesn't support SMP"); @@ -87,11 +88,17 @@ void sys_clock_set_timeout(int32_t n, bool idle) { ARG_UNUSED(idle); - uint32_t ccr; int full_ticks; /* number of complete ticks we'll wait */ uint32_t full_cycles; /* full_ticks represented as cycles */ uint32_t partial_cycles; /* number of cycles to first tick boundary */ +#ifdef CONFIG_APIC_TIMER_TSC + if (n == K_TICKS_FOREVER) { + x86_write_loapic(LOAPIC_TIMER_ICR, 0x0); + return; + } +#endif + if (n < 1) { full_ticks = 0; } else if ((n == K_TICKS_FOREVER) || (n > MAX_TICKS)) { @@ -113,8 +120,11 @@ void sys_clock_set_timeout(int32_t n, bool idle) k_spinlock_key_t key = k_spin_lock(&lock); - ccr = x86_read_loapic(LOAPIC_TIMER_CCR); - total_cycles += (cached_icr - ccr); +#ifdef CONFIG_APIC_TIMER_TSC + total_cycles = sys_clock_cycle_get_64(); +#else + total_cycles += (cached_icr - x86_read_loapic(LOAPIC_TIMER_CCR)); +#endif partial_cycles = CYCLES_PER_TICK - (total_cycles % CYCLES_PER_TICK); cached_icr = full_cycles + partial_cycles; x86_write_loapic(LOAPIC_TIMER_ICR, cached_icr); @@ -124,13 +134,15 @@ void sys_clock_set_timeout(int32_t n, bool idle) uint32_t sys_clock_elapsed(void) { - uint32_t ccr; - uint32_t ticks; + uint64_t ticks; k_spinlock_key_t key = k_spin_lock(&lock); - ccr = x86_read_loapic(LOAPIC_TIMER_CCR); +#ifdef CONFIG_APIC_TIMER_TSC + ticks = sys_clock_cycle_get_64() - last_announcement; +#else ticks = total_cycles - last_announcement; - ticks += cached_icr - ccr; + ticks += cached_icr - x86_read_loapic(LOAPIC_TIMER_CCR); +#endif k_spin_unlock(&lock, key); ticks /= CYCLES_PER_TICK; @@ -141,8 +153,8 @@ static void isr(const void *arg) { ARG_UNUSED(arg); - uint32_t cycles; int32_t ticks; + uint64_t ticks_u64; k_spinlock_key_t key = k_spin_lock(&lock); @@ -152,19 +164,24 @@ static void isr(const void *arg) * a new counter. Just ignore it. See above for more info. */ - if (x86_read_loapic(LOAPIC_TIMER_CCR) != 0) { + if ((x86_read_loapic(LOAPIC_TIMER_CCR) != 0) + || (x86_read_loapic(LOAPIC_TIMER_ICR) == 0)) { k_spin_unlock(&lock, key); return; } +#ifdef CONFIG_APIC_TIMER_TSC + total_cycles = sys_clock_cycle_get_64(); +#else /* Restart the timer as early as possible to minimize drift... */ x86_write_loapic(LOAPIC_TIMER_ICR, MAX_TICKS * CYCLES_PER_TICK); - cycles = cached_icr; + total_cycles += cached_icr; cached_icr = MAX_TICKS * CYCLES_PER_TICK; - total_cycles += cycles; - ticks = (total_cycles - last_announcement) / CYCLES_PER_TICK; - last_announcement = total_cycles; +#endif + ticks_u64 = (total_cycles - last_announcement) / CYCLES_PER_TICK; + ticks = (ticks_u64 > INT_MAX) ? INT_MAX : (int32_t)ticks_u64; + last_announcement += ticks_u64 * CYCLES_PER_TICK; k_spin_unlock(&lock, key); sys_clock_announce(ticks); } @@ -192,20 +209,18 @@ uint32_t sys_clock_elapsed(void) #ifdef CONFIG_APIC_TIMER_TSC -uint32_t sys_clock_cycle_get_32(void) +uint64_t sys_clock_cycle_get_64(void) { uint64_t tsc = z_tsc_read(); - uint32_t cycles; - cycles = (tsc * CONFIG_APIC_TIMER_TSC_M) / CONFIG_APIC_TIMER_TSC_N; - return cycles; + return (tsc * CONFIG_APIC_TIMER_TSC_M) / CONFIG_APIC_TIMER_TSC_N; } #else -uint32_t sys_clock_cycle_get_32(void) +uint64_t sys_clock_cycle_get_64(void) { - uint32_t ret; + uint64_t ret; uint32_t ccr; k_spinlock_key_t key = k_spin_lock(&lock); @@ -218,6 +233,11 @@ uint32_t sys_clock_cycle_get_32(void) #endif +uint32_t sys_clock_cycle_get_32(void) +{ + return (uint32_t)sys_clock_cycle_get_64(); +} + static int sys_clock_driver_init(void) { uint32_t val; @@ -239,6 +259,11 @@ static int sys_clock_driver_init(void) CONFIG_APIC_TIMER_IRQ_PRIORITY, isr, 0, 0); +#ifdef CONFIG_APIC_TIMER_TSC + total_cycles = sys_clock_cycle_get_64(); + last_announcement = total_cycles - (total_cycles % CYCLES_PER_TICK); + cached_icr = CYCLES_PER_TICK - (total_cycles % CYCLES_PER_TICK); +#endif x86_write_loapic(LOAPIC_TIMER_ICR, cached_icr); irq_enable(CONFIG_APIC_TIMER_IRQ);