Linux驱动开发学习笔记【5】:pinctrl和gpio子系统

目录

 

目录

一、Linux中GPIO使用

二、pinctrl子系统

1、pinctrl在设备树中的表示方法

1.1 IOMUXC SNVS控制器

1.2 IOMUXC控制器

1.3 gpr控制器

1.4 如何添加一个PIN的信息

2、pinctrl 驱动

三、gpio子系统

1、gpio在设备树中的表示方法

2、gpio 驱动

3、gpio子系统API

4、gpio相关的of函数

5、驱动中gpio的使用流程


一、Linux中GPIO使用

二、pinctrl子系统

1、pinctrl在设备树中的表示方法

1.1 IOMUXC SNVS控制器

1.2 IOMUXC控制器

1.3 gpr控制器

1.4 如何添加一个PIN的信息

2、pincrtl 驱动

三、gpio子系统

1、gpio在设备树中的表示方法

2、gpio 驱动

3、gpio子系统API

4、gpio相关的of函数

5、驱动中gpio的使用流程


目录

一、Linux中GPIO使用

二、pinctrl子系统

1、pinctrl在设备树中的表示方法

1.1 IOMUXC SNVS控制器

1.2 IOMUXC控制器

1.3 gpr控制器

1.4 如何添加一个PIN的信息

2 pincrtl 驱动

三、gpio子系统

1、gpio在设备树中的表示方法

2、gpio 驱动

3、gpio子系统API

4、gpio相关的of函数

5、驱动中gpio的使用流程


一、Linux中GPIO使用

1、通过pinctrl子系统设置PIN的复用和电气属性。

2、通过gpio子系统配置操作GPIO

二、pinctrl子系统

只需要在设备树里面设置好某个 pin的相关属性,其他的初始化工作均由 pinctrl子系统来完成, pinctrl子系统源码目录为 drivers/pinctrl。pinctrl子系统主要工作内容如下:

①、获取设备树中 pin信息。

②、根据获取到的 pin信息来设置 pin的复用功能

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

1、pinctrl在设备树中的表示方法

打开imx6ull.dtsi:

1.1 IOMUXC SNVS控制器

iomuxc_snvs: iomuxc-snvs@02290000 {
    compatible = "fsl,imx6ull-iomuxc-snvs";
    reg = <0x02290000 0x10000>;
};

1.2 IOMUXC控制器

iomuxc: iomuxc@020e0000 {
    compatible = "fsl,imx6ul-iomuxc";
    reg = <0x020e0000 0x4000>;
    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_IO09__GPIO1_IO09   0x17059 /* SD1 RESET */
                >;
    };
    ………
    }
};

根据设备的类型,创建对应的子节点,然后设备所用PIN都放到此节点。

1.3 gpr控制器

gpr: iomuxc-gpr@020e4000 {
    compatible = "fsl,imx6ul-iomuxc-gpr",
    "fsl,imx6q-iomuxc-gpr", "syscon";
    reg = <0x020e4000 0x4000>;
};

1.4 如何添加一个PIN的信息

对于 I.MX系列 SOC而言, pinctrl驱动程序是通过读取“ fsl,pins”属性值来获取 PIN的配置信息。关于 I.MX系列 SOC的 pinctrl设备树绑定信息可以参考Linux源码目录下的绑定文档 Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt

pinctrl_hog_1: hoggrp-1 {
    fsl,pins = <
        MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */
    >;
};


在imx6ul-pinfunc.h中找到:
#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19  0x0090  0x031C 0x0000 0x5 0x0


展开后:
pinctrl_hog_1: hoggrp-1 {
    fsl,pins = <
        0x0090 0x031C 0x0000 0x5 0x0 0x17059 /* SD1 CD */
    >;
};
<mux_reg   conf_reg    input_reg    mux_mode    input_val>
 0x0090      0x031C      0x0000       0x5        0x0

1、mux_reg:UART1_RTS_B这个PIN的复用功能寄存器偏移地址。IOMUXC父节点首地址0x020e0000,UART1_RTS_B这个PIN的复用功能寄存器地址是0x020e0090,

2、conf_reg:UART1_RTS_B这个PIN的电气属性寄存器偏移地址。UART1_RTS_B这个PIN的电气属性寄存器地址是0x020e0000+0x031C=0x020e 031C。

3、input_reg,输入寄存器偏移,为0表示UART1_RTS_B这个PIN没有input功能。

4、mux_mode:复用功能寄存器地址的值,5表示复用为GPIO1_IO19,将其写入0x020e 0090

5、input_val:就是写入input_reg寄存器的值。

6、0x17059:为PIN的电气属性配置寄存器值。

2、pinctrl 驱动

所有的东西都已经准备好了,包括寄存器地址和寄存器值, Linux内核相应的驱动文件就会根据这些值来做相应的初始化。接下来就找一下哪个驱动文件来做这一件事情,iomuxc节点中 compatible属性的值为“ fsl,imx6ul-iomuxc”,在 Linux内核中全局搜索“ fsl,imx6ul-iomuxc字符串就会找到对应的驱动文件。在文件drivers/pinctrl/freescale/pinctrl-imx6ul.c中有如下内容:

static struct of_device_id imx6ul_pinctrl_of_match[] = {
	{ .compatible = "fsl,imx6ul-iomuxc", .data = &imx6ul_pinctrl_info, },
	{ .compatible = "fsl,imx6ull-iomuxc-snvs", .data = &imx6ull_snvs_pinctrl_info, },
	{ /* sentinel */ }
};

static int imx6ul_pinctrl_probe(struct platform_device *pdev)
{
	const struct of_device_id *match;
	struct imx_pinctrl_soc_info *pinctrl_info;

	match = of_match_device(imx6ul_pinctrl_of_match, &pdev->dev);

	if (!match)
		return -ENODEV;

	pinctrl_info = (struct imx_pinctrl_soc_info *) match->data;

	return imx_pinctrl_probe(pdev, pinctrl_info);
}

static struct platform_driver imx6ul_pinctrl_driver = {
	.driver = {
		.name = "imx6ul-pinctrl",
		.owner = THIS_MODULE,
		.of_match_table = of_match_ptr(imx6ul_pinctrl_of_match),
	},
	.probe = imx6ul_pinctrl_probe,
	.remove = imx_pinctrl_remove,
};

当设备和驱动匹配成功以后platform_driver 的probe 成员变量所代表的函数就会执行

三、gpio子系统

pinctrl子系统重点是设置 PIN(有的 SOC叫做 PAD)的复用和电气属性,如果 pinctrl子系统将一个 PIN复用为 GPIO的话,那么接下来就要用到 gpio子系统了。gpio子系统顾名思义,就是用于初始化 GPIO并且提供相应的 API函数,比如设置 GPIO为输入输出,读取 GPIO的值等。 gpio子系统的主要目的就是方便驱动开发者使用 gpio,驱动开发者在设备树中添加 gpio相关信息,然后就可以在驱动程序中使用 gpio子系统提供的 API函数来操作 GPIO Linux内核向驱动开发者屏蔽掉了 GPIO的设置过程,极大的方便了驱动开发者使用 GPIO

1、gpio在设备树中的表示方法

I.MX系列 SOC的 GPIO控制器绑定信息请查看文档 Documentation/devicetree/bindings/gpio/ fsl-imx-gpio.txt

&usdhc1 {
    pinctrl-names = "default", "state_100mhz", "state_200mhz";
    pinctrl-0 = <&pinctrl_usdhc1>;
    pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
    pinctrl-2 = <&pinctrl_usdhc1_200mhz>;
    cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
    keep-power-in-suspend;
    enable-sdio-wakeup;
    vmmc-supply = <®_sd1_vmmc>;
    status = "okay";
};

定义了一个cd-gpios属性,“&gpio1”表示 CD引脚所使用的 IO属于 GPIO1组,19表示 GPIO1组的第 19号 IO,通过这两个值 SD卡驱动程序就知道 CD引脚使用了 GPIO1_IO19这 GPIO。“ GPIO_ACTIVE_LOW”表示低电平有效,如果改GPIO_ACTIVE_HIGH就表示高电平有效。

gpio1: gpio@0209c000 {
    compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio";
    reg = <0x0209c000 0x4000>;
    interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>,
                 <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>;
    gpio-controller;
    #gpio-cells = <2>;
    interrupt-controller;
    #interrupt-cells = <2>;
};

2、gpio 驱动

搜索compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio",在drivers/gpio/gpio-mxc.c 中找到了与之相匹配的字符串,drivers/gpio/gpio-mxc.c就是 I.MX6ULL的 GPIO驱动文件

static const struct of_device_id mxc_gpio_dt_ids[] = {
	{ .compatible = "fsl,imx1-gpio", .data = &mxc_gpio_devtype[IMX1_GPIO], },
	{ .compatible = "fsl,imx21-gpio", .data = &mxc_gpio_devtype[IMX21_GPIO], },
	{ .compatible = "fsl,imx31-gpio", .data = &mxc_gpio_devtype[IMX31_GPIO], },
	{ .compatible = "fsl,imx35-gpio", .data = &mxc_gpio_devtype[IMX35_GPIO], },
	{ /* sentinel */ }
};

static struct platform_driver mxc_gpio_driver = {
        .driver = {
        .name = "gpio-mxc",
        .of_match_table = mxc_gpio_dt_ids,
	},
	.probe	= mxc_gpio_probe,
	.id_table = mxc_gpio_devtype,
};

可以看出 GPIO驱动也是个平台设备驱动,因此当设备树中的设备节点与驱动的of_device_id匹配以后 probe函数就会执行,实现过程跟pinctl类似

3、gpio子系统API

(1)int gpio_request(unsigned gpio, const char *label)

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

gpio:要申请的 gpio标号,使用 of_get_named_gpio函数从设备树获取指定 GPIO属性信息,此函数会返回这个 GPIO的标号。

label:给 gpio设置个名字。

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

(2)void gpio_free(unsigned gpio)

如果不使用某个 GPIO了,那么就可以调用 gpio_free函数进行释放

gpio:要释放的 gpio标号

(3)int gpio_direction_input(unsigned gpio)

此函数用于设置某个 GPIO为输入

gpio:要设置为输入的 GPIO标号。

返回值: 0,设置成功;负值,设置失败。

(4)int gpio_direction_output(unsigned gpio, int value)

此函数用于设置某个 GPIO为输出,并且设置默认输出值

gpio:要设置为输出的 GPIO标号。

value GPIO默认输出值。

返回值: 0,设置成功;负值,设置失败。

(5)#define gpio_get_value int __gpio_get_value(unsigned gpio)

此函数用于获取某个 GPIO的值 (0或 1)

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

此函数用于设置某个 GPIO的值

4、gpio相关的of函数

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

(2)int of_gpio_count(struct device_node *np)

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

5、驱动中gpio的使用流程

1、首先,获取到GPIO所处的设备节点,比如of_find_node_by_path。

2、获取GPIO编号, of_get_named_gpio函数,返回值就是GPIO编号。

3、请求此编号的GPIO,gpio_request函数

4、设置GPIO,输入或输出,gpio_direction_input或gpio_direction_output。

5、如果是输入,那么通过gpio_get_value函数读取GPIO值,如果是输出,通过gpio_set_value设置GPIO值。

猜你喜欢

转载自blog.csdn.net/m0_37845735/article/details/106977195