百问网IMXU6LL--pinctrl子系统和gpio子系统编写led驱动

pinctrl子系统

pinctrl 子系统主要工作内容如下:
①、获取设备树中 pin 信息。
②、根据获取到的 pin 信息来设置 pin 的复用功能
③、根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。

对于我们使用者来讲,只需要在设备树里面设置好某个 pin 的相关属性即可,其他的初始
化工作均由 pinctrl 子系统来完成,pinctrl 子系统源码目录为 drivers/pinctrl。

首先在100ask_imx6ull-14x14.dts设备树文件中要添加PIN配置信息,毕竟 pinctrl 子系统要根据你提供的信息来配置 PIN 功能,一般会在设备树里面创建一个iomuxc 节点来描述 PIN 的配置信息。
iomuxc 节点就是 IMX6ULL 的 IOMUXC 外设对应的节点
在iomuxc下的imx6ull-evk中插入pinctrl的配置

pinctrl_led: ledgrp {
            fsl,pins = <
                MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03        0x000110A0
            >;
        }

主要格式就是以上的代码fsl,pins =<>;的两个参数是 PIN 的配置信息
一个参数是宏定义保存的寄存器地址
图中宏定义对应的5个参数为

0x0014:mux_reg 寄存器偏移地址
0x0058:conf_reg 寄存器偏移地址
0x0000:input_reg 寄存器偏移地址
5:mux_reg 寄存器值
0:input_reg 寄存器值,在这里无效。
百问网IMXU6LL--pinctrl子系统和gpio子系统编写led驱动_第1张图片

回到配置的另一个参数0x000110A0,就是conf_reg寄存器的值,用来设置IO 的上/下拉、驱动能力和速度等,IOMUXC_SNVS_SW_PAD_CTL_PAD_SNVS_TAMPER3的PAD寄存器,其地址为0X2290058H。这也是个32位寄存器,但是只用到了其中的低17位

百问网IMXU6LL--pinctrl子系统和gpio子系统编写led驱动_第2张图片

gpio 子系统

如果 pinctrl 子系统将一个 PIN 复用为 GPIO 的话,那么接下来就要用到 gpio 子系统了。gpio 子系统顾名思义,就是用于初始化 GPIO 并且提供相应的API 函数,比如设置 GPIO为输入输出,读取 GPIO 的值等。gpio 子系统的主要目的就是方便驱动开发者使用 gpio,驱动开发者在设备树中添加 gpio 相关信息,然后就可以在驱动程序中使用 gpio 子系统提供的 API函数来操作 GPIO,Linux 内核向驱动开发者屏蔽掉了 GPIO 的设置过程,极大的方便了驱动开发者使用 GPIO。

在根节点“/”下创建 LED 灯节点,节点名为“gpioled”,节点内容如下:

gpioled {
        #address-cells = <1>;
        #size-cells = <1>;
        compatible = "louis-gpioled";
        pinctrl-names = "default";
        pinctrl-0 = <&pinctrl_led>;
        led-gpio = <&gpio5 3 GPIO_ACTIVE_LOW>;
    };

pinctrl-names 属性,此属性描述 pinctrl 名字为“default”。
pinctrl-0 节点,此节点引用 45.1.3 中创建的 pinctrl_led节点,表示 led 设备的所使用的 PIN 信息保存在 pinctrl_led 节点中
led-gpio表示最后需要在 test 节点中添加 GPIO 属性信息,表明 test 所使用的 GPIO 是哪个引脚和电平有效性

查重

为什么要进行PIN的查重呢?

比如 A 这个引脚在官方开发板接的是 I2C 的 SDA,而我们所使用的硬件可能将 A 这个引脚接到了其他的外设,比如 LED 灯上,接不同的外设,A 这个引脚的配置就不同。一个引脚一次只能实现一个功能,如果 A 引脚在设备树中配置为了 I2C 的 SDA 信号,那么 A 引脚就不能再配置为 GPIO,否则的话驱动程序在申请 GPIO 的时候就会失败。

检查 PIN 有没有被其他外设使用包括两个方面:
①、检查 pinctrl 设置。
②、如果这个 PIN 配置为 GPIO 的话,检查这个 GPIO 有没有被别的外设使用。

在设备树里搜索MX6ULL_PAD_SNVS_TAMPER3__GPIO5_IO03发现在iomuxc_snvs中有一个pinctrl_leds节点这时候要把<>中的内容用/**/注释掉

驱动编写

驱动的编写和上一篇文章的编写相比略有改动,改动如下
1.取消了iomap映射寄存器虚拟地址
2.取消了led_switch函数直接对寄存器进行控制读写
3.取消了初始化中从设备树获取compatible、status、reg属性内容
4.新增gpioled_dev结构体中GPIO编号属性

struct gpioled_dev{
	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设备 */

5.新增初始化中of_get_named_gpio获取GPIO编号

gpioled.led_gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);

6.新增设备操作函数write中通过读取 filp 的 private_data 成员变量来得到设备结构体变量,也就是 gpioled。这种将设备结构体设置为 filp 私有数据的方法在 Linux 内核驱动里面非常常见

struct gpioled_dev *dev = filp->private_data;

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

	ledstat = databuf[0];		/* 获取状态值 */

	if(ledstat == LEDON) {	
		gpio_set_value(dev->led_gpio, 0);	/* 打开LED灯 */
	} else if(ledstat == LEDOFF) {
		gpio_set_value(dev->led_gpio, 1);	/* 关闭LED灯 */
	}

7.新增gpio_direction_output设置gpio的输出电平

ret = gpio_direction_output(gpioled.led_gpio, 1);

你可能感兴趣的:(Linux,arm,单片机,51单片机,驱动开发)