文章目录
全系列传送门
Linux嵌入式驱动开发01——第一个驱动Hello World(附源码)
Linux嵌入式驱动开发06——第一个相对完整的驱动实践编写
Linux嵌入式驱动开发07——GPIO驱动过程记录(飞凌开发板)
Linux嵌入式驱动开发11——平台总线模型修改为设备树实例
Linux嵌入式驱动开发12——pinctl和gpio子系统实践操作
Linux嵌入式驱动开发13——ioctl接口(gpio控制使用)
Linux嵌入式驱动开发14——中断的原理以及按键中断的实现(tasklet中断下文)
1. 在/arch/arm/boot/dts/imx6q-pinfunc.h查找
/arch/arm/boot/dts
打开头文件
imx6q-pinfunc.h
查找EIM_A17
#define MX6QDL_PAD_EIM_A16__GPIO2_IO22 0x0f4 0x408 0x000 0x5 0x0
2. 在设备树配置文件中添加设备节点定义以及其引脚定义
然后,我们要在设备树中添加引脚定义,打开imx6qdl-sabresd.dtsi
imx6qdl-sabresd.dtsi
添加下面的两部分代码
gpio_user: gpios{
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_user>;
compatible = "gpio-user";
gpio0{
label = "D01";
gpios = <&gpio3 15 1>;
default-direction = "out";
};
gpio1{
label = "D02";
gpios = <&gpio2 21 1>;
default-direction = "out";
};
};
保证这些引脚没有被定义,把复用去掉
pinctrl_user: usergrp {
fsl,pins = <
MX6QDL_PAD_EIM_DA15__GPIO3_IO15 0x0b0b10
MX6QDL_PAD_EIM_A17__GPIO2_IO21 0x0b0b10
>;
};
3. 修改设备树文件添加配置
arch/arm/boot/dts/imx6q-c-sabresd.dts
添加代码
&gpio_user {
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_user>;
fsl,user;
status = "okay";
};
4. drivers/gpio目录下添加gpio驱动
drivers/gpio
目录下
添加gpio驱动gpio-user.c,名字需要与节点定义里的驱动名字保持相同,客户也可以自己编写驱动。
新建driver/gpio/gpio-user.c
Linux嵌入式飞凌开发板GPIO驱动模块modules_gpio_test
#include <linux/init.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/gpio.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
#include <linux/ioctl.h>
#include <linux/uaccess.h>
#include <linux/platform_device.h>
#include <linux/fs.h>
#define GPIO_U_IOCTL_BASE 'x'
#define GPIOC_OPS _IOWR(GPIO_U_IOCTL_BASE,0,int)
#define MAX_GPIO_NR 32
struct gpio_user_data{
const char *label;
bool input;
unsigned gpio;
unsigned dft;
};
static struct gpio_misc{
struct miscdevice misc;
struct gpio_user_data *data;
int gpio_count;
} *gpio_misc;
static int gpio_user_open(struct inode *inodp, struct file *filp)
{
if(!gpio_misc)
return -ENODEV;
return 0;
}
static int gpio_user_release(struct inode *inodp, struct file *filp)
{
if(!gpio_misc)
return -ENODEV;
return 0;
}
static long gpio_user_ioctl(struct file *filp, unsigned int cmd,unsigned long arg)
{
int no, offset;
unsigned long val;
unsigned long __user *p = (void __user *)arg;
struct gpio_user_data *data;
unsigned long get_value;
if(!gpio_misc)
return -ENODEV;
data = gpio_misc->data;
if(_IOC_TYPE(cmd) != GPIO_U_IOCTL_BASE)
return -EINVAL;
switch(_IOC_NR(cmd)){
case 0:
if(get_user(val,p))
return -EFAULT;
if(data[val].input){
val = gpio_get_value(data[val].gpio);
put_user(val,p);
} else {
gpio_set_value(data[val].gpio,val >> 31);
}
break;
default:
return -ENOTTY;
}
return 0;
}
static const struct file_operations gpio_user_fops = {
.owner = THIS_MODULE,
.open = gpio_user_open,
.release = gpio_user_release,
.unlocked_ioctl = gpio_user_ioctl,
};
static void gpio_user_init_default(void)
{
int i,ret;
struct gpio_user_data *data;
data = gpio_misc->data;
for(i = 0;i < gpio_misc->gpio_count;i++){
if(!gpio_is_valid(data[i].gpio)){
continue;
}
ret = gpio_request(data[i].gpio,data[i].label);
if(ret < 0){
continue;
}
if(data[i].input)
{
gpio_direction_input(data[i].gpio);
}
else
{
gpio_direction_output(data[i].gpio,data[i].dft);
}
}
}
static void gpio_user_free_default(void)
{
int i;
struct gpio_user_data *data;
data = gpio_misc->data;
for(i = 0;i < gpio_misc->gpio_count;i++){
if(!gpio_is_valid(data[i].gpio)){
continue;
}
gpio_free(data[i].gpio);
}
}
static int gpio_user_probe(struct platform_device *pdev)
{
int index;
struct device_node *node = pdev->dev.of_node, *child;
gpio_misc = devm_kzalloc(&pdev->dev,sizeof(*gpio_misc),GFP_KERNEL);
if(!gpio_misc){
return -ENOMEM;
}
gpio_misc->gpio_count = of_get_available_child_count(node);
if(!gpio_misc->gpio_count){
return -ENODEV;
}
if(gpio_misc->gpio_count > MAX_GPIO_NR){
gpio_misc->gpio_count = MAX_GPIO_NR;
}
gpio_misc->data = devm_kzalloc(&pdev->dev,sizeof(struct gpio_user_data) * gpio_misc->gpio_count,GFP_KERNEL);
if(!gpio_misc->data){
return -ENOMEM;
}
index = 0;
for_each_available_child_of_node(node,child){
const char *input;
struct gpio_user_data *data = &gpio_misc->data[index++];
data->label = of_get_property(child,"label",NULL) ? : child->name;
input = of_get_property(child,"default-direction",NULL) ? : "in";
if(strcmp(input,"in") == 0)
data->input = true;
data->gpio = of_get_gpio_flags(child,0,&data->dft);
}
gpio_user_init_default();
gpio_misc->misc.name = "gpio";
gpio_misc->misc.minor = MISC_DYNAMIC_MINOR;
gpio_misc->misc.fops = &gpio_user_fops;
return misc_register(&gpio_misc->misc);
}
static int gpio_user_remove(struct platform_device *pdev)
{
gpio_user_free_default();
misc_deregister(&gpio_misc->misc);
return 0;
}
static const struct of_device_id of_gpio_user_id_table[] = {
{ .compatible = "gpio-user",},
{},
};
MODULE_DEVICE_TABLE(of,of_gpio_user_id_table);
static struct platform_driver gpio_user_driver = {
.probe = gpio_user_probe,
.remove = gpio_user_remove,
.driver = {
.owner = THIS_MODULE,
.name = "gpio-user",
.of_match_table = of_gpio_user_id_table,
},
};
module_platform_driver(gpio_user_driver);
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:gpio-user");
同时修改Kconfig和Makefile文件。
修改dirver/gpio目录下的Kconfig和Makefile文件:
在driver/gpio/Makefile
添加:
obj-$(CONFIG_GPIO_USER_INTF) += gpio-user.o
在driver/gpio/Kconfig添加:
config GPIO_USER_INTF
tristate "gpio user interface"
---help---
this driver for all gpio control
5. 通过make menuconfig来进行配置
在目录linux-4.1.15
下,首先进行的操作
加载环境变量
. /opt/fsl-imx-x11/4.1.15-2.1.0/environment-setup-cortexa9hf-neon-poky-linux-gnueabi
然后,
清除之前的编译
make distclean
复制arch/arm/config/imx_v7_defconfig ->.config
arch/arm/config/imx_v7_defconfig
到目录linux-4.1.15
下,修改名字为
.config
make menuconfig
然后运行
make menuconfig
修改完成后,保存,然后打开 .config
文件,可以看到我们的gpio已经加载了过来
然后
复制 .config 到 arch/arm/config/imx_v7_defconfig
cp .config arch/arm/config/imx_v7_defconfig
最后
执行编译
make distclean
make imx_v7_defconfig
make zImage -j16
make dtbs
make modules -j16
编译结束后
查看验证
验证
复制我们的测试程序到开发板
我们的测试程序,测试的是DO1,也就是EIM_AD15,out是输出,0是选择了第一个DO1对应的EIM_AD15,1的话就是选择DO2对应的EIM_A17,最后的0或者1就是输出的高低电平