GPIO 和 Pinctrl 子系统是为了更方便的初始化和使用引脚。
GPIO 子系统提供操作引脚的函数。
Pinctrl 子系统用于设置引脚复用和电气属性。
pinctrl 子系统
参考文档:
pinctrl 子系统源码目录为 drivers/pinctrl
内核\Documentation\devicetree\bindings\Pinctrl\Pinctrl-bindings.txt
内核 Documentation\gpio\Pinctrl-bindings.txt
设备树格式
i.MX6ULL 的引脚名就是它的默认功能。
看开发板原理图:
如果只有一个,那就是使用引脚默认的功能,如 LCD_DATA0
如果有两个,左边的是引脚名,右边的是复用的功能,如 GPIO_5 复用为 SD1_VSELECT

1、所有的引脚配置写在 iomuxc 节点中:
&iomuxc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_hog_1>;
imx6ul-evk {
pinctrl_hog_1: hoggrp-1 {
fsl,pins = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */
MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059 /* SD1 VSELECT */
MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x13058 /* USB_OTG1_ID */
>;
};
};
};其中 pinctrl_hog_1 节点是系统启动后就初始化的引脚。
因为有些引脚没有与之对应的驱动程序,但需要初始化。
还有一个 iomuxc_snvs 节点,这个先不关心,做低功耗啥的再学这个
&iomuxc_snvs {
pinctrl-names = "default_snvs";
pinctrl-0 = <&pinctrl_hog_2>;
imx6ul-evk {
pinctrl_hog_2: hoggrp-2 {
fsl,pins = <
MX6ULL_PAD_SNVS_TAMPER0__GPIO5_IO00 0x80000000
>;
};
};
};2、同一个设备用到的 GPIO 都写在一个节点中
pinctrl_test: testgrp {
fsl,pins = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
>;
};pinctrl_test:节点的 ID,设备节点使用这个来找到自己用的引脚,如
pinctrl-0 = <&pinctrl_test>;: testgrp:描述这个节点的作用,可省略,但建议加上。
fsl,pins = <>:这种写法是 I.MX6ULL 厂商固定的,在
drivers/pinctrl/freescale/pinctrl-imx6ul.c文件中实现解析。MX6UL_PAD_UART1_RTS_B__GPIO1_IO19:格式为
MX6UL_PAD_<引脚默认功能>__<复用功能>,定义在arch/arm/boot/dts/目录下的imx6ull-pinfunc.h和imx6ul-pinfunc.h文件中,也是由厂商写的。0x17059:引脚的电气属性(上下拉、驱动能力等),通过查看参考手册中 IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B 寄存器(寄存器格式为
MUXC_SW_PAD_CTL_PAD_<引脚名>)来看怎么设置。
绝大多数 PIN 的寄存器的字段是一样的,但也有特殊的,具体看手册。
推荐使用 NXP 引脚配置工具:Config Tools for i.MX,下载:Config_Tools_for_i.MX_25.12_x64.exe

1)搜素引脚

2)在“路由详情中配置引脚”

3)在代码预览中查看设备树

3、引脚定义好了,设备节点中怎么引用呢
pinctrl-names 中有几项,后面的 pinctrl- 就有几项,并且是按顺序对应的。
pinctrl-names = "default", "state_100mhz", "state_200mhz";
pinctrl-0 = <&pinctrl_usdhc1>;
pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
pinctrl-2 = <&pinctrl_usdhc1_200mhz>;例如:
&usdhc1 {
pinctrl-names = "default", "state_100mhz", "state_200mhz";
pinctrl-0 = <&pinctrl_usdhc1>;
pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
pinctrl-2 = <&pinctrl_usdhc1_200mhz>;
/* pinctrl-3 = <&pinctrl_hog_1>; */
cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
keep-power-in-suspend;
enable-sdio-wakeup;
vmmc-supply = <®_sd1_vmmc>;
status = "okay";
};另外,一个中可以写多个节点:
pinctrl-0 = <&pinctrl_sai2
&pinctrl_sai2_hp_det_b>;4、引脚的使用
(1)单个引脚
按下面格式描述引脚:
引脚标签-gpios = <GPIO控制器 引脚编号 有效电平>
cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;然后在驱动中获取:
devm_gpiod_get(dev, "cd", GPIOD_IN);(2)多个引脚:
cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW
&gpio1 20 GPIO_ACTIVE_LOW>;获取:
// 一次拿一个
gpiod = devm_gpiod_get_index(dev, "cd", 0, GPIOD_IN);
gpiod2 = devm_gpiod_get_index(dev, "cd", 1, GPIOD_IN);
// 一次拿全部
struct gpio_descs *gpios;
gpios = devm_gpiod_get_array(dev, "cd", GPIOD_IN);gpio 子系统
参考文档:
gpio子系统源码目录为 drivers/gpio
内核\Documentation\devicetree\bindings\gpio\gpio.txt
gpio 操作函数
函数有两套 API:
基于描述符的(descriptor-based),函数前缀“gpiod_”,它使用 gpio_desc 结构体来表示 一个引脚。
老的 (legacy),函数前缀“gpio_”,它使用引脚的全局编号来表示一个引脚,驱动中写死了,芯片一换,全是问题,所以现在淘汰了。
头文件:
#include <linux/gpio/consumer.h>(1)获取:
struct gpio_desc *gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags)
struct gpio_desc *devm_gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags)
struct gpio_desc *gpiod_get_index(struct device *dev, const char *con_id, unsigned int index, enum gpiod_flags flags)
struct gpio_desc *devm_gpiod_get_index(struct device *dev, const char *con_id, unsigned int index, enum gpiod_flags flags)
struct gpio_descs *gpiod_get_array(struct device *dev, const char *con_id, enum gpiod_flags flags)
struct gpio_descs *devm_gpiod_get_array(struct device *dev, const char *con_id, enum gpiod_flags flags)返回值:
成功:gpio 描述符,即 gpio_desc 结构体指针
失败:返回错误指针(使用 IS_ERR 宏检查)并设置相应的错误码
有“devm_”前缀的表示设备自动管理资源,就是引脚不用的时候自动释放。所以优先使用“devm_”版本的相关函数。
(2)释放:
void gpiod_put(struct gpio_desc *desc)
void gpiod_put_array(struct gpio_descs *descs)(3)操作:
// 输入输出
int gpiod_direction_input(struct gpio_desc *desc)
int gpiod_direction_output(struct gpio_desc *desc, int value)
// 读
int gpiod_get_value(struct gpio_desc *desc)
int gpiod_get_value_cansleep(struct gpio_desc *desc)
int gpiod_get_raw_value(struct gpio_desc *desc)
int gpiod_get_raw_value_cansleep(struct gpio_desc *desc)
int gpiod_get_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
struct gpio_array *array_info,
unsigned long *value_bitmap)
int gpiod_get_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
struct gpio_array *array_info,
unsigned long *value_bitmap)
// 写
void gpiod_set_value(struct gpio_desc *desc, int value)
void gpiod_set_value_cansleep(struct gpio_desc *desc, int value)
void gpiod_set_raw_value(struct gpio_desc *desc, int value)
void gpiod_set_raw_value_cansleep(struct gpio_desc *desc, int value)
void gpiod_set_array_value(unsigned int array_size,
struct gpio_desc **desc_array,
struct gpio_array *array_info,
unsigned long *value_bitmap);
void gpiod_set_array_value_cansleep(unsigned int array_size,
struct gpio_desc **desc_array,
struct gpio_array *array_info,
unsigned long *value_bitmap)
// 查询
int gpiod_is_active_low(const struct gpio_desc *desc)
bool gpiod_cansleep(const struct gpio_desc *desc)
// IRQ 相关
int gpiod_to_irq(const struct gpio_desc *desc)参数:
dev:设备指针
con_id:设备树中的引脚标签
flags:pin 的使用标志,在获取 pin 的时候就设置输入输出
GPIOD_IN:设置 pin 为输入
GPIOD_OUT_LOW:设置 pin 输出低电平
GPIOD_OUT_HIGH:设置 pin 输出高电平
index:多个引脚时指定哪个引脚,从 0 开始
array_info:设为 NULL
value_bitmap:位图,每一位表示一个引脚