S5P6818_系统篇(7)uboot gpio驱动框架分析与应用

目录

目录

简介

gpio-uclass的结构

GPIO驱动与厂商驱动的对接

用户调用GPIO接口

GPIO厂商驱动

gpio-uclass驱动

U-Boot gpio-uclass.c 功能总结

GPIO应用

实现步骤

简介

在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(&regs->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(&regs->data, 1 << pin);
	else
		setbits_le32(&regs->pad_reset, 1 << pin);

	setbits_le32(&regs->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(&regs->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(&regs->data, 1 << pin);
	else
		clrbits_le32(&regs->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(&regs->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(&regs->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(&regs->data, 1 << pin);
	else
		clrbits_le32(&regs->data, 1 << pin);

	setbits_le32(&regs->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(&regs->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(&regs->data, 1 << pin);
	else
		clrbits_le32(&regs->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(&regs->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(&regs->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(&regs->data, 1 << pin);
    else
        setbits_le32(&regs->pad_reset, 1 << pin);

    setbits_le32(&regs->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(&regs->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(&regs->data, 1 << pin);
    else
        clrbits_le32(&regs->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(&regs->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(&regs->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(&regs->data, 1 << pin);
    else
        clrbits_le32(&regs->data, 1 << pin);

    setbits_le32(&regs->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(&regs->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(&regs->data, 1 << pin);
    else
        clrbits_le32(&regs->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(&regs->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翻转一次。

实现步骤

  1. 引入头文件

    • 包含 U-Boot GPIO 驱动框架和延时相关的头文件。
  2. 请求 GPIO 引脚

    • 使用 gpio_request 请求 GPIO 引脚。
  3. 配置 GPIO 为输出方向

    • 使用 gpio_direction_output 将 GPIO 配置为输出方向。
  4. 翻转 GPIO 引脚

    • 使用 gpio_set_value 设置引脚的值(高或低电平)。
    • 利用变量存储当前 GPIO 的逻辑值,每次翻转后取反。
  5. 延时控制

    • 使用 U-Boot 提供的 mdelay 函数实现 100 毫秒延时。
  6. 清理资源

    • 程序结束时,释放 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Absorbed_w

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值