目录
目录
目录
一、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值。