IMX6ULL驅動學習--pinctrl子系統和gpio子系統

pinctrl子系統和gpio子系統作用

Linux 驅動講究驅動分離與分層,pinctrl 和 gpio 子系統就是驅動分離與分層思想下的產物,將驅動與底層IO電氣屬性配置分開。

pinctrl子系統

  • 無pinctrl子系統時設備初始化流程
    1.修改設備樹,添加節點,設置reg屬性,主要爲相關寄存器。
    2.獲取reg屬性寄存器,並配置寄存器設置IO功能。
    3.獲取reg寄存器,配置IO輸入輸出。初始化狀態。
  • pinctrl子系統設備初始化流程
    1.獲取設備樹中的pin信息。
    2.根據pin信息設置pin的複用功能
    3.根據獲取的pin信息設置pin電氣疼。

對於使用者來說,只需要在設備樹中定義好某個pin相關屬性即可,其他的初始化工作均由pinctrl子系統完成。

PIN配置信息詳解

使用pinctrl子系統時我們需要修改的只有pin配置信息。例如:

iomuxc: iomuxc@020e0000 {
	compatible = "fsl,imx6ul-iomuxc";
	reg = <0x020e0000 0x4000>;
	pinctrl-names = "default";
	imx6ul-evk {
		pinctrl_gpmi_nand: gpmi-nand {
			fsl,pins = <
				MX6UL_PAD_NAND_CLE__RAWNAND_CLE         0xb0b1
				MX6UL_PAD_NAND_ALE__RAWNAND_ALE         0xb0b1
				MX6UL_PAD_NAND_WP_B__RAWNAND_WP_B       0xb0b1
				MX6UL_PAD_NAND_READY_B__RAWNAND_READY_B 0xb000
				MX6UL_PAD_NAND_CE0_B__RAWNAND_CE0_B     0xb0b1
				MX6UL_PAD_NAND_CE1_B__RAWNAND_CE1_B     0xb0b1
				MX6UL_PAD_NAND_RE_B__RAWNAND_RE_B       0xb0b1
				MX6UL_PAD_NAND_WE_B__RAWNAND_WE_B       0xb0b1
				MX6UL_PAD_NAND_DATA00__RAWNAND_DATA00   0xb0b1
				MX6UL_PAD_NAND_DATA01__RAWNAND_DATA01   0xb0b1
				MX6UL_PAD_NAND_DATA02__RAWNAND_DATA02   0xb0b1
				MX6UL_PAD_NAND_DATA03__RAWNAND_DATA03   0xb0b1
				MX6UL_PAD_NAND_DATA04__RAWNAND_DATA04   0xb0b1
				MX6UL_PAD_NAND_DATA05__RAWNAND_DATA05   0xb0b1
				MX6UL_PAD_NAND_DATA06__RAWNAND_DATA06   0xb0b1
				MX6UL_PAD_NAND_DATA07__RAWNAND_DATA07   0xb0b1
			>;
		};
.........
	}
}

這裏我們把一個模塊所用到的所有GPIO配置組織到一起。如果需要在iomuxc中添加自定義外設的pin,需要新建子節點,然後將自定義外設的所有pin配置信息都放到這個子節點中。
端口配置信息如下:

MX6UL_PAD_NAND_CLE__RAWNAND_CLE         0xb0b1

其中MX6UL_PAD_NAND_CLE__RAWNAND_CLE 是一個宏定義,作用是配置端口爲RAWNAND_CLE 功能。在 imx6ul-pinfunc.h文件中定義如下:
#define MX6UL_PAD_NAND_CLE__RAWNAND_CLE 0x01B4 0x0440 0x0000 0x0 0x0
尾部五個參數含義是
<mux_reg conf_reg input_reg mux_mode input_val>

  • mux_reg: 複用寄存器地址偏移量
  • conf_reg: conf_reg 寄存器偏移地址偏移量
  • input_reg: input_reg 寄存器偏移地址
  • mux_mode: 複用功能選擇寄存器
  • input_val:input_reg寄存器值,這裏無效。

0xb0b1 爲conf_reg寄存器的值,有客戶定義,用於配置端口的電氣屬性。

設備樹添加pinctrl節點

我們用MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 端口做演示。

  • 創建對應的節點
    同一個外設用到的pin都放在一個節點下,打開 imx6ull-14x14-evk.dts 文件,在iomuxc下的imx6ul-evk 節點中添加如下信息:
			pinctrl_light: lightgrp {

		};
  • 添加 fsl,pins 屬性
			pinctrl_light: lightgrp {
			fsl,pins = <
				
			>;
  • 在 fsl,pins 屬性下添加pin配置信息
			pinctrl_light: lightgrp {
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO02__GPIO1_IO02 0x1b0b0
			>;

至此已經完成向設備樹中添加pin配置信息。

GPIO子系統

GPIO子系統用於初始化GPIO,提供GPIO操作API函數,如:設置GPIO輸入輸出,讀取GPIO值等。驅動開發者在設備樹中添加gpio相關信息,就可以在驅動程序中使用gpio子系統提供的API操作GPIO。不需要開發者自己再編寫GPIO操作函數。

向設備樹中添加GPIO信息

  • 向設備樹中添加設備節點
	light {

	};
  • 添加pinctrl信息
	light {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "gpio-light";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_light>;		//表示設備所使用的pin信息保存在pinctrl_light中。

	};
  • 添加GPIO信息
	light {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "gpio-light";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_light>;
		light-gpio = <&gpio1 2 GPIO_ACTIVE_LOW>;  //要用的到gpio配置信息,gpio1_2,默認低電平
		status = "okay";
	};

至此,設備樹配置信息添加完畢,接下來編寫驅動程序。

驅動開發

GPIO相關的of函數

  • int of_gpio_named_count(struct device_node *np, const char *propname)
    返回統計到的GPIO數量。

  • int of_gpio_count(struct device_node *np)
    返回統計到的GPIO數量,指定屬性的GPIO。

gpioled.c

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define GPIOLED_CNT      1  //設備號個數
#define GPIOLED_NAME         "gpioled"

#define LEDOFF      0   //關燈
#define LEDON       1   //開燈


//gpioled設備結構體
struct  gpioled_dev
{
    /* data */
    dev_t devid;                //設備號
    struct cdev cdev;           //cdev
    struct class *class;        //類
    struct device *device;      //設備
    int major;                  //主設備號
    int minor;                  //次設備號
    struct  device_node *nd;    //設備節點
    int led_gpio;               //led使用的GPIO編號
};

struct gpioled_dev gpioled;//led設備



/*
@description      :   打開設備
@param - inode    :   傳遞給驅動的inode   
@param - filp     :   設備文件 
@return           :   0 成功; 其他 失敗
*/
static int led_open(struct inode *inode, struct file *file)
{
    file->private_data = &gpioled;        //設置私有數據
    return 0;
}


/*
@description      :   從設備讀取數據 
@param - filp     :   要打開的設備文件
@param - buf      :   返回給用戶控件的數據緩衝區
@param - cnt      :   要讀取的數據長度
@param - offt     :   相對於文件首地址的偏移      
@return           :   讀取的字節數,若爲負值,讀取失敗
*/
static ssize_t led_read(struct file *filp, char __user *buf, size_t cnt,loff_t *offt)
{
    return 0;
}

/*
@description      :   向設備寫數據 
@param - filp     :   設備文件
@param - buf      :   要寫入設備的數據
@param - cnt      :   要寫入的數據長度
@param - offt     :   相對於文件首地址的偏移      
@return           :   寫入的字節數,若爲負值,讀取失敗
*/
static ssize_t led_write(struct file *filp, const char __user *buf, size_t cnt,loff_t *offt)
{
    int retvalue;
    unsigned char datebuf[1];
    unsigned char ledstat;
    struct gpioled_dev *dev = filp->private_data;

    
    retvalue = copy_from_user(datebuf, buf, cnt);
    if(retvalue < 0)
    {
        printk("kernel write failed!!\r\n");
        return -EFAULT;
    }

    ledstat = datebuf[0];

    if(ledstat  == LEDON)
    {
        gpio_set_value(dev->led_gpio,0);//打開led燈
    }    
    else if (ledstat == LEDOFF)
    {
        gpio_set_value(dev->led_gpio,1);//關閉led燈
    }
    return 0;

}

/*
@description      :   關閉/釋放設備  
@param - filp     :   要關閉的設備文件 
@return           :   0 成功; 其他 失敗
*/
static int led_release(struct inode *inode, struct file *file)
{
    return 0;
}

/*   設備操作函數集合   */
static struct file_operations gpioled_fops = {
    .owner = THIS_MODULE,
    .open = led_open,
    .read = led_read,
    .write = led_write,
    .release = led_release,
};


/*
@description      :   驅動入口函數  
@param -          :   無 
@return           :   無
*/
static int __init led_init(void)
{
    int ret;

    /*  獲取設備樹中的屬性數據  */
    /*  1.獲取設備節點:light */
    gpioled.nd = of_find_node_by_path("/light");
    if(gpioled.nd == NULL){
        printk("lightled node can not found!\r\n");
        return -EINVAL;
    }
    else
    {
        printk("lightled node has been found!\r\n");

    }

    /*  2.獲取gpio屬性內容,得到led使用的gpio編號    */
    gpioled.led_gpio = of_get_named_gpio(gpioled.nd,"light-gpio",0);
    if(gpioled.led_gpio<0)
    {
        printk("can't get led-gpio.");
        return  -EINVAL;
    }
    printk("led-gpio num = %d\r\n",gpioled.led_gpio);

    

    /*   3.設置GPIO1_IO3爲輸出,並輸出高電平,默認關閉led燈   */
    ret = gpio_direction_output(gpioled.led_gpio,1);
    if(ret < 0)
    {
        printk("can't set gpio!\r\n");
    }
    


    //註冊字符設備驅動

    //1.創建設備號
    if(gpioled.major)
    {
        gpioled.devid = MKDEV(gpioled.major,0);
        register_chrdev_region(gpioled.devid,GPIOLED_CNT,GPIOLED_NAME);
    }
    else
    {
        alloc_chrdev_region(&gpioled.devid,0,GPIOLED_CNT,GPIOLED_NAME);//申請設備號
        gpioled.major = MAJOR(gpioled.devid);//獲取主設備號
        gpioled.minor = MINOR(gpioled.devid);//獲取次設備號
    }

    printk("gpioled major=%d,minor=%d\r\n",gpioled.major,gpioled.minor);

    //2.初始化cdev
    gpioled.cdev.owner = THIS_MODULE;
    cdev_init(&gpioled.cdev,&gpioled_fops);

    //3.添加cdev
    cdev_add(&gpioled.cdev,gpioled.devid,GPIOLED_CNT);

    //4.創建類
    gpioled.class = class_create(THIS_MODULE,GPIOLED_NAME);
    if(IS_ERR(gpioled.class))
    {
        return PTR_ERR(gpioled.class);
    }
    //5.創建設備
    gpioled.device = device_create(gpioled.class,NULL,gpioled.devid,NULL,GPIOLED_NAME);
    if(IS_ERR(gpioled.device))
    {
        return PTR_ERR(gpioled.device);
    }
    


    return 0;

}

static void __exit led_exit(void)
{


    //註銷字符驅動
    cdev_del(&gpioled.cdev);//刪除cdev
    unregister_chrdev_region(gpioled.devid,GPIOLED_CNT);
    device_destroy(gpioled.class,gpioled.devid);
    class_destroy(gpioled.class);
    
}

module_init(led_init);
module_exit(led_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Turing");

dtbs

	設備樹節點:
	pinctrl節點:

		};
		
	設備節點:
	light {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "gpio-light";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_light>;
		light-gpio = <&gpio1 2 GPIO_ACTIVE_LOW>;
		status = "okay";
	};
	
	
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章