没志青年
发布于 2025-06-14 / 14 阅读
0

4 Linux gpio 和 pinctrl 子系统

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
                        >;
                };
        };
};

特性

iomuxc (主 IOMUX)

iomuxc_snvs (SNVS IOMUX)

电源域

主电源域 (VDD_SOC_IN)

SNVS 电源域 (VDD_SNVS_IN)

供电电压

通常 3.3V

通常 3.0V 或电池供电

工作状态

系统掉电时失效

系统掉电时保持有效

引脚数量

多(大部分 GPIO)

少(特定 GPIO)

主要用途

常规外设接口

电源管理、唤醒、RTC、安全

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.himx6ul-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 = <&reg_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:位图,每一位表示一个引脚