Bootstrap

[linux 驱动]PWM子系统详解

目录

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_ONESHOT
 88         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 is
 495                          * only allowed when the PWM driver implements
 496                          * ->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         else
 91                 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  23
  48                          23  24  24  25  25  26  26  27
  49                          27  28  28  29  29  30  30  31
  50                          31  32  32  33  33  34  34  35
  51                          35  36  36  37  37  38  38  39
  52                          40  41  42  43  44  45  46  47
  53                          48  49  50  51  52  53  54  55
  54                          56  57  58  59  60  61  62  63
  55                          64  65  66  67  68  69  70  71
  56                          72  73  74  75  76  77  78  79
  57                          80  81  82  83  84  85  86  87
  58                          88  89  90  91  92  93  94  95
  59                          96  97  98  99 100 101 102 103
  60                         104 105 106 107 108 109 110 111
  61                         112 113 114 115 116 117 118 119
  62                         120 121 122 123 124 125 126 127
  63                         128 129 130 131 132 133 134 135
  64                         136 137 138 139 140 141 142 143
  65                         144 145 146 147 148 149 150 151
  66                         152 153 154 155 156 157 158 159
  67                         160 161 162 163 164 165 166 167
  68                         168 169 170 171 172 173 174 175
  69                         176 177 178 179 180 181 182 183
  70                         184 185 186 187 188 189 190 191
  71                         192 193 194 195 196 197 198 199
  72                         200 201 202 203 204 205 206 207
  73                         208 209 210 211 212 213 214 215
  74                         216 217 218 219 220 221 222 223
  75                         224 225 226 227 228 229 230 231
  76                         232 233 234 235 236 237 238 239
  77                         240 241 242 243 244 245 246 247
  78                         248 249 250 251 252 253 254 255
  79                 >;
  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 device
 pwm-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 }
;