1 #include "apic_timer.h"
2 #include <hal/hwtimer.h>
4 #include <lunaix/clock.h>
5 #include <lunaix/compiler.h>
6 #include <lunaix/spike.h>
7 #include <lunaix/syslog.h>
9 #include <asm/x86_isrm.h>
10 #include "asm/soc/apic.h"
12 LOG_MODULE("APIC_TIMER")
14 #define LVT_ENTRY_TIMER(vector, mode) (LVT_DELIVERY_FIXED | mode | vector)
15 #define APIC_BASETICKS 0x100000
17 // Don't optimize them! Took me an half hour to figure that out...
19 static volatile u8_t apic_timer_done = 0;
20 static volatile ticks_t base_freq = 0;
21 static volatile ticks_t systicks = 0;
23 static timer_tick_cb tick_cb = NULL;
26 temp_intr_routine_apic_timer(const struct hart_state* state)
32 apic_timer_tick_isr(const struct hart_state* state)
36 if (likely((ptr_t)tick_cb)) {
42 apic_timer_check(struct hwtimer* hwt)
44 // TODO check whether apic timer is supported
61 apic_timer_init(struct hwtimer* timer, u32_t hertz, timer_tick_cb timer_cb)
63 ticks_t frequency = hertz;
66 cpu_disable_interrupt();
70 // Remap the IRQ 8 (rtc timer's vector) to RTC_TIMER_IV in ioapic
71 // (Remarks IRQ 8 is pin INTIN8)
72 // See IBM PC/AT Technical Reference 1-10 for old RTC IRQ
73 // See Intel's Multiprocessor Specification for IRQ - IOAPIC INTIN
76 // grab ourselves these irq numbers
77 u32_t iv_timer = isrm_ivexalloc(temp_intr_routine_apic_timer);
79 // Setup a one-shot timer, we will use this to measure the bus speed. So we
80 // can then calibrate apic timer to work at *nearly* accurate hz
81 apic_write_reg(APIC_TIMER_LVT,
82 LVT_ENTRY_TIMER(iv_timer, LVT_TIMER_ONESHOT));
85 apic_write_reg(APIC_TIMER_DCR, APIC_TIMER_DIV64);
88 Timer calibration process - measure the APIC timer base frequency
90 step 1: setup a temporary isr for RTC timer which trigger at each tick
92 step 2: setup a temporary isr for #APIC_TIMER_IV
93 step 3: setup the divider, APIC_TIMER_DCR
94 step 4: Startup RTC timer
95 step 5: Write a large value, v, to APIC_TIMER_ICR to start APIC timer
96 (this must be followed immediately after step 4) step 6: issue a write to
99 When the APIC ICR counting down to 0 #APIC_TIMER_IV triggered, save the
100 rtc timer's counter, k, and disable RTC timer immediately (although the
101 RTC interrupts should be blocked by local APIC as we are currently busy
102 on handling #APIC_TIMER_IV)
104 So the apic timer frequency F_apic in Hz can be calculate as
105 v / F_apic = k / 1024
106 => F_apic = v / k * 1024
110 #ifdef __LUNAIXOS_DEBUG__
111 if (frequency < 1000) {
112 WARN("Frequency too low. Millisecond timer might be dodgy.");
118 sysrtc->cls_mask(sysrtc);
119 apic_write_reg(APIC_TIMER_ICR, APIC_BASETICKS); // start APIC timer
121 // enable interrupt, just for our RTC start ticking!
122 cpu_enable_interrupt();
124 wait_until(apic_timer_done);
126 cpu_disable_interrupt();
128 sysrtc->set_mask(sysrtc);
130 base_freq = sysrtc->get_counts(sysrtc);
131 base_freq = APIC_BASETICKS / base_freq * sysrtc->base_freq;
133 assert_msg(base_freq, "Fail to initialize timer (NOFREQ)");
135 kprintf("hw: %u Hz; os: %u Hz", base_freq, frequency);
138 isrm_ivfree(iv_timer);
140 ticks_t tphz = base_freq / frequency;
141 timer->base_freq = base_freq;
142 apic_write_reg(APIC_TIMER_ICR, tphz);
146 LVT_ENTRY_TIMER(isrm_ivexalloc(apic_timer_tick_isr), LVT_TIMER_PERIODIC));
150 apic_hwtimer_context()
152 static struct hwtimer apic_hwt = {
153 .name = "apic_timer",
154 .class = DEVCLASSV(DEVIF_SOC, DEVFN_TIME, DEV_TIMER, DEV_TIMER_APIC),
155 .init = apic_timer_init,
156 .supported = apic_timer_check,
157 .systicks = apic_get_systicks