- 了解uboot DM_GPIO 源自该文章
1.DM_GPIO 架构图
1.1.框架说明
-
gpio core 功能(在gpio uclass中实现):
- 主要是为上层提供接口
- 从dts中获取GPIO属性
- 从gpio uclass的设备链表中获取到相应的udevice设备,并使用其操作集
-
gpio uclass
- 链接属于该uclass的所有gpio udevice设备
- 为gpio udevice的driver提供统一的操作集接口
-
bank和gpio
- 有些平台上,将某些使用同一组寄存器的gpio构成一个bank
- 不是所有平台都有bank的概念,例如高通,高通的GPIO都有自己独立的寄存器,因此,可以将高通当成只有一个bank
-
gpio udevice
- 一个bank对应一个gpio udevice,用bank中的偏移来表示具体的GPIO号
- gpio udevice的driver就会根据bank以及offset对相应的寄存器上的相应的bit进行操作。
1.2.gpio的工作原理介绍
- 一个bank对应一个udevice,udevice中私有数据中存放着该bank的信息,比如相应寄存器地址等等
- 上层用gpio_desc描述符来描述一个GPIO,其中包括该GPIO所属的udevice、在bank内的偏移、以及标志位。
- 上层通过调用gpio core的接口从dtsi获取到GPIO属性对应的gpio_desc描述符
- 上层使用gpio_desc描述符来作为调用gpio core的操作接口的参数
- gpio core从gpio_desc描述符提取udevice,并调用其driver中对应的操作集,以bank内的偏移作为其参数(这样driver就能判断出是哪个GPIO了)
- driver中提取udevice的私有数据中的bank信息,并进行相应的操作
2.gpio uclass
gpio uclass实现了两部分:
- gpio uclass driver:gpio uclass驱动,提供gpio udevice绑定到uclass的设备链表之前和之后的一些操作,以及udevice的driver的操作集规范。
- gpio core:为上层提供GPIO的操作接口
2.1.宏配置
CONFIG_DM=y
CONFIG_DM_GPIO=y
CONFIG_CMD_GPIO=y
2.2.重要结构体
1).struct gpio_desc :上层是通过gpio_desc来和gpio core进行交互的。
117 struct gpio_desc {
118 struct udevice *dev; /* Device, NULL for invalid GPIO */
119 unsigned long flags;
120 #define GPIOD_REQUESTED (1 << 0) /* Requested/claimed */
121 #define GPIOD_IS_OUT (1 << 1) /* GPIO is an output */
122 #define GPIOD_IS_IN (1 << 2) /* GPIO is an input */
123 #define GPIOD_ACTIVE_LOW (1 << 3) /* value has active low */
124 #define GPIOD_IS_OUT_ACTIVE (1 << 4) /* set output active */
125
126 uint offset; /* GPIO offset within the device */
131 };
- 老版本是offset+bank的gpio_base获得具体GPIO number
- DM gpio是通过调用uclass的接口函数从dts直接获得对应GPIO的描述符
2).struct gpio_dev_priv
每个gpio uclass设备链表下的udevice都有一部分属于uclass的私有数据:udevice->uclass_priv,提供给gpio uclass使用,让gpio uclass知道这个udevice中的gpio信息和情况。
struct gpio_dev_priv {
const char *bank_name; // udevice的名字,也是bank名称
unsigned gpio_count; // 这个bank中的GPIO数量
unsigned gpio_base; // 每个bank的gpio都占有gpio uclass的一部分连续的gpio空间,gpio_base则表示该bank的第一个GPIO号。
char **name; // 阵列,每个gpio被request之后,都会有自己的label名称,存储在这里,可以防止GPIO request冲突
3).操作集dm_gpio_ops
struct dm_gpio_ops {
int (*request)(struct udevice *dev, unsigned offset, const char *label);
int (*free)(struct udevice *dev, unsigned offset);
int (*direction_input)(struct udevice *dev, unsigned offset);
int (*direction_output)(struct udevice *dev, unsigned offset,
int value);
int (*get_value)(struct udevice *dev, unsigned offset);
int (*set_value)(struct udevice *dev, unsigned offset, int value);
int (*get_function)(struct udevice *dev, unsigned offset);
/* 这里具体看注释 */
int (*xlate)(struct udevice *dev, struct gpio_desc *desc,
struct fdtdec_phandle_args *args);
};
Note:函数都是以udevice和bank内的偏移为参数,驱动中根据udevice获取到对应bank的信息以及私有数据,然后根据偏移就可以确认是哪个GPIO。
3.gpio uclass driver
DM模型中gpio模块的uclass。定义模块加载前和加载后的操作函数等等。
UCLASS_DRIVER(gpio) = {
.id = UCLASS_GPIO,
.name = "gpio",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.post_probe = gpio_post_probe,
.post_bind = gpio_post_bind,
.pre_remove = gpio_pre_remove,
.per_device_auto_alloc_size = sizeof(struct gpio_dev_priv),
};
4.gpio core
DM框架下的接口
- gpio_request_by_name
int gpio_request_by_name(struct udevice *dev, const char *list_name, int index, struct gpio_desc *desc, int flags)
通过对应的udevice找到其dtsi节点中属性名为list_name的GPIO属性并转化 gpio_desc,并且request。 - gpio_request_by_name_nodev
int gpio_request_by_name_nodev(const void *blob, int node, const char *list_name, int index, struct gpio_desc *desc, int flags)
通过对应的dtsi节点中属性名为list_name的GPIO属性并转化为gpio_desc,并且request。 - dm_gpio_request
int dm_gpio_request(struct gpio_desc *desc, const char *label)
申请gpio_desc描述的GPIO - dm_gpio_get_value
int dm_gpio_get_value(const struct gpio_desc *desc)
获取gpio_desc描述的GPIO的值 - dm_gpio_set_value
int dm_gpio_set_value(const struct gpio_desc *desc, int value)
设置gpio_desc描述的GPIO的值 - dm_gpio_set_dir_flags
int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
设置gpio_desc描述的GPIO的输入输出方向,带标志 - dm_gpio_set_dir
int dm_gpio_set_dir(struct gpio_desc *desc)
设置gpio_desc描述的GPIO的输入输出方向 - dm_gpio_is_valid
static inline bool dm_gpio_is_valid(const struct gpio_desc *desc)
判断gpio_desc是否可用
老接口:
这些接口是为了兼容老版本的接口,注意,但是最终还是会调用DM框架下的接口
- gpio_request
int gpio_request(unsigned gpio, const char *label)
申请一个GPIO - gpio_direction_input
int gpio_direction_input(unsigned gpio)
设置某个GPIO为输入 - gpio_direction_output
int gpio_direction_output(unsigned gpio, int value)
设置某个GPIO为输出 - gpio_get_value
int gpio_get_value(unsigned gpio)
获取某个GPIO上的值 - gpio_set_value
int gpio_set_value(unsigned gpio, int value)
设置GPIO的值
5.关系图
6.U-boot Configuration
To enable GPIO in u-boot, the following configurations need to be enabled.
CONFIG_DM=y
CONFIG_DM_GPIO=y
CONFIG_XXX_GPIO=y //xxx:own soc
7.Debug the gpio
It contains code handling the “gpio” command in U-Boot’s shell.
cmd/gpio.c:
U_BOOT_CMD(gpio, 3, 0, do_gpio,
"input/set/clear/toggle gpio pins",
"<input|set|clear|toggle> <pin>\n"
" - input/set/clear/toggle the specified pin");
Enable GPIO command:
CONFIG_CMD_GPIO=y //use the gpio command for debug
命令:
- gpio set [pin] : 设置gpio输出高电平
- gpio clear [pin]:设置gpio输出低电平
- gpio toggle [pin]:切换gpio高低电平
- gpio status -a :查看所有gpio状态