1.基本的使用流程
申请、使用、释放
2.初始化过程
内核初始化过程中,与静态映射同时期初始化



3. struct s3c_gpio_chip
这是三星 S3C/S5P 系列 ARM 芯片(如 S3C2440、S3C6410、Exynos 等)Linux 内核中 GPIO 控制器的核心结构体,是三星平台 GPIO 驱动的基础,用于描述一个 GPIO 端口(如 GPA、GPB 组)。
它封装了 Linux 通用 GPIO 框架 + 三星芯片私有 GPIO 特性
cpp
struct s3c_gpio_chip {
// 1. Linux 内核通用 GPIO 芯片基类(核心框架层)
struct gpio_chip chip;
// 2. 三星私有 GPIO 配置(模式、上拉/下拉等)
struct s3c_gpio_cfg *config;
// 3. 电源管理相关配置
struct s3c_gpio_pm *pm;
// 4. GPIO 寄存器物理地址映射后的虚拟基地址
void __iomem *base;
// 5. 外部中断(EINT)偏移量
int eint_offset;
// 6. 自旋锁(并发访问保护)
spinlock_t lock;
// 7. 休眠/唤醒时的寄存器保存数组(仅 CONFIG_PM 开启时有效)
#ifdef CONFIG_PM
u32 pm_save[7];
#endif
};
3.1 gpio_chip
cpp
struct gpio_chip {
/* 1. 基础描述信息 */
const char *label; // GPIO 芯片名称(如 "gpiochip0")
struct device *dev; // 关联的设备结构体(设备模型用)
struct module *owner; // 驱动所属模块(THIS_MODULE)
/* 2. GPIO 申请/释放回调(资源管理) */
int (*request)(struct gpio_chip *chip, unsigned offset);
void (*free)(struct gpio_chip *chip, unsigned offset);
/* 3. GPIO 方向与电平操作(核心功能) */
int (*direction_input)(struct gpio_chip *chip, unsigned offset);
int (*get)(struct gpio_chip *chip, unsigned offset); // 读电平
int (*direction_output)(struct gpio_chip *chip, unsigned offset, int value);
void (*set)(struct gpio_chip *chip, unsigned offset, int value); // 写电平
/* 4. 扩展功能 */
int (*set_debounce)(...); // 消抖配置(按键用)
int (*to_irq)(...); // GPIO 转中断号
void (*dbg_show)(...); // 调试信息展示
/* 5. 硬件配置参数 */
int base; // 此 GPIO 控制器的**起始编号**
u16 ngpio; // 此控制器拥有多少个 GPIO 引脚
const char *const *names; // 引脚名称数组(可选)
/* 6. 标志位 */
unsigned can_sleep:1; // 操作是否可能休眠(I2C 扩展 GPIO 会休眠)
unsigned exported:1; // 是否通过 sysfs 导出给用户态
};
bash
cd /sys/class/gpio/
[root@LXY210 gpio]# ls
export gpiochip164 gpiochip235 gpiochip316 gpiochip396 gpiochip56
gpiochip0 gpiochip172 gpiochip244 gpiochip325 gpiochip40 gpiochip62
gpiochip104 gpiochip181 gpiochip253 gpiochip334 gpiochip405 gpiochip71
gpiochip112 gpiochip188 gpiochip262 gpiochip343 gpiochip414 gpiochip80
gpiochip120 gpiochip197 gpiochip271 gpiochip35 gpiochip423 gpiochip89
gpiochip128 gpiochip206 gpiochip280 gpiochip351 gpiochip431 gpiochip9
gpiochip137 gpiochip212 gpiochip289 gpiochip360 gpiochip438 gpiochip96
gpiochip14 gpiochip221 gpiochip29 gpiochip369 gpiochip447 unexport
gpiochip146 gpiochip226 gpiochip298 gpiochip378 gpiochip456
gpiochip155 gpiochip23 gpiochip307 gpiochip387 gpiochip47
[root@LXY210 gpiochip0]# ls
base label ngpio power subsystem uevent
[root@LXY210 gpiochip0]# cat label
GPA0
[root@LXY210 gpiochip0]# cat base
0
gpiochipxxx中有base label ngpio power subsystem uevent
base:数值等于base
label:给人看的编组信息
ngpio:该组gpio的数量
为什么gpiochip0的base是0,ngpio是8,下一个却是gpiochip9?因为gpio8无效,但是保留了下来
base数值计算的具体实现:

3.2函数绑定与驱动注册samsung_gpiolib_add_4bit_chips

3.2.1 输入输出函数绑定samsung_gpiolib_add_4bit

输入输出函数的具体执行:

cpp
// 函数定义:静态函数,返回int,是GPIO子系统的输入配置回调函数
// chip:GPIO芯片抽象结构体;offset:要配置的引脚编号(相对于当前芯片)
static int samsung_gpiolib_4bit_input(struct gpio_chip *chip,
unsigned int offset)
{
// 1. 类型转换:从通用gpio_chip转为三星专用的s3c_gpio_chip结构体
// to_s3c_gpio是内核宏,用于获取三星GPIO私有数据
struct s3c_gpio_chip *ourchip = to_s3c_gpio(chip);
// 2. 获取GPIO控制器的**寄存器物理地址映射后的虚拟地址**
// __iomem:内核标记,代表这是IO内存地址(硬件寄存器地址)
void __iomem *base = ourchip->base;
// 3. 定义变量,用于存储GPIO配置寄存器的值
unsigned long con;
// 4. 读取GPIO配置寄存器(GPIOCON_OFF是配置寄存器的偏移地址)
// __raw_readl:内核底层函数,直接从硬件寄存器读32位数据(无同步、无缓存)
con = __raw_readl(base + GPIOCON_OFF);
// 5. 关键操作:清零当前引脚对应的4个配置位
// con_4bit_shift(offset):计算当前引脚在CON寄存器中的4bit起始偏移位
// ~(0xf << ...):生成掩码,把对应4bit置0
// 三星规定:4bit全为0 → 引脚配置为**输入模式**
con &= ~(0xf << con_4bit_shift(offset));
// 6. 将修改后的值写回GPIO配置寄存器,完成硬件配置
__raw_writel(con, base + GPIOCON_OFF);
// 7. 调试打印:输出函数名、寄存器基地址、配置后的值
gpio_dbg("%s: %p: CON now %08lx\n", __func__, base, con);
// 8. 函数执行成功,返回0
return 0;
}
那pm函数是什么?负责电源管理(低功耗)的函数
3.2.2电平设置与读取&驱动注册s3c_gpiolib_add

电平的读取与设置:s3c_gpiolib_set
驱动注册:ret = gpiochip_add(gc);
4. 内核驱动注册函数gpiochip_add
该函数针对所有的平台,任何平台都需要使用该函数完成驱动的创建。但该函数的输入参数由平台决定。在注册之前要完成结构体的填充。
cpp
int gpiochip_add(struct gpio_chip *chip)

所以是干了什么给注册了?

其实就是将chip结构体的首地址填到了内核维护的gpio_decs这个数组里面。
5. 内核提供的GPIO函数

这些函数就是我们移植驱动的时候使用的。
5.1 gpiochip_add
gpiochip_add:给厂家编写驱动用,用于向内核注册gpiolib
5.2 gpio_request
cpp
// 头文件:#include <linux/gpio.h>
int gpio_request(unsigned int gpio, const char *label);
- 申请 GPIO 资源:向内核注册,声明要使用该 GPIO 引脚
- 独占保护:防止多个驱动同时操作同一个 GPIO(避免硬件冲突)
- 合法性检查:校验 GPIO 编号是否有效、是否已被占用
- 标记归属 :用
label标记该 GPIO 的使用者(方便调试)
你怎么知道gpio的具体编号?在这个头文件里

5.3 gpio_free
gpio_free:释放gpio
5.4 gpio_request变种
gpio_request_one:添加输入输出功能。
gpio_request_array:申请一个数组大小的gpio
gpio_free_array:释放一个数组大小的gpio
5.5 gpiochip_is_requested
gpiochip_is_requested:当前这个gpio端口是否已经被申请了
5.6 gpio_direction_input
gpio_direction_input:设置输入模式。但是没干活,还是指到了samsung_gpiolib_4bit_input这个函数。

5.7 gpio_direction_output
gpio_direction_output:设置输出模式。但是没干活
5.8 gpio_set_debounce
gpio_set_debounce:开启【按键消抖】的标准函数。还是没干活。调用的结构体函数
5.9 __gpio_get_value
被宏定义为gpio_get_value

5.10 __gpio_set_value
类似
6. 第二类内核函数
这些函数是给内核gpiolib自己使用的,可以认为是gpiolib的attribute部分。比如你去cat 一个gpio端口的端口号需要调用的函数。

该宏定义决定了gpio设备在sys目录下是否可用。该宏定义在config文件中,可以通过修改menuconfig来取消支持。
你怎么知道?

我们在这个宏定义之间发现了这些函数,函数将chip的base写到了buf缓冲区。
6.1 base、label、ngpio的初始化
确实我们在宏定义中都找到了base、label、ngpio的调用函数。但是谁调用的?怎么绑定的?
我们还找到了value的调用函数,应该是读取gpio的引脚电平函数,为什么开发板系统中没有?

该函数执行了gpiolib的初始化,同样是调用的系统初始化宏定义
cpp
#define postcore_initcall(fn) __define_initcall("2",fn,2)
我们看看这个初始化函数写的什么

函数在for循环中进行导出,我们看下导出的是啥

该函数进行了创建设备和绑定属性函数,我们看下绑定的什么


OK!证实了被初始化的属性只有base、label、ngpio。
6.2 value、active的初始化
那这些函数在哪初始化的?
ok先是找到了这两个函数的连接

然后被gpio_export调用

函数被export_store调用

然后被放到了这里

发现这些属性竟然在class类下

果然,接下来查看export设备的说明,输入一个数字就能导出相应gpio???

还真是!


ok再试试消除

没毛病!ok gpiolib的理论学习到此为止,下节课开始实操