十三、GPIO子系统
由于之后的触摸屏驱动分析中使用到了GPIO子系统和i2c子系统,因此在分析触摸屏驱动之前我准备把这两个子系统进行简单分析。
之前我们使用GPIO引脚的方式并不是推荐的方式,当我们更改某一bit时,很有可能导致另外的bit值发生更改。而GPIO子系统进行了封装,确保每次只对一个GPIO引脚操作,而不会影响到别的GPIO引脚。
下面这段代码是我从驱动程序中摘出来的,它首先获取GPIO引脚,之后设置为输出模式。
ret = gpio_request(EXYNOS4_GPL0(), "TP1_EN");
if (ret) {
printk(KERN_ERR "failed to request TP1_EN for I2C control\n");
//return err;
} gpio_direction_output(EXYNOS4_GPL0(), ); s3c_gpio_cfgpin(EXYNOS4_GPL0(), S3C_GPIO_OUTPUT);
gpio_free(EXYNOS4_GPL0()); mdelay();
一、gpio_direction_output()分析
gpio_direction_output()方便我们操作GPIO引脚,我们来看一下gpio_direction_output()函数会做些什么。
int gpio_direction_output(unsigned gpio, int value)
{
unsigned long flags;
struct gpio_chip *chip; /* 不同单板对应不同chip,由芯片厂实现 */
struct gpio_desc *desc = &gpio_desc[gpio];
int status = -EINVAL; spin_lock_irqsave(&gpio_lock, flags);
/* 判断gpio是否可用 */
if (!gpio_is_valid(gpio))
goto fail;
chip = desc->chip;
if (!chip || !chip->set || !chip->direction_output)
goto fail;
gpio -= chip->base;
if (gpio >= chip->ngpio)
goto fail;
status = gpio_ensure_requested(desc, gpio);
if (status < )
goto fail; /* now we know the gpio is valid and chip won't vanish */ spin_unlock_irqrestore(&gpio_lock, flags); might_sleep_if(chip->can_sleep); if (status) {
status = chip->request(chip, gpio);
if (status < ) {
pr_debug("GPIO-%d: chip request fail, %d\n",
chip->base + gpio, status);
/* and it's not available to anyone else ...
* gpio_request() is the fully clean solution.
*/
goto lose;
}
}
/* 调用chip的direction_output()函数 */
status = chip->direction_output(chip, gpio, value);
if (status == )
set_bit(FLAG_IS_OUT, &desc->flags);
trace_gpio_value(chip->base + gpio, , value);
trace_gpio_direction(chip->base + gpio, , status);
lose:
return status;
fail:
spin_unlock_irqrestore(&gpio_lock, flags);
if (status)
pr_debug("%s: gpio-%d status %d\n",
__func__, gpio, status);
return status;
}
EXPORT_SYMBOL_GPL(gpio_direction_output);
通过代码第4行struct gpio_chip,我们可以推断出此结构体是内核提供给各个板商的,用于其实现不同GPIO引脚操作。
在drivers/gpio/目录下,有跟平台相关的文件,这些文件就是由厂商提供的GPIO引脚操作函数。在此我以gpio-omap.c为例进行讨论
我们先来查看probe()函数:
static int __devinit omap_gpio_probe(struct platform_device *pdev)
{
static int gpio_init_done;
struct omap_gpio_platform_data *pdata;
struct resource *res;
int id;
struct gpio_bank *bank;
...
pdata = pdev->dev.platform_data;
...
ret = init_gpio_info(pdev);
...
id = pdev->id;
bank = &gpio_bank[id]; res = platform_get_resource(pdev, IORESOURCE_IRQ, );
...
bank->irq = res->start;
bank->virtual_irq_start = pdata->virtual_irq_start;
bank->method = pdata->bank_type;
bank->dev = &pdev->dev;
bank->dbck_flag = pdata->dbck_flag;
bank->stride = pdata->bank_stride;
bank_width = pdata->bank_width; spin_lock_init(&bank->lock); res = platform_get_resource(pdev, IORESOURCE_MEM, ); bank->base = ioremap(res->start, resource_size(res));
...
omap_gpio_mod_init(bank, id);
omap_gpio_chip_init(bank);
omap_gpio_show_rev(bank); if (!gpio_init_done)
gpio_init_done = ; return ;
}
此函数首先填充了struct gpio_bank,之后使用ioremap()映射,最后调用omap_gpio_chip_init()初始化并注册chip。
static void __devinit omap_gpio_chip_init(struct gpio_bank *bank)
{
int j;
static int gpio;
/* 填充chip成员函数 */
bank->mod_usage = ;
bank->chip.request = omap_gpio_request;
bank->chip.free = omap_gpio_free;
bank->chip.direction_input = gpio_input;
bank->chip.get = gpio_get;
bank->chip.direction_output = gpio_output;
bank->chip.set_debounce = gpio_debounce;
bank->chip.set = gpio_set;
bank->chip.to_irq = gpio_2irq;
if (bank_is_mpuio(bank)) {
bank->chip.label = "mpuio";
#ifdef CONFIG_ARCH_OMAP16XX
bank->chip.dev = &omap_mpuio_device.dev;
#endif
bank->chip.base = OMAP_MPUIO();
} else {
bank->chip.label = "gpio";
bank->chip.base = gpio;
gpio += bank_width;
}
bank->chip.ngpio = bank_width; gpiochip_add(&bank->chip);
...
irq_set_chained_handler(bank->irq, gpio_irq_handler);
irq_set_handler_data(bank->irq, bank);
}
接下来我们来查看chip是如何注册到内核中的:
int gpiochip_add(struct gpio_chip *chip)
{
unsigned long flags;
int status = ;
unsigned id;
int base = chip->base;
/* 判断gpio是否可用 */
if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - )) && base >= ) {
...
}
...
if (status == ) {
for (id = base; id < base + chip->ngpio; id++) {
gpio_desc[id].chip = chip; /* 放入全局变量中 */ gpio_desc[id].flags = !chip->direction_input
? ( << FLAG_IS_OUT)
: ;
}
}
...
unlock:
spin_unlock_irqrestore(&gpio_lock, flags); status = gpiochip_export(chip);
...
return status;
}
EXPORT_SYMBOL_GPL(gpiochip_add);
重新回到gpio_direction_output()函数,我们继续向下分析。代码第40行:chip->direction_output(chip, gpio, value);最终会调用chip的direction_output()函数。我们来看一下gpio-omap.c中的direction_output()函数是怎么实现的:
static void _set_gpio_direction(struct gpio_bank *bank, int gpio, int is_input)
{
void __iomem *reg = bank->base;
u32 l; switch (bank->method) {
#ifdef CONFIG_ARCH_OMAP1
case METHOD_MPUIO:
reg += OMAP_MPUIO_IO_CNTL / bank->stride;
break;
#endif
#ifdef CONFIG_ARCH_OMAP15XX
case METHOD_GPIO_1510:
reg += OMAP1510_GPIO_DIR_CONTROL;
break;
#endif
#ifdef CONFIG_ARCH_OMAP16XX
case METHOD_GPIO_1610:
reg += OMAP1610_GPIO_DIRECTION;
break;
#endif
#if defined(CONFIG_ARCH_OMAP730) || defined(CONFIG_ARCH_OMAP850)
case METHOD_GPIO_7XX:
reg += OMAP7XX_GPIO_DIR_CONTROL;
break;
#endif
#if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
case METHOD_GPIO_24XX:
reg += OMAP24XX_GPIO_OE;
break;
#endif
#if defined(CONFIG_ARCH_OMAP4)
case METHOD_GPIO_44XX:
reg += OMAP4_GPIO_OE;
break;
#endif
default:
WARN_ON();
return;
}
l = __raw_readl(reg);
if (is_input)
l |= << gpio;
else
l &= ~( << gpio);
__raw_writel(l, reg);
}
最终它会通过readl()和writel()等操作实现对寄存器的读写操作,对于这两个函数,读者可以参考:Linux驱动函数解读第四节。
二、内核中GPIO的使用函数
1. 检测GPIO引脚是否有效
bool gpio_is_valid(int number)
2. 获取或释放GPIO引脚。如果GPIO引脚正在使用,则无法获取。原理类似于原子操作
int gpio_request(unsigned gpio, const char *label) void gpio_free(unsigned gpio)
3. 设置GPIO引脚为输入或输出
int gpio_direction_input(unsigned gpio) /* 输入 */ int gpio_direction_output(unsigned gpio, int value) /* 输出 */
4. 获取或设置GPIO引脚的值
int gpio_get_value(unsigned gpio) void gpio_set_value(unsigned gpio, int value)
5. 设置GPIO引脚为中断引脚
int gpio_to_irq(unsigned gpio) /* 返回值为request_irq()使用的中断号 */
6. 导出或撤销GPIO引脚到用户空间
int gpio_export(unsigned gpio, bool direction_may_change)
/* 参数direction_may_change表示用户程序是否可以修改GPIO的方向,
如果可以,则参数direction_may_change为真 */ void gpio_unexport(unsigned gpio) /* 撤销GPIO */
下面我们可以使用GPIO子系统优化第二章的LED程序。
三、LED驱动优化
led源代码:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/gpio.h>
#include <linux/io.h> #include <asm/uaccess.h>
#include <asm/io.h> #include <plat/gpio-cfg.h> /* 定义文件内私有结构体 */
struct led_device {
struct cdev cdev;
int stat; /* 用于保存LED状态,0为灭,1为亮 */
}; static int g_major;
module_param(g_major, int, S_IRUGO); static struct led_device* dev;
static struct class* scls;
static struct device* sdev; /* LED write()函数 */
static ssize_t led_write(struct file *filep, const char __user * buf, size_t len, loff_t *ppos)
{
struct led_device *dev = filep->private_data; if (copy_from_user(&(dev->stat), buf, ))
return -EFAULT; if (dev->stat == )
gpio_set_value(EXYNOS4_GPK1(), );
else
gpio_set_value(EXYNOS4_GPK1(), ); return ;
} /* LED open()函数 */
static int led_open(struct inode *inodep, struct file *filep)
{
struct led_device *dev;
int ret = -; dev = container_of(inodep->i_cdev, struct led_device, cdev);
// 放入私有数据中
filep->private_data = dev; // 设为输出引脚,灭灯
ret = gpio_request(EXYNOS4_GPK1(), "LED3");
if (ret)
printk(KERN_ERR "failed to request LED3\n"); gpio_direction_output(EXYNOS4_GPK1(), );
s3c_gpio_cfgpin(EXYNOS4_GPK1(), S3C_GPIO_OUTPUT); gpio_set_value(EXYNOS4_GPK1(), ); return ;
} static int led_close(struct inode *inodep, struct file *filep)
{
gpio_free(EXYNOS4_GPK1()); return ;
} /* 把定义的函数接口集合起来,方便系统调用 */
static const struct file_operations led_fops = {
.write = led_write,
.open = led_open,
.release = led_close,
}; static int __init led_init(void)
{
int ret;
dev_t devt; /* 1. 申请设备号 */
if (g_major) {
devt = MKDEV(g_major, );
ret = register_chrdev_region(devt, , "led");
}
else {
ret = alloc_chrdev_region(&devt, , , "led");
g_major = MAJOR(devt);
}
if (ret)
return ret; /* 2. 申请文件内私有结构体 */
dev = kzalloc(sizeof(struct led_device), GFP_KERNEL);
if (dev == NULL) {
ret = -ENOMEM;
goto fail_malloc;
} /* 3. 注册字符设备驱动 */
cdev_init(&dev->cdev, &led_fops); /* 初始化cdev并链接file_operations和cdev */
ret = cdev_add(&dev->cdev, devt, ); /* 注册cdev */
if (ret)
return ret; /* 4. 创建类设备,insmod后会生成/dev/led设备文件 */
scls = class_create(THIS_MODULE, "led");
sdev = device_create(scls, NULL, devt, NULL, "led"); return ; fail_malloc:
unregister_chrdev_region(devt, ); return ret;
} static void __exit led_exit(void)
{
/* 镜像注销 */
dev_t devt = MKDEV(g_major, ); device_destroy(scls, devt);
class_destroy(scls); cdev_del(&(dev->cdev));
kfree(dev); unregister_chrdev_region(devt, );
} /* 声明段属性 */
module_init(led_init);
module_exit(led_exit); MODULE_LICENSE("GPL");
Makefile:
KERN_DIR = /work/itop4412/tools/linux-3.5 all:
make -C $(KERN_DIR) M=`pwd` modules clean:
make -C $(KERN_DIR) M=`pwd` modules clean
rm -rf modules.order obj-m += led.o
测试文件:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h> int main(int argc, char** argv)
{
if (argc != ) {
printf("Usage: \n");
printf("%s <on|off>\n", argv[]);
return -;
} int fd;
fd = open("/dev/led", O_RDWR);
if (fd < ) {
printf("can't open /dev/led\n");
return -;
} char stat;
if ( == strcmp(argv[], "off")) {
stat = ;
write(fd, &stat, );
} else {
stat = ;
write(fd, &stat, );
}
close(fd); return ;
}
下一章 十四、i2c子系统
十三、GPIO子系统的更多相关文章
- Linux内核驱动之GPIO子系统API接口概述
1.前言 在嵌入式Linux开发中,对嵌入式SoC中的GPIO进行控制非常重要,Linux内核中提供了GPIO子系统,驱动开发者在驱动代码中使用GPIO子系统提供的API函数,便可以达到对GPIO控制 ...
- 【linux】gpio子系统
目录 前言 linux子系统 gpio子系统 gpio子系统实战-系统调用 前言 目前不涉及驱动源码 参考链接 linux子系统 在 Linux 系统中 绝大多数硬件设备都有非常成熟的驱动框架 驱动工 ...
- Linux内核驱动之GPIO子系统(一)GPIO的使用
转自:http://blog.csdn.net/mirkerson/article/details/8464290 一 概述 Linux内核中gpio是最简单,最常用的资源(和 interrupt , ...
- Linux内核驱动之GPIO子系统(一)GPIO的使用【转】
转自:http://blog.csdn.net/tommy_wxie/article/details/9427047 一 概述 Linux内核中gpio是最简单,最常用的资源(和 interrupt ...
- Linux GPIO子系统
一 概述 Linux内核中gpio是最简单,最常用的资源(和 interrupt ,dma,timer一样)驱动程序,应用程序都能够通过相应的接口使用gpio,gpio使用0-MAX_INT之间的整数 ...
- gpio子系统和pinctrl子系统(下)
情景分析 打算从两个角度来情景分析,先从bsp驱动工程师的角度,然后是驱动工程师的角度,下面以三星s3c6410 Pinctrl-samsung.c为例看看pinctrl输入参数的初始化过程(最开始的 ...
- gpio子系统和pinctrl子系统(中)
pinctrl子系统核心实现分析 pinctrl子系统的内容在drivers/pinctrl文件夹下,主要文件有(建议先看看pinctrl内核文档Documentation/pinctrl.txt): ...
- gpio子系统和pinctrl子系统(上)
前言 随着内核的发展,linux驱动框架在不断的变化.很早很早以前,出现了gpio子系统,后来又出现了pinctrl子系统.在网上很难看到一篇讲解这类子系统的文章.就拿gpio操作来说吧,很多时候都是 ...
- Linux驱动之GPIO子系统和pinctrl子系统
前期知识 1.如何编写一个简单的Linux驱动(一)--驱动的基本框架 2.如何编写一个简单的Linux驱动(二)--设备操作集file_operations 3.如何编写一个简单的Lin ...
随机推荐
- 内存分析工具 MAT 的使用【转】
转自:http://blog.csdn.net/aaa2832/article/details/19419679/ 1 内存泄漏的排查方法 Dalvik Debug Monitor Server (D ...
- IO流——常用IO流详解
1:字节流 字节流:用于处理以字节为单位的二进制文件(如音乐,图片等) InputStream 是抽象类 它的对应子类FileInputStream可以被实例化 构造方法: FileInputStre ...
- 解决GitHub上传大于100M文件失败
目录 问题 解决 参考 问题 push的时候遇到以下问题: remote: error: GH001: Large files detected. You may want to try Git La ...
- 转: mysql的取整函数
一.ROUND()函数用法 ROUND(X) -- 表示将值 X 四舍五入为整数,无小数位 ROUND(X,D) -- 表示将值 X 四舍五入为小数点后 D 位的数值,D为小数点后小数位数.若要 ...
- bad object refs/remotes/origin/HEAD
How to handle git gc fatal: bad object refs/remotes/origin/HEAD error: failed to run repack I random ...
- 通AI启示录,从一篇数学物理基础论文说起 原创: 关注前沿科技 量子位 今天 允中 发自 凹非寺
通AI启示录,从一篇数学物理基础论文说起 原创: 关注前沿科技 量子位 今天 允中 发自 凹非寺
- 创建Bitmap之Bitmap静态方法使用示例
package com.loaderman.customviewdemo; import android.app.Activity; import android.content.Intent; im ...
- Apache配置优化之开启GZip传输
1.确保apache已经编译的模块里有mod_deflate模块 2.确保apache的配置文件里引入了压缩的模块 3.确保要开启Gzip压缩的虚拟主机配置里有如下配置,并重启apache服务:如果要 ...
- python安装pip方法
1.先下载pip安装脚本: https://bootstrap.pypa.io/get-pip.py 2.执行python get-pip.py 3.安装完成.
- 数据结构与算法学习(二)——Master公式及其应用
本篇文章涉及公式,由于博客园没有很好的支持,建议移步我的CSDN博客和简书进行阅读. 1. Master公式是什么? 我们在解决算法问题时,经常会用到递归.递归在较难理解的同时,其算法的复杂度也不是很 ...