目录
简介
在U-Boot的DM(Device Model)驱动框架中,GPIO的驱动结构基于gpio-uclass,它提供了一个通用的接口供不同厂商的GPIO控制器驱动使用。以下是详细介绍:
gpio-uclass的结构
gpio-uclass是一个通用类(uclass),它定义了与GPIO操作相关的标准接口,所有GPIO控制器驱动都需要实现这些接口。它的主要结构如下:
gpio-uclass的定义:gpio-uclass在drivers/gpio/gpio-uclass.c中定义,核心数据结构是struct gpio_ops,其中定义了GPIO相关的操作接口,例如设置GPIO方向、读取GPIO状态、设置GPIO输出值等。
接口定义:
direction_input(struct udevice *dev, unsigned offset):设置指定GPIO引脚为输入模式。
direction_output(struct udevice *dev, unsigned offset, int value):设置指定GPIO引脚为输出模式,并赋初值。
get_value(struct udevice *dev, unsigned offset):读取指定GPIO引脚的电平值。
set_value(struct udevice *dev, unsigned offset, int value):设置指定GPIO引脚的电平值。
get_function(struct udevice *dev, unsigned offset):获取指定GPIO引脚的功能(输入、输出、其他)。
这些接口的具体实现依赖于各厂商的GPIO驱动。
GPIO驱动与厂商驱动的对接
每个厂商的GPIO驱动需要继承gpio-uclass并实现gpio_ops中定义的接口。这些厂商驱动通常定义在drivers/gpio/目录下。厂商驱动通过以下步骤与gpio-uclass对接:
定义设备节点:在设备树中,GPIO控制器需要定义相关的设备节点,通常包括寄存器基地址、GPIO引脚数量等信息。
注册驱动:厂商的GPIO驱动在初始化时,会注册一个udevice设备,绑定到gpio-uclass。在这个过程中,驱动会初始化自己特有的数据结构,并设置gpio_ops接口指针指向自己的实现。
实现gpio_ops接口:厂商驱动需要实现gpio_ops接口,例如读写寄存器,配置GPIO引脚方向,获取或设置引脚的电平等操作。
用户调用GPIO接口
用户在使用GPIO接口时,可以通过以下步骤:
获取GPIO设备:通过gpio_request_by_name()等API函数,根据设备树节点获取GPIO设备的句柄。
操作GPIO引脚:
设置GPIO引脚为输入或输出:使用dm_gpio_set_dir_flags()函数。
读取GPIO引脚状态:使用dm_gpio_get_value()函数。
设置GPIO引脚状态:使用dm_gpio_set_value()函数。
这些API内部会调用对应厂商驱动实现的gpio_ops接口,以完成具体的硬件操作。
总结
gpio-uclass在U-Boot DM框架中扮演了一个标准化接口的角色,所有厂商的GPIO驱动都需要实现它定义的接口,从而实现与GPIO硬件的对接。当用户调用GPIO接口时,实际操作是通过这些接口间接地与硬件交互的。这样设计的好处在于,用户代码不需要了解底层的硬件细节,而是通过统一的API与GPIO硬件进行交互。本章将以如上三个方面对uboot gpio驱动进行分析和使用。
GPIO厂商驱动
以s5p6818芯片厂家驱动为例,驱动代码位于\uboot\drivers\gpio\nx_gpio.c
接下来我们逐行对代码进行分析,源码如下:
/*
* (C) Copyright 2016 Nexell
* DeokJin, Lee <truevirtue@nexell.co.kr>
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <fdtdec.h>
#include <asm/io.h>
#include <asm/gpio.h>
DECLARE_GLOBAL_DATA_PTR;
struct nx_gpio_regs {
u32 data; /* Data register */
u32 outputenb; /* Output Enable register */
u32 detmode[2]; /* Detect Mode Register */
u32 intenb; /* Interrupt Enable Register */
u32 det; /* Event Detect Register */
u32 pad; /* Pad Status Register */
};
struct nx_alive_gpio_regs {
u32 pwrgate; /* Power Gating Register */
u32 reserved0[28]; /* Reserved0 */
u32 outputenb_reset;/* Alive GPIO Output Enable Reset Register */
u32 outputenb; /* Alive GPIO Output Enable Register */
u32 outputenb_read; /* Alive GPIO Output Read Register */
u32 reserved1[3]; /* Reserved1 */
u32 pad_reset; /* Alive GPIO Output Reset Register */
u32 data; /* Alive GPIO Output Register */
u32 pad_read; /* Alive GPIO Pad Read Register */
u32 reserved2[33]; /* Reserved2 */
u32 pad; /* Alive GPIO Input Value Register */
};
struct nx_gpio_platdata {
void *regs;
int gpio_count;
const char *bank_name;
};
static int nx_alive_gpio_is_check(struct udevice *dev)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
const char *bank_name = plat->bank_name;
if (!strcmp(bank_name, "gpio_alv"))
return 1;
return 0;
}
static int nx_alive_gpio_direction_input(struct udevice *dev, unsigned pin)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_alive_gpio_regs *const regs = plat->regs;
setbits_le32(®s->outputenb_reset, 1 << pin);
return 0;
}
static int nx_alive_gpio_direction_output(struct udevice *dev, unsigned pin,
int val)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_alive_gpio_regs *const regs = plat->regs;
if (val)
setbits_le32(®s->data, 1 << pin);
else
setbits_le32(®s->pad_reset, 1 << pin);
setbits_le32(®s->outputenb, 1 << pin);
return 0;
}
static int nx_alive_gpio_get_value(struct udevice *dev, unsigned pin)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_alive_gpio_regs *const regs = plat->regs;
unsigned int mask = 1UL << pin;
unsigned int value;
value = (readl(®s->pad_read) & mask) >> pin;
return value;
}
static int nx_alive_gpio_set_value(struct udevice *dev, unsigned pin, int val)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_alive_gpio_regs *const regs = plat->regs;
if (val)
setbits_le32(®s->data, 1 << pin);
else
clrbits_le32(®s->pad_reset, 1 << pin);
return 0;
}
static int nx_alive_gpio_get_function(struct udevice *dev, unsigned pin)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_alive_gpio_regs *const regs = plat->regs;
unsigned int mask = (1UL << pin);
unsigned int output;
output = readl(®s->outputenb_read) & mask;
if (output)
return GPIOF_OUTPUT;
else
return GPIOF_INPUT;
}
static int nx_gpio_direction_input(struct udevice *dev, unsigned pin)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_gpio_regs *const regs = plat->regs;
if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_direction_input(dev, pin);
clrbits_le32(®s->outputenb, 1 << pin);
return 0;
}
static int nx_gpio_direction_output(struct udevice *dev, unsigned pin,
int val)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_gpio_regs *const regs = plat->regs;
if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_direction_output(dev, pin, val);
if (val)
setbits_le32(®s->data, 1 << pin);
else
clrbits_le32(®s->data, 1 << pin);
setbits_le32(®s->outputenb, 1 << pin);
return 0;
}
static int nx_gpio_get_value(struct udevice *dev, unsigned pin)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_gpio_regs *const regs = plat->regs;
unsigned int mask = 1UL << pin;
unsigned int value;
if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_get_value(dev, pin);
value = (readl(®s->pad) & mask) >> pin;
return value;
}
static int nx_gpio_set_value(struct udevice *dev, unsigned pin, int val)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_gpio_regs *const regs = plat->regs;
if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_set_value(dev, pin, val);
if (val)
setbits_le32(®s->data, 1 << pin);
else
clrbits_le32(®s->data, 1 << pin);
return 0;
}
static int nx_gpio_get_function(struct udevice *dev, unsigned pin)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_gpio_regs *const regs = plat->regs;
unsigned int mask = (1UL << pin);
unsigned int output;
if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_get_function(dev, pin);
output = readl(®s->outputenb) & mask;
if (output)
return GPIOF_OUTPUT;
else
return GPIOF_INPUT;
}
static int nx_gpio_probe(struct udevice *dev)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
uc_priv->gpio_count = plat->gpio_count;
uc_priv->bank_name = plat->bank_name;
return 0;
}
static int nx_gpio_ofdata_to_platdata(struct udevice *dev)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
plat->regs = map_physmem(dev_get_addr(dev),
sizeof(struct nx_gpio_regs),
MAP_NOCACHE);
plat->gpio_count = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
"nexell,gpio-bank-width", 32);
plat->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset,
"gpio-bank-name", NULL);
return 0;
}
static const struct dm_gpio_ops nx_gpio_ops = {
.direction_input = nx_gpio_direction_input,
.direction_output = nx_gpio_direction_output,
.get_value = nx_gpio_get_value,
.set_value = nx_gpio_set_value,
.get_function = nx_gpio_get_function,
};
static const struct udevice_id nx_gpio_ids[] = {
{ .compatible = "nexell,nexell-gpio" },
{ }
};
U_BOOT_DRIVER(nx_gpio) = {
.name = "nx_gpio",
.id = UCLASS_GPIO,
.of_match = nx_gpio_ids,
.ops = &nx_gpio_ops,
.ofdata_to_platdata = nx_gpio_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct nx_gpio_platdata),
.probe = nx_gpio_probe,
};
这段U-Boot驱动代码实现了Nexell平台的GPIO控制器驱动。代码的整体结构是基于U-Boot DM(Device Model)框架,通过gpio-uclass
提供对GPIO设备的抽象和统一接口。我们逐行分析代码,以帮助理解各个部分的作用。
/*
* (C) Copyright 2016 Nexell
* DeokJin, Lee <truevirtue@nexell.co.kr>
*
* SPDX-License-Identifier: GPL-2.0+
*/
文件头部包含了版权信息,表明代码由Nexell公司发布,并采用GPL-2.0+开源许可证。
### 包含头文件
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <malloc.h>
#include <fdtdec.h>
#include <asm/io.h>
#include <asm/gpio.h>
这些头文件提供了U-Boot常用的功能,如基本的公共函数(common.h)、设备模型支持(dm.h)、错误处理(errno.h)、设备树解析(fdtdec.h)、内存分配(malloc.h)、I/O操作(io.h)和GPIO操作(gpio.h)。
### 全局数据指针声明
DECLARE_GLOBAL_DATA_PTR;
声明全局数据指针`gd`,用于访问U-Boot中的全局数据。
### GPIO寄存器结构定义
struct nx_gpio_regs {
u32 data; /* Data register */
u32 outputenb; /* Output Enable register */
u32 detmode[2]; /* Detect Mode Register */
u32 intenb; /* Interrupt Enable Register */
u32 det; /* Event Detect Register */
u32 pad; /* Pad Status Register */
};
定义了`nx_gpio_regs`结构体,表示标准GPIO控制器的寄存器布局,包含数据寄存器、输出使能寄存器、中断相关寄存器等。
struct nx_alive_gpio_regs {
u32 pwrgate; /* Power Gating Register */
u32 reserved0[28]; /* Reserved0 */
u32 outputenb_reset;/* Alive GPIO Output Enable Reset Register */
u32 outputenb; /* Alive GPIO Output Enable Register */
u32 outputenb_read; /* Alive GPIO Output Read Register */
u32 reserved1[3]; /* Reserved1 */
u32 pad_reset; /* Alive GPIO Output Reset Register */
u32 data; /* Alive GPIO Output Register */
u32 pad_read; /* Alive GPIO Pad Read Register */
u32 reserved2[33]; /* Reserved2 */
u32 pad; /* Alive GPIO Input Value Register */
};
定义了`nx_alive_gpio_regs`结构体,表示Nexell平台的“Alive” GPIO寄存器,它是Nexell平台特有的,具备不同于标准GPIO的寄存器布局。我们可以查看芯片手册,确认是否是这些寄存器,以标准寄存器为例。如下图,代码中定义的寄存器在手册中位置和功能相同,但手册中定义的不止这些寄存器,事实上,后面的寄存器定义与端口复用有关,所以单独拿出来进行实现,实现代码:/uboot/drivers/pinctrl/nexell/pinctrl-s5pxx18.c
### 平台数据结构定义
struct nx_gpio_platdata {
void *regs;
int gpio_count;
const char *bank_name;
};
`nx_gpio_platdata`结构体保存GPIO平台相关的数据,如寄存器基地址、GPIO数量和GPIO组名称。这里进行的定义是为获取设备树中控制器信息做准备,可以从设备树定义中看出,如下图:
### 检查是否是Alive GPIO
static int nx_alive_gpio_is_check(struct udevice *dev)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
const char *bank_name = plat->bank_name;if (!strcmp(bank_name, "gpio_alv"))
return 1;return 0;
}
该函数用于检查当前GPIO控制器是否是“Alive” GPIO类型。通过比较`bank_name`字段,判断是否为`"gpio_alv"`,若是则返回1,否则返回0。
### 设置Alive GPIO为输入方向
static int nx_alive_gpio_direction_input(struct udevice *dev, unsigned pin)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_alive_gpio_regs *const regs = plat->regs;setbits_le32(®s->outputenb_reset, 1 << pin);
return 0;
}
该函数将指定的Alive GPIO引脚设置为输入模式,通过设置`outputenb_reset`寄存器来禁用该引脚的输出功能。
### 设置Alive GPIO为输出方向
static int nx_alive_gpio_direction_output(struct udevice *dev, unsigned pin, int val)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_alive_gpio_regs *const regs = plat->regs;if (val)
setbits_le32(®s->data, 1 << pin);
else
setbits_le32(®s->pad_reset, 1 << pin);setbits_le32(®s->outputenb, 1 << pin);
return 0;
}
设置指定的Alive GPIO引脚为输出模式,并根据传入的`val`值决定引脚输出的初始值。`outputenb`寄存器则控制该引脚的输出使能。
### 读取Alive GPIO引脚值
static int nx_alive_gpio_get_value(struct udevice *dev, unsigned pin)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_alive_gpio_regs *const regs = plat->regs;
unsigned int mask = 1UL << pin;
unsigned int value;value = (readl(®s->pad_read) & mask) >> pin;
return value;
}
该函数读取指定引脚的电平值,通过读取`pad_read`寄存器并根据引脚偏移量获取具体值。
### 设置Alive GPIO引脚的值
static int nx_alive_gpio_set_value(struct udevice *dev, unsigned pin, int val)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_alive_gpio_regs *const regs = plat->regs;if (val)
setbits_le32(®s->data, 1 << pin);
else
clrbits_le32(®s->pad_reset, 1 << pin);return 0;
}
该函数设置指定引脚的电平值,`val`为1时通过设置`data`寄存器输出高电平,为0时通过清除`pad_reset`寄存器输出低电平。
### 获取Alive GPIO引脚的功能
static int nx_alive_gpio_get_function(struct udevice *dev, unsigned pin)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_alive_gpio_regs *const regs = plat->regs;
unsigned int mask = (1UL << pin);
unsigned int output;output = readl(®s->outputenb_read) & mask;
if (output)
return GPIOF_OUTPUT;
else
return GPIOF_INPUT;
}
该函数用于判断Alive GPIO引脚是输入还是输出模式,通过读取`outputenb_read`寄存器中的值来判断。
### 标准GPIO设置为输入方向
static int nx_gpio_direction_input(struct udevice *dev, unsigned pin)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_gpio_regs *const regs = plat->regs;if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_direction_input(dev, pin);clrbits_le32(®s->outputenb, 1 << pin);
return 0;
}
该函数设置标准GPIO引脚为输入模式,先判断是否为Alive GPIO,如果是,则调用对应的Alive GPIO函数。如果不是,通过清除`outputenb`寄存器中的相应位来禁用输出。
### 标准GPIO设置为输出方向
static int nx_gpio_direction_output(struct udevice *dev, unsigned pin, int val)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_gpio_regs *const regs = plat->regs;if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_direction_output(dev, pin, val);if (val)
setbits_le32(®s->data, 1 << pin);
else
clrbits_le32(®s->data, 1 << pin);setbits_le32(®s->outputenb, 1 << pin);
return 0;
}
设置标准GPIO引脚为输出模式,同样根据`val`值设置引脚输出电平,最后通过`outputenb`寄存器启用输出。
### 读取标准GPIO引脚的值
static int nx_gpio_get_value(struct udevice *dev, unsigned pin)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_gpio_regs *const regs = plat->regs;
unsigned int mask = 1UL << pin;
unsigned int value;if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_get_value(dev, pin);value = (readl(®s->pad) & mask) >> pin;
return value;
}
该函数用于读取标准GPIO引脚的电平值,同样先判断是否为Alive GPIO,再根据引脚偏移量从`pad`寄存器中获取相应值。
### 设置标准GPIO引脚的值
static int nx_gpio_set_value(struct udevice *dev, unsigned pin, int val)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_gpio_regs *const regs = plat->regs;
if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_set_value(dev, pin, val);if (val)
setbits_le32(®s->data, 1 << pin);
else
clrbits_le32(®s->data, 1 << pin);return 0;
}
该函数设置标准GPIO引脚的电平值,通过设置或清除`data`寄存器中的相应位实现。
### 获取标准GPIO引脚的功能
static int nx_gpio_get_function(struct udevice *dev, unsigned pin)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);
struct nx_gpio_regs *const regs = plat->regs;
unsigned int mask = (1UL << pin);
unsigned int output;if (nx_alive_gpio_is_check(dev))
return nx_alive_gpio_get_function(dev, pin);output = readl(®s->outputenb) & mask;
if (output)
return GPIOF_OUTPUT;
else
return GPIOF_INPUT;
}
该函数判断标准GPIO引脚的当前模式,是输出还是输入,通过读取`outputenb`寄存器判断。
### 驱动探测函数
static int nx_gpio_probe(struct udevice *dev)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct nx_gpio_platdata *plat = dev_get_platdata(dev);uc_priv->gpio_count = plat->gpio_count;
uc_priv->bank_name = plat->bank_name;return 0;
}
`probe`函数用于初始化设备,从平台数据中获取GPIO数量和名称并存储在uclass设备私有数据结构中。
### 设备树数据转换为平台数据
static int nx_gpio_ofdata_to_platdata(struct udevice *dev)
{
struct nx_gpio_platdata *plat = dev_get_platdata(dev);plat->regs = map_physmem(dev_get_addr(dev),
sizeof(struct nx_gpio_regs),
MAP_NOCACHE);
plat->gpio_count = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
"nexell,gpio-bank-width", 32);
plat->bank_name = fdt_getprop(gd->fdt_blob, dev->of_offset,
"gpio-bank-name", NULL);return 0;
}
该函数从设备树中解析GPIO控制器的相关信息,如寄存器地址、GPIO数量和银行名称,并保存到平台数据结构中。
### GPIO操作函数集
static const struct dm_gpio_ops nx_gpio_ops = {
.direction_input = nx_gpio_direction_input,
.direction_output = nx_gpio_direction_output,
.get_value = nx_gpio_get_value,
.set_value = nx_gpio_set_value,
.get_function = nx_gpio_get_function,
};
该结构体定义了GPIO操作的回调函数集,包括设置方向、获取和设置值、获取功能等操作。
### 设备ID匹配表
static const struct udevice_id nx_gpio_ids[] = {
{ .compatible = "nexell,nexell-gpio" },
{ }
};
设备ID表定义了与设备树中`compatible`属性的匹配规则。
### 驱动定义
U_BOOT_DRIVER(nx_gpio) = {
.name = "nx_gpio",
.id = UCLASS_GPIO,
.of_match = nx_gpio_ids,
.ops = &nx_gpio_ops,
.ofdata_to_platdata = nx_gpio_ofdata_to_platdata,
.platdata_auto_alloc_size = sizeof(struct nx_gpio_platdata),
.probe = nx_gpio_probe,
};
最终的U-Boot驱动定义,注册了`nx_gpio`驱动,指定了操作函数集、设备ID匹配规则、平台数据处理函数以及探测函数等。
gpio-uclass驱动
芯片厂家的驱动并不会被直接使用,而是对接到gpio-uclass驱动中,gpio-uclass驱动对厂家驱动进行封装调用并提供一组通用API,gpio-uclass.c代码如下:
/*
* Copyright (c) 2013 Google, Inc
*
* SPDX-License-Identifier: GPL-2.0+
*/
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <malloc.h>
#include <asm/gpio.h>
#include <linux/ctype.h>
DECLARE_GLOBAL_DATA_PTR;
/**
* gpio_to_device() - Convert global GPIO number to device, number
*
* Convert the GPIO number to an entry in the list of GPIOs
* or GPIO blocks registered with the GPIO controller. Returns
* entry on success, NULL on error.
*
* @gpio: The numeric representation of the GPIO
* @desc: Returns description (desc->flags will always be 0)
* @return 0 if found, -ENOENT if not found
*/
static int gpio_to_device(unsigned int gpio, struct gpio_desc *desc)
{
struct gpio_dev_priv *uc_priv;
struct udevice *dev;
int ret;
for (ret = uclass_first_device(UCLASS_GPIO, &dev);
dev;
ret = uclass_next_device(&dev)) {
uc_priv = dev_get_uclass_priv(dev);
if (gpio >= uc_priv->gpio_base &&
gpio < uc_priv->gpio_base + uc_priv->gpio_count) {
desc->dev = dev;
desc->offset = gpio - uc_priv->gpio_base;
desc->flags = 0;
return 0;
}
}
/* No such GPIO */
return ret ? ret : -ENOENT;
}
int dm_gpio_lookup_name(const char *name, struct gpio_desc *desc)
{
struct gpio_dev_priv *uc_priv = NULL;
struct udevice *dev;
ulong offset;
int numeric;
int ret;
numeric = isdigit(*name) ? simple_strtoul(name, NULL, 10) : -1;
for (ret = uclass_first_device(UCLASS_GPIO, &dev);
dev;
ret = uclass_next_device(&dev)) {
int len;
uc_priv = dev_get_uclass_priv(dev);
if (numeric != -1) {
offset = numeric - uc_priv->gpio_base;
/* Allow GPIOs to be numbered from 0 */
if (offset >= 0 && offset < uc_priv->gpio_count)
break;
}
len = uc_priv->bank_name ? strlen(uc_priv->bank_name) : 0;
if (!strncasecmp(name, uc_priv->bank_name, len)) {
if (!strict_strtoul(name + len, 10, &offset))
break;
}
}
if (!dev)
return ret ? ret : -EINVAL;
desc->dev = dev;
desc->offset = offset;
return 0;
}
int gpio_lookup_name(const char *name, struct udevice **devp,
unsigned int *offsetp, unsigned int *gpiop)
{
struct gpio_desc desc;
int ret;
if (devp)
*devp = NULL;
ret = dm_gpio_lookup_name(name, &desc);
if (ret)
return ret;
if (devp)
*devp = desc.dev;
if (offsetp)
*offsetp = desc.offset;
if (gpiop) {
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(desc.dev);
*gpiop = uc_priv->gpio_base + desc.offset;
}
return 0;
}
static int gpio_find_and_xlate(struct gpio_desc *desc,
struct fdtdec_phandle_args *args)
{
struct dm_gpio_ops *ops = gpio_get_ops(desc->dev);
/* Use the first argument as the offset by default */
if (args->args_count > 0)
desc->offset = args->args[0];
else
desc->offset = -1;
desc->flags = 0;
return ops->xlate ? ops->xlate(desc->dev, desc, args) : 0;
}
int dm_gpio_request(struct gpio_desc *desc, const char *label)
{
struct udevice *dev = desc->dev;
struct gpio_dev_priv *uc_priv;
char *str;
int ret;
uc_priv = dev_get_uclass_priv(dev);
if (uc_priv->name[desc->offset])
return -EBUSY;
str = strdup(label);
if (!str)
return -ENOMEM;
if (gpio_get_ops(dev)->request) {
ret = gpio_get_ops(dev)->request(dev, desc->offset, label);
if (ret) {
free(str);
return ret;
}
}
uc_priv->name[desc->offset] = str;
return 0;
}
static int dm_gpio_requestf(struct gpio_desc *desc, const char *fmt, ...)
{
va_list args;
char buf[40];
va_start(args, fmt);
vscnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
return dm_gpio_request(desc, buf);
}
/**
* gpio_request() - [COMPAT] Request GPIO
* gpio: GPIO number
* label: Name for the requested GPIO
*
* The label is copied and allocated so the caller does not need to keep
* the pointer around.
*
* This function implements the API that's compatible with current
* GPIO API used in U-Boot. The request is forwarded to particular
* GPIO driver. Returns 0 on success, negative value on error.
*/
int gpio_request(unsigned gpio, const char *label)
{
struct gpio_desc desc;
int ret;
ret = gpio_to_device(gpio, &desc);
if (ret)
return ret;
return dm_gpio_request(&desc, label);
}
/**
* gpio_requestf() - [COMPAT] Request GPIO
* @gpio: GPIO number
* @fmt: Format string for the requested GPIO
* @...: Arguments for the printf() format string
*
* This function implements the API that's compatible with current
* GPIO API used in U-Boot. The request is forwarded to particular
* GPIO driver. Returns 0 on success, negative value on error.
*/
int gpio_requestf(unsigned gpio, const char *fmt, ...)
{
va_list args;
char buf[40];
va_start(args, fmt);
vscnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
return gpio_request(gpio, buf);
}
int _dm_gpio_free(struct udevice *dev, uint offset)
{
struct gpio_dev_priv *uc_priv;
int ret;
uc_priv = dev_get_uclass_priv(dev);
if (!uc_priv->name[offset])
return -ENXIO;
if (gpio_get_ops(dev)->free) {
ret = gpio_get_ops(dev)->free(dev, offset);
if (ret)
return ret;
}
free(uc_priv->name[offset]);
uc_priv->name[offset] = NULL;
return 0;
}
/**
* gpio_free() - [COMPAT] Relinquish GPIO
* gpio: GPIO number
*
* This function implements the API that's compatible with current
* GPIO API used in U-Boot. The request is forwarded to particular
* GPIO driver. Returns 0 on success, negative value on error.
*/
int gpio_free(unsigned gpio)
{
struct gpio_desc desc;
int ret;
ret = gpio_to_device(gpio, &desc);
if (ret)
return ret;
return _dm_gpio_free(desc.dev, desc.offset);
}
static int check_reserved(struct gpio_desc *desc, const char *func)
{
struct gpio_dev_priv *uc_priv;
if (!dm_gpio_is_valid(desc))
return -ENOENT;
uc_priv = dev_get_uclass_priv(desc->dev);
if (!uc_priv->name[desc->offset]) {
printf("%s: %s: error: gpio %s%d not reserved\n",
desc->dev->name, func,
uc_priv->bank_name ? uc_priv->bank_name : "",
desc->offset);
return -EBUSY;
}
return 0;
}
/**
* gpio_direction_input() - [COMPAT] Set GPIO direction to input
* gpio: GPIO number
*
* This function implements the API that's compatible with current
* GPIO API used in U-Boot. The request is forwarded to particular
* GPIO driver. Returns 0 on success, negative value on error.
*/
int gpio_direction_input(unsigned gpio)
{
struct gpio_desc desc;
int ret;
ret = gpio_to_device(gpio, &desc);
if (ret)
return ret;
ret = check_reserved(&desc, "dir_input");
if (ret)
return ret;
return gpio_get_ops(desc.dev)->direction_input(desc.dev, desc.offset);
}
/**
* gpio_direction_output() - [COMPAT] Set GPIO direction to output and set value
* gpio: GPIO number
* value: Logical value to be set on the GPIO pin
*
* This function implements the API that's compatible with current
* GPIO API used in U-Boot. The request is forwarded to particular
* GPIO driver. Returns 0 on success, negative value on error.
*/
int gpio_direction_output(unsigned gpio, int value)
{
struct gpio_desc desc;
int ret;
ret = gpio_to_device(gpio, &desc);
if (ret)
return ret;
ret = check_reserved(&desc, "dir_output");
if (ret)
return ret;
return gpio_get_ops(desc.dev)->direction_output(desc.dev,
desc.offset, value);
}
int dm_gpio_get_value(struct gpio_desc *desc)
{
int value;
int ret;
ret = check_reserved(desc, "get_value");
if (ret)
return ret;
value = gpio_get_ops(desc->dev)->get_value(desc->dev, desc->offset);
return desc->flags & GPIOD_ACTIVE_LOW ? !value : value;
}
int dm_gpio_set_value(struct gpio_desc *desc, int value)
{
int ret;
ret = check_reserved(desc, "set_value");
if (ret)
return ret;
if (desc->flags & GPIOD_ACTIVE_LOW)
value = !value;
gpio_get_ops(desc->dev)->set_value(desc->dev, desc->offset, value);
return 0;
}
int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
{
struct udevice *dev = desc->dev;
struct dm_gpio_ops *ops = gpio_get_ops(dev);
int ret;
ret = check_reserved(desc, "set_dir");
if (ret)
return ret;
if (flags & GPIOD_IS_OUT) {
int value = flags & GPIOD_IS_OUT_ACTIVE ? 1 : 0;
if (flags & GPIOD_ACTIVE_LOW)
value = !value;
ret = ops->direction_output(dev, desc->offset, value);
} else if (flags & GPIOD_IS_IN) {
ret = ops->direction_input(dev, desc->offset);
}
if (ret)
return ret;
/*
* Update desc->flags here, so that GPIO_ACTIVE_LOW is honoured in
* futures
*/
desc->flags = flags;
return 0;
}
int dm_gpio_set_dir(struct gpio_desc *desc)
{
return dm_gpio_set_dir_flags(desc, desc->flags);
}
/**
* gpio_get_value() - [COMPAT] Sample GPIO pin and return it's value
* gpio: GPIO number
*
* This function implements the API that's compatible with current
* GPIO API used in U-Boot. The request is forwarded to particular
* GPIO driver. Returns the value of the GPIO pin, or negative value
* on error.
*/
int gpio_get_value(unsigned gpio)
{
int ret;
struct gpio_desc desc;
ret = gpio_to_device(gpio, &desc);
if (ret)
return ret;
return dm_gpio_get_value(&desc);
}
/**
* gpio_set_value() - [COMPAT] Configure logical value on GPIO pin
* gpio: GPIO number
* value: Logical value to be set on the GPIO pin.
*
* This function implements the API that's compatible with current
* GPIO API used in U-Boot. The request is forwarded to particular
* GPIO driver. Returns 0 on success, negative value on error.
*/
int gpio_set_value(unsigned gpio, int value)
{
struct gpio_desc desc;
int ret;
ret = gpio_to_device(gpio, &desc);
if (ret)
return ret;
return dm_gpio_set_value(&desc, value);
}
const char *gpio_get_bank_info(struct udevice *dev, int *bit_count)
{
struct gpio_dev_priv *priv;
/* Must be called on an active device */
priv = dev_get_uclass_priv(dev);
assert(priv);
*bit_count = priv->gpio_count;
return priv->bank_name;
}
static const char * const gpio_function[GPIOF_COUNT] = {
"input",
"output",
"unused",
"unknown",
"func",
};
int get_function(struct udevice *dev, int offset, bool skip_unused,
const char **namep)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct dm_gpio_ops *ops = gpio_get_ops(dev);
BUILD_BUG_ON(GPIOF_COUNT != ARRAY_SIZE(gpio_function));
if (!device_active(dev))
return -ENODEV;
if (offset < 0 || offset >= uc_priv->gpio_count)
return -EINVAL;
if (namep)
*namep = uc_priv->name[offset];
if (skip_unused && !uc_priv->name[offset])
return GPIOF_UNUSED;
if (ops->get_function) {
int ret;
ret = ops->get_function(dev, offset);
if (ret < 0)
return ret;
if (ret >= ARRAY_SIZE(gpio_function))
return -ENODATA;
return ret;
}
return GPIOF_UNKNOWN;
}
int gpio_get_function(struct udevice *dev, int offset, const char **namep)
{
return get_function(dev, offset, true, namep);
}
int gpio_get_raw_function(struct udevice *dev, int offset, const char **namep)
{
return get_function(dev, offset, false, namep);
}
int gpio_get_status(struct udevice *dev, int offset, char *buf, int buffsize)
{
struct dm_gpio_ops *ops = gpio_get_ops(dev);
struct gpio_dev_priv *priv;
char *str = buf;
int func;
int ret;
int len;
BUILD_BUG_ON(GPIOF_COUNT != ARRAY_SIZE(gpio_function));
*buf = 0;
priv = dev_get_uclass_priv(dev);
ret = gpio_get_raw_function(dev, offset, NULL);
if (ret < 0)
return ret;
func = ret;
len = snprintf(str, buffsize, "%s%d: %s",
priv->bank_name ? priv->bank_name : "",
offset, gpio_function[func]);
if (func == GPIOF_INPUT || func == GPIOF_OUTPUT ||
func == GPIOF_UNUSED) {
const char *label;
bool used;
ret = ops->get_value(dev, offset);
if (ret < 0)
return ret;
used = gpio_get_function(dev, offset, &label) != GPIOF_UNUSED;
snprintf(str + len, buffsize - len, ": %d [%c]%s%s",
ret,
used ? 'x' : ' ',
used ? " " : "",
label ? label : "");
}
return 0;
}
int gpio_claim_vector(const int *gpio_num_array, const char *fmt)
{
int i, ret;
int gpio;
for (i = 0; i < 32; i++) {
gpio = gpio_num_array[i];
if (gpio == -1)
break;
ret = gpio_requestf(gpio, fmt, i);
if (ret)
goto err;
ret = gpio_direction_input(gpio);
if (ret) {
gpio_free(gpio);
goto err;
}
}
return 0;
err:
for (i--; i >= 0; i--)
gpio_free(gpio_num_array[i]);
return ret;
}
/*
* get a number comprised of multiple GPIO values. gpio_num_array points to
* the array of gpio pin numbers to scan, terminated by -1.
*/
int gpio_get_values_as_int(const int *gpio_list)
{
int gpio;
unsigned bitmask = 1;
unsigned vector = 0;
int ret;
while (bitmask &&
((gpio = *gpio_list++) != -1)) {
ret = gpio_get_value(gpio);
if (ret < 0)
return ret;
else if (ret)
vector |= bitmask;
bitmask <<= 1;
}
return vector;
}
static int _gpio_request_by_name_nodev(const void *blob, int node,
const char *list_name, int index,
struct gpio_desc *desc, int flags,
bool add_index)
{
struct fdtdec_phandle_args args;
int ret;
desc->dev = NULL;
desc->offset = 0;
ret = fdtdec_parse_phandle_with_args(blob, node, list_name,
"#gpio-cells", 0, index, &args);
if (ret) {
debug("%s: fdtdec_parse_phandle_with_args failed\n", __func__);
goto err;
}
ret = uclass_get_device_by_of_offset(UCLASS_GPIO, args.node,
&desc->dev);
if (ret) {
debug("%s: uclass_get_device_by_of_offset failed\n", __func__);
goto err;
}
ret = gpio_find_and_xlate(desc, &args);
if (ret) {
debug("%s: gpio_find_and_xlate failed\n", __func__);
goto err;
}
ret = dm_gpio_requestf(desc, add_index ? "%s.%s%d" : "%s.%s",
fdt_get_name(blob, node, NULL),
list_name, index);
if (ret) {
debug("%s: dm_gpio_requestf failed\n", __func__);
goto err;
}
ret = dm_gpio_set_dir_flags(desc, flags | desc->flags);
if (ret) {
debug("%s: dm_gpio_set_dir failed\n", __func__);
goto err;
}
return 0;
err:
debug("%s: Node '%s', property '%s', failed to request GPIO index %d: %d\n",
__func__, fdt_get_name(blob, node, NULL), list_name, index, ret);
return ret;
}
int gpio_request_by_name_nodev(const void *blob, int node,
const char *list_name, int index,
struct gpio_desc *desc, int flags)
{
return _gpio_request_by_name_nodev(blob, node, list_name, index, desc,
flags, index > 0);
}
int gpio_request_by_name(struct udevice *dev, const char *list_name, int index,
struct gpio_desc *desc, int flags)
{
/*
* This isn't ideal since we don't use dev->name in the debug()
* calls in gpio_request_by_name(), but we can do this until
* gpio_request_by_name_nodev() can be dropped.
*/
return gpio_request_by_name_nodev(gd->fdt_blob, dev->of_offset,
list_name, index, desc, flags);
}
int gpio_request_list_by_name_nodev(const void *blob, int node,
const char *list_name,
struct gpio_desc *desc, int max_count,
int flags)
{
int count;
int ret;
for (count = 0; count < max_count; count++) {
ret = _gpio_request_by_name_nodev(blob, node, list_name, count,
&desc[count], flags, true);
if (ret == -ENOENT)
break;
else if (ret)
goto err;
}
/* We ran out of GPIOs in the list */
return count;
err:
gpio_free_list_nodev(desc, count - 1);
return ret;
}
int gpio_request_list_by_name(struct udevice *dev, const char *list_name,
struct gpio_desc *desc, int max_count,
int flags)
{
/*
* This isn't ideal since we don't use dev->name in the debug()
* calls in gpio_request_by_name(), but we can do this until
* gpio_request_list_by_name_nodev() can be dropped.
*/
return gpio_request_list_by_name_nodev(gd->fdt_blob, dev->of_offset,
list_name, desc, max_count,
flags);
}
int gpio_get_list_count(struct udevice *dev, const char *list_name)
{
int ret;
ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev->of_offset,
list_name, "#gpio-cells", 0, -1,
NULL);
if (ret) {
debug("%s: Node '%s', property '%s', GPIO count failed: %d\n",
__func__, dev->name, list_name, ret);
}
return ret;
}
int dm_gpio_free(struct udevice *dev, struct gpio_desc *desc)
{
/* For now, we don't do any checking of dev */
return _dm_gpio_free(desc->dev, desc->offset);
}
int gpio_free_list(struct udevice *dev, struct gpio_desc *desc, int count)
{
int i;
/* For now, we don't do any checking of dev */
for (i = 0; i < count; i++)
dm_gpio_free(dev, &desc[i]);
return 0;
}
int gpio_free_list_nodev(struct gpio_desc *desc, int count)
{
return gpio_free_list(NULL, desc, count);
}
/* We need to renumber the GPIOs when any driver is probed/removed */
static int gpio_renumber(struct udevice *removed_dev)
{
struct gpio_dev_priv *uc_priv;
struct udevice *dev;
struct uclass *uc;
unsigned base;
int ret;
ret = uclass_get(UCLASS_GPIO, &uc);
if (ret)
return ret;
/* Ensure that we have a base for each bank */
base = 0;
uclass_foreach_dev(dev, uc) {
if (device_active(dev) && dev != removed_dev) {
uc_priv = dev_get_uclass_priv(dev);
uc_priv->gpio_base = base;
base += uc_priv->gpio_count;
}
}
return 0;
}
int gpio_get_number(struct gpio_desc *desc)
{
struct udevice *dev = desc->dev;
struct gpio_dev_priv *uc_priv;
if (!dev)
return -1;
uc_priv = dev->uclass_priv;
return uc_priv->gpio_base + desc->offset;
}
static int gpio_post_probe(struct udevice *dev)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
uc_priv->name = calloc(uc_priv->gpio_count, sizeof(char *));
if (!uc_priv->name)
return -ENOMEM;
return gpio_renumber(NULL);
}
static int gpio_pre_remove(struct udevice *dev)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
int i;
for (i = 0; i < uc_priv->gpio_count; i++) {
if (uc_priv->name[i])
free(uc_priv->name[i]);
}
free(uc_priv->name);
return gpio_renumber(dev);
}
UCLASS_DRIVER(gpio) = {
.id = UCLASS_GPIO,
.name = "gpio",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.post_probe = gpio_post_probe,
.pre_remove = gpio_pre_remove,
.per_device_auto_alloc_size = sizeof(struct gpio_dev_priv),
};
这段代码是 U-Boot 中 GPIO 驱动实现的核心部分,主要负责 GPIO 的请求、释放、方向设置和值读取/写入等操作。以下是代码的逐行分析:
#include <common.h>
#include <dm.h>
#include <errno.h>
#include <fdtdec.h>
#include <malloc.h>
#include <asm/gpio.h>
#include <linux/ctype.h>DECLARE_GLOBAL_DATA_PTR;
1. `#include <common.h>`: 包含 U-Boot 的通用定义和宏。
2. `#include <dm.h>`: 设备模型 (Device Model, DM) 相关定义。
3. `#include <errno.h>`: 错误代码定义,用于表示函数的错误返回值。
4. `#include <fdtdec.h>`: FDT(Flattened Device Tree)解析函数,帮助解析设备树中的节点。
5. `#include <malloc.h>`: 包含动态内存分配相关函数。
6. `#include <asm/gpio.h>`: 包含 GPIO 相关的结构和函数。
7. `#include <linux/ctype.h>`: 包含字符处理函数,如 `isdigit()`。
8. `DECLARE_GLOBAL_DATA_PTR;`: 声明一个全局数据指针 `gd`,U-Boot 使用它来存储全局数据。
### `gpio_to_device` 函数
static int gpio_to_device(unsigned int gpio, struct gpio_desc *desc)
{
struct gpio_dev_priv *uc_priv;
struct udevice *dev;
int ret;for (ret = uclass_first_device(UCLASS_GPIO, &dev);
dev;
ret = uclass_next_device(&dev)) {
uc_priv = dev_get_uclass_priv(dev);
if (gpio >= uc_priv->gpio_base &&
gpio < uc_priv->gpio_base + uc_priv->gpio_count) {
desc->dev = dev;
desc->offset = gpio - uc_priv->gpio_base;
desc->flags = 0;
return 0;
}
}/* No such GPIO */
return ret ? ret : -ENOENT;
}
1. `static int gpio_to_device(unsigned int gpio, struct gpio_desc *desc)`: 将全局 GPIO 编号转换为具体设备和偏移。
2. `struct gpio_dev_priv *uc_priv;`: 定义 GPIO 设备的私有数据指针。
3. `struct udevice *dev;`: 定义设备指针。
4. `int ret;`: 定义返回值 `ret`。5. `for (ret = uclass_first_device(UCLASS_GPIO, &dev); dev; ret = uclass_next_device(&dev))`: 遍历所有 GPIO 设备,获取 `dev`。
- `uclass_first_device()` 获取第一个设备,`uclass_next_device()` 获取下一个设备。6. `uc_priv = dev_get_uclass_priv(dev);`: 获取 `dev` 的私有数据。
7. `if (gpio >= uc_priv->gpio_base && gpio < uc_priv->gpio_base + uc_priv->gpio_count)`: 检查 `gpio` 是否在当前设备的范围内。
8. `desc->dev = dev;`: 设置 `desc` 的 `dev` 字段为当前设备。
9. `desc->offset = gpio - uc_priv->gpio_base;`: 计算 `gpio` 在设备中的偏移量。
10. `desc->flags = 0;`: 初始化 `desc` 的 `flags`。
11. `return 0;`: 成功时返回 0。12. `return ret ? ret : -ENOENT;`: 如果遍历完成没有找到匹配的 GPIO,返回错误。
### `dm_gpio_lookup_name` 函数
int dm_gpio_lookup_name(const char *name, struct gpio_desc *desc)
{
struct gpio_dev_priv *uc_priv = NULL;
struct udevice *dev;
ulong offset;
int numeric;
int ret;numeric = isdigit(*name) ? simple_strtoul(name, NULL, 10) : -1;
for (ret = uclass_first_device(UCLASS_GPIO, &dev);
dev;
ret = uclass_next_device(&dev)) {
int len;uc_priv = dev_get_uclass_priv(dev);
if (numeric != -1) {
offset = numeric - uc_priv->gpio_base;
if (offset >= 0 && offset < uc_priv->gpio_count)
break;
}len = uc_priv->bank_name ? strlen(uc_priv->bank_name) : 0;
if (!strncasecmp(name, uc_priv->bank_name, len)) {
if (!strict_strtoul(name + len, 10, &offset))
break;
}
}if (!dev)
return ret ? ret : -EINVAL;desc->dev = dev;
desc->offset = offset;return 0;
}
1. `struct gpio_dev_priv *uc_priv = NULL;`: 定义并初始化 GPIO 私有数据指针。
2. `struct udevice *dev;`: 定义设备指针。
3. `ulong offset;`: 偏移量。
4. `int numeric;`: 存储 GPIO 编号。
5. `int ret;`: 返回值。6. `numeric = isdigit(*name) ? simple_strtoul(name, NULL, 10) : -1;`: 判断 `name` 是否为数字,若是则转换为无符号长整型。
7. `for (ret = uclass_first_device(UCLASS_GPIO, &dev); dev; ret = uclass_next_device(&dev))`: 遍历所有 GPIO 设备。
8. `uc_priv = dev_get_uclass_priv(dev);`: 获取设备的私有数据。9. `if (numeric != -1) {...}`: 若 `numeric` 有效,则计算偏移。
10. `len = uc_priv->bank_name ? strlen(uc_priv->bank_name) : 0;`: 获取 GPIO 名称长度。
11. `if (!strncasecmp(name, uc_priv->bank_name, len))`: 检查名称是否匹配当前设备的 GPIO 名称。
12. `if (!strict_strtoul(name + len, 10, &offset))`: 解析 `name` 后的数字部分为 `offset`。13. `if (!dev) return ret ? ret : -EINVAL;`: 若未找到设备,返回错误。
14. `desc->dev = dev;`: 设置 `desc` 的设备字段。
15. `desc->offset = offset;`: 设置 `desc` 的偏移字段。
16. `return 0;`: 成功返回 0。
### `gpio_lookup_name` 函数
int gpio_lookup_name(const char *name, struct udevice **devp,
unsigned int *offsetp, unsigned int *gpiop)
{
struct gpio_desc desc;
int ret;if (devp)
*devp = NULL;
ret = dm_gpio_lookup_name(name, &desc);
if (ret)
return ret;if (devp)
*devp = desc.dev;
if (offsetp)
*offsetp = desc.offset;
if (gpiop) {
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(desc.dev);*gpiop = uc_priv->gpio_base + desc.offset;
}return 0;
}
1. `if (devp) *devp = NULL;`: 将 `devp` 置空。
2. `ret = dm_gpio_lookup_name(name, &desc);`: 调用 `dm_gpio_lookup_name`。
3. `if (ret) return ret;`: 若查找失败,返回错误。
4. `if (devp) *devp = desc.dev;`: 若 `devp` 非空,设置为 `desc.dev`。
5. `if (offsetp) *offsetp = desc.offset;`: 若 `offsetp` 非空,设置为 `desc.offset`。
6. `if (gpiop) {...}`: 计算全局 GPIO 编号。
7. `return 0;`: 成功返回 0。
### `gpio_find_and_xlate` 函数
static int gpio_find_and_xlate(struct gpio_desc *desc,
struct fdtdec_phandle_args *args)
{
struct dm_gpio_ops *ops = gpio_get_ops(desc->dev);if (args->args_count > 0)
desc->offset = args->args[0];
else
desc->offset = -1;
desc->flags = 0;return ops->xlate ? ops->xlate(desc->dev, desc, args) : 0;
}
1. `struct dm_gpio_ops *ops = gpio_get_ops(desc->dev);`: 获取 GPIO 操作指针。
2. `if (args->args_count > 0) desc->offset = args->args[0];`: 若 `args_count` 非 0,设 `desc->offset`。
3. `desc->flags = 0;`: 清空 `flags`。
4. `return ops->xlate ? ops->xlate(desc->dev, desc, args) : 0;`: 若 `xlate` 回调存在则调用。
ps:gpio_find_and_xlate
函数的主要作用是根据设备树中的 GPIO 描述符(fdtdec_phandle_args
)将其转换为 U-Boot 的内部 GPIO 描述符(gpio_desc
)。它是设备模型中连接设备树资源与实际 GPIO 操作的关键一环。
dm_gpio_request 函数
int dm_gpio_request(struct gpio_desc *desc, const char *label)
{
struct udevice *dev = desc->dev;
struct gpio_dev_priv *uc_priv;
char *str;
int ret;uc_priv = dev_get_uclass_priv(dev);
if (uc_priv->name[desc->offset])
return -EBUSY;
str = strdup(label);
if (!str)
return -ENOMEM;
if (gpio_get_ops(dev)->request) {
ret = gpio_get_ops(dev)->request(dev, desc->offset, label);
if (ret) {
free(str);
return ret;
}
}
uc_priv->name[desc->offset] = str;return 0;
}
struct udevice *dev = desc->dev;
从描述符 desc 中获取目标 GPIO 设备对象 dev,用于后续操作。struct gpio_dev_priv *uc_priv;
声明一个指向 GPIO 设备私有数据的指针,便于访问设备的内部数据。uc_priv = dev_get_uclass_priv(dev);
调用 dev_get_uclass_priv 获取设备的私有数据(gpio_dev_priv),例如 GPIO 名称数组 name。if (uc_priv->name[desc->offset]) return -EBUSY;
检查 uc_priv->name[desc->offset] 是否非空,若非空则表示该 GPIO 已被占用,返回 -EBUSY 表示资源忙。str = strdup(label);
为 label 分配内存并复制其内容,方便记录 GPIO 标识符。if (!str) return -ENOMEM;
如果 str 为 NULL,表示内存分配失败,返回 -ENOMEM。if (gpio_get_ops(dev)->request) { ... }
检查设备的 GPIO 操作集中是否定义了 request 回调函数:如果定义了,则调用 request 请求底层驱动为该 GPIO 分配资源。
如果 request 失败,释放分配的内存并返回错误码。
uc_priv->name[desc->offset] = str;
将分配好的 str(GPIO 名称)存储到设备的私有数据中,用于标识该 GPIO。return 0;
返回 0,表示成功完成 GPIO 请求。
dm_gpio_requestf 函数
static int dm_gpio_requestf(struct gpio_desc *desc, const char *fmt, ...)
{
va_list args;
char buf[40];va_start(args, fmt);
vscnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
return dm_gpio_request(desc, buf);
}
va_list args;
声明一个变长参数列表 args,用于处理可变数量的输入参数。va_start(args, fmt);
初始化变长参数列表,准备处理 fmt 和其后的参数。vscnprintf(buf, sizeof(buf), fmt, args);
将格式化后的字符串写入缓冲区 buf,缓冲区长度限制为 40 字节。va_end(args);
结束对变长参数列表的处理,释放资源。return dm_gpio_request(desc, buf);
调用 dm_gpio_request 请求 GPIO,并使用格式化后的字符串 buf 作为 GPIO 标识符。
gpio_request 函数
int gpio_request(unsigned gpio, const char *label)
{
struct gpio_desc desc;
int ret;ret = gpio_to_device(gpio, &desc);
if (ret)
return ret;return dm_gpio_request(&desc, label);
}
struct gpio_desc desc;
定义一个 GPIO 描述符 desc,用于存储设备和偏移信息。ret = gpio_to_device(gpio, &desc);
调用 gpio_to_device,根据全局 GPIO 编号 gpio 查找对应的设备和偏移,结果存储在 desc 中。if (ret) return ret;
如果 gpio_to_device 返回错误,直接返回错误码。return dm_gpio_request(&desc, label);
调用 dm_gpio_request 请求 GPIO,并为其分配标识符 label。
gpio_requestf 函数
int gpio_requestf(unsigned gpio, const char *fmt, ...)
{
va_list args;
char buf[40];va_start(args, fmt);
vscnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
return gpio_request(gpio, buf);
}
va_list args;
声明一个变长参数列表 args。va_start(args, fmt);
初始化变长参数列表,用于处理 fmt 和其后的参数。vscnprintf(buf, sizeof(buf), fmt, args);
格式化变长参数,将结果写入缓冲区 buf。va_end(args);
结束变长参数列表处理。return gpio_request(gpio, buf);
调用 gpio_request,请求 GPIO,并使用格式化后的字符串 buf 作为标识符。
_dm_gpio_free 函数
int _dm_gpio_free(struct udevice *dev, uint offset)
{
struct gpio_dev_priv *uc_priv;
int ret;uc_priv = dev_get_uclass_priv(dev);
if (!uc_priv->name[offset])
return -ENXIO;
if (gpio_get_ops(dev)->free) {
ret = gpio_get_ops(dev)->free(dev, offset);
if (ret)
return ret;
}free(uc_priv->name[offset]);
uc_priv->name[offset] = NULL;return 0;
}
uc_priv = dev_get_uclass_priv(dev);
获取设备的私有数据 uc_priv。if (!uc_priv->name[offset]) return -ENXIO;
检查 name[offset] 是否为空,若为空则返回 -ENXIO,表示 GPIO 未被请求。if (gpio_get_ops(dev)->free) { ... }
如果设备定义了 free 回调函数,则调用它释放 GPIO 资源。free(uc_priv->name[offset]);
释放 name[offset] 所指向的内存。uc_priv->name[offset] = NULL;
将 name[offset] 置为 NULL,表示该 GPIO 未被占用。return 0;
返回 0 表示成功。
gpio_free 函数
int gpio_free(unsigned gpio)
{
struct gpio_desc desc;
int ret;ret = gpio_to_device(gpio, &desc);
if (ret)
return ret;return _dm_gpio_free(desc.dev, desc.offset);
}
gpio_to_device(gpio, &desc);
使用全局 GPIO 编号查找对应的设备和偏移量,结果存储在 desc 中。if (ret) return ret;
如果查找失败,返回错误码。return _dm_gpio_free(desc.dev, desc.offset);
调用 _dm_gpio_free 释放目标设备的 GPIO。
check_reserved 函数
static int check_reserved(struct gpio_desc *desc, const char *func)
{
struct gpio_dev_priv *uc_priv;if (!dm_gpio_is_valid(desc))
return -ENOENT;uc_priv = dev_get_uclass_priv(desc->dev);
if (!uc_priv->name[desc->offset]) {
printf("%s: %s: error: gpio %s%d not reserved\n",
desc->dev->name, func,
uc_priv->bank_name ? uc_priv->bank_name : "",
desc->offset);
return -EBUSY;
}return 0;
}
if (!dm_gpio_is_valid(desc)) return -ENOENT;检查 desc 是否有效:
dm_gpio_is_valid 检查描述符是否为空,或是否包含有效的设备指针。
如果无效,返回 -ENOENT,表示 GPIO 不存在。
uc_priv = dev_get_uclass_priv(desc->dev);获取设备的私有数据 uc_priv,其中存储了 GPIO 控制器的内部状态和配置。
if (!uc_priv->name[desc->offset]) {...}检查 name[desc->offset] 是否为空:
如果为空,说明该 GPIO 未被请求,输出错误信息并返回 -EBUSY。
printf(...);打印未请求的 GPIO 信息,包括设备名称、调用的函数名称、GPIO 的bank名称和偏移量。
return 0;如果上述检查通过,返回 0,表示 GPIO 已经被正确请求。
gpio_direction_input 函数
int gpio_direction_input(unsigned gpio)
{
struct gpio_desc desc;
int ret;ret = gpio_to_device(gpio, &desc);
if (ret)
return ret;
ret = check_reserved(&desc, "dir_input");
if (ret)
return ret;return gpio_get_ops(desc.dev)->direction_input(desc.dev, desc.offset);
}
struct gpio_desc desc;定义一个 GPIO 描述符 desc,用于存储目标设备和偏移量。
ret = gpio_to_device(gpio, &desc);根据全局 GPIO 编号查找对应的设备和偏移量,并填充到 desc 中。
如果查找失败,返回错误码。
ret = check_reserved(&desc, "dir_input");检查该 GPIO 是否已被正确请求。
如果未被请求,返回 -EBUSY。
return gpio_get_ops(desc.dev)->direction_input(...);(此处调用的direction_input即为驱动文件中注册的函数)调用设备的 direction_input 回调,将 GPIO 设置为输入方向。
如果成功,返回 0;如果失败,返回错误码。
gpio_direction_output 函数
int gpio_direction_output(unsigned gpio, int value)
{
struct gpio_desc desc;
int ret;ret = gpio_to_device(gpio, &desc);
if (ret)
return ret;
ret = check_reserved(&desc, "dir_output");
if (ret)
return ret;return gpio_get_ops(desc.dev)->direction_output(desc.dev,
desc.offset, value);
}
struct gpio_desc desc;定义 GPIO 描述符。
ret = gpio_to_device(gpio, &desc);根据全局 GPIO 编号查找设备和偏移量,存储到 desc 中。
ret = check_reserved(&desc, "dir_output");检查该 GPIO 是否已被正确请求。
如果未被请求,返回 -EBUSY。
return gpio_get_ops(desc.dev)->direction_output(...);调用设备的 direction_output 回调函数,设置 GPIO 为输出方向并指定初始值。
如果成功,返回 0;如果失败,返回错误码。
dm_gpio_get_value 函数
int dm_gpio_get_value(struct gpio_desc *desc)
{
int value;
int ret;ret = check_reserved(desc, "get_value");
if (ret)
return ret;value = gpio_get_ops(desc->dev)->get_value(desc->dev, desc->offset);
return desc->flags & GPIOD_ACTIVE_LOW ? !value : value;
}
ret = check_reserved(desc, "get_value");检查该 GPIO 是否已被正确请求。
如果未被请求,返回 -EBUSY。
value = gpio_get_ops(desc->dev)->get_value(...);调用设备的 get_value 回调函数,获取 GPIO 的当前值(高或低)。
return desc->flags & GPIOD_ACTIVE_LOW ? !value : value;如果描述符中的标志位 GPIOD_ACTIVE_LOW 被设置,返回值取反(逻辑高低颠倒)。
否则,直接返回读取到的值。
dm_gpio_set_value 函数
int dm_gpio_set_value(struct gpio_desc *desc, int value)
{
int ret;ret = check_reserved(desc, "set_value");
if (ret)
return ret;if (desc->flags & GPIOD_ACTIVE_LOW)
value = !value;
gpio_get_ops(desc->dev)->set_value(desc->dev, desc->offset, value);
return 0;
}
ret = check_reserved(desc, "set_value");检查该 GPIO 是否已被正确请求。
如果未被请求,返回 -EBUSY。
if (desc->flags & GPIOD_ACTIVE_LOW) value = !value;如果描述符中的标志位 GPIOD_ACTIVE_LOW 被设置,反转目标值。
gpio_get_ops(desc->dev)->set_value(...);调用设备的 set_value 回调函数,将指定值写入目标 GPIO。
return 0;返回 0,表示成功。
dm_gpio_set_dir_flags 函数
int dm_gpio_set_dir_flags(struct gpio_desc *desc, ulong flags)
{
struct udevice *dev = desc->dev;
struct dm_gpio_ops *ops = gpio_get_ops(dev);
int ret;ret = check_reserved(desc, "set_dir");
if (ret)
return ret;if (flags & GPIOD_IS_OUT) {
int value = flags & GPIOD_IS_OUT_ACTIVE ? 1 : 0;if (flags & GPIOD_ACTIVE_LOW)
value = !value;
ret = ops->direction_output(dev, desc->offset, value);
} else if (flags & GPIOD_IS_IN) {
ret = ops->direction_input(dev, desc->offset);
}
if (ret)
return ret;/* Update desc->flags here, so that GPIO_ACTIVE_LOW is honoured in futures */
desc->flags = flags;return 0;
}
struct udevice *dev = desc->dev;
获取描述符 desc 中的设备指针 dev,表示操作的目标设备。struct dm_gpio_ops *ops = gpio_get_ops(dev);
获取设备的 GPIO 操作集 ops,用于调用设备驱动的具体实现。ret = check_reserved(desc, "set_dir");
检查 GPIO 是否已被正确请求。如果未被请求,返回 -EBUSY。if (flags & GPIOD_IS_OUT) {...}
判断标志位 flags 是否要求将 GPIO 设置为输出:如果设置为输出,计算输出值 value:
如果 flags & GPIOD_IS_OUT_ACTIVE 为真,初始值为 1。
如果 flags & GPIOD_ACTIVE_LOW 为真,反转初始值。
调用 ops->direction_output 设置方向为输出,并初始化 GPIO 值。
else if (flags & GPIOD_IS_IN) {...}
如果标志位要求设置为输入方向,调用 ops->direction_input。if (ret) return ret;
如果方向设置失败,返回错误码。desc->flags = flags;
更新描述符的标志位,使其与操作同步。return 0;
返回 0,表示操作成功。
dm_gpio_set_dir 函数
int dm_gpio_set_dir(struct gpio_desc *desc)
{
return dm_gpio_set_dir_flags(desc, desc->flags);
}
return dm_gpio_set_dir_flags(desc, desc->flags);
调用 dm_gpio_set_dir_flags,使用描述符中存储的标志位设置 GPIO 的方向。
gpio_get_value 函数
int gpio_get_value(unsigned gpio)
{
int ret;
struct gpio_desc desc;ret = gpio_to_device(gpio, &desc);
if (ret)
return ret;
return dm_gpio_get_value(&desc);
}
struct gpio_desc desc;
定义一个 GPIO 描述符 desc。ret = gpio_to_device(gpio, &desc);
根据全局 GPIO 编号查找对应的设备和偏移量,并填充到 desc 中。if (ret) return ret;
如果查找失败,返回错误码。return dm_gpio_get_value(&desc);
调用 dm_gpio_get_value 获取 GPIO 的值,并返回结果。
gpio_set_value 函数
int gpio_set_value(unsigned gpio, int value)
{
struct gpio_desc desc;
int ret;ret = gpio_to_device(gpio, &desc);
if (ret)
return ret;
return dm_gpio_set_value(&desc, value);
}
struct gpio_desc desc;
定义一个 GPIO 描述符 desc。ret = gpio_to_device(gpio, &desc);
根据全局 GPIO 编号查找对应的设备和偏移量,并填充到 desc 中。if (ret) return ret;
如果查找失败,返回错误码。return dm_gpio_set_value(&desc, value);
调用 dm_gpio_set_value 设置 GPIO 的值,并返回结果。
gpio_get_bank_info 函数
const char *gpio_get_bank_info(struct udevice *dev, int *bit_count)
{
struct gpio_dev_priv *priv;/* Must be called on an active device */
priv = dev_get_uclass_priv(dev);
assert(priv);*bit_count = priv->gpio_count;
return priv->bank_name;
}
struct gpio_dev_priv *priv;
定义一个指向设备私有数据的指针 priv。priv = dev_get_uclass_priv(dev);
获取目标设备的私有数据,其中包含 GPIO 控制器的信息。assert(priv);
确保设备的私有数据非空。如果为空,程序将终止。*bit_count = priv->gpio_count;
将 GPIO 控制器的 GPIO 数量存储到 bit_count 中。return priv->bank_name;
返回 GPIO 控制器的bank名称(bank_name),用于标识该 GPIO 控制器。
gpio_function 数组
static const char * const gpio_function[GPIOF_COUNT] = {
"input",
"output",
"unused",
"unknown",
"func",
};
定义 gpio_function 数组:定义了一个字符串数组,描述 GPIO 的不同功能状态。每个功能对应一个固定的字符串:
"input":GPIO 配置为输入。
"output":GPIO 配置为输出。
"unused":GPIO 未被使用。
"unknown":GPIO 功能未知。
"func":GPIO 配置为特殊功能。
数组大小由 GPIOF_COUNT 定义:GPIOF_COUNT 是枚举值,表示支持的 GPIO 功能状态总数。
get_function 函数
int get_function(struct udevice *dev, int offset, bool skip_unused,
const char **namep)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
struct dm_gpio_ops *ops = gpio_get_ops(dev);BUILD_BUG_ON(GPIOF_COUNT != ARRAY_SIZE(gpio_function));
if (!device_active(dev))
return -ENODEV;
if (offset < 0 || offset >= uc_priv->gpio_count)
return -EINVAL;
if (namep)
*namep = uc_priv->name[offset];
if (skip_unused && !uc_priv->name[offset])
return GPIOF_UNUSED;
if (ops->get_function) {
int ret;ret = ops->get_function(dev, offset);
if (ret < 0)
return ret;
if (ret >= ARRAY_SIZE(gpio_function))
return -ENODATA;
return ret;
}return GPIOF_UNKNOWN;
}
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
获取设备的 GPIO 私有数据,用于访问 GPIO 相关信息。struct dm_gpio_ops *ops = gpio_get_ops(dev);
获取设备的 GPIO 操作集,调用驱动实现的 GPIO 操作函数。BUILD_BUG_ON(GPIOF_COUNT != ARRAY_SIZE(gpio_function));
编译时检查,确保 gpio_function 数组的大小与 GPIOF_COUNT 一致。如果不一致,会导致编译失败。if (!device_active(dev)) return -ENODEV;
检查设备是否处于激活状态。如果未激活,返回 -ENODEV。if (offset < 0 || offset >= uc_priv->gpio_count) return -EINVAL;
检查 GPIO 偏移是否有效。如果偏移值超出范围,返回 -EINVAL。if (namep) *namep = uc_priv->name[offset];
如果提供了 namep 参数,将 GPIO 的名称存储到 namep。if (skip_unused && !uc_priv->name[offset]) return GPIOF_UNUSED;
如果启用了 skip_unused 并且 GPIO 没有名称,返回 GPIOF_UNUSED 表示未使用。if (ops->get_function) {...}
如果驱动实现了 get_function 回调函数,调用该函数获取 GPIO 的功能状态。
检查返回值的范围是否有效(小于 gpio_function 数组大小)。
return GPIOF_UNKNOWN;
如果未实现 get_function 回调,返回 GPIOF_UNKNOWN。
gpio_get_function 函数
int gpio_get_function(struct udevice *dev, int offset, const char **namep)
{
return get_function(dev, offset, true, namep);
}
return get_function(dev, offset, true, namep);
调用 get_function,启用 skip_unused 标志,忽略未使用的 GPIO,返回 GPIO 的功能状态。
gpio_get_raw_function 函数
int gpio_get_raw_function(struct udevice *dev, int offset, const char **namep)
{
return get_function(dev, offset, false, namep);
}
return get_function(dev, offset, false, namep);
调用 get_function,禁用 skip_unused 标志,即使 GPIO 未被使用,也返回其功能状态。
gpio_get_status 函数
int gpio_get_status(struct udevice *dev, int offset, char *buf, int buffsize)
{
struct dm_gpio_ops *ops = gpio_get_ops(dev);
struct gpio_dev_priv *priv;
char *str = buf;
int func;
int ret;
int len;BUILD_BUG_ON(GPIOF_COUNT != ARRAY_SIZE(gpio_function));
*buf = 0;
priv = dev_get_uclass_priv(dev);
ret = gpio_get_raw_function(dev, offset, NULL);
if (ret < 0)
return ret;
func = ret;
len = snprintf(str, buffsize, "%s%d: %s",
priv->bank_name ? priv->bank_name : "",
offset, gpio_function[func]);
if (func == GPIOF_INPUT || func == GPIOF_OUTPUT ||
func == GPIOF_UNUSED) {
const char *label;
bool used;ret = ops->get_value(dev, offset);
if (ret < 0)
return ret;
used = gpio_get_function(dev, offset, &label) != GPIOF_UNUSED;
snprintf(str + len, buffsize - len, ": %d [%c]%s%s",
ret,
used ? 'x' : ' ',
used ? " " : "",
label ? label : "");
}return 0;
}
BUILD_BUG_ON(GPIOF_COUNT != ARRAY_SIZE(gpio_function));
编译时检查数组大小。*buf = 0;
初始化缓冲区,清空内容。priv = dev_get_uclass_priv(dev);
获取 GPIO 设备的私有数据。ret = gpio_get_raw_function(dev, offset, NULL);
获取 GPIO 的功能状态。func = ret;
保存 GPIO 的功能状态。snprintf(str, buffsize, ...)
将 GPIO 信息(bank名称、偏移、功能状态)格式化并写入缓冲区。检查功能状态:
如果功能状态为 GPIOF_INPUT、GPIOF_OUTPUT 或 GPIOF_UNUSED,继续读取 GPIO 的值。
ret = ops->get_value(dev, offset);
调用驱动的 get_value 回调获取 GPIO 的当前值。used = gpio_get_function(...) != GPIOF_UNUSED;
检查 GPIO 是否被使用。snprintf(str + len, ...)
格式化并追加 GPIO 的值和状态信息。return 0;
返回 0,表示操作成功。
gpio_claim_vector 函数
int gpio_claim_vector(const int *gpio_num_array, const char *fmt)
{
int i, ret;
int gpio;for (i = 0; i < 32; i++) {
gpio = gpio_num_array[i];
if (gpio == -1)
break;
ret = gpio_requestf(gpio, fmt, i);
if (ret)
goto err;
ret = gpio_direction_input(gpio);
if (ret) {
gpio_free(gpio);
goto err;
}
}return 0;
err:
for (i--; i >= 0; i--)
gpio_free(gpio_num_array[i]);return ret;
}
遍历 GPIO 数组:遍历最多 32 个 GPIO,直到数组中出现 -1(终止标志)。
ret = gpio_requestf(gpio, fmt, i);
请求 GPIO 资源,并为其分配格式化的名称。ret = gpio_direction_input(gpio);
将 GPIO 配置为输入方向。错误处理:
如果请求或配置失败,释放已分配的 GPIO 并返回错误码。
return 0;
返回 0,表示所有 GPIO 操作成功。
gpio_get_values_as_int 函数
int gpio_get_values_as_int(const int *gpio_list)
{
int gpio;
unsigned bitmask = 1;
unsigned vector = 0;
int ret;while (bitmask &&
((gpio = *gpio_list++) != -1)) {
ret = gpio_get_value(gpio);
if (ret < 0)
return ret;
else if (ret)
vector |= bitmask;
bitmask <<= 1;
}return vector;
}
int gpio;
定义一个整型变量 gpio,用于存储当前处理的 GPIO 编号。unsigned bitmask = 1;
定义一个位掩码,用于标记当前 GPIO 对应的位。unsigned vector = 0;
定义一个整型变量 vector,用于存储所有 GPIO 的值。int ret;
定义返回值 ret,用于接收 gpio_get_value 的结果。while (bitmask && ((gpio = *gpio_list++) != -1)) {...}
遍历 GPIO 列表:从 gpio_list 中逐个读取 GPIO 编号,直到遇到 -1(列表结束)。
如果位掩码溢出(超出 32 位),停止遍历。
ret = gpio_get_value(gpio);
获取当前 GPIO 的值(高或低)。if (ret < 0) return ret;
如果读取 GPIO 值失败,返回错误码。else if (ret) vector |= bitmask;
如果当前 GPIO 值为高电平(非零),将对应位的掩码加入 vector。bitmask <<= 1;
将位掩码左移一位,用于处理下一个 GPIO。return vector;
返回最终的 vector,表示所有 GPIO 的值。
_gpio_request_by_name_nodev 函数
static int _gpio_request_by_name_nodev(const void *blob, int node,
const char *list_name, int index,
struct gpio_desc *desc, int flags,
bool add_index)
{
struct fdtdec_phandle_args args;
int ret;desc->dev = NULL;
desc->offset = 0;
ret = fdtdec_parse_phandle_with_args(blob, node, list_name,
"#gpio-cells", 0, index, &args);
if (ret) {
debug("%s: fdtdec_parse_phandle_with_args failed\n", __func__);
goto err;
}ret = uclass_get_device_by_of_offset(UCLASS_GPIO, args.node,
&desc->dev);
if (ret) {
debug("%s: uclass_get_device_by_of_offset failed\n", __func__);
goto err;
}
ret = gpio_find_and_xlate(desc, &args);
if (ret) {
debug("%s: gpio_find_and_xlate failed\n", __func__);
goto err;
}
ret = dm_gpio_requestf(desc, add_index ? "%s.%s%d" : "%s.%s",
fdt_get_name(blob, node, NULL),
list_name, index);
if (ret) {
debug("%s: dm_gpio_requestf failed\n", __func__);
goto err;
}
ret = dm_gpio_set_dir_flags(desc, flags | desc->flags);
if (ret) {
debug("%s: dm_gpio_set_dir failed\n", __func__);
goto err;
}return 0;
err:
debug("%s: Node '%s', property '%s', failed to request GPIO index %d: %d\n",
__func__, fdt_get_name(blob, node, NULL), list_name, index, ret);
return ret;
}
struct fdtdec_phandle_args args;
定义一个结构体变量 args,用于存储设备树解析的 GPIO 参数。int ret;
定义返回值变量 ret。desc->dev = NULL; desc->offset = 0;
初始化 GPIO 描述符 desc,清空设备指针和偏移量。ret = fdtdec_parse_phandle_with_args(...);
调用设备树解析函数,从指定节点中解析 GPIO 参数。如果解析失败,打印调试信息并跳转到 err 标签。
ret = uclass_get_device_by_of_offset(...);
根据设备树偏移获取 GPIO 控制器设备。如果失败,打印调试信息并跳转到 err 标签。
ret = gpio_find_and_xlate(...);
调用 gpio_find_and_xlate,将解析的参数转换为内部 GPIO 描述符。ret = dm_gpio_requestf(...);
请求 GPIO 资源,并为其分配格式化名称。ret = dm_gpio_set_dir_flags(...);
设置 GPIO 的方向和标志位。return 0;
如果所有步骤成功,返回 0。错误处理:
如果某一步失败,打印调试信息并返回错误码。
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)
{
return _gpio_request_by_name_nodev(blob, node, list_name, index, desc,
flags, index > 0);
}
调用 _gpio_request_by_name_nodev:
封装 _gpio_request_by_name_nodev 函数,传递 index > 0 作为 add_index 参数。
返回 _gpio_request_by_name_nodev 的结果。
gpio_request_by_name 函数
int gpio_request_by_name(struct udevice *dev, const char *list_name, int index,
struct gpio_desc *desc, int flags)
{
return gpio_request_by_name_nodev(gd->fdt_blob, dev->of_offset,
list_name, index, desc, flags);
}
调用 gpio_request_by_name_nodev:
使用全局设备树数据 gd->fdt_blob 和设备偏移 dev->of_offset,调用 gpio_request_by_name_nodev 请求 GPIO。
返回请求结果。
gpio_request_list_by_name_nodev 函数
int gpio_request_list_by_name_nodev(const void *blob, int node,
const char *list_name,
struct gpio_desc *desc, int max_count,
int flags)
{
int count;
int ret;for (count = 0; count < max_count; count++) {
ret = _gpio_request_by_name_nodev(blob, node, list_name, count,
&desc[count], flags, true);
if (ret == -ENOENT)
break;
else if (ret)
goto err;
}return count;
err:
gpio_free_list_nodev(desc, count - 1);return ret;
}
int count; int ret;
定义计数器和返回值变量。for (count = 0; count < max_count; count++) {...}
循环请求 GPIO,最多请求 max_count 个 GPIO。ret = _gpio_request_by_name_nodev(...);
调用 _gpio_request_by_name_nodev 请求单个 GPIO。错误处理:
如果返回 -ENOENT,说明列表结束,跳出循环。
如果返回其他错误码,释放已请求的 GPIO 并返回错误。
return count;
返回成功请求的 GPIO 数量。
gpio_request_list_by_name 函数
int gpio_request_list_by_name(struct udevice *dev, const char *list_name,
struct gpio_desc *desc, int max_count,
int flags)
{
/*
* This isn't ideal since we don't use dev->name in the debug()
* calls in gpio_request_by_name(), but we can do this until
* gpio_request_list_by_name_nodev() can be dropped.
*/
return gpio_request_list_by_name_nodev(gd->fdt_blob, dev->of_offset,
list_name, desc, max_count,
flags);
}
函数定义:参数:
dev:目标设备。
list_name:GPIO 列表的名称(从设备树中解析)。
desc:存储 GPIO 描述符的数组。
max_count:要请求的最大 GPIO 数量。
flags:GPIO 的方向和标志位。
返回值:成功请求的 GPIO 数量或错误码。
return gpio_request_list_by_name_nodev(...)调用核心函数 gpio_request_list_by_name_nodev:
使用全局设备树数据 gd->fdt_blob 和设备偏移 dev->of_offset。
请求最多 max_count 个 GPIO。
直接返回该函数的结果。
gpio_get_list_count 函数
int gpio_get_list_count(struct udevice *dev, const char *list_name)
{
int ret;ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev->of_offset,
list_name, "#gpio-cells", 0, -1,
NULL);
if (ret) {
debug("%s: Node '%s', property '%s', GPIO count failed: %d\n",
__func__, dev->name, list_name, ret);
}return ret;
}
函数定义:参数:
dev:目标设备。
list_name:GPIO 列表的名称。
返回值:GPIO 列表中的 GPIO 数量或错误码。
ret = fdtdec_parse_phandle_with_args(...)使用 fdtdec_parse_phandle_with_args 解析设备树中的 GPIO 列表。
-1 表示查询列表中所有的 GPIO 数量。
if (ret)如果解析失败,打印调试信息,包括设备名称、属性名称和错误码。
return ret;返回 GPIO 数量或错误码。
dm_gpio_free 函数
int dm_gpio_free(struct udevice *dev, struct gpio_desc *desc)
{
/* For now, we don't do any checking of dev */
return _dm_gpio_free(desc->dev, desc->offset);
}
return _dm_gpio_free(desc->dev, desc->offset);
调用核心函数 _dm_gpio_free 释放指定的 GPIO:
参数 desc->dev 表示 GPIO 所属设备。
参数 desc->offset 表示 GPIO 在设备中的偏移量。
返回释放操作的结果。
gpio_free_list 函数
int gpio_free_list(struct udevice *dev, struct gpio_desc *desc, int count)
{
int i;/* For now, we don't do any checking of dev */
for (i = 0; i < count; i++)
dm_gpio_free(dev, &desc[i]);return 0;
}
参数:desc:GPIO 描述符数组。
count:需要释放的 GPIO 数量。
遍历所有 GPIO:调用 dm_gpio_free 逐个释放描述符数组中的 GPIO。
return 0;返回 0,表示成功释放所有 GPIO。
gpio_free_list_nodev 函数
int gpio_free_list_nodev(struct gpio_desc *desc, int count)
{
return gpio_free_list(NULL, desc, count);
}
return gpio_free_list(NULL, desc, count);
调用 gpio_free_list 释放 GPIO 列表。
传递 NULL 作为设备参数,表示不依赖设备信息。
gpio_renumber 函数
static int gpio_renumber(struct udevice *removed_dev)
{
struct gpio_dev_priv *uc_priv;
struct udevice *dev;
struct uclass *uc;
unsigned base;
int ret;ret = uclass_get(UCLASS_GPIO, &uc);
if (ret)
return ret;/* Ensure that we have a base for each bank */
base = 0;
uclass_foreach_dev(dev, uc) {
if (device_active(dev) && dev != removed_dev) {
uc_priv = dev_get_uclass_priv(dev);
uc_priv->gpio_base = base;
base += uc_priv->gpio_count;
}
}return 0;
}
ret = uclass_get(UCLASS_GPIO, &uc);获取 GPIO 类的所有设备实例。
uclass_foreach_dev(dev, uc)遍历 GPIO 类的所有设备。
检查设备状态:如果设备已激活且不是要移除的设备,重新编号。
重新编号:将设备的 gpio_base 设置为当前基地址。
将基地址累加该设备的 GPIO 数量。
返回结果:返回 0 表示成功。
gpio_get_number 函数
int gpio_get_number(struct gpio_desc *desc)
{
struct udevice *dev = desc->dev;
struct gpio_dev_priv *uc_priv;if (!dev)
return -1;
uc_priv = dev->uclass_priv;return uc_priv->gpio_base + desc->offset;
}
获取 GPIO 描述符的设备:如果设备为空,返回 -1 表示错误。
获取 GPIO 全局编号:返回 gpio_base + desc->offset,表示 GPIO 在系统中的全局编号。
gpio_post_probe 函数
static int gpio_post_probe(struct udevice *dev)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);uc_priv->name = calloc(uc_priv->gpio_count, sizeof(char *));
if (!uc_priv->name)
return -ENOMEM;return gpio_renumber(NULL);
}
分配 GPIO 名称数组:为设备的每个 GPIO 分配一个名称指针。
检查分配结果:如果分配失败,返回 -ENOMEM。
重新编号:调用 gpio_renumber,为所有 GPIO 重新分配基地址。
gpio_pre_remove 函数
static int gpio_pre_remove(struct udevice *dev)
{
struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
int i;for (i = 0; i < uc_priv->gpio_count; i++) {
if (uc_priv->name[i])
free(uc_priv->name[i]);
}
free(uc_priv->name);return gpio_renumber(dev);
}
释放 GPIO 名称数组:遍历设备的所有 GPIO,释放每个名称的内存。
调用 gpio_renumber:为剩余的 GPIO 重新编号。
UCLASS_DRIVER(gpio) 宏
UCLASS_DRIVER(gpio) = {
.id = UCLASS_GPIO,
.name = "gpio",
.flags = DM_UC_FLAG_SEQ_ALIAS,
.post_probe = gpio_post_probe,
.pre_remove = gpio_pre_remove,
.per_device_auto_alloc_size = sizeof(struct gpio_dev_priv),
};
UCLASS_GPIO:定义 GPIO 类的设备模型。
gpio_post_probe:在设备初始化后调用,用于分配资源。
gpio_pre_remove:在设备移除前调用,用于清理资源。
per_device_auto_alloc_size:自动为每个设备分配 gpio_dev_priv 的内存。
U-Boot gpio-uclass.c 功能总结
gpio-uclass.c 是 U-Boot GPIO 子系统的一部分,提供了 GPIO 的抽象和管理功能。该文件实现了 GPIO 设备模型的核心逻辑,负责管理 GPIO 控制器设备及其引脚的请求、释放、方向设置、状态查询等操作。以下是该文件的主要功能总结:
1. GPIO 设备模型管理
设备类定义:
使用 UCLASS_DRIVER(gpio) 定义了 GPIO 设备类,标识该设备类为 UCLASS_GPIO。提供了设备初始化(post_probe)和移除(pre_remove)的钩子,用于分配和释放设备资源。
GPIO 控制器的注册与编号:
支持多个 GPIO 控制器,每个控制器在注册时通过 gpio_post_probe 进行初始化。使用 gpio_renumber 对系统中的 GPIO 进行统一编号,确保全局唯一性。
2. GPIO 引脚操作
提供了一组对 GPIO 引脚进行操作的接口:
引脚请求与释放:
gpio_request_by_name 和 gpio_request_list_by_name:从设备树中解析 GPIO 列表并请求引脚资源。
dm_gpio_free 和 gpio_free_list:释放单个或多个 GPIO,引脚资源被回收。
方向设置:
dm_gpio_set_dir_flags 和 dm_gpio_set_dir:设置 GPIO 引脚的输入/输出方向,同时支持设置初始值。
逻辑值读写:
dm_gpio_get_value:获取 GPIO 当前的逻辑值(高/低电平)。
dm_gpio_set_value:设置 GPIO 的逻辑值(高/低电平)。
功能状态查询:
gpio_get_function 和 gpio_get_raw_function:查询 GPIO 的当前功能状态(如 input、output)。
gpio_get_status:获取 GPIO 的详细状态信息,包括方向、值、标识等。
3. 设备树支持
提供了从设备树解析 GPIO 定义的功能:
gpio_request_by_name_nodev 和 gpio_request_list_by_name_nodev:通过设备树的节点信息请求单个或多个 GPIO。支持设备树属性解析,如 #gpio-cells、GPIO 偏移、方向标志等。
4. 批量操作支持
gpio_get_values_as_int:
获取多个 GPIO 的值,并将其编码为整数返回。常用于需要批量读取 GPIO 状态的场景,如键盘扫描等。
gpio_claim_vector:
批量请求多个 GPIO 并配置为输入方向。
5. 错误处理与调试
错误处理:
通过标准错误码(如 -EINVAL、-ENOENT 等)指示操作失败原因。遇到错误时,释放已占用的资源,确保系统一致性。
调试支持:
在解析设备树、请求引脚等关键操作中,打印调试信息便于开发和调试。
6. 内存与资源管理
在设备初始化(post_probe)时分配资源:
为每个 GPIO 控制器分配名称数组,用于记录引脚的标识。
在设备移除(pre_remove)时释放资源:
释放所有 GPIO 的名称和其他动态分配的内存。通过 per_device_auto_alloc_size 为每个设备分配私有数据(gpio_dev_priv)。
7. 全局功能实现
GPIO 全局编号:
提供了对 GPIO 的全局编号管理。gpio_get_number 返回 GPIO 的全局编号(基地址 + 偏移)。
GPIO 功能查询与状态获取:
提供 GPIO 的功能状态查询(如 input、output、unused)。通过 gpio_get_status 获取 GPIO 的详细状态。
核心设计特点
设备模型抽象:
基于设备模型(Device Model,DM),每个 GPIO 控制器作为一个设备(udevice)管理。支持多个 GPIO 控制器,通过设备树自动解析和绑定。
灵活的设备树支持:
解析设备树中的 GPIO 定义,支持兼容性强的 GPIO 控制器驱动。
模块化设计:
引脚的请求、释放、方向设置、值读写等功能独立实现,易于维护和扩展。
批量操作支持:
支持批量请求、释放和读取 GPIO,适用于复杂的硬件场景。
总结
gpio-uclass.c 是 U-Boot 中 GPIO 子系统的重要组成部分,提供了对 GPIO 控制器和引脚的统一管理接口。它通过设备模型和设备树支持,实现了 GPIO 的抽象化管理和跨平台兼容性。该文件的功能涵盖了 GPIO 的请求、释放、方向配置、逻辑值操作,以及设备的初始化与移除,是 GPIO 子系统的核心驱动代码。
GPIO应用
我们使用uboot的gpio框架实现对100号pin进行输出控制,100ms翻转一次。
实现步骤
-
引入头文件:
- 包含 U-Boot GPIO 驱动框架和延时相关的头文件。
-
请求 GPIO 引脚:
- 使用
gpio_request
请求 GPIO 引脚。
- 使用
-
配置 GPIO 为输出方向:
- 使用
gpio_direction_output
将 GPIO 配置为输出方向。
- 使用
-
翻转 GPIO 引脚:
- 使用
gpio_set_value
设置引脚的值(高或低电平)。 - 利用变量存储当前 GPIO 的逻辑值,每次翻转后取反。
- 使用
-
延时控制:
- 使用 U-Boot 提供的
mdelay
函数实现 100 毫秒延时。
- 使用 U-Boot 提供的
-
清理资源:
- 程序结束时,释放 GPIO 引脚资源。
#include <common.h>
#include <dm.h>
#include <gpio.h>
#include <asm/io.h>
#include <linux/delay.h>
#define GPIO_NUM 100
#define TOGGLE_INTERVAL_MS 100
int gpio_toggle_example(void)
{
int ret;
int value = 0; // 初始值为低电平
// 1. 请求 GPIO
ret = gpio_request(GPIO_NUM, "toggle_gpio");
if (ret) {
printf("Failed to request GPIO %d: %d\n", GPIO_NUM, ret);
return ret;
}
// 2. 配置为输出方向
ret = gpio_direction_output(GPIO_NUM, value);
if (ret) {
printf("Failed to set GPIO %d direction: %d\n", GPIO_NUM, ret);
gpio_free(GPIO_NUM);
return ret;
}
printf("Starting GPIO %d toggle every %d ms\n", GPIO_NUM, TOGGLE_INTERVAL_MS);
// 3. 循环翻转 GPIO
//在uboot中不要使用while(1)循环,会导致uboot死循环无法引导linux,可以使用带有条件的循环保证可退出
while (1) {
// 翻转值
value = !value;
// 设置 GPIO 输出
gpio_set_value(GPIO_NUM, value);
// 打印当前值
printf("GPIO %d set to %d\n", GPIO_NUM, value);
// 延时 100 ms
mdelay(TOGGLE_INTERVAL_MS);
}
// 4. 释放 GPIO (虽然实际不会到达这里)
gpio_free(GPIO_NUM);
return 0;
}