目录
1 描述
2 结构体
2.1 pwm_chip
2.2 pwm_ops
2.3 pwm_device
2.4 pwm_class
3 相关函数
3.1 注册与注销 PWM 控制器
3.1.1 pwmchip_add
3.1.2 pwmchip_remove
3.2 申请与释放 PWM 设备
3.2.1 pwm_request
3.2.2 devm_pwm_get
3.2.3 pwm_free
3.3 控制 PWM
3.3.1 pwm_config
3.3.2 pwm_enable
3.3.3 pwm_get_state
3.3.4 pwm_apply_state
3.4 PWM 文件系统操作
3.4.1 pwmchip_sysfs_export
3.4.2 pwmchip_sysfs_unexport
4 PWM 驱动
4.1 pwm-rockchip 驱动
4.1.1 源码
4.1.2 核心代码
4.2 pwm-backlight 驱动
4.2.1 源码
4.2.2 设备树
4.2.3 核心代码
4.2.4 操作
4.3 pwm_beeper 驱动
4.3.1 原理图
4.3.2 源码
4.3.3 设备树
4.3.4 核心代码
1 描述
pwm 子系统框架如下图所示
/sys/class/pwm/文件系统下可以操作 PWM
inaro@linaro-alip:~$ ls /sys/class/pwm/
pwmchip0 pwmchip1 pwmchip2 pwmchip3
linaro@linaro-alip:~$ ls /sys/class/pwm/pwmchip0/
device export npwm power subsystem uevent unexport
linaro@linaro-alip:~$ ls /sys/class/pwm/pwmchip0/device
driver driver_override modalias of_node power pwm subsystem supplier:platform:pinctrl uevent
linaro@linaro-alip:~$
pwm 相关驱动位于drivers/pwm/
zwzn2064@zwzn2064-CVN-Z690D5-GAMING-PRO:~/sdb1/android11/kernel$ ls drivers/pwm/
built-in.a pwm-atmel-hlcdc.c pwm-cros-ec.c pwm-lpc18xx-sct.c pwm-mxs.c pwm-rockchip.o pwm-tiecap.c
core.c pwm-atmel-tcb.c pwm-ep93xx.c pwm-lpc32xx.c pwm-omap-dmtimer.c pwm-samsung.c pwm-tiehrpwm.c
core.o pwm-bcm2835.c pwm-fsl-ftm.c pwm-lpss.c pwm-pca9685.c pwm-spear.c pwm-tipwmss.c
Kconfig pwm-bcm-iproc.c pwm-gpio.c pwm-lpss.h pwm-puv3.c pwm-sti.c pwm-twl.c
Makefile pwm-bcm-kona.c pwm-hibvt.c pwm-lpss-pci.c pwm-pxa.c pwm-stm32.c pwm-twl-led.c
modules.builtin pwm-berlin.c pwm-img.c pwm-lpss-platform.c pwm-rcar.c pwm-stm32-lp.c pwm-vt8500.c
modules.order pwm-brcmstb.c pwm-imx.c pwm-mediatek.c pwm-renesas-tpu.c pwm-stmpe.c pwm-zx.c
pwm-ab8500.c pwm-clps711x.c pwm-jz4740.c pwm-meson.c pwm-rockchip.c pwm-sun4i.c sysfs.c
pwm-atmel.c pwm-crc.c pwm-lp3943.c pwm-mtk-disp.c pwm-rockchip-i2s.c pwm-tegra.c sysfs.o
zwzn2064@zwzn2064-CVN-Z690D5-GAMING-PRO:~/sdb1/android11/kernel$
2 结构体
2.1 pwm_chip
struct pwm_chip 是 Linux 内核中用于表示一个 PWM 控制器的结构体。这个结构体定义了与 PWM 控制器相关的各种信息和操作接口,允许内核对 PWM 硬件进行管理和控制。
290 struct pwm_chip {
291 struct device *dev;
292 const struct pwm_ops *ops;
293 int base;
294 unsigned int npwm;
295
296 struct pwm_device * (*of_xlate)(struct pwm_chip *pc,
297 const struct of_phandle_args *args);
298 unsigned int of_pwm_n_cells;
299
300 /* only used internally by the PWM framework */
301 struct list_head list;
302 struct pwm_device *pwms;
303 };
struct device *dev:
这是一个指向 device 结构体的指针,表示与该 PWM 控制器相关联的设备。这使得 PWM 控制器能够通过设备模型与内核中的其他部分进行交互。
const struct pwm_ops *ops:
这是一个指向 pwm_ops 结构体的指针,定义了该 PWM 控制器支持的操作函数。这些操作函数可能包括启动/停止 PWM、设置频率、设置占空比等功能。
int base:
这是 PWM 控制器的基地址,通常用于确定该控制器下不同 PWM 通道的索引或地址。在多通道 PWM 控制器中,这个值帮助确定每个通道的起始位置。
unsigned int npwm:
表示该 PWM 控制器支持的 PWM 通道数量。每个通道可以独立控制,允许同时驱动多个 PWM 输出。
struct pwm_device * (*of_xlate)(struct pwm_chip *pc, const struct of_phandle_args *args):
这是一个函数指针,指向一个将设备树中描述的句柄转换为对应的 pwm_device 结构体的函数。该函数通常在设备树驱动程序中使用,以从设备树中解析 PWM 设备信息。
unsigned int of_pwm_n_cells:
表示在设备树中与 PWM 相关的参数的单元数量。这个值帮助内核理解从设备树节点中解析 PWM 参数时需要多少个单元。
struct list_head list:
这是一个链表节点,用于在 PWM 控制器列表中链接。这使得多个 pwm_chip 结构体可以组织成一个链表,方便内核进行遍历和管理。
struct pwm_device *pwms:
这是指向该控制器的 PWM 通道的数组或链表。它存储了与该 PWM 控制器相关的所有 pwm_device 实例,允许内核访问和管理这些通道。
2.2 pwm_ops
pwm_ops 结构体提供了一个抽象接口,使得不同的PWM硬件实现可以通过一致的方法进行操作。驱动程序可以通过实现这些函数来控制特定的PWM设备,从而实现更高效和模块化的设计。
259 struct pwm_ops {
260 int (*request)(struct pwm_chip *chip, struct pwm_device *pwm);
261 void (*free)(struct pwm_chip *chip, struct pwm_device *pwm);
262 int (*capture)(struct pwm_chip *chip, struct pwm_device *pwm,
263 struct pwm_capture *result, unsigned long timeout);
264 int (*apply)(struct pwm_chip *chip, struct pwm_device *pwm,
265 const struct pwm_state *state);
266 void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm,
267 struct pwm_state *state);
268 struct module *owner;
269
270 /* Only used by legacy drivers */
271 int (*config)(struct pwm_chip *chip, struct pwm_device *pwm,
272 int duty_ns, int period_ns);
273 int (*set_polarity)(struct pwm_chip *chip, struct pwm_device *pwm,
274 enum pwm_polarity polarity);
275 int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm);
276 void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm);
277 };
int (*request)(struct pwm_chip *chip, struct pwm_device *pwm);
这个函数指针用于请求访问特定的PWM设备。它通常在驱动程序初始化时被调用,以确保PWM设备可以被正确配置和使用。返回值通常指示请求是否成功。
void (*free)(struct pwm_chip *chip, struct pwm_device *pwm);
这个函数指针用于释放对PWM设备的访问。它在设备不再使用时被调用,以清理相关资源。
int (*capture)(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_capture *result, unsigned long timeout);
该函数指针用于捕获PWM信号的状态或特征。它通常用于测量PWM信号的实际占空比和频率等参数,结果存储在 result 中。timeout 参数用于设置操作的最大持续时间。
int (*apply)(struct pwm_chip *chip, struct pwm_device *pwm, const struct pwm_state *state);
该函数指针用于应用给定的PWM状态(例如占空比、频率等)到指定的PWM设备。这是配置设备的关键操作。
void (*get_state)(struct pwm_chip *chip, struct pwm_device *pwm, struct pwm_state *state);
该函数指针用于获取当前PWM设备的状态,并将其填充到 state 结构中。这个操作可以用来检查当前的设置和状态。
struct module *owner;
这个字段指向拥有该操作集的模块。它通常用于内核模块的管理,确保相关的模块在使用这些操作时是有效的。
int (*config)(struct pwm_chip *chip, struct pwm_device *pwm, int duty_ns, int period_ns);
该函数指针仅在旧版驱动中使用,用于配置PWM的占空比和周期。duty_ns 和 period_ns 参数分别表示占空比和周期的时间(以纳秒为单位)。
int (*set_polarity)(struct pwm_chip *chip, struct pwm_device *pwm, enum pwm_polarity polarity);
该函数指针用于设置PWM信号的极性(正极性或负极性)。极性设置可以影响PWM信号的行为。
int (*enable)(struct pwm_chip *chip, struct pwm_device *pwm);
该函数指针用于启用PWM设备,使其开始产生PWM信号。启用操作通常涉及设置硬件寄存器或启动计时器。
void (*disable)(struct pwm_chip *chip, struct pwm_device *pwm);
该函数指针用于禁用PWM设备,停止PWM信号的产生。这可以用于节省功耗或在不需要PWM信号时安全地关闭设备。
2.3 pwm_device
76 struct pwm_device {77 const char *label;78 unsigned long flags;79 unsigned int hwpwm;80 unsigned int pwm;81 struct pwm_chip *chip;82 void *chip_data;83 84 struct pwm_args args;85 struct pwm_state state;86 };
const char *label:
这个字段用于保存设备的标签名称,方便在日志或调试中标识该PWM设备。标签通常是字符串形式,便于人类阅读。
unsigned long flags:
该字段是一个标志位,可以用来存储设备的状态或特性,比如是否正在使用、是否可用等。这些标志位通常定义在相应的头文件中。
unsigned int hwpwm:
这是一个硬件级的PWM通道编号,表示该设备在硬件PWM控制器中的位置。不同的PWM控制器可能支持多个通道。
unsigned int pwm:
这是一个唯一标识符,用于在系统中区分不同的PWM设备。这个值通常在设备注册时被分配。
struct pwm_chip *chip:
指向与该PWM设备关联的PWM控制器芯片的指针。PWM控制器负责管理多个PWM设备的操作,比如配置和控制。
void *chip_data:
这个字段可以用来存储与特定芯片相关的额外数据。其具体类型和内容通常由驱动程序定义,以适应不同的芯片。
struct pwm_args args:
这是一个参数结构体,包含了配置PWM设备所需的具体参数,比如频率、占空比、周期等。这些参数在驱动程序中通常会被设置或修改。
struct pwm_state state:
这个结构体表示PWM设备的当前状态,包含设备的工作状态和配置信息(如开启或关闭、当前占空比等)。这个字段用于维护PWM设备的实际运行状态。
41 struct pwm_args {42 u64 period;43 enum pwm_polarity polarity;44 };
81 struct pwm_state {82 u64 period;83 u64 duty_cycle;84 enum pwm_polarity polarity;85 enum pwm_output_type output_type;86 struct pwm_output_pattern *output_pattern;87 #ifdef CONFIG_PWM_ROCKCHIP_ONESHOT88 u64 oneshot_count;89 #endif /* CONFIG_PWM_ROCKCHIP_ONESHOT */90 bool enabled;91 };
2.4 pwm_class
pwm_class 在整个 PWM 子系统中起着关键作用。任何 PWM 设备实例都可以被注册到 pwm_class 下,用户空间应用可以通过 /sys/class/pwm 访问这些设备,实现对 PWM 功能的控制。
635 static struct class pwm_class = {
636 .name = "pwm",
637 .owner = THIS_MODULE,
638 .dev_groups = pwm_chip_groups,
639 .pm = &pwm_class_pm_ops,
640 };
3 相关函数
3.1 注册与注销 PWM 控制器
3.1.1 pwmchip_add
函数原型 | int pwmchip_add(struct pwm_chip *chip) | |
参数 | struct pwm_chip *chip | 指向 pwm_chip 结构体的指针,该结构体描述了要添加的 PWM 控制器。这个结构体包含关于 PWM 硬件的信息和操作函数(如设置 PWM 输出的频率和占空比等)。 |
返回值 | ||
功能 | 将 PWM 控制器添加到 Linux 内核的 PWM 子系统中,允许对 PWM 硬件的管理和控制 |
321 int pwmchip_add(struct pwm_chip *chip)322 { 323 return pwmchip_add_with_polarity(chip, PWM_POLARITY_NORMAL);324 }
251 int pwmchip_add_with_polarity(struct pwm_chip *chip,252 enum pwm_polarity polarity)253 {254 struct pwm_device *pwm;255 unsigned int i;256 int ret;257 258 if (!chip || !chip->dev || !chip->ops || !chip->npwm)259 return -EINVAL;260 261 if (!pwm_ops_check(chip->ops))262 return -EINVAL;263 264 mutex_lock(&pwm_lock);265 266 ret = alloc_pwms(chip->base, chip->npwm);267 if (ret < 0)268 goto out;269 270 chip->pwms = kcalloc(chip->npwm, sizeof(*pwm), GFP_KERNEL);271 if (!chip->pwms) {272 ret = -ENOMEM;273 goto out;274 }275 276 chip->base = ret;277 278 for (i = 0; i < chip->npwm; i++) {279 pwm = &chip->pwms[i];280 281 pwm->chip = chip;282 pwm->pwm = chip->base + i;283 pwm->hwpwm = i;284 pwm->state.polarity = polarity;285 286 if (chip->ops->get_state)287 chip->ops->get_state(chip, pwm, &pwm->state);288 289 radix_tree_insert(&pwm_tree, pwm->pwm, pwm);290 }291 292 bitmap_set(allocated_pwms, chip->base, chip->npwm);293 294 INIT_LIST_HEAD(&chip->list);295 list_add(&chip->list, &pwm_chips);296 297 ret = 0;298 299 if (IS_ENABLED(CONFIG_OF))300 of_pwmchip_add(chip);301 302 out:303 mutex_unlock(&pwm_lock);304 305 if (!ret)306 pwmchip_sysfs_export(chip);307 308 return ret;309 }
此函数会调用 pwmchip_sysfs_export 函数,pwmchip_sysfs_export 函数会在/sys/class/pwm/文件系统下增加pwmchip*
3.1.2 pwmchip_remove
函数原型 | int pwmchip_remove(struct pwm_chip *chip) | |
参数 | struct pwm_chip *chip | 指向 pwm_chip 结构体的指针,该结构体描述了要添加的 PWM 控制器。这个结构体包含关于 PWM 硬件的信息和操作函数(如设置 PWM 输出的频率和占空比等)。 |
返回值 | int | 成功:0 失败:负数 |
功能 | 将 PWM 控制器从 Linux 内核的 PWM 子系统中移除 |
336 int pwmchip_remove(struct pwm_chip *chip)337 {338 unsigned int i;339 int ret = 0;340 341 pwmchip_sysfs_unexport(chip);342 343 mutex_lock(&pwm_lock);344 345 for (i = 0; i < chip->npwm; i++) {346 struct pwm_device *pwm = &chip->pwms[i];347 348 if (test_bit(PWMF_REQUESTED, &pwm->flags)) {349 ret = -EBUSY;350 goto out;351 }352 }353 354 list_del_init(&chip->list);355 356 if (IS_ENABLED(CONFIG_OF))357 of_pwmchip_remove(chip);358 359 free_pwms(chip);360 361 out:362 mutex_unlock(&pwm_lock);363 return ret;364 }
此函数会调用 pwmchip_sysfs_unexport 函数,pwmchip_sysfs_unexport 函数会在/sys/class/pwm/文件系统下删除 pwmchip*
3.2 申请与释放 PWM 设备
3.2.1 pwm_request
函数原型 | struct pwm_device *pwm_request(int pwm, const char *label) | |
参数 | int pwm | 这是要请求的 PWM 设备的编号。每个 PWM 设备都有一个唯一的编号,通常从 0 开始。 |
const char *label | 这是一个字符串,用于标识请求的 PWM 设备。这个标签可以用于调试和日志记录,帮助识别不同的设备请求。 | |
返回值 | struct pwm_device * | 成功时: 返回指向 pwm_device 结构体的指针,该结构体包含了关于请求的 PWM 设备的信息。 失败时: 返回 NULL,表示请求失败。失败的原因可能包括设备不存在、设备已被其他驱动占用等。 |
功能 | 请求系统中的一个 PWM 设备,使得后续操作(如设置频率、占空比等)能够针对该设备进行。 |
390 struct pwm_device *pwm_request(int pwm, const char *label)391 {392 struct pwm_device *dev;393 int err;394 395 if (pwm < 0 || pwm >= MAX_PWMS)396 return ERR_PTR(-EINVAL);397 398 mutex_lock(&pwm_lock); 399 400 dev = pwm_to_device(pwm);401 if (!dev) { 402 dev = ERR_PTR(-EPROBE_DEFER); 403 goto out; 404 }405 406 err = pwm_device_request(dev, label);407 if (err < 0)408 dev = ERR_PTR(err); 409 410 out:411 mutex_unlock(&pwm_lock);412 413 return dev; 414 }
3.2.2 devm_pwm_get
函数原型 | struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id) | |
参数 | struct device *dev | 指向当前设备的指针,通常是驱动程序的主设备结构。这有助于管理资源的生命周期。 |
const char *con_id | 指向字符串的指针,表示 PWM 设备的连接 ID。这通常对应于设备树或硬件描述中的 PWM 设备标识符。 | |
返回值 | struct pwm_device * | 成功时,返回指向 pwm_device 结构的指针,表示已成功请求的 PWM 设备。 失败时,返回一个错误指针(通常是 ERR_PTR 宏封装的错误代码)。 |
功能 | 请求 PWM 设备:它会根据提供的连接 ID 从系统中请求一个 PWM 设备。 自动管理资源:与传统的 pwm_request 函数不同,devm_pwm_get 会自动管理设备的生命周期。当对应的设备被销毁时,内核会自动释放相关资源,避免内存泄漏。使用 devm_pwm_get 后,通常不需要手动调用 pwm_free 来释放 PWM 设备。 |
938 struct pwm_device *devm_pwm_get(struct device *dev, const char *con_id)939 {940 struct pwm_device **ptr, *pwm;941 942 ptr = devres_alloc(devm_pwm_release, sizeof(*ptr), GFP_KERNEL);943 if (!ptr)944 return ERR_PTR(-ENOMEM);945 946 pwm = pwm_get(dev, con_id);947 if (!IS_ERR(pwm)) {948 *ptr = pwm;949 devres_add(dev, ptr);950 } else {951 devres_free(ptr);952 }953 954 return pwm;955 }
3.2.3 pwm_free
函数原型 | void pwm_free(struct pwm_device *pwm) | |
参数 | struct pwm_device *pwm | 指向 pwm_device 结构体的指针,该结构体包含了 PWM 设备的信息。 |
返回值 | ||
功能 | 释放之前申请的 PWM 设备资源。在使用 PWM 设备时,通常会先通过 pwm_request 函数申请资源,一旦不再需要这些资源,必须调用 pwm_free 进行释放,以避免内存泄漏或资源冲突。 |
455 void pwm_free(struct pwm_device *pwm)456 {457 pwm_put(pwm);458 }
3.3 控制 PWM
3.3.1 pwm_config
函数原型 | int pwm_config(struct pwm_device *pwm, int duty_ns,int period_ns) | |
参数 | struct pwm_device *pwm | struct pwm_device *pwm: 这是指向 PWM 设备结构体的指针,该结构体是在调用 pwm_request 函数时获取的。它包含了关于 PWM 设备的状态和配置的信息。 |
int duty_ns | 这是占空比,单位为纳秒(ns)。占空比是 PWM 信号在一个周期内高电平的持续时间。其值必须小于或等于周期时间。 | |
int period_ns | 这是 PWM 信号的周期,单位为纳秒(ns)。它表示一个完整的 PWM 信号周期的持续时间。通常,周期的值应该大于占空比。 | |
返回值 | int | 成功时: 返回 0,表示配置成功。 失败时: 返回负的错误码,表示配置失败。常见的错误包括参数无效(如占空比大于周期)等。 |
功能 | 用于设置 PWM 信号的具体参数,使得后续的 PWM 输出能够按照期望的频率和占空比运行。 |
444 static inline int pwm_config(struct pwm_device *pwm, int duty_ns,
445 int period_ns)
446 {
447 struct pwm_state state;
448
449 if (!pwm)
450 return -EINVAL;
451
452 if (duty_ns < 0 || period_ns < 0)
453 return -EINVAL;
454
455 pwm_get_state(pwm, &state);
456 if (state.duty_cycle == duty_ns && state.period == period_ns)
457 return 0;
458
459 state.duty_cycle = duty_ns;
460 state.period = period_ns;
461 return pwm_apply_state(pwm, &state);
462 }
3.3.2 pwm_enable
函数原型 | int pwm_enable(struct pwm_device *pwm) | |
参数 | struct pwm_device *pwm | 这是指向 PWM 设备结构体的指针,该结构体是在调用 pwm_request 函数时获取的。它包含了有关 PWM 设备的状态和配置的信息。 |
返回值 | int | 成功时: 返回 0,表示 PWM 启用成功。 失败时: 返回负的错误码,表示启用失败。常见的错误包括设备未配置、硬件故障等。 |
功能 | 启动 PWM 输出,使其开始生成 PWM 信号 |
531 static inline int pwm_enable(struct pwm_device *pwm)
532 {
533 struct pwm_state state;
534
535 if (!pwm)
536 return -EINVAL;
537
538 pwm_get_state(pwm, &state);
539 if (state.enabled)
540 return 0;
541
542 state.enabled = true;
543 return pwm_apply_state(pwm, &state);
544 }
3.3.3 pwm_get_state
函数原型 | void pwm_get_state(const struct pwm_device *pwm,struct pwm_state *state) | |
参数 | const struct pwm_device *pwm | 指向 PWM 设备的指针,该设备的状态将被查询。 |
struct pwm_state *state | 指向 pwm_state 结构体的指针,用于保存查询到的 PWM 状态信息。 | |
返回值 | ||
功能 | 提取与指定 PWM 设备相关的配置参数,包括: PWM 的占空比(duty cycle) PWM 的周期(period) PWM 是否处于启用状态(enabled/disabled) |
121 static inline void pwm_get_state(const struct pwm_device *pwm,
122 struct pwm_state *state)
123 {
124 *state = pwm->state;
125 }
3.3.4 pwm_apply_state
函数原型 | int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state) | |
参数 | struct pwm_device *pwm | 指向要设置状态的 PWM 设备的指针。 |
struct pwm_state *state | 指向 pwm_state 结构体的指针,该结构体包含需要应用到 PWM 设备的状态信息(如占空比、周期等)。 | |
返回值 | int | 返回 0 表示成功,负值则表示失败 |
功能 | 将指定的 PWM 状态应用到给定的 PWM 设备上 |
468 int pwm_apply_state(struct pwm_device *pwm, struct pwm_state *state)469 {470 int err;471 472 if (!pwm || !state || !state->period ||473 state->duty_cycle > state->period)474 return -EINVAL;475 476 if (!memcmp(state, &pwm->state, sizeof(*state)))477 return 0;478 479 if (pwm->chip->ops->apply) {480 err = pwm->chip->ops->apply(pwm->chip, pwm, state);481 if (err)482 return err;483 484 pwm->state = *state;485 } else {486 /*487 * FIXME: restore the initial state in case of error.488 */489 if (state->polarity != pwm->state.polarity) {490 if (!pwm->chip->ops->set_polarity)491 return -ENOTSUPP;492 493 /*494 * Changing the polarity of a running PWM is495 * only allowed when the PWM driver implements496 * ->apply().497 */498 if (pwm->state.enabled) {499 pwm->chip->ops->disable(pwm->chip, pwm);500 pwm->state.enabled = false;501 }502 503 err = pwm->chip->ops->set_polarity(pwm->chip, pwm,504 state->polarity);505 if (err)506 return err;507 508 pwm->state.polarity = state->polarity;509 }510 511 if (state->output_type != pwm->state.output_type) {512 if (!pwm->chip->ops->set_output_type)513 return -ENOTSUPP;514 515 err = pwm->chip->ops->set_output_type(pwm->chip, pwm,516 state->output_type);517 if (err)518 return err;519 520 pwm->state.output_type = state->output_type;521 }522 523 if (state->output_pattern != pwm->state.output_pattern &&524 state->output_pattern != NULL) {525 if (!pwm->chip->ops->set_output_pattern)526 return -ENOTSUPP;527 528 err = pwm->chip->ops->set_output_pattern(pwm->chip,529 pwm, state->output_pattern);530 if (err)531 return err;532 533 pwm->state.output_pattern = state->output_pattern;534 }535 536 if (state->period != pwm->state.period ||537 state->duty_cycle != pwm->state.duty_cycle) {538 if (pwm->chip->ops->config_extend) {539 err = pwm->chip->ops->config_extend(pwm->chip,540 pwm, state->duty_cycle,541 state->period);542 } else {543 if (state->period > UINT_MAX)544 pr_warn("period %llu duty_cycle %llu will be truncated\n",545 state->period,546 state->duty_cycle);547 err = pwm->chip->ops->config(pwm->chip, pwm,548 state->duty_cycle,549 state->period);550 }551 if (err)552 return err;553 554 pwm->state.duty_cycle = state->duty_cycle;555 pwm->state.period = state->period;556 }557 558 if (state->enabled != pwm->state.enabled) {559 if (state->enabled) {560 err = pwm->chip->ops->enable(pwm->chip, pwm);561 if (err)562 return err;563 } else {564 pwm->chip->ops->disable(pwm->chip, pwm);565 }566 567 pwm->state.enabled = state->enabled;568 }569 }570 571 return 0;572 }
3.4 PWM 文件系统操作
3.4.1 pwmchip_sysfs_export
函数原型 | void pwmchip_sysfs_export(struct pwm_chip *chip) | |
参数 | struct pwm_chip *chip | 指向 PWM 控制器结构的指针,包含有关 PWM 控制器的状态和能力的信息。 |
返回值 | ||
功能 | 函数会从 /sys/class/pwm 目录中增加与该 chip 相关的文件和目录,使得用户可以通过文件系统接口访问 PWM 控制器的属性和功能。通过这些条目,用户可以在用户空间进行 PWM 设置,比如启动、停止和配置 PWM 信号的频率和占空比。 |
427 void pwmchip_sysfs_export(struct pwm_chip *chip)
428 {
429 struct device *parent;
430
431 /*
432 * If device_create() fails the pwm_chip is still usable by
433 * the kernel its just not exported.
434 */
435 parent = device_create(&pwm_class, chip->dev, MKDEV(0, 0), chip,
436 "pwmchip%d", chip->base);
437 if (IS_ERR(parent)) {
438 dev_warn(chip->dev,
439 "device_create failed for pwm_chip sysfs export\n");
440 }
441 }
3.4.2 pwmchip_sysfs_unexport
函数原型 | void pwmchip_sysfs_unexport(struct pwm_chip *chip) | |
参数 | struct pwm_chip *chip | 指向 PWM 控制器结构的指针,包含有关 PWM 控制器的状态和能力的信息。 |
返回值 | ||
功能 | 函数会从 /sys/class/pwm 目录中删除与该 chip 相关的文件和目录。这通常涉及到: 删除 PWM 设备的属性文件(例如,周期、占空比、使能等)。 删除特定的 PWM 设备条目,使其不再可见。 |
443 void pwmchip_sysfs_unexport(struct pwm_chip *chip)
444 {
445 struct device *parent;
446 unsigned int i;
447
448 parent = class_find_device(&pwm_class, NULL, chip,
449 pwmchip_sysfs_match);
450 if (!parent)
451 return;
452
453 for (i = 0; i < chip->npwm; i++) {
454 struct pwm_device *pwm = &chip->pwms[i];
455
456 if (test_bit(PWMF_EXPORTED, &pwm->flags))
457 pwm_unexport_child(parent, pwm);
458 }
459
460 put_device(parent);
461 device_unregister(parent);
462 }
4 PWM 驱动
4.1 pwm-rockchip 驱动
4.1.1 源码
./drivers/pwm/pwm-rockchip.c
pwm-rockchip.c
4.1.2 核心代码
60 static void rockchip_pwm_get_state(struct pwm_chip *chip,61 struct pwm_device *pwm,62 struct pwm_state *state)63 {64 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);65 u32 enable_conf = pc->data->enable_conf;66 unsigned long clk_rate;67 u64 tmp;68 u32 val;69 int ret;70 71 ret = clk_enable(pc->pclk);72 if (ret)73 return;74 75 clk_rate = clk_get_rate(pc->clk);76 77 tmp = readl_relaxed(pc->base + pc->data->regs.period);78 tmp *= pc->data->prescaler * NSEC_PER_SEC;79 state->period = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate);80 81 tmp = readl_relaxed(pc->base + pc->data->regs.duty);82 tmp *= pc->data->prescaler * NSEC_PER_SEC;83 state->duty_cycle = DIV_ROUND_CLOSEST_ULL(tmp, clk_rate);84 85 val = readl_relaxed(pc->base + pc->data->regs.ctrl);86 state->enabled = (val & enable_conf) == enable_conf;87 88 if (pc->data->supports_polarity && !(val & PWM_DUTY_POSITIVE))89 state->polarity = PWM_POLARITY_INVERSED;90 else91 state->polarity = PWM_POLARITY_NORMAL;92 93 clk_disable(pc->pclk);94 }180 static int rockchip_pwm_apply(struct pwm_chip *chip, struct pwm_device *pwm,
181 const struct pwm_state *state)
182 {
183 struct rockchip_pwm_chip *pc = to_rockchip_pwm_chip(chip);
184 struct pwm_state curstate;
185 bool enabled;
186 int ret = 0;
187
188 ret = clk_enable(pc->pclk);
189 if (ret)
190 return ret;
191
192 pwm_get_state(pwm, &curstate);
193 enabled = curstate.enabled;
194
195 if (state->polarity != curstate.polarity && enabled &&
196 !pc->data->supports_lock) {
197 ret = rockchip_pwm_enable(chip, pwm, false);
198 if (ret)
199 goto out;
200 enabled = false;
201 }
202
203 rockchip_pwm_config(chip, pwm, state);
204 if (state->enabled != enabled) {
205 ret = rockchip_pwm_enable(chip, pwm, state->enabled);
206 if (ret)
207 goto out;
208 }
209
210 out:
211 clk_disable(pc->pclk);
212
213 return ret;
214 }274 static const struct pwm_ops rockchip_pwm_ops = {
275 .get_state = rockchip_pwm_get_state,
276 .apply = rockchip_pwm_apply,
277 .owner = THIS_MODULE,
278 };352 static int rockchip_pwm_probe(struct platform_device *pdev)
353 {
354 const struct of_device_id *id;
355 struct rockchip_pwm_chip *pc;
356 struct resource *r;
357 int ret, count;
358
359 id = of_match_device(rockchip_pwm_dt_ids, &pdev->dev);
360 if (!id)
361 return -EINVAL;
362
363 pc = devm_kzalloc(&pdev->dev, sizeof(*pc), GFP_KERNEL);
364 if (!pc)
365 return -ENOMEM;
366
367 r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
368 pc->base = devm_ioremap(&pdev->dev, r->start,
369 resource_size(r));
370 if (IS_ERR(pc->base))
371 return PTR_ERR(pc->base);
372
373 pc->clk = devm_clk_get(&pdev->dev, "pwm");
374 if (IS_ERR(pc->clk)) {
375 pc->clk = devm_clk_get(&pdev->dev, NULL);
376 if (IS_ERR(pc->clk)) {
377 ret = PTR_ERR(pc->clk);
378 if (ret != -EPROBE_DEFER)
379 dev_err(&pdev->dev, "Can't get bus clk: %d\n",
380 ret);
381 return ret;
382 }
383 }
384
385 count = of_count_phandle_with_args(pdev->dev.of_node,
386 "clocks", "#clock-cells");
387 if (count == 2)
388 pc->pclk = devm_clk_get(&pdev->dev, "pclk");
389 else
390 pc->pclk = pc->clk;
391
392 if (IS_ERR(pc->pclk)) {
393 ret = PTR_ERR(pc->pclk);
394 if (ret != -EPROBE_DEFER)
395 dev_err(&pdev->dev, "Can't get APB clk: %d\n", ret);
396 return ret;
397 }
398
399 ret = clk_prepare_enable(pc->clk);
400 if (ret) {
401 dev_err(&pdev->dev, "Can't prepare enable bus clk: %d\n", ret);
402 return ret;
403 }
404
405 ret = clk_prepare(pc->pclk);
406 if (ret) {
407 dev_err(&pdev->dev, "Can't prepare APB clk: %d\n", ret);
408 goto err_clk;
409 }
410
411 pc->pinctrl = devm_pinctrl_get(&pdev->dev);
412 if (IS_ERR(pc->pinctrl)) {
413 dev_err(&pdev->dev, "Get pinctrl failed!\n");
414 return PTR_ERR(pc->pinctrl);
415 }
416
417 pc->active_state = pinctrl_lookup_state(pc->pinctrl, "active");
418 if (IS_ERR(pc->active_state)) {
419 dev_err(&pdev->dev, "No active pinctrl state\n");
420 return PTR_ERR(pc->active_state);
421 }
422
423 platform_set_drvdata(pdev, pc);
424
425 pc->data = id->data;
426 pc->chip.dev = &pdev->dev;
427 pc->chip.ops = &rockchip_pwm_ops;
428 pc->chip.base = -1;
429 pc->chip.npwm = 1;
430 pc->clk_rate = clk_get_rate(pc->clk);
431
432 if (pc->data->supports_polarity) {
433 pc->chip.of_xlate = of_pwm_xlate_with_flags;
434 pc->chip.of_pwm_n_cells = 3;
435 }
436
437 pc->center_aligned =
438 device_property_read_bool(&pdev->dev, "center-aligned");
439
440 ret = pwmchip_add(&pc->chip);
441 if (ret < 0) {
442 dev_err(&pdev->dev, "pwmchip_add() failed: %d\n", ret);
443 goto err_pclk;
444 }
445
446 /* Keep the PWM clk enabled if the PWM appears to be up and running. */
447 if (!pwm_is_enabled(pc->chip.pwms))
448 clk_disable(pc->clk);
449
450 return 0;
451
452 err_pclk:
453 clk_unprepare(pc->pclk);
454 err_clk:
455 clk_disable_unprepare(pc->clk);
456
457 return ret;
458 }
459
460 static int rockchip_pwm_remove(struct platform_device *pdev)
461 {
462 struct rockchip_pwm_chip *pc = platform_get_drvdata(pdev);
463
464 clk_unprepare(pc->pclk);
465 clk_unprepare(pc->clk);
466
467 return pwmchip_remove(&pc->chip);
468 }
469
470 static struct platform_driver rockchip_pwm_driver = {
471 .driver = {
472 .name = "rockchip-pwm",
473 .of_match_table = rockchip_pwm_dt_ids,
474 },
475 .probe = rockchip_pwm_probe,
476 .remove = rockchip_pwm_remove,
477 };
479 static int __init rockchip_pwm_driver_init(void)
480 {
481 return platform_driver_register(&rockchip_pwm_driver);
482 }
484
485 static void __exit rockchip_pwm_driver_exit(void)
486 {
487 platform_driver_unregister(&rockchip_pwm_driver);
488 }
4.2 pwm-backlight 驱动
4.2.1 源码
drivers/video/backlight/pwm_bl.c
pwm_bl.c
4.2.2 设备树
16 / {43 backlight: backlight {44 compatible = "pwm-backlight";45 pwms = <&pwm0 0 25000 0>;46 brightness-levels = <47 0 20 20 21 21 22 22 2348 23 24 24 25 25 26 26 2749 27 28 28 29 29 30 30 3150 31 32 32 33 33 34 34 3551 35 36 36 37 37 38 38 3952 40 41 42 43 44 45 46 4753 48 49 50 51 52 53 54 5554 56 57 58 59 60 61 62 6355 64 65 66 67 68 69 70 7156 72 73 74 75 76 77 78 7957 80 81 82 83 84 85 86 8758 88 89 90 91 92 93 94 9559 96 97 98 99 100 101 102 10360 104 105 106 107 108 109 110 11161 112 113 114 115 116 117 118 11962 120 121 122 123 124 125 126 12763 128 129 130 131 132 133 134 13564 136 137 138 139 140 141 142 14365 144 145 146 147 148 149 150 15166 152 153 154 155 156 157 158 15967 160 161 162 163 164 165 166 16768 168 169 170 171 172 173 174 17569 176 177 178 179 180 181 182 18370 184 185 186 187 188 189 190 19171 192 193 194 195 196 197 198 19972 200 201 202 203 204 205 206 20773 208 209 210 211 212 213 214 21574 216 217 218 219 220 221 222 22375 224 225 226 227 228 229 230 23176 232 233 234 235 236 237 238 23977 240 241 242 243 244 245 246 24778 248 249 250 251 252 253 254 25579 >;80 default-brightness-level = <200>;81 };1229 };
19 / {
3005 pwm0 {
3006 pwm0_pin: pwm0-pin {
3007 rockchip,pins =
3008 <4 RK_PC2 1 &pcfg_pull_none>;
3009 };
3010
3011 pwm0_pin_pull_down: pwm0-pin-pull-down {
3012 rockchip,pins =
3013 <4 RK_PC2 1 &pcfg_pull_down>;
3014 };
3015
3016 vop0_pwm_pin: vop0-pwm-pin {
3017 rockchip,pins =
3018 <4 RK_PC2 2 &pcfg_pull_none>;
3019 };
3020
3021 vop1_pwm_pin: vop1-pwm-pin {
3022 rockchip,pins =
3023 <4 RK_PC2 3 &pcfg_pull_none>;
3024 };
3025 };
3124 };
4.2.3 核心代码
49 static void pwm_backlight_power_on(struct pwm_bl_data *pb, int brightness)50 {51 int err;52 53 if (pb->enabled)54 return;55 56 err = regulator_enable(pb->power_supply);57 if (err < 0)58 dev_err(pb->dev, "failed to enable power supply\n");59 60 pwm_enable(pb->pwm);61 62 if (pb->post_pwm_on_delay)63 msleep(pb->post_pwm_on_delay);64 65 if (pb->enable_gpio)66 gpiod_set_value_cansleep(pb->enable_gpio, 1);67 68 pb->enabled = true;69 }70 71 static void pwm_backlight_power_off(struct pwm_bl_data *pb)72 {73 if (!pb->enabled)74 return;75 76 if (pb->enable_gpio)77 gpiod_set_value_cansleep(pb->enable_gpio, 0);78 79 if (pb->pwm_off_delay)80 msleep(pb->pwm_off_delay);81 82 pwm_config(pb->pwm, 0, pb->period);83 pwm_disable(pb->pwm);84 85 regulator_disable(pb->power_supply);86 pb->enabled = false;87 }105 static int pwm_backlight_update_status(struct backlight_device *bl)
106 {
107 struct pwm_bl_data *pb = bl_get_data(bl);
108 int brightness = bl->props.brightness;
109 int duty_cycle;
110
111 if (bl->props.power != FB_BLANK_UNBLANK ||
112 bl->props.fb_blank != FB_BLANK_UNBLANK ||
113 bl->props.state & BL_CORE_FBBLANK)
114 brightness = 0;
115
116 if (pb->notify)
117 brightness = pb->notify(pb->dev, brightness);
118
119 if (brightness > 0) {
120 duty_cycle = compute_duty_cycle(pb, brightness);
121 pwm_config(pb->pwm, duty_cycle, pb->period);
122 pwm_backlight_power_on(pb, brightness);
123 } else
124 pwm_backlight_power_off(pb);
125
126 if (pb->notify_after)
127 pb->notify_after(pb->dev, brightness);
128
129 return 0;
130 } 444 static int pwm_backlight_probe(struct platform_device *pdev)
445 {
446 struct platform_pwm_backlight_data *data = dev_get_platdata(&pdev->dev);
447 struct platform_pwm_backlight_data defdata;
448 struct backlight_properties props;
449 struct backlight_device *bl;
450 struct device_node *node = pdev->dev.of_node;
451 struct pwm_bl_data *pb;
452 struct pwm_state state;
453 struct pwm_args pargs;
454 unsigned int i;
455 int ret;
456
457 if (!data) {
458 ret = pwm_backlight_parse_dt(&pdev->dev, &defdata);
459 if (ret < 0) {
460 dev_err(&pdev->dev, "failed to find platform data\n");
461 return ret;
462 }
463
464 data = &defdata;
465 }
466
467 if (data->init) {
468 ret = data->init(&pdev->dev);
469 if (ret < 0)
470 return ret;
471 }
472
473 pb = devm_kzalloc(&pdev->dev, sizeof(*pb), GFP_KERNEL);
474 if (!pb) {
475 ret = -ENOMEM;
476 goto err_alloc;
477 }
478
479 pb->notify = data->notify;
480 pb->notify_after = data->notify_after;
481 pb->check_fb = data->check_fb;
482 pb->exit = data->exit;
483 pb->dev = &pdev->dev;
484 pb->enabled = false;
485 pb->post_pwm_on_delay = data->post_pwm_on_delay;
486 pb->pwm_off_delay = data->pwm_off_delay;
487
488 pb->enable_gpio = devm_gpiod_get_optional(&pdev->dev, "enable",
489 GPIOD_ASIS);
490 if (IS_ERR(pb->enable_gpio)) {
491 ret = PTR_ERR(pb->enable_gpio);
492 goto err_alloc;
493 }
494
495 /*
496 * Compatibility fallback for drivers still using the integer GPIO
497 * platform data. Must go away soon.
498 */
499 if (!pb->enable_gpio && gpio_is_valid(data->enable_gpio)) {
500 ret = devm_gpio_request_one(&pdev->dev, data->enable_gpio,
501 GPIOF_OUT_INIT_HIGH, "enable");
502 if (ret < 0) {
503 dev_err(&pdev->dev, "failed to request GPIO#%d: %d\n",
504 data->enable_gpio, ret);
505 goto err_alloc;
506 }
507
508 pb->enable_gpio = gpio_to_desc(data->enable_gpio);
509 }
510
511 pb->power_supply = devm_regulator_get(&pdev->dev, "power");
512 if (IS_ERR(pb->power_supply)) {
513 ret = PTR_ERR(pb->power_supply);
514 goto err_alloc;
515 }
516
517 pb->pwm = devm_pwm_get(&pdev->dev, NULL);
518 if (IS_ERR(pb->pwm) && PTR_ERR(pb->pwm) != -EPROBE_DEFER && !node) {
519 dev_err(&pdev->dev, "unable to request PWM, trying legacy API\n");
520 pb->legacy = true;
521 pb->pwm = pwm_request(data->pwm_id, "pwm-backlight");
522 }
523
524 if (IS_ERR(pb->pwm)) {
525 ret = PTR_ERR(pb->pwm);
526 if (ret != -EPROBE_DEFER)
527 dev_err(&pdev->dev, "unable to request PWM\n");
528 goto err_alloc;
529 }
530
531 dev_dbg(&pdev->dev, "got pwm for backlight\n");
532
533 if (!data->levels) {
534 /* Get the PWM period (in nanoseconds) */
535 pwm_get_state(pb->pwm, &state);
536
537 ret = pwm_backlight_brightness_default(&pdev->dev, data,
538 state.period);
539 if (ret < 0) {
540 dev_err(&pdev->dev,
541 "failed to setup default brightness table\n");
542 goto err_alloc;
543 }
544 }
545
546 for (i = 0; i <= data->max_brightness; i++) {
547 if (data->levels[i] > pb->scale)
548 pb->scale = data->levels[i];
549
550 pb->levels = data->levels;
551 }
552
553 pwm_adjust_config(pb->pwm);
554
555 /*
556 * The DT case will set the pwm_period_ns field to 0 and store the
557 * period, parsed from the DT, in the PWM device. For the non-DT case,
558 * set the period from platform data if it has not already been set
559 * via the PWM lookup table.
560 */
561 pwm_get_args(pb->pwm, &pargs);
562 pb->period = pargs.period;
563 if (!pb->period && (data->pwm_period_ns > 0))
564 pb->period = data->pwm_period_ns;
565
566 pb->lth_brightness = data->lth_brightness * (pb->period / pb->scale);
567
568 memset(&props, 0, sizeof(struct backlight_properties));
569 props.type = BACKLIGHT_RAW;
570 props.max_brightness = data->max_brightness;
571 bl = backlight_device_register(dev_name(&pdev->dev), &pdev->dev, pb,
572 &pwm_backlight_ops, &props);
573 if (IS_ERR(bl)) {
574 dev_err(&pdev->dev, "failed to register backlight\n");
575 ret = PTR_ERR(bl);
576 if (pb->legacy)
577 pwm_free(pb->pwm);
578 goto err_alloc;
579 }
580
581 if (data->dft_brightness > data->max_brightness) {
582 dev_warn(&pdev->dev,
583 "invalid default brightness level: %u, using %u\n",
584 data->dft_brightness, data->max_brightness);
585 data->dft_brightness = data->max_brightness;
586 }
587
588 bl->props.brightness = data->dft_brightness;
589 bl->props.power = pwm_backlight_initial_power_state(pb);
590 backlight_update_status(bl);
591
592 platform_set_drvdata(pdev, bl);
593 return 0;
594
595 err_alloc:
596 if (data->exit)
597 data->exit(&pdev->dev);
598 return ret;
599 }
4.2.4 操作
cat /sys/kernel/debug/pwm
console:/ $ cat /sys/kernel/debug/pwm
platform/ff420020.pwm, 1 PWM devicepwm-0 (vdd-log ): requested enabled period: 24997 ns duty: 3997 ns polarity: inverse
console:/ $
dmesg | grep pwm
console:/ $ dmesg | grep pwm
[ 1.017350] pwm-backlight backlight: backlight supply power not found, using dummy regulator
[ 1.017425] pwm-backlight backlight: Linked as a consumer to regulator.0
[ 1.017484] pwm-backlight backlight: Dropping the link to regulator.0
[ 1.158786] .. rk pwm remotectl v2.0 init
[ 1.158988] input: ff420030.pwm as /devices/platform/ff420030.pwm/input/input0
[ 1.159248] remotectl-pwm ff420030.pwm: pwm version is 0x1000000
[ 1.159264] remotectl-pwm ff420030.pwm: pwm version is less v2.0
[ 1.159300] remotectl-pwm ff420030.pwm: Support ATF Wakeup
[ 1.353959] pwm-backlight backlight: backlight supply power not found, using dummy regulator
[ 1.354029] pwm-backlight backlight: Linked as a consumer to regulator.0
[ 1.354090] pwm-backlight backlight: Dropping the link to regulator.0
[ 1.500251] pwm-backlight backlight: backlight supply power not found, using dummy regulator
[ 1.500344] pwm-backlight backlight: Linked as a consumer to regulator.0
[ 1.500412] pwm-backlight backlight: Dropping the link to regulator.0
[ 1.503115] pwm-backlight backlight: backlight supply power not found, using dummy regulator
[ 1.503209] pwm-backlight backlight: Linked as a consumer to regulator.0
[ 1.503266] pwm-backlight backlight: Dropping the link to regulator.0
[ 1.507552] pwm-backlight backlight: backlight supply power not found, using dummy regulator
[ 1.507590] pwm-backlight backlight: Linked as a consumer to regulator.0
[ 1.507631] pwm-backlight backlight: Dropping the link to regulator.0
[ 1.510837] pwm-backlight backlight: backlight supply power not found, using dummy regulator
[ 1.510882] pwm-backlight backlight: Linked as a consumer to regulator.0
[ 1.510923] pwm-backlight backlight: Dropping the link to regulator.0
[ 1.515705] pwm-backlight backlight: backlight supply power not found, using dummy regulator
[ 1.515740] pwm-backlight backlight: Linked as a consumer to regulator.0
[ 1.515783] pwm-backlight backlight: Dropping the link to regulator.0
[ 1.685262] pwm-backlight backlight: backlight supply power not found, using dummy regulator
[ 1.685332] pwm-backlight backlight: Linked as a consumer to regulator.0
[ 1.685375] pwm-backlight backlight: Dropping the link to regulator.0
[ 1.968665] pwm-backlight backlight: backlight supply power not found, using dummy regulator
[ 1.968767] pwm-backlight backlight: Linked as a consumer to regulator.0
[ 1.968799] pwm-backlight backlight: Dropping the link to regulator.0
[ 2.260186] pwm-backlight backlight: backlight supply power not found, using dummy regulator
[ 2.260303] pwm-backlight backlight: Linked as a consumer to regulator.0
[ 2.260348] pwm-backlight backlight: Dropping the link to regulator.0
[ 3.379801] pwm-backlight backlight: backlight supply power not found, using dummy regulator
[ 3.379999] pwm-backlight backlight: Linked as a consumer to regulator.0
[ 3.380077] pwm-backlight backlight: Dropping the link to regulator.0
[ 4.107097] pwm-backlight backlight: backlight supply power not found, using dummy regulator
[ 4.107273] pwm-backlight backlight: Linked as a consumer to regulator.0
[ 4.107317] pwm-backlight backlight: Dropping the link to regulator.0
[ 4.109553] pwm-backlight backlight: backlight supply power not found, using dummy regulator
[ 4.109668] pwm-backlight backlight: Linked as a consumer to regulator.0
[ 4.109711] pwm-backlight backlight: Dropping the link to regulator.0
[ 4.510913] pwm-backlight backlight: backlight supply power not found, using dummy regulator
[ 4.511049] pwm-backlight backlight: Linked as a consumer to regulator.0
[ 4.511088] pwm-backlight backlight: Dropping the link to regulator.0
[ 4.530080] pwm-backlight backlight: backlight supply power not found, using dummy regulator
[ 4.530256] pwm-backlight backlight: Linked as a consumer to regulator.0
[ 4.530328] pwm-backlight backlight: Dropping the link to regulator.0
[ 6.684735] pwm-backlight backlight: backlight supply power not found, using dummy regulator
[ 6.684904] pwm-backlight backlight: Linked as a consumer to regulator.0
[ 6.685013] pwm-backlight backlight: Dropping the link to regulator.0
[ 6.688076] pwm-backlight backlight: backlight supply power not found, using dummy regulator
[ 6.688247] pwm-backlight backlight: Linked as a consumer to regulator.0
[ 6.688311] pwm-backlight backlight: Dropping the link to regulator.0
[ 11.025570] pwm-backlight backlight: backlight supply power not found, using dummy regulator
[ 11.025689] pwm-backlight backlight: Linked as a consumer to regulator.0
[ 11.025729] pwm-backlight backlight: Dropping the link to regulator.0
ls /sys/class/pwm/
console:/ $ ls /sys/class/pwm/
pwmchip0
console:/ $ ls /sys/class/pwm/pwmchip0/
device export npwm power subsystem uevent unexport
console:/ $
4.3 pwm_beeper 驱动
4.3.1 原理图
蜂鸣器连接在 rk3399 的 GPIO4_C1/PWM6 引脚上
4.3.2 源码
./kernel/drivers/input/misc/pwm-beeper.c
pwm-beeper.c
4.3.3 设备树
12 / {
197 buzzer {
198 compatible = "pwm-beeper";
199 pwms = <&pwm6 0 500000 0>;
200 };
626 };
搜索“rockchip,rk3328-pwm”,发现在./drivers/pwm/pwm-rockchip.c 出现,所以 pwm6 的驱动是pwm-rockchip。因而pwm-beeper 驱动依赖pwm-rockchip 驱动
16 / {
6243 pwm6: pwm@febd0020 {
6244 compatible = "rockchip,rk3588-pwm", "rockchip,rk3328-pwm";
6245 reg = <0x0 0xfebd0020 0x0 0x10>;
6246 interrupts = <GIC_SPI 346 IRQ_TYPE_LEVEL_HIGH>;
6247 #pwm-cells = <3>;
6248 pinctrl-names = "active";
6249 pinctrl-0 = <&pwm6m0_pins>;
6250 clocks = <&cru CLK_PWM1>, <&cru PCLK_PWM1>;
6251 clock-names = "pwm", "pclk";
6252 status = "disabled";
6253 };
6830 };
4.3.4 核心代码
18 struct pwm_beeper {19 struct input_dev *input;20 struct pwm_device *pwm;21 struct regulator *amplifier;22 struct work_struct work;23 unsigned long period;24 unsigned int bell_frequency;25 bool suspended;26 bool amplifier_on;27 };31 static int pwm_beeper_on(struct pwm_beeper *beeper, unsigned long period)32 {33 struct pwm_state state;34 int error;35 36 pwm_get_state(beeper->pwm, &state);37 38 state.enabled = true;39 state.period = period;40 pwm_set_relative_duty_cycle(&state, 50, 100);41 42 error = pwm_apply_state(beeper->pwm, &state);43 if (error)44 return error;45 46 if (!beeper->amplifier_on) {47 error = regulator_enable(beeper->amplifier);48 if (error) {49 pwm_disable(beeper->pwm);50 return error;51 }52 53 beeper->amplifier_on = true;54 }55 56 return 0;57 }58 59 static void pwm_beeper_off(struct pwm_beeper *beeper)60 {61 if (beeper->amplifier_on) {62 regulator_disable(beeper->amplifier);63 beeper->amplifier_on = false;64 }65 66 pwm_disable(beeper->pwm);67 }122 static int pwm_beeper_probe(struct platform_device *pdev)
123 {
124 struct device *dev = &pdev->dev;
125 struct pwm_beeper *beeper;
126 struct pwm_state state;
127 u32 bell_frequency;
128 int error;
129
130 beeper = devm_kzalloc(dev, sizeof(*beeper), GFP_KERNEL);
131 if (!beeper)
132 return -ENOMEM;
133
134 beeper->pwm = devm_pwm_get(dev, NULL);
135 if (IS_ERR(beeper->pwm)) {
136 error = PTR_ERR(beeper->pwm);
137 if (error != -EPROBE_DEFER)
138 dev_err(dev, "Failed to request PWM device: %d\n",
139 error);
140 return error;
141 }
142
143 /* Sync up PWM state and ensure it is off. */
144 pwm_init_state(beeper->pwm, &state);
145 state.enabled = false;
146 error = pwm_apply_state(beeper->pwm, &state);
147 if (error) {
148 dev_err(dev, "failed to apply initial PWM state: %d\n",
149 error);
150 return error;
151 }
152
153 beeper->amplifier = devm_regulator_get(dev, "amp");
154 if (IS_ERR(beeper->amplifier)) {
155 error = PTR_ERR(beeper->amplifier);
156 if (error != -EPROBE_DEFER)
157 dev_err(dev, "Failed to get 'amp' regulator: %d\n",
158 error);
159 return error;
160 }
161
162 INIT_WORK(&beeper->work, pwm_beeper_work);
163
164 error = device_property_read_u32(dev, "beeper-hz", &bell_frequency);
165 if (error) {
166 bell_frequency = 1000;
167 dev_dbg(dev,
168 "failed to parse 'beeper-hz' property, using default: %uHz\n",
169 bell_frequency);
170 }
171
172 beeper->bell_frequency = bell_frequency;
173
174 beeper->input = devm_input_allocate_device(dev);
175 if (!beeper->input) {
176 dev_err(dev, "Failed to allocate input device\n");
177 return -ENOMEM;
178 }
179
180 beeper->input->name = "pwm-beeper";
181 beeper->input->phys = "pwm/input0";
182 beeper->input->id.bustype = BUS_HOST;
183 beeper->input->id.vendor = 0x001f;
184 beeper->input->id.product = 0x0001;
185 beeper->input->id.version = 0x0100;
186
187 input_set_capability(beeper->input, EV_SND, SND_TONE);
188 input_set_capability(beeper->input, EV_SND, SND_BELL);
189
190 beeper->input->event = pwm_beeper_event;
191 beeper->input->close = pwm_beeper_close;
192
193 input_set_drvdata(beeper->input, beeper);
194
195 error = input_register_device(beeper->input);
196 if (error) {
197 dev_err(dev, "Failed to register input device: %d\n", error);
198 return error;
199 }
200
201 platform_set_drvdata(pdev, beeper);
202
203 return 0;
204 }