时间子系统10_hpet时钟初始化

//	时钟mult :mult/2^shift = ns/cyc 

//	参考:http://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;
}

你可能感兴趣的:(Linux时间子系统)