Skip to content

LV520-gpio驱动分析

gpio1 节点的 compatible 属性描述了兼容性,在 Linux 内核中搜索“fsl, imx6ul-gpio”和“fsl, imx35-gpio”这两个字符串,查找 GPIO 驱动文件。gpio-mxc.c - drivers/gpio/gpio-mxc.c 就是 I.MX6ULL 的 GPIO 驱动文件。这里就不再详细去分析了,也挺复杂的,感觉会用就行了。

一、核心数据结构

这里大概了解下重要的 3 个核心数据结构。记住 GPIO Controller 的要素,这有助于理解它的驱动程序:

  • 一个 GPIO Controller 里有多少个引脚?有哪些引脚?
  • 需要提供函数,设置引脚方向、读取/设置数值
  • 需要提供函数,把引脚转换为中断

以 Linux 面向对象编程的思想,一个 GPIO Controller 必定会使用一个结构体来表示,这个结构体必定含有这些信息:

  • GPIO 引脚信息
  • 控制引脚的函数
  • 中断相关的函数

1. gpio_device

image-20260121155520659

每个 GPIO Controller 用一个 gpio_device 来表示:

  • 里面每一个 gpio 引脚用一个 gpio_desc 来表示。
  • gpio 引脚的函数(引脚控制、中断相关),都放在 gpio_chip 里。
c
struct gpio_device {
	int			id;              // 它是系统中第几个 GPIO Controller
	struct device   dev;
	struct cdev		chrdev;
	struct device		*mockdev;
	struct module		*owner;
	struct gpio_chip	*chip;   // 含有各类操作函数
	struct gpio_desc	*descs;  // 用来描述引脚,每个引脚对应一个 gpio_desc
	int			         base;   // 这些 GPIO 的号码基值
	u16			         ngpio;  // 这个 GPIO Controller 支持多少个 gpio
	const char		    *label;  // 标签,名字
	void			    *data;
	struct list_head     list;

#ifdef CONFIG_PINCTRL
	/*
	 * If CONFIG_PINCTRL is enabled, then gpio controllers can optionally
	 * describe the actual pin range which they serve in an SoC. This
	 * information would be used by pinctrl subsystem to configure
	 * corresponding pins for gpio usage.
	 */
	struct list_head pin_ranges;
#endif
};

2. gpio_chip

我们并不需要自己创建 gpio_device,编写驱动时要创建的是 gpio_chip,里面提供了:

  • 控制引脚的函数
  • 中断相关的函数
  • 引脚信息:支持多少个引脚?各个引脚的名字?
c
struct gpio_chip {
	const char		*label;
	struct gpio_device	*gpiodev;
	struct device		*parent;
	struct module		*owner;
	// 一些函数
	int  (*request)(struct gpio_chip *chip, unsigned offset);
	void (*free)(struct gpio_chip *chip, unsigned offset);
	int  (*get_direction)(struct gpio_chip *chip, unsigned offset);
	int  (*direction_input)(struct gpio_chip *chip, unsigned offset);
	int  (*direction_output)(struct gpio_chip *chip, unsigned offset, int value);
	int  (*get)(struct gpio_chip *chip, unsigned offset);
	int  (*get_multiple)(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits);
	void (*set)(struct gpio_chip *chip, unsigned offset, int value);
	//......
	int			base;  // GPIO Controller 中引脚号码的基值,个数
	u16			ngpio;
	const char		*const *names;// 每个引脚的名字
	bool			can_sleep;
	//......
};

这一结构体用于描述 GPIO 芯片的属性和操作函数, 可以通过函数指针调用相应的函数来请求、 释放、 设置、 获取 GPIO 的状态和数值等操作, 从而实现对 GPI O 的控制和管理, 需要注意的是这个结构体中的一系列函数都不需要我们来填充, 这些工作都是由芯片原厂工程师来完成的。

3. gpio_desc

我们去使用 GPIO 子系统时,首先是获得某个引脚对应的 gpio_descgpio_device 表示一个 GPIO Controller,也就是 GPIO 控制器,每个控制器里面支持多个 GPIO。在 gpio_device 中有一个 gpio_desc 数组,每一引脚有一项 gpio_desc

c
struct gpio_desc {
	struct gpio_device	*gdev; // 属于哪个 GPIO Controller
	unsigned long		flags;
    /* flag symbols are bit numbers */
    #define FLAG_REQUESTED	0
	//......
	/* Connection label */
	const char		*label; // 一般等于 gpio_chip 的 label
	/* Name of the GPIO */
	const char		*name;  // 引脚名
};

二、gpio 子系统驱动

这个驱动就不详细去看了,imx6ull 平台的 gpio 子系统驱动相关的代码在这里:gpio-mxc.c - drivers/gpio/gpio-mxc.c - Linux source code v4.19.71,这个也是平台设备驱动,驱动的匹配表 mxc_gpio_dt_ids 如下:

c
static const struct of_device_id mxc_gpio_dt_ids[] = {
	{ .compatible = "fsl,imx1-gpio", .data = &mxc_gpio_devtype[IMX1_GPIO], },
	{ .compatible = "fsl,imx21-gpio", .data = &mxc_gpio_devtype[IMX21_GPIO], },
	{ .compatible = "fsl,imx31-gpio", .data = &mxc_gpio_devtype[IMX31_GPIO], },
	{ .compatible = "fsl,imx35-gpio", .data = &mxc_gpio_devtype[IMX35_GPIO], },
	{ .compatible = "fsl,imx7d-gpio", .data = &mxc_gpio_devtype[IMX35_GPIO], },
	{ /* sentinel */ }
};