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