[Linux笔记]gpiolib的调用过程分析

转载 2013年12月02日 17:55:37
http://weimenlove.blog.163.com/blog/static/1777547320139251031924/
1、通用头文件如何与具体的平台联系
       不管是什么体系或平台,比如atmel、TI等,使用gpiolib只需要包含头文件include/linux/gpio.h即可,以使用TI的DM8148为例。
1.1 include/linux/gpio.h包含如下内容,当配置了使用gpiolib时,包含头文件arch/arm/include/asm/gpio.h
#ifdef CONFIG_GENERIC_GPIO
#include <asm/gpio.h>
#else
...
#endif
1.2 头文件arch/arm/include/asm/gpio.h里包含如下内容,包含了头文件arch/arm/mach-omap2/include/mach/gpio.h
#ifndef _ARCH_ARM_GPIO_H
#define _ARCH_ARM_GPIO_H
/* not all ARM platforms necessarily support this API ... */
#include <mach/gpio.h>
#endif /* _ARCH_ARM_GPIO_H */
1.3 头文件arch/arm/mach-omap2/include/mach/gpio.h包含了如下内容,即包含头文件arch/arm/plat-omap/include/plat/gpio.h
#include <plat/gpio.h>

2、gpiolib如何和具体的体系相对应
       以函数gpio_set_value()为例。
       当在驱动中使用头文件arch/arm/plat-omap/include/plat/gpio.h中包含的函数gpio_set_value()时,会调用__gpio_set_value(),该函数定义在driver/gpio/gpiolib.c中。
void __gpio_set_value(unsigned gpio, int value)
{
struct gpio_chip *chip;

chip = gpio_to_chip(gpio);
WARN_ON(chip->can_sleep);
chip->set(chip, gpio - chip->base, value);
}
       该函数通过chip->set来执行gpio电平的设置,而struct gpio_chip *chip的赋值则是在文件arch/arm/plat-omap/gpio.c中进行,即执行gpio_set来设置具体gpio电平。
static void __init omap_gpio_chip_init(struct gpio_bank *bank)
{
。。。//其他内容已经此处已经省略
bank->chip.set = gpio_set;
。。。
gpiochip_add(&bank->chip);
。。。
}
       函数__gpio_set_value()调用了函数gpio_to_chip(),其中的gpio_desc[gpio]定义如下,它的赋值是如何进行的?
static struct gpio_desc gpio_desc[ARCH_NR_GPIOS];
static inline struct gpio_chip *gpio_to_chip(unsigned gpio)
{
return gpio_desc[gpio].chip;
}

3、gpio_desc[gpio]是如何赋值的?
       gpio_desc的定义如下,对变量gpio_desc[ARCH_NR_GPIOS]的赋值是在函数gpiochip_add()中的参数chip进行的,即通过文件arch/arm/plat-omap/gpio.c中的函数omap_gpio_chip_init()调用。那问题来了,chip参数来自于omap_gpio_chip_init(),omap_gpio_chip_init()的参数struct gpio_bank *bank是哪里来的?
struct gpio_desc {
struct gpio_chip *chip;
unsigned long flags;
#ifdef CONFIG_DEBUG_FS
const char *label;
#endif
};
       函数gpiochip_add()的值如下所示:
int gpiochip_add(struct gpio_chip *chip)
{
。。。
for (id = base; id < base + chip->ngpio; id++) {
gpio_desc[id].chip = chip;

/* REVISIT:  most hardware initializes GPIOs as
 * inputs (often with pullups enabled) so power
 * usage is minimized.  Linux code should set the
 * gpio direction first thing; but until it does,
 * we may expose the wrong direction in sysfs.
 */
gpio_desc[id].flags = !chip->direction_input
? (1 << FLAG_IS_OUT)
: 0;
}
。。。
}

4、gpio_bank是怎么赋值的?
4.1 gpio_bank空间的申请
       omap_gpio_chip_init()在函数omap_gpio_probe()中被调用,其参数来自gpio_bank,gpio_bank的定义是static struct gpio_bank *gpio_bank;在函数omap_gpio_probe()中调用函数init_gpio_info()来为gpio_bank申请内存空间,如下所示。
static inline int init_gpio_info(struct platform_device *pdev)
{
/* TODO: Analyze removing gpio_bank_count usage from driver code */
gpio_bank = kzalloc(gpio_bank_count * sizeof(struct gpio_bank),
GFP_KERNEL);
if (!gpio_bank) {
dev_err(&pdev->dev, "Memory alloc failed for gpio_bank\n");
return -ENOMEM;
}
return 0;
}
       上面函数中的gpio_bank_count定义在arch/arm/plat-omap/gpio.c文件中,int gpio_bank_count;它是一个非静态的变量,可以被其他的文件所引用,并且在定义文件中并没有赋值操作。
4.2 gpio_bank_count的赋值
       查找gpio_bank_count,发现在文件arch/arm/mach-omap2/gpio.c中有赋值操作,如下所示:
static int omap2_gpio_dev_init(struct omap_hwmod *oh, void *unused)
{
。。。
gpio_bank_count++;
。。。
}
       可知,调用函数omap2_gpio_dev_init()一次,gpio_bank_count的值就会增加1。发现是在同一文件的omap2_gpio_init()函数中调用。
static int __init omap2_gpio_init(void)
{
return omap_hwmod_for_each_by_class("gpio", omap2_gpio_dev_init,
NULL);
}
       在文件arch/arm/mach-omap2/omap_hwmod.c中定义:
int omap_hwmod_for_each_by_class(const char *classname,
 int (*fn)(struct omap_hwmod *oh,
   void *user),
 void *user)
{
struct omap_hwmod *temp_oh;
int ret = 0;

if (!classname || !fn)
return -EINVAL;

pr_debug("omap_hwmod: %s: looking for modules of class %s\n",
 __func__, classname);

list_for_each_entry(temp_oh, &omap_hwmod_list, node) {
if (!strcmp(temp_oh->class->name, classname)) {
pr_debug("omap_hwmod: %s: %s: calling callback fn\n",
 __func__, temp_oh->name);
ret = (*fn)(temp_oh, user);
if (ret)
break;
}
}

if (ret)
pr_debug("omap_hwmod: %s: iterator terminated early: %d\n",
 __func__, ret);

return ret;
}
       上面的函数会遍历链表omap_hwmod_list,查找其中包含字符串classname,即带“gpio”的成员,找到一个执行一次指针函数*fn,而gpio_bank_count也相应增加1。
4.3 链表omap_hwmod_list成员个数
       在文件arch/arm/mach-omap2/omap_hwmod.c中的函数_register中
static int __init _register(struct omap_hwmod *oh)
{
int ret, ms_id;

if (!oh || !oh->name || !oh->class || !oh->class->name ||
    (oh->_state != _HWMOD_STATE_UNKNOWN))
return -EINVAL;

pr_debug("omap_hwmod: %s: registering\n", oh->name);

if (_lookup(oh->name))
return -EEXIST;

ms_id = _find_mpu_port_index(oh);
if (!IS_ERR_VALUE(ms_id)) {
oh->_mpu_port_index = ms_id;
oh->_mpu_rt_va = _find_mpu_rt_base(oh, oh->_mpu_port_index);
} else {
oh->_int_flags |= _HWMOD_NO_MPU_PORT;
}

list_add_tail(&oh->node, &omap_hwmod_list);

spin_lock_init(&oh->_lock);

oh->_state = _HWMOD_STATE_REGISTERED;

ret = 0;

return ret;
}
       上面的函数将参数oh成员中的node成员添加到链表中,函数由函数omap_hwmod_init()调用,调用几次,添加几个成员。
int __init omap_hwmod_init(struct omap_hwmod **ohs)
{
struct omap_hwmod *oh;
int r;

if (inited)
return -EINVAL;

inited = 1;

if (!ohs)
return 0;

oh = *ohs;
while (oh) {
if (omap_chip_is(oh->omap_chip)) {
r = _register(oh);
WARN(r, "omap_hwmod: %s: _register returned "
     "%d\n", oh->name, r);
}
oh = *++ohs;
}

return 0;
}
       上面函数则表明,看ohs的成员个数就可以知道_register()调用次数,也就知道omap_hwmod_list成员的个数,继续跟踪。
       在文件arch/arm/mach-omap2/omap_hwmod_81xx_data.c中
static __initdata struct omap_hwmod *ti81xx_hwmods[] = {
&ti816x_l3_slow_hwmod,
&ti816x_l4_slow_hwmod,
&ti816x_mpu_hwmod,
&ti816x_uart1_hwmod,
&ti816x_uart2_hwmod,
&ti816x_uart3_hwmod,
&ti814x_uart4_hwmod,
&ti814x_uart5_hwmod,
&ti814x_uart6_hwmod,
&ti816x_wd_timer2_hwmod,
&ti814x_wd_timer1_hwmod,
&ti81xx_i2c1_hwmod, /* Note: In TI814X this enables I2C0/2 */
&ti816x_i2c2_hwmod,
&ti814x_i2c3_hwmod, /* Note: In TI814X this enables I2C1/3 */
&ti814x_i2c4_hwmod, /* Note: In TI814X this enables I2C1/3 */
&ti81xx_gpio1_hwmod,
&ti81xx_gpio2_hwmod,
&ti814x_gpio3_hwmod,
&ti814x_gpio4_hwmod,
&ti81xx_usbss_hwmod,
&ti81xx_elm_hwmod,
NULL,
};

int __init ti81xx_hwmod_init(void)
{
return omap_hwmod_init(ti81xx_hwmods);
}
       可知ti81xx_hwmods共有4个含“gpio”成员,分别是&ti81xx_gpio1_hwmod, &ti81xx_gpio2_hwmod,&ti814x_gpio3_hwmod和&ti814x_gpio4_hwmod,而gpio的具体物理地址,也在其中进行的定义。

4.4 gpio_bank的赋值
       回过头看4.1 gpio_bank空间的申请,gpio_bank有四个struct gpio_bank类型的成员。
       在文件arch/arm/plat-omap/gpio.c中的函数omap_gpio_probe()中:
static int __devinit omap_gpio_probe(struct platform_device *pdev)
{
...
pdata = pdev->dev.platform_data;
...
bank = &gpio_bank[id];
...
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;
...
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (unlikely(!res)) {
dev_err(&pdev->dev, "GPIO Bank %i Invalid mem resource\n", id);
return -ENODEV;
}

bank->base = ioremap(res->start, resource_size(res));
...
}
       在GPIO注册的过程中,probe函数会被执行4次,分别对4个gpio_bank的具体内容进行赋值。

4.5 gpio和中断号的关系
       我们在使用GPIO时,常常通过gpio_to_irq()来获取中断号,gpio_to_irq()在头文件arch/arm/plat-omap/include/plat/gpio.h中调用文件driver/gpio/gpiolib.c中的__gpio_to_irq()。
int __gpio_to_irq(unsigned gpio)
{
struct gpio_chip *chip;

chip = gpio_to_chip(gpio);
return chip->to_irq ? chip->to_irq(chip, gpio - chip->base) : -ENXIO;
}
       chip的赋值在文件arch/arm/plat-omap/gpio.c中被赋值为gpio_2irq来获取中断号,代码如下所示。
static void __init omap_gpio_chip_init(struct gpio_bank *bank)
{
。。。//其他内容已经此处已经省略
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.direction_output_array = gpio_output_array;
bank->chip.set_debounce = gpio_debounce;
bank->chip.set = gpio_set;
bank->chip.to_irq = gpio_2irq;
。。。
}
       gpio_2irq()的内容如下,返回的值为bank->virtual_irq_start + offset,而bank->virtual_irq_start在4.4小节中由pdata->virtual_irq_start所赋值。
static int gpio_2irq(struct gpio_chip *chip, unsigned offset)
{
struct gpio_bank *bank;

bank = container_of(chip, struct gpio_bank, chip);
return bank->virtual_irq_start + offset;
}
       pdata则是pdev中所保存的驱动私有数据,而gpio的设备注册则是在文件arch/arm/mach-omap2/gpio.c文件中进行的,可以通过driver的name找的。
static struct platform_driver omap_gpio_driver = {
.probe = omap_gpio_probe,
.driver = {
.name = "omap_gpio",
},
};
       arch/arm/mach-omap2/gpio.c中的omap2_gpio_dev_init()先通过sscanf获取序号id,取值为1~4之间,表示属于哪个bank。IH_GPIO_BASE=160,所以irq=gpio+160。
static int omap2_gpio_dev_init(struct omap_hwmod *oh, void *unused)
{
...
char *name = "omap_gpio";
int id;

sscanf(oh->name, "gpio%d", &id);
...
pdata->virtual_irq_start = IH_GPIO_BASE + 32 * (id - 1);
}

相关文章推荐

gpiolib的调用过程分析

[Linux笔记]gpiolib的调用过程分析     1、通用头文件如何与具体的平台联系        不管是什么体系或平台,比如atmel、TI等,使用gpiolib...

gpiolib的调用过程分析

1、通用头文件如何与具体的平台联系        不管是什么体系或平台,比如atmel、TI等,使用gpiolib只需要包含头文件include/linux/gpio.h即可,以使用TI的DM814...

gpiolib管理gpio过程

在linux2.6.38中提供了统一管理外部io的模块。本文的内容是跟踪这些模块,是如何关联起来的。 环境:龙芯1b开发板 一、重点关注的相关的结构体: gpiolib.c文件...

整理--linux内核的gpiolib学习

知识整理–linux内核的gpiolib学习 gpiolib引入:(1)一个事实:很多硬件都要用到GPIO,GPIO会复用;(2)如果同一个GPIO被2个驱动同时控制了,就会出现bug;(3)内核...

Linux GPIO 驱动(基于GPIOLIB)

为了实现EVB板上Linux系统中SD卡插拔自动检测,最近进行了GPIO驱动的开发,基于GPIOLIB。 做个记录,以备以后参考。。。     参考已有的驱动文件:arch/arm/plat-...
  • junllee
  • junllee
  • 2013年04月11日 15:39
  • 3838

linux内核gpiolib文档

自己翻译的内核gpiolib文档,是2.6.38版本的。原文在:http://lxr.linux.no/linux+v2.6.38/Documentation/gpio.txt   内核文档 ...

linux内核gpiolib文档

============================================ 作者:yuanlulu http://blog.csdn.net/yuanlulu 版权没有,但是转...

Linux2.6.32驱动笔记(3)分析应用程序read访问驱动过程

摘要: 利用objdump反汇编编译好的应用程序,分析了应用程序程序中调用read时时候,如何一层层的找到驱动中我们自己实现的对应的read方法。 一、反汇编编译好的应用程序 之前有一个读...

网络配置过程分析(linux网络协议栈笔记)

配置过程分析在本篇,我们先介绍配置普通设备的IP地址的内部过程,接着再转到loopback接口的配置过程,这两个过程有相似之处,所以一起解说。然后再转入FIB系统,讲解路由系统,并用图例演示路由表的变...

Linux内核分析学习笔记:system_call中断处理过程

前两篇博文从汇编的角度分析了linux系统的系统调用方法,本博客在实验楼平台下写了一个简单的系统调用程序,并分析系统调用的实际过程。 本文实验平台为实验楼Linux内核分析的第5个实验:分析syste...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:[Linux笔记]gpiolib的调用过程分析
举报原因:
原因补充:

(最多只允许输入30个字)