[Linux笔记]gpiolib的调用过程分析
原文来自: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);
}