arm_arch_timer代码阅读
文章目录
画个图镇楼,凭空想象凭空捏造,有错请指出,我肯定会改。
硬件中断以及设备树配置
设备树配置:
arm-timer {
compatible = "arm,armv8-timer";
interrupts = <1 13 0xf04>,
<1 14 0xf04>;
clock-frequency = <50000000>;
};
硬件定时器中断号:
硬件中断号 | 中断描述 |
---|---|
29 | Secure physical Timer |
30 | Non-secure physical Timer |
根据drivers/irqchip/irq-gic-v3.c
里面的代码来看,这两个定时器的中断是属于PPI中断,从设备树映射时,会将中断号+16
处理,即<1 13 0xf04>
里面的13+16
才是真实的硬件中断号29
。
从设备的/proc/interrupt
中获取中断号:(第二个中断arch_timer-no
名称被我修改了)
# cat /proc/interrupts
CPU0 CPU1 CPU2 CPU3
1: 0 0 0 0 GIC-0 29 Level arch_timer
2: 1881 1858 1782 1876 GIC-0 30 Level arch_timer-no
定时器初始化的简要过程
在arch_timer_of_init()
在892行出对设备树里面的中断号进行了解析,也就是第一章节里面的硬件中断号配置,然后存放到
arch_timer_ppi`数组里面。
获取时钟频率
中断映射的过程就先不看了,看一下时钟频率的读取。一开始先从设备树解析时钟频率,这里因为设备树有配置,所以读出来了50MHz
的频率;如果设备树解析不到,那么根据第一个参数输入的情况,选择走if
还是else
分支,这里参数是NULL
,走的是else
分支。
看一下读芯片频率的代码:
查看arm-v8a
手册,可以看到是ARM自带的一个寄存器:通过mrs
指令读取。
申请中断
接下来看arch_timer_init()
函数。一开始如果是 HYP 模式或者虚拟的计时器没有中断,那么走入这个判断分支。当前配置是没有 HYP 模式, 也没有虚拟中断号。所以此分支会执行到。
接下来是为定时器申请中断:当前注册两个,PHYS_SECURE_PPI
和PHYS_NONSECURE_PPI
,和第一节的内容是对应的。
中断处理函数
这里先看一下中断处理函数:arch_timer_handler_phys()
,调用timer_handler()
函数,并将访问类型设置为ARCH_TIMER_PHYS_ACCESS
。
timer_handler()
函数先读定时器的访问控制寄存器,有中断来时清中断,并调用注册的时钟事件设备的回调函数,这个注册的函数回头再看,这里往下看一下读寄存器的过程。
时钟事件设备的处理函数目前来看应该是tick_handle_periodic()
,在周期性时钟事件注册的部分有描述到。
访问定时器的控制寄存器
arch_timer_reg_read()
函数根据访问类型选择不同分支,这里走arch_timer_reg_read_cp15()
。
arch_timer_reg_read_cp15()
函数根据访问类型和寄存器类型,当前读取cntp_ctl_el0
寄存器。
在手册中瞄一眼这个寄存器:
定时器的一些通用初始化
打印时钟相关信息的如下:
当前设备上的打印如下:
arm_arch_timer: Architected cp15 timer(s) running at 50.00MHz (phys).
读定时器计数值
arch_timer_read_counter
函数指针被赋值为arch_counter_get_cntvct()
,读定时器的虚拟计数定时器,里面的数值和物理计数定时器的计数值一致。
arch_timer_reg_read_stable()
如下:
read_sysreg()
则是汇编指令的宏:
关于后续的clocksource
,clockcounter
,timercounter
,sched clock
这些内容,预计还是很大的代码量,暂时先略过,后续有机会补充。
周期性时钟事件的注册
注册周期性的设备里面走的路子比较远,这里就简单看一下过程好了:
CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_of_init);
↘ arch_timer_of_init()
↘ irq_of_parse_and_map()
↘ arch_timer_detect_rate()
↘ arch_timer_init()
↘ arch_timer_register()
↘ request_percpu_irq()
→ PHYS_SECURE_PPI → arch_timer_handler_phys()
→ PHYS_NONSECURE_PPI → arch_timer_handler_phys()
↘ arch_timer_cpu_pm_init()
↘ cpu_pm_register_notifier()
↘ cpuhp_setup_state() → arch_timer_starting_cpu()
→ arch_timer_dying_cpu()
↘ __cpuhp_setup_state()
↘ cpuhp_issue_call()
↘ cpuhp_invoke_callback()
↘ arch_timer_starting_cpu()
↘ __arch_timer_setup()
↘ clockevents_config_and_register()
↘ clockevents_register_device()
↘ tick_check_new_device()
↘ tick_setup_device()
↘ tick_setup_periodic() → tick_handle_periodic()
获取每CPU变量arch_timer_evt
的当前CPU变量,交个__arch_timer_setup()
作为入参。
调用clockevents_config_and_register()
注册和配置时钟事件设备:
clockevents_config_and_register()
配置完后,调用clockevents_register_device()
注册时钟事件设备。
clockevents_register_device()
调用tick_check_new_device()
注册tick
设备。
tick_check_new_device()
调用tick_setup_device()
建立新的tick
设备。
tick_setup_device()
调用tick_setup_periodic()
建立周期性的tick
事件。
tick_setup_periodic()
调用tick_set_periodic_handler()
设置时钟事件设备的回调。
当前来看应该是设置的tick_handle_periodic()
回调。