1 #include "apic_timer.h"
2 #include <hal/hwtimer.h>
5 #include <lunaix/clock.h>
6 #include <lunaix/compiler.h>
7 #include <lunaix/spike.h>
8 #include <lunaix/syslog.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 apic_timer_count_stop(irq_t irq, const struct hart_state* state)
20 struct hwtimer_pot* pot;
22 pot = irq_payload(irq, struct hwtimer_pot);
23 pot->systick_raw = (ticks_t)-1;
27 apic_timer_tick_isr(irq_t irq, const struct hart_state* state)
29 struct hwtimer_pot* pot;
31 pot = irq_payload(irq, struct hwtimer_pot);
34 if (likely(__ptr(pot->callback))) {
40 __apic_timer_calibrate(struct hwtimer_pot* pot, u32_t hertz)
43 ticks_t base_freq = 0;
45 cpu_disable_interrupt();
47 irq = irq_declare_direct(apic_timer_count_stop);
48 irq_set_payload(irq, pot);
49 irq_assign(irq_get_default_domain(), irq, NULL);
51 // Setup a one-shot timer, we will use this to measure the bus speed. So we
52 // can then calibrate apic timer to work at *nearly* accurate hz
53 apic_write_reg(APIC_TIMER_LVT,
54 LVT_ENTRY_TIMER(irq->vector, LVT_TIMER_ONESHOT));
57 apic_write_reg(APIC_TIMER_DCR, APIC_TIMER_DIV64);
60 Timer calibration process - measure the APIC timer base frequency
62 step 1: setup a temporary isr for RTC timer which trigger at each tick
64 step 2: setup a temporary isr for #APIC_TIMER_IV
65 step 3: setup the divider, APIC_TIMER_DCR
66 step 4: Startup RTC timer
67 step 5: Write a large value, v, to APIC_TIMER_ICR to start APIC timer
68 (this must be followed immediately after step 4) step 6: issue a write to
71 When the APIC ICR counting down to 0 #APIC_TIMER_IV triggered, save the
72 rtc timer's counter, k, and disable RTC timer immediately (although the
73 RTC interrupts should be blocked by local APIC as we are currently busy
74 on handling #APIC_TIMER_IV)
76 So the apic timer frequency F_apic in Hz can be calculate as
78 => F_apic = v / k * 1024
82 sysrtc->ops->set_proactive(sysrtc, true);
83 apic_write_reg(APIC_TIMER_ICR, APIC_BASETICKS); // start APIC timer
86 cpu_enable_interrupt();
88 wait_until(!(pot->systick_raw + 1));
90 cpu_disable_interrupt();
92 sysrtc->ops->set_proactive(sysrtc, false);
94 base_freq = sysrtc->live;
95 base_freq = APIC_BASETICKS / base_freq * sysrtc->base_freq;
96 pot->base_freq = base_freq;
99 assert_msg(base_freq, "Fail to initialize timer (NOFREQ)");
100 INFO("hw: %u Hz; os: %u Hz", base_freq, hertz);
102 irq_set_servant(irq, apic_timer_tick_isr);
104 apic_write_reg(APIC_TIMER_ICR, base_freq / hertz);
107 LVT_ENTRY_TIMER(irq->vector, LVT_TIMER_PERIODIC));
110 static struct hwtimer_pot_ops potops = {
111 .calibrate = __apic_timer_calibrate,
115 __apic_timer_load(struct device_def* def)
117 struct device* timer;
119 timer = device_allocsys(NULL, NULL);
121 hwtimer_attach_potens(timer, HWTIMER_MAX_PRECEDENCE, &potops);
122 register_device_var(timer, &def->class, "apic-timer");
127 static struct device_def x86_apic_timer =
129 def_device_class(INTEL, TIME, TIMER_APIC),
130 def_device_name("Intel APIC Timer"),
132 def_on_load(__apic_timer_load)
134 EXPORT_DEVICE(apic_timer, &x86_apic_timer, load_sysconf);