开发板:正点原子阿尔法
由于平台差异性,本文只涉及如何使用,不涉及原理以及驱动源码分析
1.前言
Linux系统为PIN脚的配置提供了pinctrl子系统,用来配置pin的复用功能与电气属性 ,比如上/下拉、速度、驱动,pinctrl子系统的主要功能为:
- 获取设备树的pin信息
- 根据获取到的pin信息设置pin的复用功能
- 根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等
2.I.MX6ULL的pinctrl子系统
2.1 PIN配置信息详解
要想使用pinctrl子系统,需要在设备树中配置PIN信息,即在设备树中创建一个结点来描述PIN脚的配置信息,用来汇总所有PIN脚的配置信息,IMX6ULL的配置信息在设备树imx6ull.dtsi中,结点为iomuxc
iomuxc: iomuxc@20e0000 {
compatible = "fsl,imx6ul-iomuxc";
reg = <0x20e0000 0x4000>;
};
其中reg为引脚配置寄存器的基地址,打开正点原子创建的设备树文件 imx6ull-alientek-emmc.dts,
&iomuxc {
pinctrl-names = "default","init","sleep";
pinctrl-0 = <&pinctrl_hog_1>;
pinctrl-1 =<&xxx>;
pinctrl-2 =<&yyy>;
imx6ul-evk {
pinctrl_hog_1: hoggrp-1 {
fsl,pins = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059
MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x13058
>;
};
pinctrl_csi1: csi1grp {
fsl,pins = <
MX6UL_PAD_CSI_MCLK__CSI_MCLK 0x1b008
MX6UL_PAD_CSI_PIXCLK__CSI_PIXCLK 0x1b008
MX6UL_PAD_CSI_VSYNC__CSI_VSYNC 0x1b008
MX6UL_PAD_CSI_HSYNC__CSI_HSYNC 0x1b008
>;
};
-----
从上面追加的数据看出,不同的外设使用的pin脚不一样,将某一个外设所使用的所有PIN脚都放在一个子结点里面。结点的配置方式为:
- pinctrl-names:定义引脚状态(名字)
- pinctrl-0:定义第0种状态需要使用到的引脚,可引用其他节点标识
- pinctrl-1:定义第1种状态需要使用到的引脚,以此类推
2.2 结点的配置信息记录
以pinctrl_hog_1 子节点为例子,由fsl,pins以及其属性值构成,fsl,pins会结合imx6ull的pinctrl子系统驱动使用,以该属性值来标识引脚的配置信息,其属性值包括一个宏 和一个16进制数 ,这里以UART1_RTS_B为例,其配置信息为
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19宏定义的原型在arch/arm/boot/dts/imx6ul-pinfunc.h中,imx6ull.dtsi 会引用 imx6ull-pinfunc.h 这个头文件,而imx6ull-pinfunc.h 又会引用 imx6ul-pinfunc.h 这个头文件,此宏定义为
····
#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0
#define MX6UL_PAD_UART1_RTS_B__USDHC2_CD_B 0x0090 0x031C 0x0674 0x8 0x2
····
宏值定义为:
<mux_reg conf_reg input_reg mux_mode input_val>
0x0090 0x031C 0x0000 0x5 0x0
-
0x0090:mux_reg 寄存器偏移地址,IOMUXC 外 设 寄 存 器 起 始 地 址 为 0x020e0000,所以MX6UL_PAD_UART1_RTS_B__GPIO1_IO19的寄存器地址为0x020e0000+0x0090=0x020e009, -
0x031C: conf_reg 寄存器偏移地址,和 mux_reg 一样,0x020e0000+0x031c=0x020e031c,这个就是寄存器 IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B 的地址。 - -
0x0000: input_reg 寄存器偏移地址,有些外设有 input_reg 寄存器,有 input_reg 寄存器的外设需要配置 input_reg 寄存器。没有的话就不需要设置, UART1_RTS_B 这个 PIN 在做GPIO1_IO19 的时候是没有 input_reg 寄存器,因此这里 intput_reg 是无效的。 -
0x5 : mux_reg 寄 存 器 的值 , 在 这 里 就 相 当 于 设 置IOMUXC_SW_MUX_CTL_PAD_UART1_RTS_B 寄存器为 0x5,也即是设置 UART1_RTS_B 这个 PIN 复用为 GPIO1_IO19。 回到原来的pinctrl定义,其中0x17059则是config_reg寄存器的值,通过此值来设置一个 IO 的上/下拉、驱动能力和速度等。在这里就相当于设置寄存器IOMUXC_SW_PAD_CTL_PAD_UART1_RTS_B 的值为 0x17059
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059
3.在设备树添加pinctrl结点
关于I.MX系列SOC的pinctrl的设备树绑定信息可以参考文档Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt ,我们新建一个“test”设备,test设备使用了 GPIO1_IO00,pinctrl结点的添加过程为:
3.1 创建对应的节点
同一个外设的PIN都放到一个节点里面,打开 imx6ull-alientek-emmc.dts ,在iomuxc 节点中imx6ul-evk 子节点下添加pinctrl_test 结点,注意节点前缀一定要为pinctrl ,添加完成以后如下所示:
pinctrl_test: testgrp {
};
3.2 添加fsl,pins 属性
设备树是通过属性来保存信息的,因此需要添加一个属性,属性名字一定要为fsl,pins ,对于I.MX系列的SOC来说,pinctrl驱动程序是通过fsl,pins 属性值来获取PIN的配置信息,如下所示
pinctrl_test: testgrp {
fsl,pins = <
>;
};
3.3 在fsl.pins属性中添加PIN配置信息
pinctrl_test: testgrp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO00__GPIO1_IO00 config
>;
};
通过以上步骤,添加好了test设备所需要的PIN配置信息,设置了test设备使用的PIN脚复用与驱动能力,上下拉等
4.gpio子系统
gpio 子系统目的是方便驱动开发者使用 gpio,在设备树中添加 gpio 相关信息,在驱动中调用gpio子系统提供的API函数操作GPIO,可以设置gpio为输入输出,以及读取设置gpio的值。
5. I.MX的gpio子系统
I.MX6ULL-ALPHA 开发板上的 UART1_RTS_B 做为 SD 卡的检测引脚, UART1_RTS_B 复用为 GPIO1_IO19,通过读取这个 GPIO 的高低电平就可以知道 SD 卡有没有插入。首先肯定是将 UART1_RTS_B 这个 PIN 复用为 GPIO1_IO19,并且设置电气属性,打开 imx6ull-alientek-emmc.dts, UART1_RTS_B 这个 PIN 的 pincrtl 设置如下:
&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";
};
pinctrl_hog_1: hoggrp-1 {
fsl,pins = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 ......
>;
};
- usdhc1为SD卡设备的总结点,pinctrl_hog_1结点描述SD卡CD引脚的pinctrl信息
- cd-gpios描述了SD卡的CD引脚使用的是哪个IO,属性值一共有3个,
&gpio1 表示CD引脚使用的IO属于GPIO1组,19 表示GPIO1的19号IO,GPIO_ACTIVE_HIGH 表示低电平有效,GPIO_ACTIVE_HIGH 表示高电平有效。
在imx6ull.dtsi有以下描述:
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>;
};
gpio1结点信息描述了GPIO1控制器的所有信息,关 于 I.MX 系 列 SOC 的 GPIO 控 制 器 绑 定 信 息 请可 看 文 档Documentation/devicetree/bindings/gpio/ fsl-imx-gpio.txt。 gpio-controller表示gpio1 节点是个 GPIO 控制器 ,“#gpio-cells”属性 为2表示一共有两个cell,第一个cell为gpio编号,比如&gpio1 3 表示GPIO1_IO03,第二个cell表示gpio的极性,如 果 为 0(GPIO_ACTIVE_HIGH) 的 话 表 示 高 电 平 有 效 , 如 果 为1(GPIO_ACTIVE_LOW)的话表示低电平有效。
6.gpio子系统API函数
6.1 gpio_request
用于申请一共GPIO管脚,在使用一个 GPIO 之前一定要使用 gpio_request进行申请,函数原型如下:
int gpio_request(unsigned gpio, const char *label)
6.2 gpio_free
不使用某个 GPIO ,调用 gpio_free 函数进行释放
void gpio_free(unsigned gpio)
6.3 gpio_direction_input
设置某一个gpio为输入
int gpio_direction_input(unsigned gpio)
6.4 gpio_direction_output
设置某个 GPIO 为输出,并且设置默认输出值
int gpio_direction_output(unsigned gpio, int value)
6.5 gpio_get_value
获取某个 GPIO 的值(0 或 1),此函数是个宏
#define gpio_get_value __gpio_get_value
int __gpio_get_value(unsigned gpio)
6.6 gpio_set_value
用于设置某个 GPIO 的值,此函数是个宏
#define gpio_set_value __gpio_set_value
void __gpio_set_value(unsigned gpio, int value)
7.在设备树添加gpio结点模板
7.1.创建test设备结点
在根节点“/”下创建 test 设备子节点,如下所示:
test {
};
7.2 添加pinctrl信息
在iomuxc 节点中imx6ul-evk 子节点下添加pinctrl_test 结点
test {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_test>;
};
?`````
pinctrl_test: testgrp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO00__GPIO1_IO00 config
>;
};
- pinctrl-names 属性,此属性描述 pinctrl 名字为“default”。
添加 pinctrl-0 节点,此节点引用 pinctrl_test 节点,表示 tset 设备的所使用的 PIN 信息保存在 pinctrl_test 节点中
7.3 添加 GPIO 属性信息
在 test 节点中添加 GPIO 属性信息,表明 test 所使用的 GPIO 是哪个引脚
test {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_test>;
gpio = <&gpio1 0 GPIO_ACTIVE_LOW>;
};
8.与gpio相关的OF函数
8.1.of_gpio_named_count
of_gpio_named_count 函数用于获取设备树某个属性里面定义了几个 GPIO 信息,要注意的是空的 GPIO 信息也会被统计到,比如:
gpios = <0
&gpio1 1 2
0 &
gpio2 3 4>;
上述代码的“gpios”节点一共定义了 4 个 GPIO,但是有 2 个是空的,没有实际的含义。 通过 of_gpio_named_count 函数统计出来的 GPIO 数量就是 4 个,此函数原型如下:
int of_gpio_named_count(struct device_node *np, const char *propname)
- np:设备节点。
- propname:要统计的 GPIO 属性。
- 返回值: 正值,统计到的 GPIO 数量;负值,失败
8.2 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 编号;负值,失败
9.举例
使用I.MX6ULL开发板的LED灯作为;例子
9.1 在设备中添加pinctrl结点
I.MX6U-ALPHA 开发板上的 LED 灯使用了 GPIO1_IO03 这个 PIN,打开 imx6ull-alientekemmc.dts,在 iomuxc 节点的 imx6ul-evk 子节点下创建一个名为“pinctrl_led”的子节点 ,将 GPIO1_IO03 这个 PIN 复用为 GPIO1_IO03,电气属性值为 0X10B0
pinctrl_led: ledgrp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0
>;
};
9.2 添加 LED 设备节点
在根节点“/”下创建 LED 灯节点,节点名为“gpioled”,节点内容如下:
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";
};
- pinctrl-0 属性设置 LED 灯所使用的 PIN 对应的 pinctrl 节点
- led-gpio 属性指定了 LED 灯所使用的 GPIO,在这里就是 GPIO1 的 IO03,低电平有效。稍后编写驱动程序的时候会获取 led-gpio 属性的内容来得到 GPIO 编号,因为 gpio 子系统的 API 操作函数需要 GPIO 编号
9.3 检查PIN脚有没有冲突
①、检查 pinctrl 设置。 ②、如果这个 PIN 配置为 GPIO 的话,检查这个 GPIO 有没有被别的外设使用。
在实验中 LED 灯使用的 PIN 为 GPIO1_IO03,先检查 GPIO_IO03 这个 PIN 有没有被其他的 pinctrl 节点使用,在 imx6ull-alientek-emmc.dts 中找到如下内容:
pinctrl_tsc: tscgrp {
fsl,pins = <
MX6UL_PAD_GPIO1_IO01__GPIO1_IO01 0xb0
MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0xb0
MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0xb0
MX6UL_PAD_GPIO1_IO04__GPIO1_IO04 0xb0
>;
};
pinctrl_tsc 节点是 TSC(电阻触摸屏接口)的,默认情况下GPIO1_IO03 作为了 TSC 外设的 PIN。所以需要将此行屏蔽掉,在 imx6ull-alientek-emmc.dts 中搜索“gpio1 3”
&tsc {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_tsc>;
xnur-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
measure-delay-time = <0xffff>;
pre-charge-time = <0xfff>;
status = "okay";
};
tsc 是 TSC 的外设节点,tsc 外设使用了 GPIO1_IO03,需要将这一行屏蔽掉
9.4 编译设备树
设备树编写完成以后使用“ make dtbs”命令重新编译设备树,然后使用新编译出来的imx6ull-alientek-emmc.dtb 文件启动 Linux 系统。启动成功以后进入“/proc/device-tree”目录中查看“gpioled”节点是否存在,如果存在的话就说明设备树基本修改成功(具体还要驱动验证)
9.5 驱动中使用GPIO子系统API函数举例
struct device_node *nd;
int led_gpio;
nd = of_find_node_by_path("/gpioled");
if(nd == NULL) {
printk("gpioled node cant not found!\r\n");
return -EINVAL;
} else {
printk("gpioled node has been found!\r\n");
}
led_gpio = of_get_named_gpio(nd, "led-gpio", 0);
if(led_gpio < 0) {
printk("can't get led-gpio");
return -EINVAL;
}
ret = gpio_direction_output(led_gpio, 1);
if(ret < 0) {
printk("can't set gpio!\r\n");
}
gpio_set_value(led_gpio, 0);
gpio_set_value(led_gpio, 1);
|