1 #include <hal/apic_timer.h>
3 #include <hal/hwtimer.h>
5 #include <lunaix/compiler.h>
6 #include <lunaix/isrm.h>
7 #include <lunaix/spike.h>
8 #include <lunaix/syslog.h>
12 LOG_MODULE("APIC_TIMER")
14 #define LVT_ENTRY_TIMER(vector, mode) (LVT_DELIVERY_FIXED | mode | vector)
15 #define APIC_CALIBRATION_CONST 0x100000
17 // Don't optimize them! Took me an half hour to figure that out...
19 static volatile u32_t rtc_counter = 0;
20 static volatile u8_t apic_timer_done = 0;
21 static volatile ticks_t base_freq = 0;
22 static volatile ticks_t systicks = 0;
24 static timer_tick_cb tick_cb = NULL;
27 __rtc_on_tick_cb(struct hwrtc* param)
33 temp_intr_routine_apic_timer(const isr_param* param)
35 base_freq = APIC_CALIBRATION_CONST / rtc_counter * current_rtc->base_freq;
40 apic_timer_tick_isr(const isr_param* param)
44 if (likely((ptr_t)tick_cb)) {
50 apic_timer_check(struct hwtimer_context* hwt)
52 // TODO check whether apic timer is supported
69 apic_timer_init(struct hwtimer_context* timer,
71 timer_tick_cb timer_cb)
73 ticks_t frequency = hertz;
76 cpu_disable_interrupt();
80 // Remap the IRQ 8 (rtc timer's vector) to RTC_TIMER_IV in ioapic
81 // (Remarks IRQ 8 is pin INTIN8)
82 // See IBM PC/AT Technical Reference 1-10 for old RTC IRQ
83 // See Intel's Multiprocessor Specification for IRQ - IOAPIC INTIN
86 // grab ourselves these irq numbers
87 u32_t iv_timer = isrm_ivexalloc(temp_intr_routine_apic_timer);
89 // Setup a one-shot timer, we will use this to measure the bus speed. So we
90 // can then calibrate apic timer to work at *nearly* accurate hz
91 apic_write_reg(APIC_TIMER_LVT,
92 LVT_ENTRY_TIMER(iv_timer, LVT_TIMER_ONESHOT));
95 apic_write_reg(APIC_TIMER_DCR, APIC_TIMER_DIV64);
98 Timer calibration process - measure the APIC timer base frequency
100 step 1: setup a temporary isr for RTC timer which trigger at each tick
102 step 2: setup a temporary isr for #APIC_TIMER_IV
103 step 3: setup the divider, APIC_TIMER_DCR
104 step 4: Startup RTC timer
105 step 5: Write a large value, v, to APIC_TIMER_ICR to start APIC timer
106 (this must be followed immediately after step 4) step 6: issue a write to
109 When the APIC ICR counting down to 0 #APIC_TIMER_IV triggered, save the
110 rtc timer's counter, k, and disable RTC timer immediately (although the
111 RTC interrupts should be blocked by local APIC as we are currently busy
112 on handling #APIC_TIMER_IV)
114 So the apic timer frequency F_apic in Hz can be calculate as
115 v / F_apic = k / 1024
116 => F_apic = v / k * 1024
120 #ifdef __LUNAIXOS_DEBUG__
121 if (frequency < 1000) {
122 kprintf(KWARN "Frequency too low. Millisecond timer might be dodgy.");
129 current_rtc->do_ticking(current_rtc, __rtc_on_tick_cb);
130 apic_write_reg(APIC_TIMER_ICR, APIC_CALIBRATION_CONST); // start APIC timer
132 // enable interrupt, just for our RTC start ticking!
133 cpu_enable_interrupt();
135 wait_until(apic_timer_done);
137 current_rtc->end_ticking(current_rtc);
139 cpu_disable_interrupt();
141 assert_msg(base_freq, "Fail to initialize timer (NOFREQ)");
143 kprintf(KINFO "hw: %u Hz; os: %u Hz\n", base_freq, frequency);
146 isrm_ivfree(iv_timer);
150 LVT_ENTRY_TIMER(isrm_ivexalloc(apic_timer_tick_isr), LVT_TIMER_PERIODIC));
152 timer->base_freq = base_freq;
154 ticks_t tphz = base_freq / frequency;
155 apic_write_reg(APIC_TIMER_ICR, tphz);
158 struct hwtimer_context*
159 apic_hwtimer_context()
161 static struct hwtimer_context apic_hwt = { .name = "apic_timer",
162 .init = apic_timer_init,
163 .supported = apic_timer_check,
164 .systicks = apic_get_systicks };