# pinctrl和GPIO子系统 ## pinctrl子系统 管理gpio的复用功能 管理gpio的电气特性(上下拉,驱动能力等) ```bash iomuxc: iomuxc@020e0000 { compatible = "fsl,imx6ul-iomuxc"; …… pinctrl_led{ fsl,pins = < MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03 0x1b0b0 >; …… }; ``` 在`arch/arm/boot/dts/imx6ull-pinfunc-snvs.h` 里面有宏定义定义 ```c #define MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03 0x0014 0x0058 0x0000 0x5 0x0 ``` 后面四个值分别代表的意思如下: ```bash 0x0014:mux_reg 复用寄存器偏移地址 基地址通过父节点iomuxc找到 可以看出MX6ULL_PAD_SNVS_TAMPER3的mux_reg地址就是020e0000+0x0014 查阅手册就可以看出是正确的 0x0058:conf_reg 配置寄存器偏移地址 0x0000:input_reg 输入寄存器偏移地址 (不是每个io都有,这里就没有所以0x0000无效的值) 0x5 :mux_mode 配置mux_reg寄存器的值,即选择了ALT5 复用为GPIO 0x0 :input_val 配置input_reg的值,这里无效 ``` 而pinctrl_led里面的0x1b0b0 就是设置conf_reg寄存器的值,用来设置电器属性(上下拉等等) 所以通过 pinctrl_led描述,就可以确定这个IO功能。 根据`iomuxc的属性compatible = "fsl,imx6ul-iomuxc"`可以确定其驱动程序在 `drivers/pinctrl/freescale/pinctrl-imx6ul.c`文件中,这个即是fsl 官方为linux适配的pinctrl子系统的驱动程序 ## gpio子系统 在`arch/arm/boot/dts/imx6ull.dtsi`文件中定义各gpio控制器的属性 ```bash gpio5: gpio@020ac000 { // 可以找到fsl官方为imx适配的linux的gpio子系统的源代码驱动文件 drivers/gpio/gpio-mxc.c compatible = "fsl,imx6ul-gpio", "fsl,imx35-gpio"; reg = <0x020ac000 0x4000>; // GPIO5寄存器的基地址(GPIO5_DR GPIO5_GDIR……) interrupts = , ; gpio-controller; // 表示 gpio5节点是个 GPIO控制器。 /* 有两个cells 第一个是GPIO编号,例如&gpio5 3 * 第二个指GPIO的电平,0(GPIO_ACTIVE_HIGH)高电平有效 1(GPIO_ACTIVE_LOW)低电平有效。 */ #gpio-cells = <2>; interrupt-controller; #interrupt-cells = <2>; }; ``` 那么我们在下面需要引用到gpio节点的时候如下 ```bash leds { compatible = "xym-led"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_led>; led0: cpu { gpios = <&gpio5 3 GPIO_ACTIVE_LOW>; // 正如这句,说明要用GPIO5_03 默认低电平有效 status = "okay"; }; }; ``` ### 有关gpio子系统的API函数接口 ```c //1: 用于申请一个 GPIO管脚,在使用一个GPIO之前一定要申请 label是给GPIO设置个名字 返回0代表成功 int gpio_request(unsigned gpio, const char *label) //2:释放 void gpio_free(unsigned gpio) //3: 设置为输入 返回0代表成功 int gpio_direction_input(unsigned gpio) //4: 设置为输出,默认输出值为value 返回0代表成功 c int gpio_direction_output(unsigned gpio, int value) //5: 获取gpio的值,负值失败 #define gpio_get_value __gpio_get_value int __gpio_get_value(unsigned gpio) //6: 设置gpio的值, 为value #define gpio_set_value __gpio_set_value void __gpio_set_value(unsigned gpio, int value) ``` ### 与 gpio相关的 OF函数 ```c int of_gpio_named_count(struct device_node *np, const char *propname) // 获取设备树某个属性里面定义了几个 GPIO信息,要注意的是空的 GPIO信息也会被统计到,比如下面的代码会得到的返回值是 4 // gpios = <0 // &gpio1 1 2 // 0 // &gpio2 3 4>; ``` ```c int of_gpio_count(struct device_node *np) // 和of_gpio_named_count函数一样,但是不同的地方在于,此函数统计的是“gpios”这个属 // 性的GPIO数量,而 of_gpio_named_count函数可以统计任意属性的 GPIO信息, ``` ```c int of_get_named_gpio(struct device_node *np, const char *propname, int index) // 此函数获取GPIO编号, // np:设备节点。 // propname:包含要获 取 GPIO信息的属性名。 // index GPIO索引,因为一个属性里面可能包含多个 GPIO,此参数指定要获取哪个 GPIO的编号,如果只有一个 GPIO信息的话此参数为 0。 // 返回值: 正值,获取到的 GPIO编号;负值,失败。 ``` ## 代码分析 参见代码【05.led_pinctrl_gpio】工程; 和上章设备树【04.led_driver_device_tree】唯一不同的就是设备树文件和底层驱动文件的修改如下: 1. 设备树修改的地方,不在采用以前的传入寄存器资源,用户自己配了,这里直接传入gpio子系统需要的设备节点信息`led-gpio = <&gpio5 3 GPIO_ACTIVE_LOW>` ```c /****************************************修改的地方****************************************************/ /* leds { compatible = "gpio-leds"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_led>; led0: cpu { label = "cpu"; gpios = <&gpio5 3 GPIO_ACTIVE_LOW>; default-state = "on"; linux,default-trigger = "heartbeat"; }; }; */ xym_led { compatible = "xym-led"; pinctrl-names = "default"; pinctrl-0 = <&pinctrl_led>; led-gpio = <&gpio5 3 GPIO_ACTIVE_LOW>; }; /****************************************修改的地方****************************************************/ ``` 2. 底层驱动文件【board_fire_imx6ull_pro.c】:这里直接得到led的管脚号_hw_led_gpio,接着就可以通过`gpio_request`、`gpio_direction_output`等GPIO子系统下的接口来操作GPIO。 ```c void hw_led_init(struct device_node *nd) { int ret; /* 2、 获取设备树中的gpio属性,得到LED所使用的LED编号 */ _hw_led_gpio = of_get_named_gpio(nd, "led-gpio", 0); if(_hw_led_gpio < 0) { printk("can't get led-gpio"); return ; } printk("led-gpio num = %d\r\n", _hw_led_gpio); gpio_free(_hw_led_gpio); ret = gpio_request(_hw_led_gpio, "led"); if(!ret){ printk("can't req gpio!\r\n"); return; } /* 3、设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯 */ ret = gpio_direction_output(_hw_led_gpio, 1); if(ret < 0) { printk("can't set gpio!\r\n"); } c } ```