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为例:
- 首先根据DTS配置从GPIO子系统中获取GPIO描述符。
- 然后根据描述符对GPIO进行设置。
- 在实际使用中对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