531
技術社區[雲棲]
時間子係統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