Generic timer

本文深入剖析了Linux在ARM平台上的通用定时器(generictimer)驱动,包括物理计数器和虚拟计数器的概念,以及在不同安全和虚拟化扩展场景下的timer配置。分析了timer的 CompareValue 和 TimerValue 寄存器功能,以及如何通过它们触发中断。此外,还讨论了在系统不支持协处理器访问时如何使用内存映射方式来访问定时器硬件。

Reference: Linux时间子系统之(十七):ARM generic timer驱动代码分析 - ArnoldLu - 博客园 (cnblogs.com)


Timer其实就是定时器,它可以定义一段指定的时间,当时间到了,就会assert一个外部的输出信号

溢出时间    至少40年 ??? REF_CLK=25M/100M, 64bit gray code.能有40年?要算一下

  • 处理器可以通过CNTPCT寄存器来获取system counter的当前值,我们称之physical counter。
  • 有physical就有virtual,processor可以通过CNTVCT寄存器访问virtual counter
  • 对于不支持security extension和virtualization extension的系统,virtual counter和physical counter是一样的值。

每个processor都会附着多个timer 处理逻辑

(1)对于不支持security extension的SOC(不支持security extension也就意味着 不支持virtualization extension),timer实际上有两个,一个是physical timer,另外一个是virtual timer。虽然有两个,不过从行为上看,virtual timer和physical timer行为一致

(2)对于支持security extension但不支持virtualization extension的SOC,每个cpu有三个timer:Non-secure physical timer,Secure physical timer和virtual timer

(3)对于支持virtualization extension的SOC,每个cpu有四个timer:Non-secure PL1 physical timer,Secure PL1 physical timer,Non-secure PL2 physical timer和virtual timer

PL1是什么?

"

每个timer都会有三个寄存器(我们用physical timer为例描述):

(1)64-bit CompareValue register。该寄存器配合system counter可以实现一个64 bit unsigned upcounter。如果physical counter - CompareValue >= 0的话,触发中断。也就是说,CompareValue register其实就是一个64比特的upcounter,设定为一个比当前system counter要大的值,随着system counter的不断累加,当system counter value触及CompareValue register设定的值的时候,便会向GIC触发中断。

(2)32-bit TimerValue register。该寄存器配合system counter可以实现一个32 bit signed downcounter(有的时候,使用downcounter会让软件逻辑更容易,看ARM generic timer的设计人员考虑的多么周到)。开始的时候,我们可以设定TimerValue寄存器的值为1000(假设我们想down count 1000,然后触发中断),向该寄存器写入1000实际上也就是设定了CompareValue register的值是system counter值加上1000。随着system counter的值不断累加,TimerValue register的值在递减,当值<=0的时候,便会向GIC触发中断

(3)32-bit控制寄存器。该寄存器主要对timer进行控制,具体包括:enable或是disable该timer,mask或者unmask该timer的output signal(timer interrupt)

各个processor的各个Timer都可以产生中断,因此它和GIC有接口。当然,由于timer的中断是属于各个CPU的,因此使用PPI类型的中断,具体可以参考GIC文档。当然,如果让timer触发中断,当然要确保该timer是enable并且是umask的。

"

此外,有些系统不支持协处理的访问,这种情况下又想给系统增加ARM generic timer的功能,这时候必须使用memory mapped register的方式来访问ARM generic timer的所有硬件block(包括system counter和per cpu的timer)。这时候,在访问timer硬件的时候虽然性能不佳,但总是好过功能丧失。  utility bus?

<think>我们正在查找ARM Generic Timer在Linux内核中的实现代码。根据之前提供的引用,我们知道ARM Generic Timer涉及系统计数器(System Counter)和每个处理核心(PE-Core)的定时器组(Timers)。在Linux内核中,ARM Generic Timer的实现主要位于以下路径: 1. **驱动代码**:通常在`drivers/clocksource/`目录下,具体文件可能是`arm_arch_timer.c`。 2. **设备树绑定**:在`Documentation/devicetree/bindings/timer/`目录下,有关于ARM架构定时器的设备树绑定文档。 3. **头文件**:在`include/clocksource/`目录下,可能有相关的头文件,如`arm_arch_timer.h`。 此外,ARM Generic Timer在Linux内核中作为时钟源(Clocksource)和时钟事件设备(Clock event device)使用。因此,我们也可以关注内核中与时间子系统相关的部分。 ### 具体代码位置 根据Linux内核版本的不同,路径可能略有差异,但通常ARM Generic Timer的实现位于: - `drivers/clocksource/arm_arch_timer.c` 这个文件实现了ARM架构定时器的驱动,包括系统计数器和每个CPU的定时器。 ### 代码分析 在`arm_arch_timer.c`中,主要实现了以下功能: - 初始化系统计数器(通过读取CNTFRQ寄存器获取频率)。 - 为每个CPU注册时钟事件设备(clock event device)。 - 注册时钟源(clocksource)。 - 处理虚拟化相关的定时器(如果内核配置了虚拟化支持)。 ### 如何查找 1. **在线查看**:可以通过在线内核代码浏览器(如elixir.bootlin.com)搜索`arm_arch_timer.c`。 2. **本地查找**:如果已经下载了Linux内核源码,可以使用以下命令查找: ```bash find /path/to/linux -name "arm_arch_timer.c" ``` ### 示例代码片段 以下是一个简化的代码片段(基于Linux 5.15内核),展示了如何注册时钟源: ```c static u64 arch_counter_read(struct clocksource *cs) { return arch_timer_read_counter(); } static struct clocksource clocksource_counter = { .name = "arch_sys_counter", .rating = 400, .read = arch_counter_read, .mask = CLOCKSOURCE_MASK(56), .flags = CLOCK_SOURCE_IS_CONTINUOUS, }; static int __init arch_timer_clocksource_init(void) { // ... 初始化代码 return clocksource_register_hz(&clocksource_counter, arch_timer_rate); } ``` ### 设备树绑定 在设备树中,ARM Generic Timer通常被描述为一个定时器节点。例如: ```dts timer { compatible = "arm,armv8-timer"; interrupts = <GIC_PPI 13 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>, <GIC_PPI 14 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>, <GIC_PPI 11 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>, <GIC_PPI 10 (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>; }; ``` ### 总结 - **主要文件**:`drivers/clocksource/arm_arch_timer.c` - **相关头文件**:`include/clocksource/arm_arch_timer.h` - **设备树绑定文档**:`Documentation/devicetree/bindings/timer/arm,arch_timer.yaml` 如果您需要更详细的内容,建议查阅具体的内核版本代码。 §§相关问题§§ 1. ARM Generic Timer在Linux内核中是如何初始化的? 2. 如何配置ARM Generic Timer作为系统时钟源? 3. ARM Generic Timer在虚拟化环境中是如何工作的?</think>### ARM Generic Timer在Linux内核中的实现代码位置 ARM Generic Timer在Linux内核中的实现主要集中在以下核心文件中: 1. **驱动实现** 核心代码位于: `drivers/clocksource/arm_arch_timer.c` 该文件实现了: - 定时器寄存器访问(CNTPCT/CNTVCT等) - 时钟源(`clocksource`)和时钟事件(`clock_event_device`)注册 - 虚拟化支持(如`cntvoff`虚拟偏移处理) - 中断处理(物理/虚拟定时器中断) 2. **设备树绑定** 设备树规范定义在: `Documentation/devicetree/bindings/timer/arm,arch_timer.yaml` 描述了硬件参数配置,如: ```dts timer { compatible = "arm,armv8-timer"; interrupts = <1 13 0xf08>, // PPI, secure phys <1 14 0xf08>; // non-secure phys }; ``` 3. **关键头文件** - 寄存器定义:`arch/arm64/include/asm/arch_timer.h` - 虚拟化支持:`arch/arm64/kvm/arch_timer.c` ### 核心功能实现示例 1. **定时器初始化流程**(`arm_arch_timer.c`): ```c static int __init arch_timer_init(void) { // 检测CPU是否支持Generic Timer if (!arch_timer_registered && arch_timer_use_virtual) arch_timer_register(); // 注册时钟源 clocksource_register_hz(&clocksource_counter, arch_timer_rate); } ``` 2. **虚拟化时间处理**(`arch_timer.c`): ```c void kvm_timer_vcpu_init(struct kvm_vcpu *vcpu) { // 设置虚拟计数器偏移 write_sysreg(cntvoff_el2, timer->cntvoff); } ``` ### 配置依赖 内核需启用选项: ```kconfig CONFIG_ARM_ARCH_TIMER=y CONFIG_ARM_ARCH_TIMER_VCT_ACCESS=y // 虚拟计数器访问 CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND=y // 勘误处理 ``` ### 调试建议 1. 通过`dmesg | grep arch_timer`查看初始化日志: ``` [ 0.000000] clocksource: arch_sys_counter: mask: 0xffffffffffffff max_cycles: 0x171024e7e, max_idle_ns: 440795205315 ns ``` 2. 查看`/sys/kernel/debug/clocksource`确认当前时钟源 > 注:完整代码需结合具体内核版本,以上基于Linux 5.15+实现[^1][^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值