ARM Linux 驱动开发篇---GPIO子系统详解-- Ubuntu20.04

🎬 渡水无言个人主页渡水无言

专栏传送门 : 《linux专栏》《嵌入式linux驱动开发》《freertos专栏

⭐️流水不争先,争的是滔滔不绝

📚博主简介:第二十届中国研究生电子设计竞赛全国二等奖 |国家奖学金 | 省级三好学生

| 省级优秀毕业生获得者 | csdn新星杯TOP18 | 半导纵横专栏博主 | 211在读研究生

在这里主要分享自己学习的linux嵌入式领域知识;有分享错误或者不足的地方欢迎大佬指导,也欢迎各位大佬互相三连

目录

前言

[一、GPIO 子系统核心定位](#一、GPIO 子系统核心定位)

[二、IMX6ULL 的 GPIO 子系统实战(设备树 + 驱动)](#二、IMX6ULL 的 GPIO 子系统实战(设备树 + 驱动))

[2.1、配置设备树中的 GPIO](#2.1、配置设备树中的 GPIO)

[2.1.1、pinctrl 配置](#2.1.1、pinctrl 配置)

[2.1.2、在设备节点中指定 GPIO](#2.1.2、在设备节点中指定 GPIO)

[2.1.3、GPIO 控制器节点](#2.1.3、GPIO 控制器节点)

[2.2、GPIO 驱动程序简介](#2.2、GPIO 驱动程序简介)

2.2.1、驱动匹配

2.2.2、驱动入口

2.2.3、核心结构体(gpio_chip)

总结


前言

上一节我们讲解了pinctrl子系统,其核心作用是配置 PIN 的复用功能电气属性 。若pinctrl将某个 PIN 复用为 GPIO,后续对 GPIO 的操作(输入 / 输出、读写电平)就需要依靠GPIO 子系统来完成。

GPIO 子系统的设计目标是**简化 GPIO 的驱动开发,**这样我们只需在设备树中配置 GPIO 相关信息,无需直接操作底层寄存器,即可通过内核提供的 API 函数轻松操控 GPIO。

本文基于 IMX6ULL 开发板,从「子系统简介→设备树配置→驱动原理」逐步拆解,详细讲解了GPIO 子系统。


一、GPIO 子系统核心定位

GPIO 子系统是 Linux 内核针对 GPIO 外设封装的标准化框架,核心职责包括:

初始化 GPIO 控制器(解析设备树 GPIO 节点信息);

提供统一的 GPIO 操作 API(申请、释放、输入 / 输出配置、电平读写);

屏蔽不同 SOC 的 GPIO 硬件差异(如 IMX6ULL 与 STM32 的 GPIO 寄存器差异)。

简单来说,pinctrl负责 "把 PIN 变成 GPIO",GPIO 子系统负责 "把 GPIO 用起来",二者协同工作,构成 GPIO 驱动的完整链路。

二、IMX6ULL 的 GPIO 子系统实战(设备树 + 驱动)

以 IMX6ULL 开发板的「SD 卡检测引脚」为例(UART1_RTS_B 复用为 GPIO1_IO19),介绍 GPIO 子系统的设备树配置和驱动底层原理。

2.1、配置设备树中的 GPIO

GPIO 的设备树配置主要分为两步:

1、通过 pinctrl 配置 PIN 复用。

2、在设备节点中指定 GPIO 信息

2.1.1、pinctrl 配置

打开开发板专属的设备树文件imx6ull-alientek-emmc.dts,可以看到在&iomuxc节点的imx6ul-evk子节点中,已存在 SD 卡检测引脚的 pinctrl 配置:

复制代码
pinctrl_hog_1: hoggrp-1 {
    fsl,pins = <
        MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD 引脚,复用为GPIO1_IO19 */
        // 其他PIN配置...
    >;
};

上述代码就是将 UART1_RTS_B 引脚复用为 GPIO1_IO19,并配置电气属性,和上一期博客里的 pinctrl 配置逻辑完全一致。

2.1.2、在设备节点中指定 GPIO

SD 卡挂载在 IMX6ULL 的usdhc1接口上,usdhc1节点就是 SD 卡的设备树节点。我们需要在该节点中添加cd-gpios属性,这样便能让驱动知道SD 卡检测引脚使用的是哪个 GPIO:

复制代码
&usdhc1 {
    pinctrl-names = "default", "state_100mhz", "state_200mhz";
    pinctrl-0 = <&pinctrl_usdhc1>;
    pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
    pinctrl-2 = <&pinctrl_usdhc1_200mhz>;
    
    cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>; /* SD卡检测GPIO:GPIO1_IO19,低电平有效 */
    
    keep-power-in-suspend;
    enable-sdio-wakeup;
    vmmc-supply = <&reg_sd1_vmmc>;
    status = "okay";
};

cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>

此行代码就是添加的描述 SD 卡的 CD 引脚 pinctrl 信息所在的子节点。

&gpio1:指定 GPIO 所属的控制器(GPIO1 控制器);

19:指定 GPIO1 控制器的第 19 号引脚(即 GPIO1_IO19);

GPIO_ACTIVE_LOW:指定 GPIO 低电平有效,也就是检测到低电平时,表示 SD 卡插入,若改为GPIO_ACTIVE_HIGH则为高电平有效。
根据上面这些信息, SD 卡驱动程序就可以使用 GPIO1_IO19 来检测 SD 卡的 CD 信号了。

2.1.3、GPIO 控制器节点

打开imx6ull.dtsi,可找到 GPIO1 控制器的节点定义,这是 GPIO 子系统工作的基础:

复制代码
gpio1: gpio@0209c000 {
    compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio"; /* 驱动匹配标识 */
    reg = <0x0209c000 0x4000>; /* GPIO1控制器寄存器基地址+长度 */
    interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
                 <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>; /* 中断配置 */
    gpio-controller; /* 标识此节点是GPIO控制器 */
    #gpio-cells = <2>; /* GPIO属性的cell数量,固定为2 */
    interrupt-controller; /* 标识此节点是中断控制器 */
    #interrupt-cells = <2>;
};

gpio1 节点信息描述了 GPIO1 控制器的所有信息,重点就是 GPIO1 外设寄存器基地址以及兼容 属 性。

gpio1 节点的 compatible 属性有两个,分别为"fsl,imx6ul-gpio"和"fsl,imx35- gpio",在 Linux 内核中搜索这两个字符串就可以找到 I.MX6UL 的 GPIO 驱动程序。

reg 属性设置了 GPIO1 控制器的寄存器基地址为 0X0209C000,从I.MX6ULL 参考手册中可以看到GPIO1 控制器的基地址就是 0X0209C000,如下图所示:

gpio-controller:必须添加,表明该节点是一个 GPIO 控制器;
#gpio-cells = <2>:指定描述一个 GPIO 时需要 2 个参数(第一个是 GPIO 编号,第二个是电平极性,如 0 = 高电平有效,1 = 低电平有效);

2.2、GPIO 驱动程序简介

IMX6ULL 的 GPIO 驱动文件是drivers/gpio/gpio-mxc.c,该驱动是典型的平台设备驱动,核心逻辑是解析设备树 GPIO 节点、初始化 GPIO 控制器,并向内核注册 GPIO 操作接口。

2.2.1、驱动匹配

gpio-mxc.c中,通过of_device_id结构体数组与设备树的compatible属性匹配:

复制代码
static const struct of_device_id mxc_gpio_dt_ids[] = {
    { .compatible = "fsl,imx1-gpio", .data = &mxc_gpio_devtype[IMX1_GPIO], },
    { .compatible = "fsl,imx35-gpio", .data = &mxc_gpio_devtype[IMX35_GPIO], }, /* 匹配GPIO1节点 */
    { /* 哨兵节点 */ }
};

设备树中 GPIO1 节点的compatible = "fsl,imx35-gpio",因此该驱动会被匹配执行。

因此 gpio-mxc.c 就是 I.MX6ULL 的 GPIO 控制器驱动文件。gpio-mxc.c 所在的目录为drivers/gpio,打开这个目录可以看到很多芯片的 gpio 驱动文件, "gpiolib"开始的文件是 gpio 驱动的核心文件。

2.2.2、驱动入口

驱动匹配成功后,mxc_gpio_probe函数会被调用,核心工作的是:

  1. 读取设备树中 GPIO 控制器的reg属性(寄存器基地址),并进行内存映射;
  2. 初始化 GPIO 寄存器(关闭中断、清除状态等);
  3. 初始化gpio_chip结构体(封装 GPIO 操作函数,如输入 / 输出配置、电平读写);
  4. 通过gpiochip_add函数向内核注册 GPIO 控制器,供上层 API 调用。

2.2.3、核心结构体(gpio_chip)

gpio_chip是 GPIO 子系统的核心结构体,封装了所有 GPIO 操作函数,内核通过该结构体提供统一的 GPIO API:

复制代码
struct gpio_chip {
    const char *label;
    struct device *dev;
    int (*request)(struct gpio_chip *chip, unsigned offset); /* GPIO申请 */
    void (*free)(struct gpio_chip *chip, unsigned offset); /* GPIO释放 */
    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); /* 读取GPIO电平 */
    void (*set)(struct gpio_chip *chip, unsigned offset, int value); /* 设置GPIO电平 */
    // 其他成员...
};

半导体厂商已在驱动中实现了这些函数,我们无需关心底层寄存器操作,直接使用内核提供的 API 即可。


总结

本期博客详细讲解了GPIO 子系统。

相关推荐
上海合宙LuatOS2 小时前
LuatOS核心库API——【iperf 】吞吐量测试
linux·运维·服务器·单片机·嵌入式硬件·物联网·硬件工程
芝士雪豹只抽瑞克五2 小时前
Linux Virtual Server (LVS) 负载均衡集群笔记
linux·笔记·负载均衡·lvs
老四啊laosi2 小时前
【Linux系统】15. 进程控制
linux·进程控制
一只程序熊2 小时前
uniappx richtext img 图片无法显示
linux·服务器·数据库
charlie1145141912 小时前
嵌入式C++教程——Lambda捕获与性能影响
开发语言·c++·笔记·嵌入式·现代c++·工程实践
Felven2 小时前
国产沐创N20 100G网卡性能测试
linux·性能测试·国产100g网卡·沐创
senijusene2 小时前
Linux软件编程: 线程属性与线程间通信详解
java·linux·jvm·算法
Bryce_Zhou2 小时前
rv1126bp RGB666屏幕花屏问题
linux
我真的想 啸8 小时前
在 Ubuntu 24.04 系统上安装并使用 Codex CLI
linux·ubuntu·arcgis