GPIO 子系统层次结构
GPIO 子系统可分为3层,分别是使用者、抽象层、控制器驱动:
- 使用者:即使用 GPIO子系统的驱动,它们使用抽象层向上提供的接口
- 抽象层:启承上启下作用,向上为使用者提供统一接口,向下提供管理多个 GPIO 控制器驱动的功能
- 控制器驱动:用于驱动 GPIO 控制器,它按照抽象层的要求实现相应得到的硬件控制
gpio_devic 对象
gpio_devic 对象表示一个 GPIO 控制器,其核心成员如下:
//ID ,表示是系统中的第几个 GPIO 控制器,由系统自动分配
int id;
//提供 GPIO 操作函数集合及一些创建 gpio_devic 所需的基本消息
struct gpio_chip *chip;
//用于描述 GPIO 的数组,每个 GPIO 都应该有一个结构体
struct gpio_desc *descs;
//GPIO 基值
int base;
//GPIO 个数
u16 ngpio;
//控制器名字
const char *label;
//gpio_pin_range 类型的链表,用于描述 GPIO 子系统中的 GPIO 编号与 pinctrl 控制器中的引脚编号的对应关系
struct list_head pin_ranges;
gpio_chip 对象
gpio_chip 对象中包含操作 GPIO 的函数,其主要成员如下:
//请求 GPIO
int (*request)(struct gpio_chip *chip, unsigned offset);
//释放 GPIO
void (*free)(struct gpio_chip *chip, unsigned offset);
//获取 IO 方向,0输出,1输入
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);
//获取 IO 状态
int (*get)(struct gpio_chip *chip, unsigned offset);
//设置 IO 状态
void (*set)(struct gpio_chip *chip, unsigned offset, int value);
//配置 GPIO。config参数参考enum pin_config_param
int (*set_config)(struct gpio_chip *chip, unsigned offset, unsigned long config);
//对 GPIO 的中断进行映射
int (*to_irq)(struct gpio_chip *chip, unsigned offset);
//GPIO 基值,为-1表示由系统分配
int base;
//GPIO 个数
u16 ngpio;
//控制器名字
const char *label;
//GPIO 控制器的设备树节点
struct device_node *of_node;
//访问 IO 时是否可能会执行引起休眠的函数,在通过 I2C 或 SPI 访问 GPIO 扩展芯片时必须为 true
bool can_sleep;
gpio_desc 对象
gpio_desc 对象用于描述单个 GPIO ,其核心成员如下:
//引脚属于哪个 GPIO 控制器
struct gpio_device *gdev;
//连接标签,可以用来描述当前功能,如驱动led、按键中断等
const char *label;
//GPIO 名字
const char *name;
gpio_pin_range 对象
gpio_pin_range 对象用于描述 GPIO 子系统中的 GPIO 编号与 pinctrl 控制中的引脚编号之间的对应关系,其核心成员如下:
//链表节点,用于在struct gpio_device中构成链表
struct list_head node;
//GPIO控制器所关联的 pinctrl_dev
struct pinctrl_dev *pctldev;
//引脚映射关系
struct pinctrl_gpio_range range;
/* pinctrl_gpio_range 展开后的核心成员如下 */
//链表节点,用于在struct pinctrl_dev构成链表
struct list_head node;
//名称
const char *name;
//ID 号
unsigned int id;
//GPIO 控制器中的 GPIO 编号
unsigned int base;
//对应的 pinctrl 控制器中的引脚编号
unsigned int pin_base;
//引脚数量
unsigned int npins;
//所对应的 gpio_chip
struct gpio_chip *gc;
pinctrl 子系统与 GPIO 子系统的关系
在 struct pinctrl_dev 中有一个成员 struct list_head gpio_ranges ,它是一个链表,其中包含一系列的 struct pinctrl_gpio_range 对象(struct pinctrl_dev中的gpio_ranges 链表与struct gpio_device中的struct list_head pin_ranges链表都包含有一个相同的struct pinctrl_gpio_range对象,只不过struct gpio_device是通过gpio_pin_range对象来间接包含的),用于描述 GPIO 控制器中的 GPIO 编号与 pinctrl 控制器中的引脚编号之间的对应关系。
在驱动开发中可通过在 GPIO 控制器的设备树节点中添加 gpio-ranges 属性将 pinctrl 子系统与 GPIO 子系统关联到一起,gpio-ranges属性的格式如下:
gpio-ranges = <引用关联的 pinctrl 节点 GPIO 控制器的 GPIO 编号 GPIO 编号对应的 pinctrl 引脚号 引脚数>;
如 gpio-ranges = <&pinctrl 0 32 16> 表示此 GPIO 控制器的0号引脚对应 pinctrl 控制器的32号引脚,总共映射16个引脚
如果设备树没有提供 gpio-ranges 属性还可以通过 gpiochip_add_pin_range 等函数进行关联(实际上设备树增加gpio-ranges属性后,在注册GPIO控制器过程中便会自动会调用gpiochip_add_pin_range 函数)。
将 GPIO 子系统与 pinctrl 子系统关联后 GPIO 子系统也不能自动调用 pinctrl 子系统将引脚配置为 GPIO 模式,还需要实现如下函数:
在 GPIO 子系
- 在设备树中提供 gpio-ranges 属性,或者在注册后调用gpiochip_add_pin_range函数
- 在 GPIO 控制器驱动实现 request 函数,这个函数需要调用 gpiochip_generic_request 函数
- 在 GPIO 控制器驱动实现 free 函数,这个函数需要调用 gpiochip_generic_free 函数
- 在 pinctrl 控制器驱动的 pinmux_ops 中实现 gpio_request_enable 或 request 函数,用于将引脚配置为 GPIO 功能
- 在 pinctrl 控制器驱动的 pinmux_ops 中实现 gpio_disable_free 或 free 函数,用于实现第 3 步的反操作 [可选]
stm32 的 pinctrl 子系统与 GPIO 子系统关联方法
在STM32的驱动中采用了另外一种办法来关联GPIO控制器驱动和pinctrl 控制器驱动:
- 在设备树中提供 gpio-ranges 属性
- 在 GPIO 控制器驱动中实现 request 函数,并调用了 pinctrl_gpio_request 函数(gpiochip_generic_request 也是采用此函数实现)
- 在 GPIO 子系统的 direction_input 和 direction_output 中调用了 pinctrl_gpio_direction_input 和 pinctrl_gpio_direction_output 函数来将引脚配置为 GPIO 模式并设置方向(pinctrl_gpio_direction_input和pinctrl_gpio_direction_output 最终会调用到 pinctrl 控制器驱动 pinmux_ops 中的 gpio_set_direction 函数)
NOTE:在 STM32 的驱动中没有为 pinctrl 的 pinmux_ops 提供 gpio_request_enable 和 request 函数,导致在 GPIO
子系统中 request 函数不能将引脚配置为 GPIO 模式