pinctrl和GPIO子系统

pinctrl子系统

管理gpio的复用功能

管理gpio的电气特性(上下拉,驱动能力等)

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 里面有宏定义定义

#define MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03   0x0014     0x0058     0x0000     0x5       0x0

后面四个值分别代表的意思如下:

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控制器的属性

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 = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>,      
			     <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
	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节点的时候如下

 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函数接口

//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函数

int of_gpio_named_count(struct device_node *np, const char *propname)
// 获取设备树某个属性里面定义了几个 GPIO信息,要注意的是空的 GPIO信息也会被统计到,比如下面的代码会得到的返回值是 4
// gpios = <0 
// 		&gpio1 1 2 
// 		0 
// 		&gpio2 3 4>;
int of_gpio_count(struct device_node *np)

// 和of_gpio_named_count函数一样,但是不同的地方在于,此函数统计的是“gpios”这个属
// 性的GPIO数量,而 of_gpio_named_count函数可以统计任意属性的 GPIO信息,
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>

    /****************************************修改的地方****************************************************/
    /*
    	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_requestgpio_direction_output等GPIO子系统下的接口来操作GPIO。

    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
    
    }