時間子係統10_hpet時鍾初始化
// 時鍾mult :mult/2^shift = ns/cyc
// 參考:https://www.bluezd.info/archives/reg_clock_event_device_1
// x86平台初始化
// 注:arch/x86/kernel/x86_init.c
1.1 struct x86_init_ops x86_init __initdata = {
...
//時鍾初始化
.timers = {
.setup_percpu_clockev = setup_boot_APIC_clock,
.tsc_pre_init = x86_init_noop,
.timer_init = hpet_time_init,
},
...
};
// 時鍾初始化
// 函數任務:
// 1.初始化hpet時鍾源,時鍾事件設備
// 2.初始化tsc時鍾源
// 調用路徑start_kernel->time_init
1.1 void __init time_init(void)
{
//初始化hpet時鍾源
x86_init.timers.timer_init();
tsc_init();
}
// 初始化hpet
// 函數任務:
// 1.注冊hpet clocksource,hpet clockevent
// 2.注冊時鍾中斷處理程序
// 調用路徑:time_init->hpet_time_init
2.1 void __init hpet_time_init(void)
{
//注冊hpet clocksource,hpet clockevent
if (!hpet_enable())
setup_pit_timer();
//分配時鍾中斷
setup_default_timer_irq();
}
// 注冊hpet時鍾源
// 調用路徑:hpet_time_init->hpet_enable->hpet_clocksource_register
// 函數任務:
// 1.初始化hpet clocksource硬件設備
// 2.檢查hpet計數器是否正常工作
// 2.1 等待200000 tsc,判斷hpet計數是否變化
// 3.計算hpet mult值
// 4.向係統注冊hpet時鍾源
2.2 static int hpet_clocksource_register(void)
{
u64 start, now;
cycle_t t1;
//初始化hpet clocksource硬件設備
hpet_restart_counter();
//檢測hpet計數器是否工作
t1 = hpet_readl(HPET_COUNTER);
rdtscll(start);
//檢測辦法
// 200000tsc之後,判斷hpet計數器是否發生變化
do {
rep_nop();
rdtscll(now);
} while ((now - start) < 200000UL);
if (t1 == hpet_readl(HPET_COUNTER)) {
printk(KERN_WARNING
"HPET counter not counting. HPET disabled\n");
return -ENODEV;
}
//計算hpet clocksource的shift值
clocksource_hpet.mult = div_sc(hpet_period, FSEC_PER_NSEC, HPET_SHIFT);
//注冊hpet時鍾源
clocksource_register(&clocksource_hpet);
return 0;
}
// hpet時鍾源
2.3 static struct clocksource clocksource_hpet = {
.name = "hpet",
.rating = 250,//精度排名
.read = read_hpet,
.mask = HPET_MASK,
.shift = HPET_SHIFT,
.flags = CLOCK_SOURCE_IS_CONTINUOUS,//連續時鍾源
.resume = hpet_resume_counter,
};
// 注冊hpet 時鍾事件設備
// 調用路徑:hpet_time_init->hpet_enable->hpet_legacy_clockevent_register
// 函數任務:
// 1.hpet clockevent硬件初始化
// 2.計算hpet mult
// 3.計算hpet cpu掩碼
// 3.1 IO_APIC初始化完成後,設置hpet對所有cpu可用
// 4.計算hpet clockevent設備可重新編程的最大、最小時間間隔
// 5.設置hpet clockevent設備為全局事件設備
// 注:
// hpet clockevent存在可重新編程的上下時間間隔
3.1 static void hpet_legacy_clockevent_register(void)
{
//hpet clockevent硬件初始化
hpet_enable_legacy_int();
//計算hpet clockevent的mult
hpet_clockevent.mult = div_sc((unsigned long) FSEC_PER_NSEC,
hpet_period, hpet_clockevent.shift);
//最大時間間隔
hpet_clockevent.max_delta_ns = clockevent_delta2ns(0x7FFFFFFF,
&hpet_clockevent);
//重新編程的最小時間間隔5us
hpet_clockevent.min_delta_ns = 5000;
//當io-apic初始化完成後,才會使hpet 服務於所有cpu
hpet_clockevent.cpumask = cpumask_of(smp_processor_id());
//注冊hpet clockevent
clockevents_register_device(&hpet_clockevent);
//hpet作為全局時鍾事件設備
global_clock_event = &hpet_clockevent;
}
// hpet clockevent設備
3.2 static struct clock_event_device hpet_clockevent = {
.name = "hpet",
.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,//支持周期和單觸發模式
.set_mode = hpet_legacy_set_mode,
.set_next_event = hpet_legacy_next_event,//設置事件將要發生的時間
.shift = 32,
.irq = 0,//使用0號irq
.rating = 50,
};
// 時鍾中斷控製塊
4.1 static struct irqaction irq0 = {
.handler = timer_interrupt,
.flags = IRQF_DISABLED | IRQF_NOBALANCING | IRQF_IRQPOLL | IRQF_TIMER,
.name = "timer"
};
// 設置時鍾中斷處理程序
// 調用路徑:hpet_time_init->setup_default_timer_irq
4.2 void __init setup_default_timer_irq(void)
{
setup_irq(0, &irq0);
}
// 時鍾中斷處理程序
// 函數任務:
// 1.向中斷控製器應答
// 2.通知全局時鍾事件設備處理時間到期
4.3 static irqreturn_t timer_interrupt(int irq, void *dev_id)
{
//向中斷控製器應答時鍾中斷
if (timer_ack) {
raw_spin_lock(&i8259A_lock);
outb(0x0c, PIC_MASTER_OCW3);
/* Ack the IRQ; AEOI will end it automatically. */
inb(PIC_MASTER_POLL);
raw_spin_unlock(&i8259A_lock);
}
//全局時鍾源設備的時間處理函數
global_clock_event->event_handler(global_clock_event);
if (MCA_bus)
outb_p(inb_p(0x61)| 0x80, 0x61);
return IRQ_HANDLED;
}
最後更新:2017-04-03 14:54:27