Linux PWM接口概述
PWM(脉冲宽度调制)接口用于控制LED、风扇或手机中的振动器。具有固定目的的PWM无需实现Linux PWM API(尽管它们可以)。然而,在SoC上通常会发现作为离散设备的PWM,它们没有固定的用途。将它们连接到LED或风扇取决于板设计者。为了提供这种灵活性,存在通用PWM API。
PWM的识别
使用传统PWM API的用户使用唯一ID来引用PWM设备。而不是通过其唯一ID引用PWM设备,板设置代码应该注册一个静态映射,可以用于将PWM消费者与提供者匹配,如下例所示:
static struct pwm_lookup board_pwm_lookup[] = { PWM_LOOKUP("tegra-pwm", 0, "pwm-backlight", NULL, 50000, PWM_POLARITY_NORMAL), }; static void __init board_init(void) { ... pwm_add_table(board_pwm_lookup, ARRAY_SIZE(board_pwm_lookup)); ... }
使用PWM
消费者使用pwm_get()
函数,并传递消费者设备或消费者名称。pwm_put()
用于释放PWM设备。还存在getter的托管变体devm_pwm_get()
和devm_fwnode_pwm_get()
。
请求后,PWM必须使用以下方式配置:
int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state);
此API控制PWM周期/占空比配置以及启用/禁用状态。
作为消费者,不要依赖于已禁用的PWM的输出状态。如果可能的话,驱动程序应该发出非活动状态,但有些驱动程序可能无法这样做。如果您依赖于获取非活动状态,请使用.duty_cycle=0, .enabled=true
。
还有一个usage_power
设置:如果设置,PWM驱动程序只需要维持功率输出,但在信号形式方面具有更大的自由度。如果驱动程序支持,信号可以进行优化,例如通过相移芯片的各个通道来改善电磁干扰。
pwm_config()
、pwm_enable()
和pwm_disable()
函数只是pwm_apply_state()
的包装器,如果用户希望一次更改多个参数,则不应使用它们。例如,如果您在同一个函数中看到pwm_config()
和pwm_{enable,disable}()
调用,这可能意味着您应该切换到pwm_apply_state()
。
PWM用户API还允许查询上一次pwm_apply_state()
调用传递的PWM状态,使用pwm_get_state()
。请注意,如果请求无法完全满足硬件的实际实现,这与驱动程序实际实现的不同。目前,消费者无法获取实际实现的设置。
除了PWM状态之外,PWM API还公开了PWM参数,这是应该在此PWM上使用的参考PWM配置。PWM参数通常是特定于平台的,并允许PWM用户只关心相对于完整周期的占空比(例如,占空比=周期的50%)。struct pwm_args
包含2个字段(周期和极性),应该用于设置初始PWM配置(通常在PWM用户的探测函数中完成)。使用pwm_get_args()
检索PWM参数。
所有消费者在恢复时都应该重新配置PWM。这是确保一切按正确顺序恢复的唯一方法。
使用sysfs接口的PWM
如果在内核配置中启用了CONFIG_SYSFS
,则提供了一个简单的sysfs接口,用于从用户空间使用PWM。它在/sys/class/pwm/
下公开。每个探测到的PWM控制器/芯片将导出为pwmchipN
,其中N是PWM芯片的基址。在该目录中,您将找到:
npwm
:此芯片支持的PWM通道数(只读)。export
:导出一个PWM通道供sysfs使用(只写)。unexport
:从sysfs取消导出PWM通道(只写)。
PWM通道使用从0到npwm-1
的每个芯片索引进行编号。当导出PWM通道时,将在其关联的pwmchipN
目录中创建一个pwmX
目录,其中X是导出的通道号。然后将可用以下属性:
period
:PWM信号的总周期(读/写)。值以纳秒为单位,是PWM的活动时间和非活动时间的总和。duty_cycle
:PWM信号的活动时间(读/写)。值以纳秒为单位,必须小于周期。polarity
:更改PWM信号的极性(读/写)。仅当PWM芯片支持更改极性时,对此属性的写入才有效。只有在PWM未启用时才能更改极性。值为字符串"normal"或"inversed"。enable
:启用/禁用PWM信号(读/写)。
- 0 - 禁用
- 1 - 启用
实现PWM驱动程序
目前有两种实现PWM驱动程序的方法。传统上只有裸骨API,这意味着每个驱动程序必须自己实现pwm_*()
函数。这意味着系统中不可能有多个PWM驱动程序。因此,新驱动程序必须使用通用PWM框架。
可以使用pwmchip_add()
添加新的PWM控制器/芯片,并使用pwmchip_remove()
将其移除。pwmchip_add()
接受填充的struct pwm_chip
作为参数,该结构提供了PWM芯片的描述、芯片提供的PWM设备数量以及支持的PWM操作的芯片特定实现。
在PWM驱动程序中实现极性支持时,确保遵守PWM框架中的信号约定。按定义,正常极性表示信号在占空比的持续时间内开始高电平,并在周期的其余时间内变为低电平。相反,具有反转极性的信号在占空比的持续时间内开始低电平,并在周期的其余时间内变为高电平。
鼓励驱动程序实现->apply()
而不是传统的->enable()
、->disable()
和->config()
方法。这样做应该在PWM配置工作流程中提供原子性,这在PWM控制关键设备(如调节器)时是必需的。
也鼓励实现->get_state()
(用于检索初始PWM状态)出于同样的原因:让PWM用户了解当前PWM状态将使他避免故障。
驱动程序不应该实现任何电源管理。换句话说,消费者应该按照“使用PWM”部分中描述的方式实现它。
锁定
PWM核心列表操作受互斥锁保护,因此pwm_get()
和pwm_put()
不能从原子上下文中调用。目前,PWM核心不强制对pwm_enable()
、pwm_disable()
和pwm_config()
进行任何锁定,因此调用上下文目前是特定于驱动程序的。这是从以前的裸骨API衍生出的问题,应该很快修复。
助手
目前,PWM只能使用period_ns
和duty_ns
进行配置。对于几种用例,freq_hz
和duty_percent
可能更好。请考虑向框架添加适当的助手,而不是在驱动程序中计算这些值。