LXR | KVM | PM | Time | Interrupt | Systems Performance | Bootup Optimization

LED/GPIO/pinctrl子系统(1):基于GPIO的LED子系统概述

关键词:led、gpio、led trigger(heartbeat/netdev/timer)等等。

本系列文档以LED为起点,结合GPIO LED首先了解LED子系统;然后梳理背后的GPIO子系统;进而梳理GPIO子系统背后的pinctrl子系统。

LED子系统为设备提供指示灯、闪光灯等功能,对内核或用户程序运行状态提供指示信息。

下面分别从LED子系统框架、如何配置LED子系统、配置dts、相关sysfs,并以GPIO LED为例了解驱动和测试程序。

1 Linux LED子系统框架和API

1.1 LED子系统框架

如下图,基本将LED子系统分为4大部分,由底层到上层依次是:

  • 硬件层:输出高低电平的GPIO,以及对应的LED灯。
  • LED驱动:配置相关驱动芯片或者GPIO等,并将设备注册到platform bus等创建设备。
  • LED核心层:处理用户空间请求,并将请求通过LED驱动注册的接口写入外设中。
  • 用户层:通过对LED sysfs操作,达到开关、Trigger配置、亮度设置等。

1.2 LED子系统API

LED子系统对外提供的API主要分为两部分:

  • LED驱动使用的注册注销等API。
  • 创建LED Trigger的API。

struct led_classdev表示注册的一个LED设备,包含了对设备设置接口函数、设备属性、对应的Trigger、属性节点等:

struct led_classdev {
    const char        *name;
    enum led_brightness     brightness;--当前光强。
    enum led_brightness     max_brightness;--最大光强。
    int             flags;

    unsigned long        work_flags;

    void        (*brightness_set)(struct led_classdev *led_cdev,
                      enum led_brightness brightness);--光强设置接口。

    int (*brightness_set_blocking)(struct led_classdev *led_cdev,
                       enum led_brightness brightness);--非阻塞式光强设置接口。
    enum led_brightness (*brightness_get)(struct led_classdev *led_cdev);--获取光强。

    int        (*blink_set)(struct led_classdev *led_cdev,
                     unsigned long *delay_on,
                     unsigned long *delay_off);--闪烁接口。

    int (*pattern_set)(struct led_classdev *led_cdev,
               struct led_pattern *pattern, u32 len, int repeat);--LED模式设置。
    int (*pattern_clear)(struct led_classdev *led_cdev);
...
#ifdef CONFIG_LEDS_TRIGGERS
...
    struct led_trigger    *trigger;--当前LED的Trigger。
...
#endif
};

填充好struct led_classdev后,即可通过如下函数对LED注册和注销:

extern int led_classdev_register_ext(struct device *parent,
                     struct led_classdev *led_cdev,
                     struct led_init_data *init_data);
static inline int led_classdev_register(struct device *parent,
                    struct led_classdev *led_cdev)
{
    return led_classdev_register_ext(parent, led_cdev, NULL);
}
extern int devm_led_classdev_register_ext(struct device *parent,
                      struct led_classdev *led_cdev,
                      struct led_init_data *init_data);
static inline int devm_led_classdev_register(struct device *parent,
                         struct led_classdev *led_cdev)
{
    return devm_led_classdev_register_ext(parent, led_cdev, NULL);
}
extern void led_classdev_unregister(struct led_classdev *led_cdev);
extern void devm_led_classdev_unregister(struct device *parent,
                     struct led_classdev *led_cdev);

struct led_trigger表示一个LED Trigger,通过如下函数可以注册和注销LED Trigger:

extern int led_trigger_register(struct led_trigger *trigger);
extern void led_trigger_unregister(struct led_trigger *trigger);
extern int devm_led_trigger_register(struct device *dev,
                     struct led_trigger *trigger);

extern void led_trigger_register_simple(const char *name,
                struct led_trigger **trigger);
extern void led_trigger_unregister_simple(struct led_trigger *trigger);

2 LED menuconfig配置

对LED的配置,主要是配置LED驱动和需要支持的Trigger类型:

Device Drivers
  ->LED Support
    ->LED Class Support
    ->LED Support for GPIO connected LEDs--支持GPIO类型LED控制。
    ->LED Trigger support--LED class所支持的Trigger类型,通过sysfs可以将内核时间通过LED表示。
      ->LED Timer Trigger--定义off/on时间,周期性显示。
      ->LED One-shot Trigger
      ->LED Disk Trigger--磁盘活动指示灯。
      ->LED MTD (NAND/NOR) Trigger--NAND Flash活动指示灯。
      ->LED Heartbeat Trigger--模拟心跳效果,LED闪烁或呼吸。
      ->LED backlight Trigger
      ->LED CPU Trigger--根据CPU活动显示LED。
      ->LED activity Trigger
      ->LED GPIO Trigger
      ->LED Default ON Trigger
      ->*** iptables trigger is under Netfilter config (LED target)
      ->LED Transient Trigger
      ->LED Camera Flash/Torch Trigger
      ->LED Panic Trigger
      ->LED Netdev Trigger
      ->LED Pattern Trigger--允许自定义LED显示模式。
      ->Audio Mute LED Trigger

LED子系统涉及到的主要文件有:

drivers/leds/├── led-class.c--创建led_class类,并且定义LED通用属性brightness/max_brightness/trigger/brightness_hw_changed,以及LED设备的注册注销函数。
├── led-core.c--LED核心,包括基本功能初始化、设备管理、闪烁、亮度等设置。
├── leds-gpio.c--基于GPIO实现的LED驱动。
├── leds.h--LED对外头文件。
├── led-triggers.c--针对LED Trigger抽象出来的注册注销、设置、闪烁等功能函数。
├── trigger
│   ├── ledtrig-activity.c
│   ├── ledtrig-audio.c
│   ├── ledtrig-backlight.c
│   ├── ledtrig-camera.c
│   ├── ledtrig-cpu.c
│   ├── ledtrig-default-on.c
│   ├── ledtrig-disk.c
│   ├── ledtrig-gpio.c
│   ├── ledtrig-heartbeat.c│   ├── ledtrig-mtd.c
│   ├── ledtrig-netdev.c
│   ├── ledtrig-oneshot.c
│   ├── ledtrig-panic.c
│   ├── ledtrig-pattern.c
│   ├── ledtrig-timer.c
│   ├── ledtrig-transient.c

3 LED dts

 编写GPIO LED dts:

    leds {
         compatible = "gpio-leds";--使用的是LED GPIO驱动。

         led1 {
             label = "sys-led";--LED名称。
             gpios = <&gpioi 0 1>;--使用的GPIO节点。
             linux,default-trigger = "heartbeat";--默认Trigger类型。
             default-state = "on";--默认状态。
             status = "okay";--设备是否生效。
         };

         led2 {
             label = "user-led";
             gpios = <&gpiof 3 1>;
             linux,default-trigger = "none";
             default-state = "on";
             status = "okay";
         };

         beep {--beep其实可以作为类LED设备处理。
             label = "beep";
             gpios = <&gpioc 7 1>;
             default-state = "off";
         };
     };

运行中的DTS查看如下:

/sys/firmware/devicetree/base/leds/
|-- beep
|   |-- default-state
|   |-- gpios
|   |-- label
|   `-- name
|-- compatible
|-- led1
|   |-- default-state
|   |-- gpios
|   |-- label
|   |-- linux,default-trigger
|   |-- name
|   `-- status
|-- led2
|   |-- default-state
|   |-- gpios
|   |-- label
|   |-- linux,default-trigger
|   |-- name
|   `-- status
`-- name

更多节点说明参考《common.txt - Documentation/devicetree/bindings/leds/common.txt》。

4 LED GPIO驱动解析

LED GPIO驱动主要工作:从dts中获取每个子节点;根据获取到的信息初始化struct led_classdev;注册LED Class设备。

platform_driver
  gpio_led_probe--和DTS中的gpio-leds匹配后进行probe。
    gpio_leds_create
      device_get_child_node_count/device_for_each_child_node--获取子节点数,并遍历子节点。
      devm_fwnode_get_gpiod_from_child--获取LED dts子节点。
      fwnode_property_read_string--获取DTS中LED配置。
      create_gpio_led--填充struct led_classdev,并将其注册到LED Core中。将对LED设备的操作转化为对GPIO的操作。
        devm_led_classdev_register_ext--注册LED Class设备。
      gpiod_set_consumer_name--设置当前GPIO的消费者名称。
  gpio_led_shutdown
    gpio_led_set--关闭LED输出。

完成后就创建platform设备,并且在/sys/class/leds下创建对LED配置的sysfs。

5 sysfs

常规的LED sysfs节点主要包括:brightness/max_brightness/trigger。

/sys/devices/platform/leds/
|-- driver -> ../../../bus/platform/drivers/leds-gpio
|-- driver_override
|-- leds
|   |-- beep
|   |   |-- brightness--设置当前LED亮度值。
|   |   |-- max_brightness--LED最大亮度值。
|   |   |-- trigger--显示或配置LED触发方式,其中[]表示当前选中的Trigger。
|   |-- sys-led
|   |   |-- brightness
|   |   |-- max_brightness
|   |   |-- trigger
|   `-- user-led
|       |-- brightness
|       |-- max_brightness
|       |-- trigger

 对于timer的其他Trigger类型的LED,可能还有其他sysfs节点。

/sys/class/leds/user-led
|-- brightness
|-- delay_off--一次timer周期内,关闭持续时间。
|-- delay_on--一次timer周期内,打开持续时间。
|-- max_brightness|-- trigger

当trigger设置为timer后,设置delay_off和delay_on,LED则以此off/on时间间隔循环显示。

通过sysfs即可对LED进行测试。 

6 其他

关于Camera闪光灯,需要打开:

Device Drivers
  -> LED Support    -> LED Class Support
      ->LED Flash Class Support

关于Flash LED和其他Trigger的介绍参考《common.txt - Documentation/devicetree/bindings/leds/common.txt》。

更多参考《Camera | 10.linux驱动 led架构-基于rk3568》。

posted on 2023-12-30 23:59  ArnoldLu  阅读(348)  评论(0编辑  收藏  举报

导航