GPIO - Pinctrl 控制器
Linux Pinctrl(引脚控制)子系统是 Linux 内核中用于管理和控制硬件引脚(GPIO、I2C、SPI等)的一个子系统。它允许 Linux 内核与底层硬件交互,配置和控制系统中的各种引脚以实现特定的功能。
模块配置
驱动配置
Allwinner BSP --->
Device Drivers --->
Pinctrl Drivers --->
<*> Pinctrl Support for Allwinner SoCs
<*> {CHIP_PLATFORM} PIO
<*> {CHIP_PLATFORM} RTC_PIO
提示
{CHIP_PLATFORM} 为对应芯片平台的标识符(如 V821 对应 SUN300IW1),具体名称请参考对应芯片的 SDK 驱动配置菜单。
:::

设备树配置
对于 Pinctrl,设备树公共配置如下,这部分是定义控制器资源,中断,时钟,开发一般无需修改
pio: pinctrl@42000000 {
compatible = "allwinner,{chip_platform}-pinctrl";
reg = <0x0 0x42000000 0x0 0x500>;
interrupts-extended = <&plic0 68 IRQ_TYPE_LEVEL_HIGH>,
<&plic0 72 IRQ_TYPE_LEVEL_HIGH>,
<&plic0 74 IRQ_TYPE_LEVEL_HIGH>;
clocks = <&aon_ccu CLK_APB0>, <&aon_ccu CLK_DCXO>, <&losc>;
clock-names = "apb", "hosc", "losc";
device_type = "pio";
gpio-controller;
interrupt-controller;
#interrupt-cells = <3>;
#address-cells = <0>;
#gpio-cells = <3>;
};
提示
{chip_platform} 为对应芯片平台 的标识符(如 V821 对应 sun300iw1),具体 compatible 字符串请参考对应芯片的 SDK 设备树配置。
:::
部分芯片平台存在两个 GPIO 域,一般来说 GPIO L 前的是 PIO 域,从 GPIO L 开始之后的是 RPIO 域,配置如下。开发一般无需修改
rtc_pio: pinctrl@42000540 {
#address-cells = <1>;
compatible = "allwinner,{chip_platform}-rtc-pinctrl";
reg = <0x0 0x42000540 0x0 0x100>;
interrupts-extended = <&plic0 84 IRQ_TYPE_LEVEL_HIGH>; /* GPIOL */
clocks = <&dcxo24M>;
clock-names = "hosc";
gpio-controller;
#gpio-cells = <3>;
interrupt-controller;
#interrupt-cells = <3>;
};
提示
具体芯片平台是否存在 RTC PIO 域以及 compatible 字符串,请参考对应芯片的 SDK 设备树配置。
:::
配置通用GPIO功能/中断功能
这里以在 Linux 中配置一个按键为示例,讲解如何配置单个 IO 的功能与中断功能
soc{
gpiokey {
device_type = "gpiokey";
compatible = "gpio-keys";
key_in_pio {
device_type = "key_in_pio";
label = "key_in_pio";
gpios = <&pio PA 4 GPIO_ACTIVE_HIGH>; // PL 前的 GPIO 使用 &pio
linux,code = <0x1c>;
};
key_in_rtc {
device_type = "key_in_rtc";
label = "key_in_rtc";
gpios = <&rtc_pio PL 3 GPIO_ACTIVE_HIGH>; // PL 后的 GPIO 使用 &rtc_pio
linux,code = <0x1d>;
};
};
};
以下是这段设备树定义的内容:
| 字段 | 说明 |
|---|---|
device_type | "gpiokey":定义设备类型为 GPIO 按键设备。 |
compatible | "gpio-keys":表示设备与 gpio-keys 驱动兼容,适用于 Linux 内核中的 GPIO 按键驱动。 |
key_in_pio(按键配置) | |
device_type | "key_in_pio":该按键设备的类型是 "key_in_pio"。 |
label | "key_in_pio":按键的标签,用于标识该按键。 |
gpios | <&pio PA 4 GPIO_ACTIVE_HIGH>:该按键连接到 PIO 的 PA 4 引脚,且引脚在按键按下时电平为高 (GPIO_ACTIVE_HIGH)。 |
linux,code | <0x1c>:按键的扫描码为 0x1c,对应于某个键值(如 "OK" 按键) 。 |
key_in_rtc(按键配置) | |
device_type | "key_in_rtc":该按键设备的类型是 "key_in_rtc"。 |
label | "key_in_rtc":按键的标签,用于标识该按键。 |
gpios | <&rtc_pio PL 3 GPIO_ACTIVE_HIGH>;:该按键连接到RTC PIO的 PL 3 引脚,且引脚在按键按下时电平为高 (GPIO_ACTIVE_HIGH)。 |
linux,code | <0x1d>:按键的扫描码为 0x1d,对应于某个键值(如 "WAKE" 按键)。 |
gpios = <&pio PA 4 GPIO_ACTIVE_HIGH>;
| | |
| | `------------------- gpio active时状态,如果需要上下拉,还可以或上GPIO_PULL_UP、GPIO_PULL_DOWN标志
| `----------------------- 哪个bank,这里是 bank PA
`--------------------------- 指向哪个pio,这里是 PL 前的 GPIO,使用 &pio
gpios = <&rtc_pio PL 4 GPIO_ACTIVE_HIGH>;
| | |
| | `------------------- gpio active时状态,如果需要上下拉,还可以或上GPIO_PULL_UP、GPIO_PULL_DOWN标志
| `----------------------- 哪个bank,这里是 bank PL
`--------------------------- 指向哪个pio,这里是 PL 后的 GPIO,使用 &rtc_pio
配置设备引脚
&pio {
uart0_pins_default: uart0_pins@0 {
pins = "PD22", "PD23";
function = "uart0";
};
uart0_pins_sleep: uart0_pins@1 {
pins = "PD22", "PD23";
function = "io_disabled";
};
};
&uart0 {
pinctrl-names = "default", "sleep";
pinctrl-0 = <&uart0_pins_default>;
pinctrl-1 = <&uart0_pins_sleep>;
status = "okay";
};
这段设备树配置主要是为 UART0(串口0)配置两个不同的引脚状态:默认状态和睡眠状态。它描述了如何配置引脚复用功能和串口的状态。
uart0_pins_default:默认状态uart0_pins_sleep:睡眠状态
PIO 节点用于配置引脚实际IO,功能:
| 字段 | 说明 |
|---|---|
uart0_pins_default | uart0_pins@0:定义了默认状态下 UART0 引脚的配置。 |
pins | "PD22", "PD23":指定引脚 PD22 和 PD23 用作 UART0 的 TX/RX 引脚。 |
function | "uart0":指定这两个引脚的功能为 UART0,允许它们用于串行通信。 |
uart0_pins_sleep | uart0_pins@1:定义了睡眠状态下 UART0 引脚的配置。 |
pins | "PD22", "PD23":同样指定引脚 PD22 和 PD23,但在睡眠模式下这些引脚的功能被禁用。 |
function | "io_disabled":表示在睡眠状态下禁用这两个引脚的功能。 |
UART 节点引用配置的引脚,提供给驱动操作
| 字段 | 说明 |
|---|---|
pinctrl-names | "default", "sleep":定义了两种引脚配置名称,分别为默认配置和睡眠配置。 |
pinctrl-0 | <&uart0_pins_default>:指定默认状态下使用 uart0_pins_default 配置。 |
pinctrl-1 | <&uart0_pins_sleep>:指定睡眠状态下使用 uart0_pins_sleep 配置。 |
status | "okay":启用 UART0 控制器,表示 UART0 处于工作状态。 |
- 默认状态 (
uart0_pins_default): 在默认工作状态下,引脚PD22和PD23被配置为 UART0 的 TX 和 RX 引脚,支持串行通信功能。 - 睡眠状态 (
uart0_pins_sleep): 在睡眠模式下,引脚PD22和PD23的功能被禁用 (io_disabled),以节省电能。 &uart0节点: 定义了 UART0 控制器的引脚配置,使用default和sleep两种模式。
计算 GPIO 编号
GPIO 号转换器
源码定义
全志的GPIO号在 sunxi-gpio.h 中定义
sunxi-gpio.h
GPIO号定义
每个 BANK 有 32 组 GPIO,BANK A 作为 0,则 BANK B 为 32,以此类推。
#define SUNXI_PA_BASE 0
#define SUNXI_PB_BASE 32
#define SUNXI_PC_BASE 64
#define SUNXI_PD_BASE 96
#define SUNXI_PE_BASE 128
#define SUNXI_PF_BASE 160
#define SUNXI_PG_BASE 192
#define SUNXI_PH_BASE 224
#define SUNXI_PI_BASE 256
#define SUNXI_PJ_BASE 288
#define SUNXI_PK_BASE 320
#define SUNXI_PL_BASE 352
#define SUNXI_PM_BASE 384
#define SUNXI_PN_BASE 416
#define SUNXI_PO_BASE 448
#define AXP_PIN_BASE 1024
计算方式
举两个例子
1、PA13
PA 是从0开始定义的,那么就是: 0 +13 = 13
那么 PA13 的GPIO号 就是 GPIO13
2、PE10
PE 是从128开始定义的,那么就是:128 + 10 = 138
那么 PE10 的GPIO号 就是 GPIO138
模块 SYS 接口
模块支持通过用户层调用,启用方法如下:
make kernel_menuconfig
Device Drivers --->
GPIO Support --->
[*] /sys/class/gpio/... (sysfs interface)

这里以 PD16 为例,其 GPIO 号为 PD16,先检查是否被其他设备使用。
确认该 GPIO 没有被其他模块复用
cat /sys/kernel/debug/pinctrl/42000000.pinctrl/pinmux-pins
可以看到 PIN PD16 是 UNCLAIMED 的状态,PIN 号是 112 号,表示该 GPIO 没有被其他设备使用,其 IO 号是 112 号

导出该 GPIO
echo 112 > /sys/class/gpio/export
cd /sys/class/gpio/gpio112

然后再将该 IO 置为输出状态,即可操作其电平:
echo out > direction
echo 1 > value # 拉高
echo 0 > value # 拉低
驱动模块接口
pinctrl_get
-
函数原型:
struct pinctrl *pinctrl_get(struct device *dev); -
作用:获取设备的pin操作句柄,所有pin操作必须基于此pinctrl句柄。
-
参数:
- dev:指向申请pin操作句柄的设备句柄。
-
返回:
- 成功,返回pinctrl句柄。
- 失败,返回NULL。
pinctrl_put
-
函数原型:
void pinctrl_put(struct pinctrl *p) -
作用:释放pinctrl句柄,必须与pinctrl_get配对使用。
-
参数:
- p:指向释放的pinctrl句柄。
-
返回:
- 没有返回值。
devm_pinctrl_get
-
函数原型:
struct pinctrl *devm_pinctrl_get(struct device *dev) -
作用:根据设备获取pin操作句柄,所有pin操作必须基于此pinctrl句柄,与pinctrl_get功能完全一样,只是devm_pinctrl_get会将申请到的pinctrl句柄做记录,绑定到设备句柄信息中。设备驱动申请pin资源, 推荐优先使用devm_pinctrl_get接口。
-
参数:
- dev:指向申请pin操作句柄的设备句柄。
-
返回:
- 成功,返回pinctrl句柄。
- 失败,返回NULL。
devm_pinctrl_put
-
函数原型:
void devm_pinctrl_put(struct pinctrl *p) -
作用:释放pinctrl句柄,必须与devm_pinctrl_get配对使用。
-
参数:
- p:指向释放的pinctrl句柄。
-
返回:
- 没有返回值。
pinctrl_lookup_state
-
函数原型:
struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p, const char *name) -
作用:根据pin操作句柄,查找state状态句柄。
-
参数:
- p:指向要操作的pinctrl句柄。
- name:指向状态名称,如"default"、"sleep"等。
-
返回:
- 成功,返回执行pin 状态的句柄struct pinctrl_state *。
- 失败,返回NULL。
pinctrl_select_state
-
函数原型:
int pinctrl_select_state(struct pinctrl *p, struct pinctrl_state *s) -
作用:将pin句柄对应的pinctrl设置为state句柄对应的状态。
-
参数:
- p:指向要操作的pinctrl句柄。
- s:指向state 句柄。
-
返回:
- 成功,返回0。
- 失败,返回错误码。
devm_pinctrl_get_select
-
函数原型:
struct pinctrl *devm_pinctrl_get_select(struct device *dev, const char *name) -
作用:获取设备的pin操作句柄,并将句柄设定为指定状态。
-
参数:
- dev:指向管理pin操作句柄的设备句柄。
- name:要设置的state名称,如"default"、"sleep"等。
-
返回:
- 成功,返回pinctrl句柄。
- 失败,返回NULL。
devm_pinctrl_get_select_default
-
函数原型:
struct pinctrl *devm_pinctrl_get_select_default(struct device *dev) -
作用:获取设备的pin操作句柄,并将句柄设定为默认状态。
-
参数:
- dev:指向管理pin操作句柄的设备句柄。
-
返回:
- 成功,返回pinctrl句柄。
- 失败,返回NULL。
gpio_request
-
函数原型:
int gpio_request(unsigned gpio, const char *label) -
作用:申请gpio,获取gpio的访问权。
-
参数:
- gpio:gpio编号。
- label:gpio名称,可以为NULL。
-
返回:
- 成功,返回0。
- 失败,返回错误码。
gpio_free
-
函数原型:
void gpio_free(unsigned gpio) -
作用:释放gpio。
-
参数:
- gpio:gpio编号。
-
返回:
- 无返回值。
gpio_direction_input
-
函数原型:
int gpio_direction_input(unsigned gpio) -
作用:设置gpio为input。
-
参数:
- gpio:gpio编号。
-
返回:
- 成功,返回0。
- 失败,返回错误码。
gpio_direction_output
-
函数原型:
int gpio_direction_output(unsigned gpio, int value) -
作用:设置gpio为output。
-
参数:
- gpio:gpio编号。
- value:期望设置的gpio电平值,非0表示高, 0表示低。
-
返回:
- 成功,返回0.
- 失败,返回错误码。
__gpio_get_value
-
函数原型:
int __gpio_get_value(unsigned gpio) -
作用:获取gpio电平值(gpio已为input/output状态)。
-
参数:
- gpio:gpio编号。
-
返回:
- 返回gpio对应的电平逻辑,1表示高, 0表示低。
__gpio_set_value
-
函数原型:
void __gpio_set_value(unsigned gpio, int value) -
作用:设置gpio电平值(gpio已为input/output状态)。
-
参数:
- gpio:gpio编号。
- value:期望设置的gpio电平值,非0表示高, 0表示低。
-
返回:
- 无返回值
of_get_named_gpio
-
函数原型:
int of_get_named_gpio(struct device_node *np, const char *propname, int index) -
作用:通过名称从dts解析gpio属性并返回gpio编号。
-
参数:
- np:指向使用gpio的设备结点。
- propname:dts中属性的名称。
- index:dts中gpio属性的索引值。
-
返回:
- 成功,返回gpio编号。
- 失败,返回错误码。
of_get_named_gpio_flags
-
函数原型:
int of_get_named_gpio_flags(struct device_node *np, const char *list_name, int index, enum of_gpio_flags *flags) -
作用:通过名称从dts解析gpio属性并返回gpio编号。
-
参数:
- np:指向使用gpio的设备结点。
- propname:dts中属性的名称。
- index:dts中gpio属性的索引值
- flags:输入、输出信息
of_gpio_flags
-
返回:
- 成功,返回gpio编号。
- 失败,返回错误码。
使用示例
配置设备引脚
一般设备驱动只需要使用一个接口 devm_pinctrl_get_select_default 就可以申请到设备所有pin资源。
static int pin_req_demo(struct platform_device *pdev)
{
struct pinctrl *pinctrl;
pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
if (IS_ERR_OR_NULL(pinctrl))
return -EINVAL;
return 0;
}
获取GPIO号
static int pin_req_demo(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *np = dev->of_node;
unsigned int gpio;
gpio = of_get_named_gpio(np, "vdevice_3", 0);
if (!gpio_is_valid(gpio)) {
if (gpio != -EPROBE_DEFER)
dev_err(dev, "Error getting vdevice_3\n");
return gpio;
}
}