介绍
通用定时器可以在三个 Linux 子系统中工作。
时钟源 —— 通用定时器可以作为时间子系统的时钟源。
MFD 定时器 —— 仅用于在 MFD 子系统中实现通用定时器的基本定时功能。
PWM – timer8 是 PWM 定时器,并在 PWM 子系统中实现。
这三个部分将分别进行描述。
时钟源
介绍
架构
通用定时器可以用作时钟源或时钟事件。典型用法是在时间子系统中用作广播定时器。下图显示了 Linux 时间子系统的架构。

与时间相关的模块(如节拍设备、hrtimer、时间保持等)依赖于时钟源模块和时钟事件模块,这些模块依赖于硬件定时器。通用定时器是硬件定时器,因此可以用作时钟源模块和时钟事件模块。
通用定时器的频率不足以让其成为系统的节拍设备。实际上,CPU 本地定时器是用于为系统生成节拍的设备。
然而,通用定时器在时间子系统中仍然发挥作用,即作为广播定时器。广播定时器的存在对时间子系统很有用,因为它使 CPU 本地定时器可以使用单次模式。此外,系统可以在空闲状态下停止 CPU 本地定时器以节省功耗。
所有通用定时器都可以用作时钟源。
备注
Timer0 可以用作 LP 和 NP 系统的系统定时器,因此不建议将 Timer0 用作时钟源。
实现
通用定时器的时钟源驱动实现如下文件:
驱动程序位置 |
介绍 |
---|---|
<linux>/drivers/rtkdrivers/clocksource/Kconfig |
时钟源驱动程序 Kconfig |
<linux>/drivers/rtkdrivers/clocksource/Makefile |
时钟源驱动程序 Makefile |
<linux>/drivers/rtkdrivers/clocksource/timer-rtk.c |
通用定时器的时钟源驱动程序 |
配置
设备树配置
设备树的配置如下所示。以 TIMER1 为例:
timer1: timer@4200B200 {
compatible = "realtek,ameba-timer-clk";
reg = <0x4200B200 0x200>;
interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&rcc RTK_CKE_TIM1>;
};
其设备树属性的描述如下:
属性 |
描述 |
可配置? |
---|---|---|
compatible |
用于匹配驱动程序和设备的 ID |
否 |
reg |
寄存器资源 |
否 |
interrupts |
SPI 中断 |
否 |
clocks |
通用定时器时钟节点 |
否 |
编译配置
选择
:

APIs
用户空间 API
无。
内核空间 API
参考 timers_information v5.4 或 timers_information v6.6 获取更多信息。
MFD 定时器
介绍
软件架构
通用定时器的基本计时功能被放置在 MFD(多功能设备)子系统中。由于出现了一种具有多种功能的外设设备,Linux 提供了一个 MFD 子系统。
实际上,MFD 定时器驱动并未使用 MFD 子系统的任何内容,因此此处未展示 MFD 架构。
MFD 定时器驱动只是为内核提供 API,以便内核可以简单地使用定时器中断功能。
所有通用定时器都可以是 MFD 定时器。
备注
Timer0 是 LP 和 NP 的系统定时器,因此不建议将 Timer0 用作 MFD 定时器。
实现
MFD 定时器驱动实现为以下文件:
驱动程序位置 |
介绍 |
---|---|
<linux>/drivers/rtkdrivers/mfd_timer/Kconfig |
MFD 定时器驱动程序 Kconfig |
<linux>/drivers/rtkdrivers/mfd_timer/Makefile |
MFD 定时器驱动程序 Makefile |
<linux>/drivers/rtkdrivers/mfd_timer/rtk-timer.c |
通用定时器的 MFD 定时器驱动程序 |
配置
设备树配置
其设备树配置如下所示。以 timer2 为例:
timer2: timer@4200B400 {
compatible = "realtek,ameba-timer";
reg = <0x4200B400 0x200>;
interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&rcc RTK_CKE_TIM2>;
};
下面列出其属性的描述:
属性 |
描述 |
可配置? |
---|---|---|
compatible |
用于匹配驱动程序和设备的 ID |
否 |
reg |
寄存器资源 |
否 |
interrupts |
SPI 中断 |
否 |
clocks |
通用时钟节点 |
否 |
编译配置
选择
:
APIs
用户空间 API
无。
内核空间 API
API |
描述 |
---|---|
rtk_gtimer_init |
用特定的通道号初始化一个 MFD 定时器 |
rtk_gtimer_dynamic_init |
初始化一个 MFD 定时器,并使用任意空闲通道 |
rtk_gtimer_deinit |
重置定时器 |
rtk_gtimer_int_config |
启用或禁用定时器更新中断 |
rtk_gtimer_int_clear |
清除中断标志 |
rtk_gtimer_change_period |
改变定时器周期 |
rtk_gtimer_start |
启动/停止定时器(计数器不会被同时置为0) |
以下是一个示例,展示如何使用内核 API。
初始化 MFD 定时器设备。
rtk_gtimer_init(Tim_Idx, 10000000, timer_callback, cbdata);
配置 MFD 定时器中断。
rtk_gtimer_int_config(Tim_Idx, 1);
启动定时器。
rtk_gtimer_start(Tim_Idx, 1);
定时器中断将在 10000000 纳秒后触发,然后执行使用参数 cbdata 的处理函数 time_callback。
PWM
介绍
架构
PWM 定时器可以用作 PMW 设备。Linux PWM 子系统的架构如下所示。

应用程序通过 sysfs 层控制 PWM 设备,而内核通过 API 控制它。PWM 核心实现 PWM 子系统的核心逻辑。
备注
只有 timer8 可以用作 PWM 设备。
实现
PWM 驱动程序的实现如下所列:
驱动程序位置 |
介绍 |
---|---|
<linux>/drivers/rtkdrivers/pwm/Kconfig |
PWM 定时器驱动程序 Kconfig |
<linux>/drivers/rtkdrivers/pwm/Makefile |
PWM 定时器驱动程序 Makefile |
<linux>/drivers/rtkdrivers/pwm/rtk-pwm.c |
PWM 定时器的 PWM 驱动程序 |
配置
设备树配置
由于 PWM 定时器也可以作为通用定时器使用,因此 PWM 节点位于定时器节点中。设备树配置如下。
timer8: timer@4100A000 {
compatible = "realtek,ameba-timer";
#address-cells = <1>;
#size-cells = <1>;
reg = <0x4100A000 0x200>;
interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&rcc RTK_CKE_TIM_PWM>;
pwm: pwm@0 {
compatible = "realtek,ameba-pwm";
status = "disabled";
};
};
其属性如下表所述:
属性 |
描述 |
可配置? |
---|---|---|
compatible |
用于匹配驱动程序和设备的 ID |
否 |
reg |
寄存器资源 |
否 |
status |
是否启用设备
|
是 |
pinctrl 的设备树节点在 <dts>/rtl8730e-pinctrl.dtsi
中描述。
pwm_pins: pwm@0 {
pins1 {
pinmux = <REALTEK_PINMUX('B', 14, PWM)>, // HS_PWM0
<REALTEK_PINMUX('B', 15, PWM)>, // HS_PWM1
<REALTEK_PINMUX('B', 16, PWM)>, // HS_PWM2
<REALTEK_PINMUX('A', 11, PWM)>, // HS_PWM3
<REALTEK_PINMUX('A', 12, PWM)>; // HS_PWM4
bias-pull-up;
slew-rate = <0>;
drive-strength = <0>;
};
pins2 {
pinmux = <REALTEK_PINMUX('A', 13, PWM)>; // HS_PWM5
bias-pull-up;
swd-disable;
slew-rate = <0>;
drive-strength = <0>;
};
};
下表列出其属性的描述:
属性 |
描述 |
可配置? |
---|---|---|
pinmux |
PWM 的引脚定义 |
是 |
bias-pull-up |
引脚上拉/下拉状态 |
是 |
slew-rate |
引脚电压转换速率 |
否 |
drive-strength |
引脚驱动强度 |
是 |
备注
请参考引脚复用(pinmux)规范来选择引脚分配。
编译配置
选择
:
APIs
用户空间 API
PWM 子系统提供用户空间接口: /sys/class/pwm
。
用户可以通过 sysfs 层控制 PWM 设备。PWM 节点是 /sys/class/pwm/pwmchip0
。
下面是一个示例:
用户应首先选择一个通道进行导出。导出通道 2 的命令是:
echo 2 > /sys/class/pwm/pwmchip0/export
配置周期为 2000000ns 的命令是:
echo 2000000 > /sys/class/pwm/pwmchip0/pwm2/period
配置占空比为 25%的命令是:
echo 500000 > /sys/class/pwm/pwmchip0/pwm2/duty_cycle
启用 PWM 通道的命令是:
echo 1 > /sys/class/pwm/pwmchip0/pwm2/enable
然后,通道 2 将输出频率为 500Hz、占空比为 25%的方波。
用户空间的 PWM 演示位于 <test>/pwm
。请参考 PWM 演示以获得更多信息。
内核空间 API
PWM 子系统为内核空间提供 API。
API |
描述 |
---|---|
pwm_request |
请求PWM设备 |
pwm_free |
是否PWM设备 |
pwm_apply_state |
原子性地将新状态应用于PWM设备 |
pwm_adjust_config |
将当前PWM配置调整为PWM参数 |
pwm_config |
改变PWM设备的配置 |
pwm_enable |
启动PWM输出切换 |
pwm_disable |
停止PWM输出切换 |
参考 PWM_information v5.4 或 PWM_information v6.6 获取更多细节。
以下是一个展示如何使用内核 API 的例子
请求 PWM 设备。
pwm_dev= pwm_request(pwm_channel_id, NULL);
配置和应用 PWM 设置。
ret = pwm_config(pwm_dev, 500000, 1000000);
启动 PWM 输出。
ret = pwm_enable(pwm_dev);