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

LED/GPIO/pinctrl子系统(2):GPIO子系统概述

关键词:gpiolib、pinctrl、gpio-keys等。

在了解pinctrl基础上,分析GPIO子系统按照如下几方面进行:

  • GPIO总体框架、gpiolib主要文件、menuconfig配置GPIO。
  • 了解gpiolib的主要数据结构和API(GPIO Controller和Consumer)。
  • GPIO相关DTS,包括GPIO Controller和GPIO Consumer。
  • 简单分析GPIO Controller和Consumer(gpio-leds)驱动程序。
  • 分析GPIO相关的调试和设置sysfs。

1 GPIO框架

Linux GPIO基于pinctrl子系统,主要负责如下工作:

  • 基于pinctrl提供创建GPIO Controller驱动。
  • 提供API给外设驱动进行GPIO配置。
  • 导出GPIO相关调试和配置sysfs到用户空间。

pin controller负责引脚复用功能配置、以及一些上拉下拉电气特性配置等。GPIO可能是一个pin复用功能中的一种,负责配置输入输出、输出高低、读取输入等功能。

 下面是gpiolib的主要文件:

drivers/gpio/├── gpiolib.c--实现对GPIO注册、获取释放、使能关闭、读写、设置等接口。
├── gpiolib-devprop.c--仅有devprop_gpiochip_set_names(),根据gpio-line-names设置GPIO线名称。
├── gpiolib-devres.c--gpio_request()/gpio_free()等函数的devm版本。
├── gpiolib-legacy.c
├── gpiolib-of.c--处理GPIO相关的Device Tree处理。
├── gpiolib-sysfs.c--创建gpio_class,并且创建GPIO专属的属性sysfs等。

2 GPIO配置

 Kernel中打开GPIO功能,以及GPIO调试和基于不同接口的GPIO扩展:

Device Drivers
  ->GPIO Support
    ->Maximum number of GPIOs for fast path     ->Debug GPIO calls     ->/sys/class/gpio/... (sysfs interface)     ->Memory mapped GPIO drivers     ->Port-mapped I/O GPIO drivers     ->I2C GPIO expanders     ->MFD GPIO expanders     ->SPI GPIO expanders     ->USB GPIO expanders
    ->GPIO Testing Driver

3 GPIO API

 struct gpio_chip表示一个GPIO Controller,主要包括对GPIO的操作函数、GPIO基准号和数量等。

struct gpio_chip {
    const char        *label;
    struct gpio_device    *gpiodev;
    struct device        *parent;
    struct module        *owner;

    int            (*request)(struct gpio_chip *chip,
                        unsigned offset);
    void            (*free)(struct gpio_chip *chip,
                        unsigned offset);
    int            (*get_direction)(struct gpio_chip *chip,
                        unsigned offset);
    int            (*direction_input)(struct gpio_chip *chip,
                        unsigned offset);
    int            (*direction_output)(struct gpio_chip *chip,
                        unsigned offset, int value);
    int            (*get)(struct gpio_chip *chip,
                        unsigned offset);
    int            (*get_multiple)(struct gpio_chip *chip,
                        unsigned long *mask,
                        unsigned long *bits);
    void            (*set)(struct gpio_chip *chip,
                        unsigned offset, int value);
    void            (*set_multiple)(struct gpio_chip *chip,
                        unsigned long *mask,
                        unsigned long *bits);
    int            (*set_config)(struct gpio_chip *chip,
                          unsigned offset,
                          unsigned long config);
    int            (*to_irq)(struct gpio_chip *chip,
                        unsigned offset);

    void            (*dbg_show)(struct seq_file *s,
                        struct gpio_chip *chip);

    int            (*init_valid_mask)(struct gpio_chip *chip,
                           unsigned long *valid_mask,
                           unsigned int ngpios);

    int            base;
    u16            ngpio;
    const char        *const *names;
    bool            can_sleep;
};

struct gpio_device是GPIO子系统内部表示一个GPIO Controller设备:

struct gpio_device {
    int            id;
    struct device        dev;
    struct cdev        chrdev;
    struct device        *mockdev;
    struct module        *owner;
    struct gpio_chip    *chip;
    struct gpio_desc    *descs;
    int            base;
    u16            ngpio;
    const char        *label;
    void            *data;
    struct list_head        list;
};

通过gpiochip_add_data()函数根据struct gpio_chip注册一个struct gpio_device结构,创建一个GPIO Controller。

extern int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
                      struct lock_class_key *lock_key,
                      struct lock_class_key *request_key);
#define gpiochip_add_data(chip, data) gpiochip_add_data_with_key(chip, data, NULL, NULL)
static inline int gpiochip_add(struct gpio_chip *chip)
{
    return gpiochip_add_data(chip, NULL);
}
extern void gpiochip_remove(struct gpio_chip *chip);
extern int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip,
                  void *data);

extern struct gpio_chip *gpiochip_find(void *data,
                  int (*match)(struct gpio_chip *chip, void *data));
void *gpiochip_get_data(struct gpio_chip *chip);

struct gpio_desc表示一个GPIO引脚。 对于Device驱动,在获取到strcut gpio_desc后,即可调用gpiolib提供的API对GPIO进行设置:

struct gpio_desc {
    struct gpio_device    *gdev;
    unsigned long        flags;
    /* Connection label */
    const char        *label;
    /* Name of the GPIO */
    const char        *name;
};

使用GPIO的Device驱动API:

int gpiod_request(struct gpio_desc *desc, const char *label);
void gpiod_free(struct gpio_desc *desc);
int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id,
        unsigned long lflags, enum gpiod_flags dflags);

int gpiod_get_direction(struct gpio_desc *desc);
int gpiod_direction_input(struct gpio_desc *desc);
int gpiod_direction_output(struct gpio_desc *desc, int value);
int gpiod_direction_output_raw(struct gpio_desc *desc, int value);

/* Value get/set from non-sleeping context */
int gpiod_get_value(const struct gpio_desc *desc);
void gpiod_set_value(struct gpio_desc *desc, int value);

以及上述函数的devm_版本。

4 GPIO相关DTS

作为GPIO功能提供者,pinctrl子系统将pin配置为GPIO,并进行属性配置。

        pinctrl: pin-controller@50002000 {
            #address-cells = <1>;
            #size-cells = <1>;
            compatible = "st,stm32mp157-pinctrl";
            ranges = <0 0x50002000 0xa400>;
            interrupt-parent = <&exti>;--
            st,syscfg = <&exti 0x60 0xff>;
            hwlocks = <&hsem 0 1>;
            pins-are-numbered;

            gpioi: gpio@5000a000 {
                gpio-controller;--表示gpioi是一个GPIO控制器。
                #gpio-cells = <2>;--表示GPIO有两个cell属性:第一个cell是GPIO在bank中的的编号;第二个cell表示GPIO的属性,GPIO_ACTIVE_HIGH(0)或GPIO_ACTIVE_LOW(1)。
                interrupt-controller;
                #interrupt-cells = <2>;
                reg = <0x8000 0x400>;--第一个cell为表示相对于pinctrl基地址的偏移地址0x5000200+0x8000;第二个cell表示范围。
                clocks = <&rcc GPIOI>;
                st,bank-name = "GPIOI";--gpioi bank名称。
                status = "disabled";
            };
        };

下面是对上面GPIO配置的补充:

&pinctrl {
    st,package = <STM32MP_PKG_AA>;
    gpioi: gpio@5000a000 {
        status = "okay";
        ngpios = <16>;--GPIOI bank中有16个GPIO。PI0~PI15对应pinctrl的128~143。
        gpio-ranges = <&pinctrl 0 128 16>;--第一个cell指向pin controller;第3个cell是bank的GPIO的偏移量;第4个cell是bank的GPIO数量。
    };
};

下面是使用GPIO的外设配置。通过gpios找到对应的GPIO号,再通过gpio-ranges找到对应的pinctrl号。即可完成对应GPIO配置和pin位置。

    leds {
         compatible = "gpio-leds";

         led1 {
             label = "sys-led";
             gpios = <&gpioi 0 1>;--表示使用GPIOI bank的PI0,1表示Low为有效、Push_Pull、OpenSource。
             linux,default-trigger = "heartbeat";
             default-state = "on";
             status = "okay";
         };
     };

关于GPIO属性设置参考:

/* Bit 0 express polarity */
#define GPIO_ACTIVE_HIGH 0
#define GPIO_ACTIVE_LOW 1

/* Bit 1 express single-endedness */
#define GPIO_PUSH_PULL 0
#define GPIO_SINGLE_ENDED 2

/* Bit 2 express Open drain or open source */
#define GPIO_LINE_OPEN_SOURCE 0
#define GPIO_LINE_OPEN_DRAIN 4

/*
 * Open Drain/Collector is the combination of single-ended open drain interface.
 * Open Source/Emitter is the combination of single-ended open source interface.
 */
#define GPIO_OPEN_DRAIN (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_DRAIN)
#define GPIO_OPEN_SOURCE (GPIO_SINGLE_ENDED | GPIO_LINE_OPEN_SOURCE)

/* Bit 3 express GPIO suspend/resume and reset persistence */
#define GPIO_PERSISTENT 0
#define GPIO_TRANSITORY 8

/* Bit 4 express pull up */
#define GPIO_PULL_UP 16

/* Bit 5 express pull down */
#define GPIO_PULL_DOWN 32

5 GPIO Controller(STM32MP157D)驱动

GPIO Controller从DTS中获取GPIO配置,调用pinctrl子系统API将GPIO注册到gpiolib中,并创建GPIO Controller和GPIO Pin的调试和配置sysfs接口。

stm32_pctl_probe
  ->stm32_gpiolib_register_bank
    pinctrl_add_gpio_range
    ->gpiochip_add_data
      gpiochip_add_data_with_key
        gpiodev_add_to_list--根据GPIO base将当前GPIO Controller加入到gpio_devices列表中。
        of_gpiochip_add
          of_gpiochip_add_pin_range--根据DTS关于GPIO的gpio-ranges描述,将GPIO资源加入到gpiolib中。
        gpiochip_setup_dev
          cdev_device_add--创建GPIO字符设备。
          gpiochip_sysfs_register
            device_create_with_groups--将GPIO Controller注册到/sys/class/gpio下。

6 GPIO Consumer驱动(gpio-leds)

以gpio-leds为例:

  1. 首先根据DTS配置从GPIO子系统中获取GPIO描述符。
  2. 然后根据描述符对GPIO进行设置。
  3. 在实际使用中对GPIO输出高低等操作。
gpio_led_probe
  gpio_led_get_gpiod--获取GPIO的struct gpio_desc。
    devm_gpiod_get_index--根据DTS到GPIO子系统中查找。
      gpiod_get_index
        of_find_gpio
        gpiod_request
        gpiod_configure_flags
    gpiod_set_consumer_name--设置GPIO的consumer名称。
    devm_gpio_request_one--申请GPIO,并初始化。
  create_gpio_led--创建一个基于GPIO的LED实体,并配置基于GPIO的操作函数。
    gpiod_direction_output--设置GPIO输出模式。
    gpio_led_set
      gpiod_set_value--设置GPIO输出高低。

7 GPIO相关sysfs

GPIO相关sysfs包括两部分:

  • 通过export/unexport将某个GPIO导出到用户空间。
  • 每个GPIO Controller节点。
/sys/class/gpio/
|-- export
|-- gpiochip0 -> ../../devices/platform/soc/soc:pin-controller@50002000/gpio/gpiochip0
`-- unexport

 当将需要导出某个GPIO或者取消导出某个GPIO通过写export/unexport实现:

echo N > /sys/class/gpio/export
echo out > /sys/class/gpio/gpioN/direction
echo 1 > /sys/class/gpio/gpioN/value
echo N > /sys/class/gpio/unexport

当导出某个GPIO后可以在用户空间进行设置:

/sys/class/gpio/gpio0/
|-- active_low--逻辑电平和物理电平的翻转。
|-- direction--设置输入输出。
|-- edge--GPIO中断触发方式。none 无中断触发;rising 上升沿触发; falling 下降沿触发;both 上升沿和下降沿都出发。
`-- value--对于输出GPIO,0/1表示输出高低电平;对于输入GPIO,0/1表示读取到的引脚电平。

对于每个GPIO Controller信息和控制节点如下:

/sys/devices/platform/soc/soc:pin-controller@50002000
|-- driver -> ../../../../bus/platform/drivers/stm32mp157-pinctrl--Pin Controller对应的驱动。
|-- driver_override
|-- gpio--Pin Controller下所有的GPIO,按照Bank分别显示。
|   |-- gpiochip0
|   |   |-- base--GPIO基序号。
|   |   |-- device -> ../../../soc:pin-controller@50002000
|   |   |-- label--GPIO Controller名称。
|   |   |-- ngpio--GPIO数量。
|   |   |-- power
|   |   |   |-- autosuspend_delay_ms
|   |   |   |-- control
|   |   |   |-- runtime_active_time
|   |   |   |-- runtime_status
|   |   |   `-- runtime_suspended_time
|   |   |-- subsystem -> ../../../../../../class/gpio
|   |   `-- uevent
`-- uevent

通过/sys/kernel/debug/gpio查看GPIO使用情况:

cat /sys/kernel/debug/gpio

gpiochip8: GPIOs 128-143, parent: platform/soc:pin-controller@50002000, GPIOI:
 gpio-128 (                    |sys-led             ) out hi ACTIVE LOW
 gpio-129 (                    |irq                 ) in  hi IRQ

 

posted on 2024-01-06 23:59  ArnoldLu  阅读(381)  评论(0编辑  收藏  举报

导航