閱讀531 返回首頁    go 技術社區[雲棲]


時間子係統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

  上一篇:go 時間子係統11_tsc時鍾初始化
  下一篇:go Featured Domains