linux驱动开发学习3 pinctl和gpio子系统试验

linux对于gpio有专门的驱动框架

借助pinctl子系统 做pin的复用·和电气属性

借助gpio子系统 来 使用 gpio

        大多数 SOC pin 都是支持复用的,比如 I.MX6ULL GPIO1_IO03 既可以作为普通的
GPIO 使用,也可以作为 I2C1 SDA 等等。此外我们还需要配置 pin 的电气特性,比如上 /
拉、速度、驱动能力等等。传统的配置 pin 的方式就是直接操作相应的寄存器,但是这种配置
方式比较繁琐、而且容易出问题 ( 比如 pin 功能冲突 ) pinctrl 子系统就是为了解决这个问题而引
入的, pinctrl 子系统主要工作内容如下:
①、获取设备树中 pin 信息。
②、根据获取到的 pin 信息来设置 pin 的复用功能
③、根据获取到的 pin 信息来设置 pin 的电气特性,比如上 / 下拉、速度、驱动能力等。
        对于我们使用者来讲,只需要在设备树里面设置好某个 pin 的相关属性即可,其他的初始
化工作均由 pinctrl 子系统来完成, pinctrl 子系统源码目录为 drivers/pinctrl

 

linux驱动的分离与分层

Linux 驱动讲究驱动分离与分层, pinctrl gpio 子系统就是驱动分离与分层思想下的产物,
驱动分离与分层其实就是按照面向对象编程的设计思想而设计的设备驱动框架,关于驱动的分
离与分层我们后面会讲。本来 pinctrl gpio 子系统应该放到驱动分离与分层章节后面讲解,但

是不管什么外设驱动, GPIO 驱动基本都是必须的,而 pinctrl gpio 子系统又是 GPIO 驱动必
须使用的,所以就将 pintrcl gpio 子系统这一章节提前了。
=========================================================================

pincrtl驱动——看compatible·(pincontroller)

设备树里面的设备节点是如何跟驱动匹配的呢?
所以我们只需要全局搜索,设备节点里面的compatible属性的值,看看在哪个.c文件里面有,那么此.c文件就是驱动文件

找到pinctrl-imx6ull.c文件,那么此文件就是6UL/6ULL的pinctrl驱动文件。

 这两张图好好看,讲的是驱动程序分析

 

=========================================================================

gpio子系统详解和驱动分析

定义了一个cd-gpios的属性

如何从设备树中获取要使用的GPIO信息-of函数

 

具体操作步骤

 

 ================================================GPIO点灯(最常用)

led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>

用的是GPIO1这一组的GPIO 03 引脚 低电平有效

 具体的宏定义去imx6ul-pinfunc.h中寻找,0x10B0是配置的电气属性

总结:先从设备树读节点,然后根据节点读gpio,然后申请gpio,设置gpio,最后使用gpio

1 申请IO失败时,很大可能是IO被其他程序复用了,你要去imx6ull-alientek-emmc.dts中搜索程序GPIO1_IO03 查看是否被复用(检查复用,也就是pinctl设置)

2 gpio使用(或者 &tsc的status属性设置为disabled)

 depmod

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

/***************************************************************

Copyright © ALIENTEK Co., Ltd. 1998-2029. All rights reserved.

文件名		: gpioled.c

作者	  	: 左忠凯

版本	   	: V1.0

描述	   	: 采用pinctrl和gpio子系统驱动LED灯。

其他	   	: 无

论坛 	   	: www.openedv.com

日志	   	: 初版V1.0 2019/7/13 左忠凯创建

***************************************************************/

#define GPIOLED_CNT			1		  	/* 设备号个数 */

#define GPIOLED_NAME		"gpioled"	/* 名字 */

#define LEDOFF 				0			/* 关灯 */

#define LEDON 				1			/* 开灯 */



/* gpioled设备结构体 */

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



/*

 * @description		: 打开设备

 * @param - inode 	: 传递给驱动的inode

 * @param - filp 	: 设备文件,file结构体有个叫做private_data的成员变量

 * 					  一般在open的时候将private_data指向设备结构体。

 * @return 			: 0 成功;其他 失败

 */

static int led_open(struct inode *inode, struct file *filp)

{

	filp->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 databuf[1];

	unsigned char ledstat;

	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灯  gpio_set_value*/

	} 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 *filp)

{

	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 = 0;

//

	/* 设置LED所使用的GPIO */

	/* 1、获取设备节点:gpioled */

	gpioled.nd = of_find_node_by_path("/gpioled");

	if(gpioled.nd == NULL) {

		printk("gpioled node not find!\r\n");

		return -EINVAL;

	} else {

		printk("gpioled node find!\r\n");

	}



	/* 2、 获取设备树中的gpio属性,得到LED所使用的LED编号 */

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

	if(gpioled.led_gpio < 0) {

		printk("can't get led-gpio");

		ret = -EINVAL;

		goto fail_findnode;

	}

	printk("led-gpio num = %d\r\n", gpioled.led_gpio);



	/* 3、第二步只是获取了LED编号,还需要 申请IO 避免编号重复  */

	ret = gpio_request(gpioled.led_gpio,"led-gpio");

	if(ret){

		printk("Failed to request the led gpio\r\n");

		ret = -EINVAL;

		goto fail_findnode;

	}



	/* 4、使用IO 设置GPIO1_IO03为输出,并且输出高电平,默认关闭LED灯 */

	ret = gpio_direction_output(gpioled.led_gpio, 1);

	if(ret < 0) {

		printk("can't set gpio!\r\n");

		goto fail_setoutput;

	}

//

	/* 注册字符设备驱动 */

	/* 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;

fail_setoutput:

	gpio_free(gpioled.led_gpio);

fail_findnode:

	return ret;

}



/*

 * @description	: 驱动出口函数

 * @param 		: 无

 * @return 		: 无

 */

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);



	/* 释放IO (有申请就一定有释放) */

	gpio_free(gpioled.led_gpio);

}



module_init(led_init);

module_exit(led_exit);

MODULE_LICENSE("GPL");

MODULE_AUTHOR("whz");

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值