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("timer(apic)")
14 #define LVT_ENTRY_TIMER(vector, mode) (LVT_DELIVERY_FIXED | mode | vector)
15 #define APIC_BASETICKS 0x100000
18 temp_intr_routine_apic_timer(const struct hart_state* state)
20 struct hwtimer_pot* pot;
22 pot = (struct hwtimer_pot*)isrm_get_payload(state);
23 pot->systick_raw = (ticks_t)-1;
27 apic_timer_tick_isr(const struct hart_state* state)
29 struct hwtimer_pot* pot;
31 pot = (struct hwtimer_pot*)isrm_get_payload(state);
34 if (likely(__ptr(pot->callback))) {
40 __apic_timer_calibrate(struct hwtimer_pot* pot, u32_t hertz)
42 ticks_t base_freq = 0;
44 cpu_disable_interrupt();
48 // grab ourselves these irq numbers
49 u32_t iv_timer = isrm_ivexalloc(temp_intr_routine_apic_timer);
50 isrm_set_payload(iv_timer, __ptr(pot));
52 // Setup a one-shot timer, we will use this to measure the bus speed. So we
53 // can then calibrate apic timer to work at *nearly* accurate hz
54 apic_write_reg(APIC_TIMER_LVT,
55 LVT_ENTRY_TIMER(iv_timer, LVT_TIMER_ONESHOT));
58 apic_write_reg(APIC_TIMER_DCR, APIC_TIMER_DIV64);
61 Timer calibration process - measure the APIC timer base frequency
63 step 1: setup a temporary isr for RTC timer which trigger at each tick
65 step 2: setup a temporary isr for #APIC_TIMER_IV
66 step 3: setup the divider, APIC_TIMER_DCR
67 step 4: Startup RTC timer
68 step 5: Write a large value, v, to APIC_TIMER_ICR to start APIC timer
69 (this must be followed immediately after step 4) step 6: issue a write to
72 When the APIC ICR counting down to 0 #APIC_TIMER_IV triggered, save the
73 rtc timer's counter, k, and disable RTC timer immediately (although the
74 RTC interrupts should be blocked by local APIC as we are currently busy
75 on handling #APIC_TIMER_IV)
77 So the apic timer frequency F_apic in Hz can be calculate as
79 => F_apic = v / k * 1024
83 sysrtc->ops->set_proactive(sysrtc, true);
84 apic_write_reg(APIC_TIMER_ICR, APIC_BASETICKS); // start APIC timer
87 cpu_enable_interrupt();
89 wait_until(!(pot->systick_raw + 1));
91 cpu_disable_interrupt();
92 isrm_ivfree(iv_timer);
94 sysrtc->ops->set_proactive(sysrtc, false);
96 base_freq = sysrtc->live;
97 base_freq = APIC_BASETICKS / base_freq * sysrtc->base_freq;
98 pot->base_freq = base_freq;
101 assert_msg(base_freq, "Fail to initialize timer (NOFREQ)");
102 INFO("hw: %u Hz; os: %u Hz", base_freq, hertz);
104 apic_write_reg(APIC_TIMER_ICR, base_freq / hertz);
106 iv_timer = isrm_ivexalloc(apic_timer_tick_isr);
107 isrm_set_payload(iv_timer, __ptr(pot));
111 LVT_ENTRY_TIMER(iv_timer, LVT_TIMER_PERIODIC));
114 static struct hwtimer_pot_ops potops = {
115 .calibrate = __apic_timer_calibrate,
119 __apic_timer_load(struct device_def* def)
121 struct device* timer;
123 timer = device_allocsys(NULL, NULL);
125 hwtimer_attach_potens(timer, HWTIMER_MAX_PRECEDENCE, &potops);
126 register_device_var(timer, &def->class, "apic-timer");
131 static struct device_def x86_apic_timer =
133 def_device_class(INTEL, TIME, TIMER_APIC),
134 def_device_name("Intel APIC Timer"),
136 def_on_load(__apic_timer_load)
138 EXPORT_DEVICE(apic_timer, &x86_apic_timer, load_sysconf);