Pinctrl 子系统和 GPIO 子系统简介

目录

一、pinctrl 子系统

二、gpio子系统

1.gpio 子系统 API 函数

①gpio_request

②gpio_free

③gpio_direction_input

④gpio_direction_output

⑤gpio_get_value

⑥gpio_set_value

 2.gpio 相关的 OF 函数

①of_gpio_named_count

②of_gpio_count

③of_get_named_gpio

 3.设备树中添加 gpio 节点模板

①调用pinctrl信息

②配置GPIO 属性信息

 小结


一、pinctrl 子系统

在学习了设备树之后, 如果还按照裸板开发的方式配置寄存器来控制 IO 的方式太过于原始, Linux 内核提供了 pinctrl 子系统和 gpio 子系统用于 GPIO 驱动。

pinctrl 子系统主要工作内容如下:

①获取设备树中 pin 信息,管理系统中所有的可以控制的 pin, 在系统初始化的时候, 枚举所有可以控制的 pin, 并标识这些 pin

②根据获取到的 pin 信息来设置 pin 的复用功能,对于 SOC 而言, 其引脚除了配置成普通的 GPIO 之外,若干个引脚还可以组成一个 pin group, 形成特定的功能
 


③根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。

对应使用者来说,只需要在设备树里面设置好某个 pin 的相关属性即可,其他的初始化工作均由 pinctrl 子系统来完成

Pinctrl 子系统和 GPIO 子系统简介_第1张图片

pinctrl 子系统要根据你提供的信息来配置 PIN 功能,一般会在设备树里面创建一个节点来描述 PIN 的配置信息。pinctrl 子系统一般在iomuxc子节点下所有需要配置用户自己的pinctrl需要在该节点下添加

Pinctrl 子系统和 GPIO 子系统简介_第2张图片

从图中可以知道UART1_RTS_B 的配置信息如下:

MX6UL_PAD_UART1_RTS_B__GPIO1_IO19        0x17059

 UART1_RTS_B 的 配 置 信 息 分 为 两 部 分 :MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 和 0x17059。

对于一个 PIN 的配置主要包括两方面:PIN 的复用功能和PIN 的电气特性。这里先给出结论:前面设置UART1_RTS_B 的复用功能,后面用来设置 UART1_RTS_B 的电气特性。

在源码中一共有八个宏定义,对应着将UART1_RTS_B复用为不同的功能。MX6UL_PAD_UART1_RTS_B__GPIO1_IO19是一个宏定义,如下:

#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19  0x0090  0x031C  0x0000   0x5  0x0

宏定义的具体值就是后面的5个数值,其含有如下

0x0090               0x031C             0x0000              0x5                     0x0

mux_reg :寄存器偏移地址,设备树中的 iomuxc 节点就是 IOMUXC 外设对应的节点 , 根 据 其 reg 属 性 可 知 IOMUXC 外 设 寄 存 器 起 始 地 址 为 0x020e0000。

因 此0x020e0000+0x0090=0x020e0090, IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B 寄存器地址是 0x020e0090。

Pinctrl 子系统和 GPIO 子系统简介_第3张图片

所以,0x020e0000+mux_reg 就是 PIN 的复用寄存器地址。其余也是类似的。

imxuio里面的0x17059 ,此值由用户自行设置,通过此值来设置一个 IO 的上/下拉、驱动能力和速度,配置PIN的电气特性
 

设备树中添加 pinctrl 节点模板

pinctrl_test: testgrp {
    fsl,pins = <
    MX6UL_PAD_GPIO1_IO00__GPIO1_IO00   config //onfig 是具体设置值
    >;
};

同一个外设的 PIN 都放到一个节点里面,在 iomuxc 节点中下添加“pinctrl_test”节点。

注意:节点前缀一定要为“pinctrl_”

设备树是通过属性来保存信息的,因此需要添加一个属性,属性名字一定要为“fsl,pins”,对于 I.MX 系列 SOC 而言, pinctrl 驱动程序是通过读取“fsl,pins”属性值来获取 PIN 的配置信息

然后在“fsl,pins”属性中添加具体的 PIN 配置信息即可。通过以上的操作我们就可以完成对一个引脚的复用配置以及初始化。

调用 pinctrl 一般在设备树中进行,在外设节点中调用

gpioled {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "atkalpha-gpioled";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_led>;
		led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
		status = "okay";
	};

只需要关注gpioled设备节点下的“ pinctrl-names ” 和“ pinctrl-0” 两条语句, 这两句就是引用在 iomuxc 中配置的 pinctrl 节点

pinctrl-names = "default"; 设备的状态, 可以有多个状态, default 为状态 0。

pinctrl-0 = <&pinctrl_led>;第 0 个状态所对应的引脚配置, default 状态对应的引脚在 pin controller 里面定义好的节点 pinctrl_led 里面的管脚配置

二、gpio子系统

当使用 pinctrl 子系统将引脚的复用设置为 GPIO,可以使用 GPIO 子系统来操作GPIO,Linux 内核提供了 pinctrl 子系统和 gpio 子系统用于 GPIO 驱动。

通过 GPIO 子系统功能要实现:

引脚功能的配置(设置为 GPIO,GPIO 的方向, 输入输出模式,读取/设置 GPIO 的值)

②实现软硬件的分离(分离出硬件差异, 有厂商提供的底层支持; 软件分层。 驱动只需要调用接口 API 即可操作 GPIO)

③iommu 内存管理(直接调用宏即可操作 GPIO)
 

gpio 子系统的主要目的就是方便驱动开发者使用 gpio,驱动开发者在设备树中添加 gpio 相关信息,然后就可以在驱动程序中使用 gpio 子系统提供的 API函数来操作 GPIO, Linux 内核向驱动开发者屏蔽掉了 GPIO 的设置过程,极大的方便了驱动开发者使用 GPIO。

1.gpio 子系统 API 函数

①gpio_request


gpio_request 函数用于申请一个 GPIO 管脚,在使用一个 GPIO 之前一定要使用 gpio_request
进行申请。

int gpio_request(unsigned gpio, const char *label)
gpio:要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信
息,此函数会返回这个 GPIO 的标号

label:给 gpio 设置个名字。

返回值: 0,申请成功;其他值,申请失败。

②gpio_free

如果不使用某个 GPIO ,需要调用 gpio_free 函数进行释放。

void gpio_free(unsigned gpio);  // gpio:要释放的 gpio 标号。

③gpio_direction_input

设置某个 GPIO 为输入

int gpio_direction_input(unsigned gpio) //gpio:要设置为输入的 GPIO 标号。

④gpio_direction_output

设置某个 GPIO 为输出,并且设置默认输出值

int gpio_direction_output(unsigned gpio, int value)
gpio:要设置为输出的 GPIO 标号。
value: GPIO 默认输出值。
返回值: 0,设置成功;负值,设置失败

⑤gpio_get_value

获取某个 GPIO 的值(0 或 1)

#define gpio_get_value __gpio_get_value

int __gpio_get_value(unsigned gpio)

gpio:要获取的 GPIO 标号。
返回值: 非负值,得到的 GPIO 值;负值,获取失败

 ⑥gpio_set_value

设置某个 GPIO 的值
 

#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)


gpio:要设置的 GPIO 标号。
value: 要设置的值

 2.gpio 相关的 OF 函数

在驱动程序中需要读取 gpio 属性内容, Linux 内核提供了几个与 GPIO 有关的 OF 函数。

①of_gpio_named_count

获取设备树某个属性里面定义了几个GPIO 信息。

int of_gpio_named_count(struct device_node *np, const char *propname)

np:设备节点。
propname:要统计的 GPIO 属性。
返回值: 正值,统计到的 GPIO 数量;负值,失败

注意空的GPIO信息也会被统计到

gpios = <0
               &gpio1 1 2
                0 &
                gpio2 3 4

>;

如上GPIO的数量为4。

②of_gpio_count

此函数统计的是gpios属性的 GPIO 数量,而 of_gpio_named_count 函数可以统计任意属性的GPIO 信息

int of_gpio_count(struct device_node *np)
np:设备节点。
返回值: 正值,统计到的 GPIO 数量;负值,失败

③of_get_named_gpio

获取 GPIO 编号,在Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编号,此函数会将设备树中类似<&gpio5     7     GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO 编号

int of_get_named_gpio(struct device_node *np,
                                        const char *propname,
                                        int index)

np:设备节点。

propname:包含要获取 GPIO 信息的属性名。

index: GPIO 索引,因为一个属性里面可能包含多个 GPIO,此参数指定要获取哪个 GPIO
的编号,如果只有一个 GPIO 信息的话此参数为 0。
返回值: 正值,获取到的 GPIO 编号;负值,失败。


 3.设备树中添加 gpio 节点模板

①调用pinctrl信息

调用pinctrl_test 节点,此节点描述了 test 设备所使用的 PIN 的信息(复用功能,电气特性)。

在根节点“/”下创建 test 设备子节点,并调用pinctrl。

test {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_test>;
    /* 其他节点内容 */
};

pinctrl-names = "default", "wake up";
pinctrl-0 = <&pinctrl_hog_1>;
pinctrl-1 = <&pinctrl_hog_2>;

pinctrl-names = "default", "wake up"; 设备的状态, 可以有多个状态, default 为状态 0, wake up 为状态 1。

pinctrl-0 = <&pinctrl_hog_1>;第 0 个状态所对应的引脚配置, 也就是 default 状态对应的引脚在 pin controller 里面定义好的节点 pinctrl_hog_1 里面的管脚配置。

pinctrl-1 = <&pinctrl_hog_2>;第 1 个状态所对应的引脚配置也就是 wake up 状态对应的引脚在 pin  controller 里面定义好的节点 pinctrl_hog_2 里面的管脚配置。
 

②配置GPIO 属性信息


 在 test 节点中添加 GPIO 属性信息,表明 test 所使用的 GPIO 是哪个引脚,

gpio = <&gpio1  0 GPIO_ACTIVE_LOW>;

 test 设备所使用的 gpio1,配置引脚GPIO1_IO00,低电平有效。GPIO_ACTIVE_LOW的宏定义为1,GPIO_ACTIVE_HIGH为0.

 小结

需要配置某个外设的引脚为GPIO

①在 IOMUXC/ pinctrl 中对某一个引脚进行配置, 两个部分: 复用、 初始化(电气特性) 

②在外设节点中调用(设备树中)


③在驱动中获取设备节点以及 GPIO

④对 GPIO 进行配置

模板:

//client端:
@节点名字 {
    pinctrl-names = "default";
    pinctrl-0 = <&pinctrl_自定义名字A>;
    status = "okay";
};
 
//pincontroller服务端
pinctrl_自定义名字A: 自定义名字B {
    fsl,pins = <
            引脚复用宏定义   PAD(引脚)属性, // 引脚 A
            引脚复用宏定义   PAD(引脚)属性; // 引脚 B
    >;
};

//驱动
gpio_request(leddev.led0, "led0");
gpio_direction_output(leddev.led0, 1);
gpio_set_value(leddev.led0, 0);
gpio_direction_input(key.irqkeydesc[0].gpio);
gpio_get_value(keydesc->gpio);
gpio_free(leddev.led0);

引用复用宏定义一般在arch/arm/boot/dts/imx6ul-pinfunc.h和imx6ull-pinfunc.h里面定义。


 


 

你可能感兴趣的:(#,Linux驱动开发,linux,运维,服务器)