1. 简介
驱动程序中使用 GPIO 之前需要向 gpio 子系统申请,申请成功之后才可以使用,
例如设置 GPIO 的输入、输出方向,设置 GPIO 输出高或低电平、读取 GPIO 输入电平等等。
2.设备树描述
2.1 gpio设备定义,如下:
“ &gpio0”表示 led 引脚所使用的 IO 属于 gpio0
“ 7”表示 gpio0 的第 7 号 IO,
“ GPIO_ACTIVE_HIGH” 表示高电平有效
2.2 GPIO控制器在设备树中定义,如下:
“ gpio-controller”表示 gpio0 节点是个 GPIO 控制器,表示这个节点对应的驱动程序是 gpio 驱动。
#gpio-cells”属性和“ #address-cells”类似, 在 gpio0 节点中#gpio-cells的值等于 2,表示一共有两个 cell。
大家可以这样理解,使用 gpio0 的时候,需要传递 2 个参数过去:
第一个参数为 GPIO 编号,比如“ &gpio0 7”就表示 GPIO0_IO07。
第二个参数表示 GPIO极性,如果为 0(GPIO_ACTIVE_HIGH)的话表示高电平有效,如果为 1(GPIO_ACTIVE_LOW)的话表示低电平有效。
3. gpio驱动介绍
3.1 API函数
1 申请/释放
int gpio_request(unsigned gpio, const char *label)
void gpio_free(unsigned gpio)
gpio:要申请的 gpio 标号,使用 of_get_named_gpio 函数从设备树获取指定 GPIO 属性信
息,此函数会返回这个 GPIO 的标号。
label:给 gpio 设置个名字。
返回值: 0,申请成功;其他值,申请失败。
2.输入/输出
int gpio_direction_input(unsigned gpio)
int gpio_direction_output(unsigned gpio, int value)
3.设置/获取GPIO的值
int gpio_get_value(unsigned gpio)
void gpio_set_value(unsigned gpio, int value)
注意:gpio设置为输出模式,不能通过gpio_get_value获取gpio状态!!!
同理,gpio设置为输入模式,通过gpio_set_value设置没有意义。
3.2 OF相关函数
1.获取设备树某个属性里面定义了几个 GPIO 信息
int of_gpio_named_count(struct device_node *np, const char *propname)
int of_gpio_count(struct device_node *np)
np:设备节点。
propname:要统计的 GPIO 属性。返回值: 正值,统计到的 GPIO 数量;负值,失败。
of_gpio_named_count 统计任意属性的 GPIO 信息of_gpio_count 统计的是“ gpios”这个属性的 GPIO 数量
举例:
gpios = < 0
&gpio1 1 2
0
&gpio2 3 4>;“ gpios”节点一共定义了 4 个 GPIO,但是有 2 个是空的,没有实际的含义。
通过 of_gpio_named_count 函数统计出来的 GPIO 数量就是 4 个。
2.获取 GPIO 编号
int of_get_named_gpio(struct device_node *np, const char *propname, int index)
np:设备节点
propname:包含要获取 GPIO 信息的属性名。
index: GPIO 索引,因为一个属性里面可能包含多个 GPIO,此参数指定要获取哪个 GPIO的编号,如果只有一个 GPIO 信息的话此参数为 0。
返回值: 正值,获取到的 GPIO 编号;负值,失败。
说明:此函数会将设备树中类似<&gpio0 7 GPIO_ACTIVE_LOW>的属性信息转换为对应的 GPIO 编号。Linux 内核中关于 GPIO 的 API 函数都要使用 GPIO 编号。
4.pinctrl子系统
根据imx6ull芯片讲解。
4.1作用
①、获取设备树中 pin 信息。
②、根据获取到的 pin 信息来设置 pin 的复用功能
③、根据获取到的 pin 信息来设置 pin 的电气特性,比如上/下拉、速度、驱动能力等。
4.2 设备树中描述
说明:
compatible 属性值为“fsl,imx6ul-iomuxc”,在 Linux 内核源码中全局搜索字符串“fsl,imx6ul-iomuxc”就会找到 I.MX6ULL 这颗 SOC 的 pinctrl 驱动文件。
UART1_RTS_B 的配置信息如下:
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x170590x17059 就是 conf_reg 寄存器值
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 的宏定义内容如下:
#define MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0
0x0090 0x031C 0x0000 0x5 0x0,表示为:
<mux_reg conf_reg input_reg mux_mode input_val>0x0090: mux_reg 寄存器偏移地址
0x031C: conf_reg 寄存器偏移地址
0x0000: input_reg 寄存器偏移地址
0x5 : mux_reg 寄 存 器 值
0x0: input_reg 寄存器值,在这里无效。
5.GPIO驱动-led实验
5.1 修改设备树
led {
compatible = "m0mo,led";
status = "okay";
default-state = "on";
led-gpio = <&gpio0 7 GPIO_ACTIVE_HIGH>;
};
修改后,编译设备树:
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- system-top.dtb
5.2 gpio驱动demo
/*gpio操作:1.获取gpio; 2.申请使用; 3.设置输入输出 4.设置初始值*/
gpioled.gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
printk("of_get_named_gpio\r\n");
ret = gpio_request(gpioled.gpio, "led-gpio");
ret = gpio_direction_output(gpioled.gpio, 1);
gpio_set_value(gpioled.gpio, 0);
5.3 led-gpio驱动demo
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/uaccess.h>
struct gpioled_dev {
struct cdev cdev;
struct class *class;
struct device *device;
dev_t devid;
int major;
int minor;
struct device_node *nd;
int gpio;
};
static struct gpioled_dev gpioled;
static ssize_t gpio_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offset)
{
char kernelbuf[1] = {0};
copy_from_user(kernelbuf,buf,1);
printk("kernelbuf[0];%d\r\n",kernelbuf[0]);
gpio_set_value(gpioled.gpio,kernelbuf[0]);
return 0;
}
static struct file_operations m_fops = {
.owner = THIS_MODULE,
.write = gpio_write,
};
//驱动入口
static int __init xxx_init(void)
{
const char *str;
//找到led节点
gpioled.nd = of_find_node_by_path("/led");
//读取属性
int ret = of_property_read_string(gpioled.nd,"status",&str);
if(ret == 0)
{
if(strcmp("okay",str) != 0)
{
return -EINVAL;
}
}
/*gpio操作:1.获取gpio; 2.申请使用; 3.设置输入输出 4.设置初始值*/
gpioled.gpio = of_get_named_gpio(gpioled.nd, "led-gpio", 0);
printk("of_get_named_gpio\r\n");
ret = gpio_request(gpioled.gpio, "led-gpio");
ret = gpio_direction_output(gpioled.gpio, 1);
gpio_set_value(gpioled.gpio, 0);
//1.创建设备号
if(gpioled.major){
gpioled.devid = MKDEV(gpioled.major,0);
register_chrdev_region(gpioled.devid,1,"test");
}else{
alloc_chrdev_region(&gpioled.devid,0,1,"test");
gpioled.major = MAJOR(gpioled.devid);
gpioled.minor = MINOR(gpioled.devid);
}
printk("newcheled major=%d,minor=%d\r\n",gpioled.major, gpioled.minor);
//2.初始化cdev
gpioled.cdev.owner = THIS_MODULE;
cdev_init(&gpioled.cdev,&m_fops);
//3.添加cdev
cdev_add(&gpioled.cdev,gpioled.devid,1);
//4.创建类
gpioled.class = class_create(THIS_MODULE,"xxx");
//5.创建设备
gpioled.device = device_create(gpioled.class,NULL,gpioled.devid,NULL,"xxx");
return 0;
}
//驱动退出
static void __exit xxx_exit(void)
{
gpio_free(gpioled.gpio);
//删除设备
device_destroy(gpioled.class,gpioled.devid);
//删除类
class_destroy(gpioled.class);
//删除cdev
cdev_del(&gpioled.cdev);
//注销
unregister_chrdev_region(gpioled.devid,1);
}
module_init(xxx_init);
module_exit(xxx_exit);
MODULE_AUTHOR("m0mo");
MODULE_DESCRIPTION("New Char Device Driver");
MODULE_LICENSE("GPL");