GPIO驱动结构

GPIO驱动结构
根据上图,在Linux中,会在gpiolib.c中定义一个 ARCH_NR_GPIOS 大小的数组static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];。每个元素对应于当前CPU上各个GPIO接口的信息,记录各个GPIO的描述符,即对应struct gpio_desc结构体。struct gpio_desc内的成员gpio_chip又指向了一系列关于GPIO的操作。具体的操作在 driver/gpio/gpio_xxx.c中实现。


调用流程

/* These "optional" allocation calls help prevent drivers from stomping
 * on each other, and help provide better diagnostics in debugfs.
 * They're called even less than the "set direction" calls.
 */
static int gpiod_request(struct gpio_desc *desc, const char *label)
{
    struct gpio_chip    *chip;
    int         status = -EPROBE_DEFER;
    unsigned long       flags;

    if (!desc) {
        pr_warn("%s: invalid GPIO\n", __func__);
        return -EINVAL;
    }

    spin_lock_irqsave(&gpio_lock, flags);

    chip = desc->chip;
    if (chip == NULL)
        goto done;

    if (!try_module_get(chip->owner))
        goto done;

    /* NOTE:  gpio_request() can be called in early boot,
     * before IRQs are enabled, for non-sleeping (SOC) GPIOs.
     */

    if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) {
        desc_set_label(desc, label ? : "?");
        status = 0;
    } else {
        status = -EBUSY;
        module_put(chip->owner);
        goto done;
    }

    if (chip->request) {
        /* chip->request may sleep */
        spin_unlock_irqrestore(&gpio_lock, flags);
        status = chip->request(chip, gpio_chip_hwgpio(desc));
        spin_lock_irqsave(&gpio_lock, flags);

        if (status < 0) {
            desc_set_label(desc, NULL);
            module_put(chip->owner);
            clear_bit(FLAG_REQUESTED, &desc->flags);
            goto done;
        }
    }
    if (chip->get_direction) {
        /* chip->get_direction may sleep */
        spin_unlock_irqrestore(&gpio_lock, flags);
        gpiod_get_direction(desc);
        spin_lock_irqsave(&gpio_lock, flags);
    }
done:
    if (status)
        gpiod_dbg(desc, "%s: status %d\n", __func__, status);
    spin_unlock_irqrestore(&gpio_lock, flags);
    return status;
}

int gpio_request(unsigned gpio, const char *label)
{
    return gpiod_request(gpio_to_desc(gpio), label);
}

EXPORT_SYMBOL_GPL(gpio_request);

当我们调用gpio_request(),实际上会将参数gpio在数组中查找其对应的gpio_desc,在根据gpio_chip就可以找到底层关于request()的实现。

所有的GPIO控制器驱动应包括下面的头文件,包含 struct gpio_chip 的定义。

#include <linux/gpio/driver.h>

关于GPIO的数据结构

struct gpio_chip:标记GPIO所属的控制器,里面包含诸多回调函数,用于控制GPIO的行为,各个板卡都有实现自己的gpio_chip控制模块;
struct gpio_desc:用于标记一个GPIO;


注册平台相关的GPIO驱动

driver/gpio/gpio_xxx.c文件中会注册特定平台xxx的GPIO驱动,主要内容为:

  • 定义了GPIO板级控制的内容,包括设置 set gpio,get gpio,dir_in,dir_out,gpio_to_irq等行为;
  • 获取GPIO板级的资源,包括 gpio_reg_base,并赋值给 struct gpio_chip;
  • struct gpio_chip添加到GPIOLIB中;

定义平台驱动并初始化

static struct platform_driver xxx_gpio_driver = {
    .probe      = xxx_gpio_probe,
    .remove     = xxx_gpio_remove,
    .driver.owner   = THIS_MODULE,
    .driver     = {
        .name   = "xxx-gpio",
    },
};

static int __init xxx_gpio_init(void)
{
    return platform_driver_register(&xxx_gpio_driver);
}

添加与之对应的platform设备

arch/arm/mach-xxx/xxx.c会添加 platform 的设备。

static struct platform_device xxx_gpio_device = {
    .name       = "xxx-gpio",
    .id     = 0,
    .num_resources  = ARRAY_SIZE(xxx_gpio_resources),
    .resource   = xxx_gpio_resources,
};

struct gpio_chip添加到GPIOLIB中

当设备与驱动匹配就会执行xxx_gpio_probe()的内容。
probe() 中初始化struct gpio_chip结构体的成员。并调用 gpiochip_add()gpio_chip添加到 GPIOLIB 中。
GPIOLIB 提供GPIO调用的接口函数。比如常见的 gpio_request()/gpio_free()/gpio_get_value()/gpio_is_valid() 等。


参考文档

Linux下GPIO驱动(三) —-gpio_desc()的分析
linux gpio接口

GitHub 加速计划 / li / linux-dash
10.39 K
1.2 K
下载
A beautiful web dashboard for Linux
最近提交(Master分支:2 个月前 )
186a802e added ecosystem file for PM2 4 年前
5def40a3 Add host customization support for the NodeJS version 4 年前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐