IMX6ULL驱动学习--pinctrl子系统和gpio子系统

pinctrl子系统和gpio子系统

  • pinctrl子系统和gpio子系统作用
    • pinctrl子系统
      • PIN配置信息详解
      • 设备树添加pinctrl节点
    • GPIO子系统
      • 向设备树中添加GPIO信息
  • 驱动开发
    • GPIO相关的of函数
      • gpioled.c
      • dtbs

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: 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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

#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";
	};
	
	

你可能感兴趣的:(IMX6ULL驱动学习--pinctrl子系统和gpio子系统)