调用pinctrl子系统完成gpio复用功能和电气属性的设置。
1、设置PIN的复用和电气属性
2、配置GPIO
使用GPIO子系统来使用GPIO子系统。

打开imx6ull.dts查看复用设备树。
和参考手册对应上了

根据设备的类型,创造对应的子节点,然后设备所用的pin放到子节点。
如何添加一个pin 的信息。
imx6ul-evk {
pinctrl_hog_1: hoggrp-1 {
fsl,pins = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */
MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT 0x17059 /* SD1 VSELECT */
MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID 0x13058 /* USB_OTG1_ID */
>;
};
pinctrl_hog_1: hoggrp-1 {
fsl,pins = <
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x17059 /* SD1 CD */
>;
};
我们在imx6ul-pinfunc.h中找到:
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0
<mux_reg conf_reg input_reg mux_mode input_val>
0x0090 0x031C 0x0000 0x5 0x0
IOMUXC父节点首地址0x020e0000,因此UART1_RTS_B这个PIN的mux寄存器地址 就是:0x020e0000+0x0090=0x020e 0090。
conf_reg:0x020e0000+0x031C=0x020e 031C,这个寄存器就是UART1_RTS_B的电气属性配置寄存器。
input_reg,便宜为0,表示UART1_RTS_B这个PIN没有input功能。
mux_mode:5表示复用为GPIO1_IO19,将其写入0x020e 0090
input_val:就是写入input_reg寄存器的值。
0x17059:为PIN的电气属性配置寄存器值。
如何找到IMX6ULL对应的pinctrl子系统驱动。
设备树的节点和驱动通过compatible属性匹配起来。此属性是描述一段字符串列表。驱动文件里面有一个描述驱动兼容性的东西。当设备树节点的compatible属性和驱动里面的兼容性匹配,就表示设备和驱动匹配了。
找到pinctrl-mx6ul.c文件就是找到了。
当驱动和设备匹配好了之后,执行probe函数。也就是
imx6ul_pinctrl_probe
引脚复用的时候需要用到pinctrl子系统。
引脚作为gpio使用的时候,要用到gpio子系统。

1、首先,获取到GPIO所处的设备节点,比如of_find_node_by_path。
2、获取GPIO编号, of_get_named_gpio函数,返回值就是GPIO编号。
3、请求此编号的GPIO,gpio_request函数
4、设置GPIO,输入或输出,gpio_direction_input或gpio_direction_output。
5、如果是输入,那么通过gpio_get_value函数读取GPIO值,如果是输出,通过gpio_set_value设置GPIO值。




修改设备树

找到需要使用的引脚:

GPIO复用操作:

重新加载设备树:
新的节点已经生成了。

P30 左忠凯最后一次手撕代码。
加载驱动之后申请失败了
注意:申请IO失败很大部分都是这个IO被其他外设占用了,检查设备树。查找有哪些使用同一个IO的。
1、检查复用设置。

2、查看设备树。

加载驱动成功:

小结:
1、添加pinctrl信息
2、检查设备树要使用IO有没有被占用
3、添加设备节点信息。在设备上创建一个属性,此属性藐视所使用的GPIO
4、编写驱动、获取GPIO的编号,并申请IO,申请成功可以使用此IO。

/**
*my first driver
*
*/
#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_irq.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#define LED_NAME "gpioled"
#define LED_COUNT 1
#define LEDOFF 0 /* 关灯 */
#define LEDON 1 /* 开灯 */
struct gpioled_dev {
struct cdev cdev;
struct class *class;/*类:为了自动创建节点*/
struct device *device;/*设备:为了自动创建节点*/
dev_t devid; //设备号
int major; //主设备号
int minor; //次设备号
struct device_node *nd; //设备节点
int led_gpio;
};
struct gpioled_dev gpioled; //led设备
static int gpioled_open(struct inode *inode,struct file *filp)
{
filp->private_data = &gpioled;
return 0;
}
static ssize_t gpioled_write(struct file *filp, const char __user *buf,size_t cnt, loff_t *offt)
{
struct gpioled_dev *dev = (struct gpioled_dev *)filp->private_data;
int retvalue = 0;
unsigned char databuf[1];
retvalue = copy_from_user(databuf,buf,cnt);
if (retvalue < 0)
{
printk("kernel write failed!\r\n");
return -EFAULT;
}
if (databuf[0] == LEDON)
{
gpio_set_value(dev->led_gpio,0);
}
else if(databuf[0] == LEDOFF){
gpio_set_value(dev->led_gpio,1);
}
return 0;
}
static int gpioled_release(struct inode *inode,struct file *filp)
{
//struct dtsled_dev *dev = (struct dtsled_dev *)filp->private_data;
return 0;
}
static struct file_operations led_fops = {
.owner = THIS_MODULE,
.write = gpioled_write,
.open = gpioled_open,
.release = gpioled_release,
};
static int __init led_init(void)
{
int ret = 0;
/*注册字符设备*/
gpioled.major = 0;
if (gpioled.major){
gpioled.devid = MKDEV(gpioled.major,0);//设备号
ret = register_chrdev_region(gpioled.devid,LED_COUNT,LED_NAME);
} else{
ret = alloc_chrdev_region(&gpioled.devid,0,LED_COUNT,LED_NAME);
gpioled.major = MAJOR(gpioled.devid);
gpioled.minor = MINOR(gpioled.devid);
}printk("led major = %d led minor = %d \r\n",gpioled.major,gpioled.minor);
if (ret < 0){
goto faile_devid;
}
/*2、添加字符设备*/
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev,&led_fops);
ret = cdev_add(&gpioled.cdev,gpioled.devid,LED_COUNT);
if(ret<0){
goto fail_cdev;
}
/*3、创建设备类*/
gpioled.class = class_create(THIS_MODULE,LED_NAME);
if(IS_ERR(gpioled.class)){
ret = PTR_ERR(gpioled.class);
printk("can't class_create \r\n");
goto fail_class;
}
/*创建设备节点*/
gpioled.device = device_create(gpioled.class,NULL,gpioled.devid,NULL,LED_NAME);
if(IS_ERR(gpioled.device)){
ret = PTR_ERR(gpioled.device);
printk("can't device_create \r\n");
goto fail_device;
}
/*4、获取设备属性内容*/
gpioled.nd = of_find_node_by_path("/gpioled");
if (gpioled.nd == NULL )
{
ret = -EINVAL;
goto fail_findnd;
}
/*获取LED所对应的GPIO*/
gpioled.led_gpio = of_get_named_gpio(gpioled.nd,"led-goios",0);
if (gpioled.led_gpio < 0)
{
printk("can't find gpio \r\n");
goto fail_findnode;
}
printk("led gpio number = %d\r\n",gpioled.led_gpio);
/*申请的GPIO*/
ret = gpio_request(gpioled.led_gpio,"led-gpio");
if (ret)
{
printk("gpio_request errno \r\n");
ret = -EINVAL;
}
/*使用IO*/
ret = gpio_direction_output(gpioled.led_gpio,1);
if (ret)
{
goto fail_setoutput;
}
gpio_set_value(gpioled.led_gpio,0);
return 0;
fail_setoutput:
gpio_free(gpioled.led_gpio);
fail_findnode:
// fail_findu32arr:
// fail_findrs:
fail_findnd:
device_destroy(gpioled.class,gpioled.devid);
fail_device:
class_destroy(gpioled.class);
fail_class:
cdev_del(&gpioled.cdev);
fail_cdev:
unregister_chrdev_region(gpioled.devid,LED_COUNT);
faile_devid:
return ret;
}
static void __exit led_exit(void)
{
/* 关灯 */
gpio_set_value(gpioled.led_gpio,1);
/*删除设备*/
cdev_del(&gpioled.cdev);
/*释放设备号*/
unregister_chrdev_region(gpioled.devid,LED_COUNT);
/*摧毁设备*/
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("qhy");
1、添加蜂鸣器的节点

注意:6UL和6ULL的SNVS组IO的地址是不同的!
注意区别:


例程驱动源码:
#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 BEEPNAME "beep"
#define BEEPCOUNT 1
#define BEEPON 1
#define BEEPOFF 0
struct beep_dev
{
dev_t devid; /*设备ID*/
struct cdev cdev; /*字符设备结构体*/
struct device *device; /*设备*/
struct class *class; /* 类 */
struct device_node *nd; /*设备节点*/
int beep_num; /*beep所使用的GPIO编号*/
int major; /*主设备号*/
int minor; /*次设备号*/
};
struct beep_dev beep_gpio;
/*
* @description : 打开设备
* @param - inode : 传递给驱动的inode
* @param - filp : 设备文件,file结构体有个叫做private_data的成员变量
* 一般在open的时候将private_data指向设备结构体。
* @return : 0 成功;其他 失败
*/
static int beep_open(struct inode *inode, struct file *filp)
{
filp->private_data = &beep_gpio; /* 设置私有数据 */
return 0;
}
/*
* @description : 向设备写数据
* @param - filp : 设备文件,表示打开的文件描述符
* @param - buf : 要写给设备写入的数据
* @param - cnt : 要写入的数据长度
* @param - offt : 相对于文件首地址的偏移
* @return : 写入的字节数,如果为负值,表示写入失败
*/
static ssize_t beep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt)
{
unsigned char databuf[1];
unsigned char beepstat;
int ret;
ret = copy_from_user(databuf,buf,cnt);
if(ret < 0) {
printk("kernel write failed!\r\n");
return -EFAULT;
}
beepstat = databuf[0];
if (beepstat == BEEPON)
{
gpio_direction_output(beep_gpio.beep_num,0); /*打开蜂鸣器*/
}
else if (beepstat == BEEPOFF)
{
gpio_direction_output(beep_gpio.beep_num,1); /*关闭蜂鸣器*/
}
return 0;
}
/*
* @description : 关闭/释放设备
* @param - filp : 要关闭的设备文件(文件描述符)
* @return : 0 成功;其他 失败
*/
static int beep_release(struct inode *inode, struct file *filp)
{
return 0;
}
/*设备操作函数*/
static struct file_operations beep_fops =
{
.owner = THIS_MODULE,
.open = beep_open,
.release = beep_release,
.write = beep_write,
};
static int __init beep_init(void){
int ret = 0;
/*设置GPIO所使用的GPIO*/
/*1、获取设备的节点:beep*/
beep_gpio.nd = of_find_node_by_path("/beep");
if (beep_gpio.nd == NULL)
{
printk("beep node not find!\r\n");
return -EINVAL;
}
/*2、获取设备树的GPIO的属性,得到所使用的Beep的编号*/
beep_gpio.beep_num = of_get_named_gpio(beep_gpio.nd,"beep-gpio",0);
if (beep_gpio.beep_num < 0)
{
printk("can't get beep-gpio");
return -EINVAL;
}
ret = gpio_requset(beep_gpio.num,"beep-gpio");
if(ret)
{
printk("can't gpio requset!\r\n");
}
/*3、设置GPIO5_IO01为输出,并且输出为高电平,默认关闭GPIO*/
ret = gpio_direction_output(beep_gpio.beep_num,1);
if (ret < 0)
{
printk("can't set gpio!\r\n");
}
/*注册字符设备驱动*/
/*1、创建设备号*/
if (beep_gpio.major)
{
beep_gpio.devid = MKDEV(beep_gpio.major,0);
register_chrdev_region(beep_gpio.devid,BEEPCOUNT,BEEPNAME);
}else{
alloc_chrdev_region(&beep_gpio.devid,0,BEEPCOUNT,BEEPNAME);
beep_gpio.major = MAJOR(beep_gpio.devid);
beep_gpio.minor = MINOR(beep_gpio.devid);
}
/*2、初始化字符设备*/
beep_gpio.cdev.owner = THIS_MODULE;
cdev_init(&beep_gpio.cdev,&beep_fops);
/*3、添加字符设备*/
cdev_add(&beep_gpio.cdev,beep_gpio.devid,BEEPCOUNT);
/*4、创建类:为了自动生成设备节点*/
beep_gpio.class = class_create(THIS_MODULE, BEEPNAME);
if(IS_ERR(beep_gpio.class)){
return PTR_ERR(beep_gpio.class);
}
/*5、创建设备*/
beep_gpio.device = device_create(beep_gpio.class,NULL,beep_gpio.devid,NULL,BEEPNAME);
if(IS_ERR(beep_gpio.device)){
return PTR_ERR(beep_gpio.device);
}
return 0;
}
/*
* @description : 驱动出口函数
* @param : 无
* @return : 无
*/
static void __exit beep_exit(void){
/*关闭蜂鸣器*/
gpio_direction_output(beep_gpio.beep_num,0);
/*销毁设备*/
device_destroy(beep_gpio.class,beep_gpio.devid);
/*销毁类*/
class_destroy(beep_gpio.class);
/*注销设备号*/
unregister_chrdev_region(beep_gpio.devid,BEEPCOUNT);
/*删除cdev*/
cdev_del(&beep_gpio.cdev);
gpio_free(beep_gpio.beep_num);
}
module_init(beep_init);
module_exit(beep_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("qhy");