PWM
功能描述
M1000 内置两个 PWM controller,每个 controller 提供 4 路 PWM 输出:
- PWM controller0:pwm0 ~ pwm3
- PWM controller1:pwm4 ~ pwm7
每个 controller 的前 3 个 channel 可供用户使用,最后 1 个 channel 为保留 channel。即用户通常可使用 pwm0 ~ pwm2、pwm4 ~ pwm6;pwm3 和 pwm7 如无板级设计明确说明,不建议作为用户 PWM 输出使用。
PWM 输出频率可配置范围为 25 KHz ~ 50 MHz,占空比可配置范围为 0% ~ 100%。频率、周期和占空比的关系如下:
period_ns = 1000000000 / frequency_hz
duty_percent = duty_cycle_ns * 100 / period_ns
每路 PWM channel 可复用的 GPIO pinmux 如下:
| PWM channel | 可选 GPIO pin | 用户使用说明 |
|---|---|---|
| pwm0 | GPIO220、GPIO75 | 可用 |
| pwm1 | GPIO221、GPIO78 | 可用 |
| pwm2 | GPIO222、GPIO83 | 可用 |
| pwm3 | GPIO223、GPIO85 | 保留 |
| pwm4 | GPIO224、GPIO87 | 可用 |
| pwm5 | GPIO225、GPIO89 | 可用 |
| pwm6 | GPIO226、GPIO90 | 可用 |
| pwm7 | GPIO227、GPIO91 | 保留 |
每一路 PWM channel 只能从对应的两个 GPIO 中选择一个作为输出 pin,具体选择通过entrytable启动配置完成。
软件介绍
M1000 PWM 软件栈分为 PWM provider 和 PWM consumer 两部分。
PWM provider 是 M1000 PWM controller 驱动,负责注册 PWM controller,并提供 channel、period、duty cycle、enable 等基础控制能力。设备树中的 pwm0: pwm@700000 这类节点表示 PWM controller,不表示某一路输出 channel。
PWM consumer 是使用 PWM 输出的业务驱动,例如风扇、背光、蜂鸣器或用户自定义设备驱动。consumer 通过设备树的 pwms 属性引用 PWM controller 和 channel,并在驱动中调用 PWM framework API 配置周期、占空比和使能状态。
Linux 下可通过两种方式使用 PWM:
- sysfs:适合调试和手动验证 PWM 输出,不建议作为正式产品逻辑。
- consumer 驱动:适合正式业务场景,由内核驱动在 probe、remove、suspend、resume 等生命周期内管理 PWM。
U-Boot 阶段提供 PWM 命令用于启动阶段调试,也可通过板 级启动配置固定输出参数。entrytable还独立支持在开机时配置一路 PWM参数,当前主要用于 AIModule 风扇控制。
配置流程
Linux 设备树配置
Linux 中 PWM controller 的设备树示例如下:
pwm0: pwm@700000 {
compatible = "mthreads,M1000-pwm";
reg = <0x0 0x00700000 0x0 0x1000>;
mode = <0>;
#pwm-cells = <2>;
clock-names = "peri_pwm_pclk", "peri_pwm_clk";
clocks = <&crg PERI_PWM0_PFUNC_CLK>, <&crg PERI_PWM0_MFUNC_CLK>;
reset-names = "pwm_reset";
resets = <&crg RESET_PERI_PWM0_PRST>;
interrupts = <GIC_SPI 0x10E IRQ_TYPE_LEVEL_HIGH>;
status = "okay";
};
consumer 设备通过 pwms 属性引用 PWM controller:
pwmtest {
compatible = "mthreads,pwm-test";
pwm-names = "pwm-out";
pwms = <&pwm0 0 40000>;
duty-ns = <20000>;
status = "okay";
};
pwms = <&pwm0 0 40000> 的含义如下:
| 字段 | 含义 |
|---|---|
&pwm0 | 引用 PWM controller 节点,即 PWM provider |
0 | 使用该 controller 的 channel 0 |
40000 | PWM 初始化周期,单位 ns |
pwm-names 是 consumer 侧的连接名称,可以由用户自定义,但必须和驱动中 pwm_get() 或 devm_pwm_get() 使用的名称一致。例如设备树配置 pwm-names = "pwm-out",驱动中应使用 devm_pwm_get(dev, "pwm-out") 获取该 PWM。
M1000 PWM controller 当前使用 #pwm-cells = <2>,因此 pwms 中只表达 channel 和 period,不包含 duty cycle。占空比需要由 consumer 驱动配置,或由 consumer 自定义属性表达,例如上例中的 duty-ns = <20000>。duty-ns 不是 PWM framework 的通用 controller 属性,只有对应 consumer 驱动读取并处理该属性时才会生效。
GPIO pinmux 配置
PWM 输出前必须确保对应 GPIO 已切换到 PWM function。PWM输出一般不在内核里做时分复用,所以建议在entrytable中先绑定好所需使用的管脚。
Linux consumer 驱动使用
设备树中新增 pwmtest 节点后,系统不会自动输出 PWM。必须存在与 compatible 匹配的内核驱动,并由该驱动获取和配置 PWM。
驱动中通常使用 devm_pwm_get() 获取 struct pwm_device,再使用 pwm_config()