时间子系统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