转自:http://blog.csdn.net/maopig/article/details/7428561

其原型为 int gpio_request(unsigned gpio, const char *label)先说说其参数,gpio则为你要申请的哪一个管脚,label则是为其取一个名字。其具体实现如下:

int gpio_request(unsigned gpio, const char *label)
{
struct gpio_desc *desc;//这个自己看源码
struct gpio_chip *chip;//这个自己看源码
int status = -EINVAL;
unsigned long flags; spin_lock_irqsave(&gpio_lock, flags);//屏蔽中断
if (!gpio_is_valid(gpio))//判断是否有效,也就是参数的取值范围判断
goto done;
desc = &gpio_desc[gpio];
//这个是关键gpio_desc为定义的一个全局的数组变量,这个函数的实值也就是,
//用gpio_desc里面的一个变量来表示数组中的这个元素已经被申请了,而这个变量就是下面会看到的desc->flags。
chip = desc->chip;//按理说这个这个全局的gpio_desc如果没有初始化的话,这个chip就为空了,随后就直接返回-EINVAL了。 if (chip == NULL)如果不为空继续往下走
goto done;
if (!try_module_get(chip->owner))
goto done;
/* NOTE: gpio_request() can be called in early boot,
* before IRQs are enabled, for non-sleeping (SOC) GPIOs.
*/
if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == ) {
//这里测试并设置flags的第FLAG_REQUESTED位,如果没有被申请就返回该位的原值0,分析到这儿,也差不多满足了我的个人要求。
desc_set_label(desc, label ? : "?");
status = ;
} else {
status = -EBUSY;
module_put(chip->owner);
goto done;
}
if (chip->request) {
/* chip->request may sleep */
spin_unlock_irqrestore(&gpio_lock, flags);
status = chip->request(chip, gpio - chip->base);
spin_lock_irqsave(&gpio_lock, flags);
if (status < ) {
desc_set_label(desc, NULL);
module_put(chip->owner);
clear_bit(FLAG_REQUESTED, &desc->flags);
}
}
done:
if (status)
pr_debug("gpio_request: gpio-%d (%s) status %d\n",
gpio, label ? : "?", status);
spin_unlock_irqrestore(&gpio_lock, flags);
return status;
}
[cpp] view plaincopy int gpio_request(unsigned gpio, const char *label)
{
struct gpio_desc *desc;//这个自己看源码
struct gpio_chip *chip;//这个自己看源码
int status = -EINVAL;
unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);//屏蔽中断
if (!gpio_is_valid(gpio))//判断是否有效,也就是参数的取值范围判断
goto done;
desc = &gpio_desc[gpio];
//这个是关键gpio_desc为定义的一个全局的数组变量,这个函数的实值也就是,
//用gpio_desc里面的一个变量来表示数组中的这个元素已经被申请了,而这个变量就是下面会看到的desc->flags。
chip = desc->chip;//按理说这个这个全局的gpio_desc如果没有初始化的话,这个chip就为空了,随后就直接返回-EINVAL了。
if (chip == NULL)如果不为空继续往下走
goto done;
if (!try_module_get(chip->owner))
goto done;
/* NOTE: gpio_request() can be called in early boot,
* before IRQs are enabled, for non-sleeping (SOC) GPIOs.
*/
if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == ) {
//这里测试并设置flags的第FLAG_REQUESTED位,如果没有被申请就返回该位的原值0,分析到这儿,也差不多满足了我的个人要求。
desc_set_label(desc, label ? : "?");
status = ;
} else {
status = -EBUSY;
module_put(chip->owner);
goto done;
}
if (chip->request) {
/* chip->request may sleep */
spin_unlock_irqrestore(&gpio_lock, flags);
status = chip->request(chip, gpio - chip->base);
spin_lock_irqsave(&gpio_lock, flags);
if (status < ) {
desc_set_label(desc, NULL);
module_put(chip->owner);
clear_bit(FLAG_REQUESTED, &desc->flags);
}
}
done:
if (status)
pr_debug("gpio_request: gpio-%d (%s) status %d\n",
gpio, label ? : "?", status);
spin_unlock_irqrestore(&gpio_lock, flags);
return status;
} davinci 平台:
[cpp] view plaincopy /*
* TI DaVinci GPIO Support
*
* Copyright (c) 2006 David Brownell
* Copyright (c) 2007, MontaVista Software, Inc. <source@mvista.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/ #include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/bitops.h> #include <asm/irq.h>
#include <asm/io.h>
#include <asm/hardware/clock.h> #include <asm/arch/irqs.h>
#include <asm/arch/hardware.h>
#include <asm/arch/gpio.h>
#include <asm/arch/cpu.h> #include <asm/mach/irq.h> /*
该文件实现了gpio的各种应用功能和向内核注册gpio的中断例程等功能。
用户的驱动程序可调用gpio_request和gpio_free使用或释放该gpio,
可以调用gpio_direction_input和gpio_direction_output函数设置gpio输入输出方向,
调用gpio_get_value和gpio_set_value获取设置值。
*/ static DEFINE_SPINLOCK(gpio_lock); /* 总共有DAVINCI_N_GPIO(71)个gpio引脚,故使用相应多的bit来记录这些引脚的使用状态 */
static DECLARE_BITMAP(gpio_in_use, DAVINCI_N_GPIO); /*
申请一个gpio,其实就是检查该gpio是否空闲,如果空闲就可以使用并将该gpio相应的bit置位
(在gpio_in_use中)。
*/
int gpio_request(unsigned gpio, const char *tag)
{
if (gpio >= DAVINCI_N_GPIO)
return -EINVAL;
if (test_and_set_bit(gpio, gpio_in_use))
return -EBUSY;
return ;
}
EXPORT_SYMBOL(gpio_request); /*
释放一个gpio,其实就是清除gpio相应的控制bit位(在gpio_in_use中)。
*/
void gpio_free(unsigned gpio)
{
if (gpio >= DAVINCI_N_GPIO)
return;
clear_bit(gpio, gpio_in_use);
}
EXPORT_SYMBOL(gpio_free); /* 获得gpio_controller结构体指针,gpio_controller结构体是gpio的核心控制单元,里面包含
gpio的设置和数据寄存器。该结构体和__gpio_to_controller函数在/include/asm-arm/
arch-davinci/gpio.h中定义,具体如下:
struct gpio_controller {
u32 dir;
u32 out_data;
u32 set_data;
u32 clr_data;
u32 in_data;
u32 set_rising;
u32 clr_rising;
u32 set_falling;
u32 clr_falling;
u32 intstat;
}; static inline struct gpio_controller *__iomem
__gpio_to_controller(unsigned gpio)
{
void *__iomem ptr;
if (gpio >= DAVINCI_N_GPIO)
return NULL; if (gpio < 32)
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x10);
else if (gpio < 64)
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x38);
else if (gpio < 96)
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x60);
else
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x88);
return ptr;
}
由上面的定义和ti的SPRUE25.pdf手册可以看出,__gpio_to_controller函数返回的是
gpio_controller结构体到第一个成员dir的虚拟地址。获取了这个结构体指针后,
便可以控制相应的gpio了。dm644x共有71个gpio,
所以使用三个gpio_controller结构体控制,关于这个后面会由更详细的分析,
*/
/* create a non-inlined version */
static struct gpio_controller *__iomem gpio2controller(unsigned gpio)
{
return __gpio_to_controller(gpio);
} /*
向某个gpio设置值,0或1。如果向gpio写1,则向set_data寄存器相应的位置1,如果写0,
则向clr_data寄存器相应的位置1.__gpio_mask函数在gpio.h中定义,定义如下,
static inline u32 __gpio_mask(unsigned gpio)
{
return 1 << (gpio % 32);
}
因为71个引脚由3个结构体控制,第一个控制前32个gpio,第二个控制次32个gpio,
最后一个控制剩余的7个gpio,故__gpio_mask函数的作用是找到在其相应控制结构体里的偏移数,
比如gpio34,那么其由第二个结构体控制,在这个机构体里的偏移是3(从0开始算,就是第二位)。
使用这个函数之前,必须确认该gpio设置成输出模式。
*/
/*
* Assuming the pin is muxed as a gpio output, set its output value.
*/
void __gpio_set(unsigned gpio, int value)
{
struct gpio_controller *__iomem g = gpio2controller(gpio);
// 设置gpio的值
__raw_writel(__gpio_mask(gpio), value ? &g->set_data : &g->clr_data);
}
EXPORT_SYMBOL(__gpio_set); /*
通过读取in_data寄存器相应该gpio的位来读取gpio的值。
使用这个函数之前,必须确认该gpio设置成输入模式,否则获得到值不可预料。
*/
/*
* Read the pin's value (works even if it's set up as output);
* returns zero/nonzero.
*
* Note that changes are synched to the GPIO clock, so reading values back
* right after you've set them may give old values.
*/
int __gpio_get(unsigned gpio)
{
struct gpio_controller *__iomem g = gpio2controller(gpio);
/* 读取gpio的值,!!的目的是使得返回的值为0或1.*/
return !!(__gpio_mask(gpio) & __raw_readl(&g->in_data));
} }
EXPORT_SYMBOL(__gpio_get); /*
通过dir寄存器相应该gpio的位来设置gpio输入输出方向,为0,则设置成输出,为1,则设置出输入。
该函数是设置成输入,故设置dir寄存器为1.
正如应为所说的,必须确认该引脚是作为gpio功能,而不是某个模块到功能,比如spi。通过PINMUX0
和PINMUX1两个寄存器来设置。
*/
/*--------------------------------------------------------------------------*/ /*
* board setup code *MUST* set PINMUX0 and PINMUX1 as
* needed, and enable the GPIO clock.
*/
int gpio_direction_input(unsigned gpio)
{
struct gpio_controller *__iomem g = gpio2controller(gpio);
u32 temp;
u32 mask; if (!g)
return -EINVAL;
spin_lock(&gpio_lock);
mask = __gpio_mask(gpio);
temp = __raw_readl(&g->dir);
temp |= mask; // 设置成1
__raw_writel(temp, &g->dir); // 设置该gpio为输入
spin_unlock(&gpio_lock);
return ;
}
EXPORT_SYMBOL(gpio_direction_input); /*
通过dir寄存器相应该gpio的位来设置gpio输入输出方向,为0,则设置成输出,为1,则设置出输入。
该函数是设置成输出,故设置dir寄存器为0.
value参数用于选择gpio设置成输出后该gpio输出的值。
*/
int gpio_direction_output(unsigned gpio, int value)
{
struct gpio_controller *__iomem g = gpio2controller(gpio);
u32 temp;
u32 mask;
if (!g)
return -EINVAL; spin_lock(&gpio_lock);
mask = __gpio_mask(gpio);
temp = __raw_readl(&g->dir);
temp &= ~mask; // 设置成0
//设置该gpio输出值
__raw_writel(mask, value ? &g->set_data : &g->clr_data);
__raw_writel(temp, &g->dir); // 设置gpio为输出
spin_unlock(&gpio_lock);
return ;
}
EXPORT_SYMBOL(gpio_direction_output); /*
向gpio设置值,0或1。
*/
void gpio_set_value(unsigned gpio, int value)
{
if (__builtin_constant_p(value)) {
struct gpio_controller *__iomem g;
u32 mask; if (gpio >= DAVINCI_N_GPIO)
__error_inval_gpio(); g = __gpio_to_controller(gpio);
mask = __gpio_mask(gpio);
if (value)
__raw_writel(mask, &g->set_data); // 该gpio输出高 else
__raw_writel(mask, &g->clr_data); // 该gpio输出低 return;
} __gpio_set(gpio, value);
}
EXPORT_SYMBOL(gpio_set_value); /*
读取gpio的值,0或1.
*/
int gpio_get_value(unsigned gpio)
{
struct gpio_controller *__iomem g; if (!__builtin_constant_p(gpio))/* 判断该gpio值是否为编译时常数,如果是常数,
函数返回 1,否则返回 0 */
return __gpio_get(gpio); if (gpio >= DAVINCI_N_GPIO)
return __error_inval_gpio(); g = __gpio_to_controller(gpio); // 读取该gpio的值 return !!(__gpio_mask(gpio) & __raw_readl(&g->in_data));
}
EXPORT_SYMBOL(gpio_get_value); /*
* We expect irqs will normally be set up as input pins, but they can also be
* used as output pins ... which is convenient for testing.
*
* NOTE: GPIO0..GPIO7 also have direct INTC hookups, which work in addition
* to their GPIOBNK0 irq (but with a bit less overhead). But we don't have
* a good way to hook those up ...
*
* All those INTC hookups (GPIO0..GPIO7 plus five IRQ banks) can also
* serve as EDMA event triggers.
*/ /*
禁止相应该irq的gpio的中断。每个gpio都可以作为中断的来源,其中gpio0-gpio7是独立的中断来源,
也就是分配独立的中断号,其他gpio则共用5个GPIOBNK中断线。其优先级可以在board-evm.c
中设置(已经介绍过)。在dm644x平台上,中断是电平边缘触发的,禁止中断其实就是既不设置
上升沿触发,也不设置下降沿触发。
*/
static void gpio_irq_disable(unsigned irq)
{
struct gpio_controller *__iomem g = get_irq_chipdata(irq);
u32 mask = __gpio_mask(irq_to_gpio(irq)); __raw_writel(mask, &g->clr_falling); // 清除下降沿触发 __raw_writel(mask, &g->clr_rising); // 清除上升沿触发 } /*
中断使能。
在dm644x平台上,中断是电平边缘触发的,其实就是设置为上升沿或下降沿中断。
*/
static void gpio_irq_enable(unsigned irq)
{
struct gpio_controller *__iomem g = get_irq_chipdata(irq);
u32 mask = __gpio_mask(irq_to_gpio(irq)); // 如果先前为下降沿中断,则使能为下降沿中断 if (irq_desc[irq].status & IRQT_FALLING)
__raw_writel(mask, &g->set_falling); // 如果先前为上升沿中断,则使能为上升沿中断 if (irq_desc[irq].status & IRQT_RISING)
__raw_writel(mask, &g->set_rising);
} /*
设置中断类型。
在dm644x平台上,中断有上升沿和下降沿两种触发方式。
*/
static int gpio_irq_type(unsigned irq, unsigned trigger)
{
struct gpio_controller *__iomem g = get_irq_chipdata(irq);
u32 mask = __gpio_mask(irq_to_gpio(irq)); if (trigger & ~(IRQT_FALLING | IRQT_RISING))
return -EINVAL; irq_desc[irq].status &= ~IRQT_BOTHEDGE;
irq_desc[irq].status |= trigger; __raw_writel(mask, (trigger & IRQT_FALLING)
? &g->set_falling : &g->clr_falling); // 设置为下降沿触发 __raw_writel(mask, (trigger & IRQT_RISING)
? &g->set_rising : &g->clr_rising); // 设置为上升沿触发 return ;
} /*
该结构体用于注册到所有irq的中断描述结构体中(struct irqdesc),
而所有中断描述结构体定义成一个全局数组irq_desc 。
*/
static struct irqchip gpio_irqchip = {
.unmask = gpio_irq_enable, /* 用于使能中断,
在enable_irq()等内核函数中会用到。*/
.mask = gpio_irq_disable,/* 用于禁止中断,
在disable_irq()等内核函数中会用到。*/
.type = gpio_irq_type, /* 用于设置中断类型,
在set_irq_type()内核函数中会用到。*/
}; /*
该函数将在下面的davinci_gpio_irq_setup中使用,将被注册到五个gpio bank中断的
irq_desc结构中,目的是处理所有级联的gpio中断。所谓级联的中断, 就是指有n个中断
共用同一个中断线。
在dm644x平台中,除了gpio0-gpio7外,其他63个gpio都共用五个gpiobank中断线,在这里,
gpio0-gpio7也被注册到gpiobank中断线,但实际上并不会使用,因为它们拥有自己的
中断线。其中,gpio0-gpio15共用IRQ_GPIOBNK0(56)中断线,gpio16-gpio31共用
IRQ_GPIOBNK1(57)中断线,gpio32-gpio47共用IRQ_GPIOBNK2(58)中断线,
gpio48-gpio63共用IRQ_GPIOBNK4(59)中断线,gpio64-gpio70共用
IRQ_GPIOBNK5(60)中断线,
因为寄存器是32位的,所以实际上只有三组寄存器,第一组包含bank0和bank1,
也就是gpio0-gpio31,第二组包含bank2和bank3,也就是gpio32-gpio63,
第三组包含bank4和bank5,也就是gpio64-gpio70,剩余了25个位没有使用。
*/
static void
gpio_irq_handler(unsigned irq, struct irqdesc *desc, struct pt_regs *regs)
{
struct gpio_controller *__iomem g = get_irq_chipdata(irq);
u32 mask = 0xffff; /* we only care about one bank */
// 如果bank中断线是寄数,则说明该中断的中断状态位在INTSTATn寄存器的高16位 if (irq & )
mask <<= ; /* temporarily mask (level sensitive) parent IRQ */
desc->chip->ack(irq);// 该ack函数会在arch/arm/mach-davinci/irq.c中注册。 while () {
u32 status;
struct irqdesc *gpio;
int n;
int res; /* ack any irqs */
/*gpio中断发生后,硬件会在INTSTATn寄存器中置位相应位,
以备程序查询,确定是哪个gpio*/
status = __raw_readl(&g->intstat) & mask;
if (!status)
break;
__raw_writel(status, &g->intstat); // 向该位写1清除 if (irq & )
status >>= ; /* now demux them to the right lowlevel handler */
// 从下面的davinci_gpio_irq_setup函数可以看出来以下程序的运作。 n = (int)get_irq_data(irq); // 获取该bank对应的第一个gpio号 gpio = &irq_desc[n]; // 获取该bank第一个gpio号对应的中断描述符 while (status) { // 该bank可能有多个gpio发生了中断 res = ffs(status); // 获取第一个发生了中断的位(1-32) n += res; /* 获得该gpio的中断线(系统实际上只有64(0-63)个中断线,
但那些共用的gpio的中断也有自己的断描述符和中断线(从64开始),
仅仅是为了管理,不能通过request_irq()函数来申请。*/
gpio += res; // 获得该gpio的中断描述符 /* 调用下面注册的do_simple_IRQ例程
其又会调用用户通过request_irq()
注册的中断例程
*/
desc_handle_irq(n - , gpio - , regs);
status >>= res;
}
}
desc->chip->unmask(irq); // 打开该irq中断线 /* now it may re-trigger */
} /*
* NOTE: for suspend/resume, probably best to make a sysdev (and class)
* with its suspend/resume calls hooking into the results of the set_wake()
* calls ... so if no gpios are wakeup events the clock can be disabled,
* with outputs left at previously set levels, and so that VDD3P3V.IOPWDN0
* can be set appropriately for GPIOV33 pins.
*/
/*
注册gpio中断例程到内核中,并初始化了一些寄存器。
该函数将会被board_evm.c(其浅析已经发表)中的evm_init()函数调用。具体调用过程如下:
start_kernel()-->setup_arch()-->init_machine = mdesc->init_machine
(init_machine是个全局函数指针变量,其指向的就是已经注册到机器描述符里evm_init());
调用函数指针init_machine()的例程是customize_machine(),其定义为
arch_initcall(customize_machine),所以,接下来的调用过程是:
start_kernel()-->do_basic_setup()-->do_initcalls()-->customize_machine()-->
init_machine()(也就是evm_init())-->davinci_gpio_irq_setup。
从上可以看出经历了两个过程,才调用davinci_gpio_irq_setup例程来初始化gpio中断。
*/
int __init davinci_gpio_irq_setup(void)
{
unsigned gpio, irq, bank, banks;
struct clk *clk; clk = clk_get(NULL, "gpio"); // 获取时钟 if (IS_ERR(clk)) {
printk(KERN_ERR "Error %ld getting gpio clock?\n",
PTR_ERR(clk));
return ;
} clk_enable(clk); // 使能gpio时钟并打开该模块电源 for (gpio = , irq = gpio_to_irq(), bank = (cpu_is_davinci_dm355() ?
IRQ_DM355_GPIOBNK0 : (cpu_is_davinci_dm6467() ?
IRQ_DM646X_GPIOBNK0 : IRQ_GPIOBNK0)); // dm644x的IRQ_GPIOBNK0(56) gpio < DAVINCI_N_GPIO; bank++) { // dm644x的DAVINCI_N_GPIO(71) struct gpio_controller *__iomem g = gpio2controller(gpio);
unsigned i; // 关该bank所有gpio的中断 __raw_writel(~, &g->clr_falling);
__raw_writel(~, &g->clr_rising); /* set up all irqs in this bank */
// 同一个bank的所有gpio共用一个中断例程gpio_irq_handler set_irq_chained_handler(bank, gpio_irq_handler);
set_irq_chipdata(bank, g);
set_irq_data(bank, (void *)irq); for (i = ; i < && gpio < DAVINCI_N_GPIO;
i++, irq++, gpio++) {
set_irq_chip(irq, &gpio_irqchip); /* 注册用于gpio中断禁止、设能
和类型选择的回调例程 */
set_irq_chipdata(irq, g); // 保存控制结构体(寄存器)的地址 set_irq_handler(irq, do_simple_IRQ);/* 为每个gpio中断设置同一个中
断例程do_simple_IRQ*/
set_irq_flags(irq, IRQF_VALID); // fiq中断有效 }
}
/*
一个共用bank中断线的gpio中断发生后的大致的流程是:
--> gpio_irq_handler --> do_simple_IRQ --> __do_irq -->
action->handler(用户使用request_irq()注册的中断例程)
*/
/* BINTEN -- per-bank interrupt enable. genirq would also let these
* bits be set/cleared dynamically.
*/
if (cpu_is_davinci_dm355())
banks = 0x3f;
else
banks = 0x1f; // 向BINTEN寄存器写入0x1f(共5个位,每个位控制1个bank),打开所有的bank中断 __raw_writel(banks, (void *__iomem)
IO_ADDRESS(DAVINCI_GPIO_BASE + 0x08)); printk(KERN_INFO "DaVinci: %d gpio irqs\n", irq - gpio_to_irq()); return ;
} gpio.h /*
* TI DaVinci GPIO Support
*
* Copyright (c) 2006 David Brownell
* Copyright (c) 2007, MontaVista Software, Inc. <source@mvista.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/ #ifndef __DAVINCI_GPIO_H
#define __DAVINCI_GPIO_H /*
* basic gpio routines
*
* board-specific init should be done by arch/.../.../board-XXX.c (maybe
* initializing banks together) rather than boot loaders; kexec() won't
* go through boot loaders.
*
* the gpio clock will be turned on when gpios are used, and you may also
* need to pay attention to PINMUX0 and PINMUX1 to be sure those pins are
* used as gpios, not with other peripherals.
*
* GPIOs are numbered 0..(DAVINCI_N_GPIO-1). For documentation, and maybe
* for later updates, code should write GPIO(N) or:
* - GPIOV18(N) for 1.8V pins, N in 0..53; same as GPIO(0)..GPIO(53)
* - GPIOV33(N) for 3.3V pins, N in 0..17; same as GPIO(54)..GPIO(70)
*
* For GPIO IRQs use gpio_to_irq(GPIO(N)) or gpio_to_irq(GPIOV33(N)) etc
* for now, that's != GPIO(N)
*/
#define GPIO(X) (X) /* 0 <= X <= 70 */
#define GPIOV18(X) (X) /* 1.8V i/o; 0 <= X <= 53 */
#define GPIOV33(X) ((X)+54) /* 3.3V i/o; 0 <= X <= 17 */ /*
寄存器都是32位到,每位对应一个gpio。
*/
struct gpio_controller {
u32 dir; // gpio方向设置寄存器 u32 out_data; // gpio设置为输出时,表示输出状态(0或1) u32 set_data; // gpio设置为输出时,用于输出高电平 u32 clr_data; // gpio设置为输出时,用于输出低电平 u32 in_data; // gpio设置为输入时,用于读取输入值 u32 set_rising; // gpio中断上升沿触发设置 u32 clr_rising; // gpio中断上升沿触发清除 u32 set_falling; // gpio中断下降沿触发设置 u32 clr_falling; // gpio中断下降沿触发清除 u32 intstat; // gpio中断状态位,由硬件设置,可读取,写1时清除。 }; /* The __gpio_to_controller() and __gpio_mask() functions inline to constants
* with constant parameters; or in outlined code they execute at runtime.
*
* You'd access the controller directly when reading or writing more than
* one gpio value at a time, and to support wired logic where the value
* being driven by the cpu need not match the value read back.
*
* These are NOT part of the cross-platform GPIO interface
*/
static inline struct gpio_controller *__iomem
__gpio_to_controller(unsigned gpio)
{
void *__iomem ptr; if (gpio >= DAVINCI_N_GPIO)
return NULL; if (gpio < )
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x10);
else if (gpio < )
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x38);
else if (gpio < )
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x60);
else
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x88); return ptr;
} static inline u32 __gpio_mask(unsigned gpio)
{
return << (gpio % );
} /* The get/set/clear functions will inline when called with constant
* parameters, for low-overhead bitbanging. Illegal constant parameters
* cause link-time errors.
*
* Otherwise, calls with variable parameters use outlined functions.
*/
extern int __error_inval_gpio(void); extern void __gpio_set(unsigned gpio, int value);
extern int __gpio_get(unsigned gpio); /* Returns zero or nonzero; works for gpios configured as inputs OR
* as outputs.
*
* NOTE: changes in reported values are synchronized to the GPIO clock.
* This is most easily seen after calling gpio_set_value() and then immediatly
* gpio_get_value(), where the gpio_get_value() would return the old value
* until the GPIO clock ticks and the new value gets latched.
*/
extern int gpio_get_value(unsigned gpio);
extern void gpio_set_value(unsigned gpio, int value); /* powerup default direction is IN */
extern int gpio_direction_input(unsigned gpio);
extern int gpio_direction_output(unsigned gpio, int value); #include <asm-generic/gpio.h> /* cansleep wrappers */ extern int gpio_request(unsigned gpio, const char *tag);
extern void gpio_free(unsigned gpio); static inline int gpio_to_irq(unsigned gpio)
{
return DAVINCI_N_AINTC_IRQ + gpio;
} static inline int irq_to_gpio(unsigned irq)
{
return irq - DAVINCI_N_AINTC_IRQ;
} #endif /* __DAVINCI_GPIO_H */ [cpp] view plaincopy /*
* TI DaVinci GPIO Support
*
* Copyright (c) 2006 David Brownell
* Copyright (c) 2007, MontaVista Software, Inc. <source@mvista.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/ #include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/err.h>
#include <linux/bitops.h> #include <asm/irq.h>
#include <asm/io.h>
#include <asm/hardware/clock.h> #include <asm/arch/irqs.h>
#include <asm/arch/hardware.h>
#include <asm/arch/gpio.h>
#include <asm/arch/cpu.h> #include <asm/mach/irq.h> /*
该文件实现了gpio的各种应用功能和向内核注册gpio的中断例程等功能。
用户的驱动程序可调用gpio_request和gpio_free使用或释放该gpio,
可以调用gpio_direction_input和gpio_direction_output函数设置gpio输入输出方向,
调用gpio_get_value和gpio_set_value获取设置值。
*/ static DEFINE_SPINLOCK(gpio_lock); /* 总共有DAVINCI_N_GPIO(71)个gpio引脚,故使用相应多的bit来记录这些引脚的使用状态 */
static DECLARE_BITMAP(gpio_in_use, DAVINCI_N_GPIO); /*
申请一个gpio,其实就是检查该gpio是否空闲,如果空闲就可以使用并将该gpio相应的bit置位
(在gpio_in_use中)。
*/
int gpio_request(unsigned gpio, const char *tag)
{
if (gpio >= DAVINCI_N_GPIO)
return -EINVAL;
if (test_and_set_bit(gpio, gpio_in_use))
return -EBUSY;
return ;
}
EXPORT_SYMBOL(gpio_request); /*
释放一个gpio,其实就是清除gpio相应的控制bit位(在gpio_in_use中)。
*/
void gpio_free(unsigned gpio)
{
if (gpio >= DAVINCI_N_GPIO)
return;
clear_bit(gpio, gpio_in_use);
}
EXPORT_SYMBOL(gpio_free); /* 获得gpio_controller结构体指针,gpio_controller结构体是gpio的核心控制单元,里面包含
gpio的设置和数据寄存器。该结构体和__gpio_to_controller函数在/include/asm-arm/
arch-davinci/gpio.h中定义,具体如下:
struct gpio_controller {
u32 dir;
u32 out_data;
u32 set_data;
u32 clr_data;
u32 in_data;
u32 set_rising;
u32 clr_rising;
u32 set_falling;
u32 clr_falling;
u32 intstat;
}; static inline struct gpio_controller *__iomem
__gpio_to_controller(unsigned gpio)
{
void *__iomem ptr;
if (gpio >= DAVINCI_N_GPIO)
return NULL; if (gpio < 32)
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x10);
else if (gpio < 64)
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x38);
else if (gpio < 96)
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x60);
else
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x88);
return ptr;
}
由上面的定义和ti的SPRUE25.pdf手册可以看出,__gpio_to_controller函数返回的是
gpio_controller结构体到第一个成员dir的虚拟地址。获取了这个结构体指针后,
便可以控制相应的gpio了。dm644x共有71个gpio,
所以使用三个gpio_controller结构体控制,关于这个后面会由更详细的分析,
*/
/* create a non-inlined version */
static struct gpio_controller *__iomem gpio2controller(unsigned gpio)
{
return __gpio_to_controller(gpio);
} /*
向某个gpio设置值,0或1。如果向gpio写1,则向set_data寄存器相应的位置1,如果写0,
则向clr_data寄存器相应的位置1.__gpio_mask函数在gpio.h中定义,定义如下,
static inline u32 __gpio_mask(unsigned gpio)
{
return 1 << (gpio % 32);
}
因为71个引脚由3个结构体控制,第一个控制前32个gpio,第二个控制次32个gpio,
最后一个控制剩余的7个gpio,故__gpio_mask函数的作用是找到在其相应控制结构体里的偏移数,
比如gpio34,那么其由第二个结构体控制,在这个机构体里的偏移是3(从0开始算,就是第二位)。
使用这个函数之前,必须确认该gpio设置成输出模式。
*/
/*
* Assuming the pin is muxed as a gpio output, set its output value.
*/
void __gpio_set(unsigned gpio, int value)
{
struct gpio_controller *__iomem g = gpio2controller(gpio);
// 设置gpio的值
__raw_writel(__gpio_mask(gpio), value ? &g->set_data : &g->clr_data);
}
EXPORT_SYMBOL(__gpio_set); /*
通过读取in_data寄存器相应该gpio的位来读取gpio的值。
使用这个函数之前,必须确认该gpio设置成输入模式,否则获得到值不可预料。
*/
/*
* Read the pin's value (works even if it's set up as output);
* returns zero/nonzero.
*
* Note that changes are synched to the GPIO clock, so reading values back
* right after you've set them may give old values.
*/
int __gpio_get(unsigned gpio)
{
struct gpio_controller *__iomem g = gpio2controller(gpio);
/* 读取gpio的值,!!的目的是使得返回的值为0或1.*/
return !!(__gpio_mask(gpio) & __raw_readl(&g->in_data));
} }
EXPORT_SYMBOL(__gpio_get); /*
通过dir寄存器相应该gpio的位来设置gpio输入输出方向,为0,则设置成输出,为1,则设置出输入。
该函数是设置成输入,故设置dir寄存器为1.
正如应为所说的,必须确认该引脚是作为gpio功能,而不是某个模块到功能,比如spi。通过PINMUX0
和PINMUX1两个寄存器来设置。
*/
/*--------------------------------------------------------------------------*/ /*
* board setup code *MUST* set PINMUX0 and PINMUX1 as
* needed, and enable the GPIO clock.
*/
int gpio_direction_input(unsigned gpio)
{
struct gpio_controller *__iomem g = gpio2controller(gpio);
u32 temp;
u32 mask; if (!g)
return -EINVAL;
spin_lock(&gpio_lock);
mask = __gpio_mask(gpio);
temp = __raw_readl(&g->dir);
temp |= mask; // 设置成1
__raw_writel(temp, &g->dir); // 设置该gpio为输入
spin_unlock(&gpio_lock);
return ;
}
EXPORT_SYMBOL(gpio_direction_input); /*
通过dir寄存器相应该gpio的位来设置gpio输入输出方向,为0,则设置成输出,为1,则设置出输入。
该函数是设置成输出,故设置dir寄存器为0.
value参数用于选择gpio设置成输出后该gpio输出的值。
*/
int gpio_direction_output(unsigned gpio, int value)
{
struct gpio_controller *__iomem g = gpio2controller(gpio);
u32 temp;
u32 mask;
if (!g)
return -EINVAL; spin_lock(&gpio_lock);
mask = __gpio_mask(gpio);
temp = __raw_readl(&g->dir);
temp &= ~mask; // 设置成0
//设置该gpio输出值
__raw_writel(mask, value ? &g->set_data : &g->clr_data);
__raw_writel(temp, &g->dir); // 设置gpio为输出
spin_unlock(&gpio_lock);
return ;
}
EXPORT_SYMBOL(gpio_direction_output); /*
向gpio设置值,0或1。
*/
void gpio_set_value(unsigned gpio, int value)
{
if (__builtin_constant_p(value)) {
struct gpio_controller *__iomem g;
u32 mask; if (gpio >= DAVINCI_N_GPIO)
__error_inval_gpio(); g = __gpio_to_controller(gpio);
mask = __gpio_mask(gpio);
if (value)
__raw_writel(mask, &g->set_data); // 该gpio输出高 else
__raw_writel(mask, &g->clr_data); // 该gpio输出低 return;
} __gpio_set(gpio, value);
}
EXPORT_SYMBOL(gpio_set_value); /*
读取gpio的值,0或1.
*/
int gpio_get_value(unsigned gpio)
{
struct gpio_controller *__iomem g; if (!__builtin_constant_p(gpio))/* 判断该gpio值是否为编译时常数,如果是常数,
函数返回 1,否则返回 0 */
return __gpio_get(gpio); if (gpio >= DAVINCI_N_GPIO)
return __error_inval_gpio(); g = __gpio_to_controller(gpio); // 读取该gpio的值 return !!(__gpio_mask(gpio) & __raw_readl(&g->in_data));
}
EXPORT_SYMBOL(gpio_get_value); /*
* We expect irqs will normally be set up as input pins, but they can also be
* used as output pins ... which is convenient for testing.
*
* NOTE: GPIO0..GPIO7 also have direct INTC hookups, which work in addition
* to their GPIOBNK0 irq (but with a bit less overhead). But we don't have
* a good way to hook those up ...
*
* All those INTC hookups (GPIO0..GPIO7 plus five IRQ banks) can also
* serve as EDMA event triggers.
*/ /*
禁止相应该irq的gpio的中断。每个gpio都可以作为中断的来源,其中gpio0-gpio7是独立的中断来源,
也就是分配独立的中断号,其他gpio则共用5个GPIOBNK中断线。其优先级可以在board-evm.c
中设置(已经介绍过)。在dm644x平台上,中断是电平边缘触发的,禁止中断其实就是既不设置
上升沿触发,也不设置下降沿触发。
*/
static void gpio_irq_disable(unsigned irq)
{
struct gpio_controller *__iomem g = get_irq_chipdata(irq);
u32 mask = __gpio_mask(irq_to_gpio(irq)); __raw_writel(mask, &g->clr_falling); // 清除下降沿触发 __raw_writel(mask, &g->clr_rising); // 清除上升沿触发 } /*
中断使能。
在dm644x平台上,中断是电平边缘触发的,其实就是设置为上升沿或下降沿中断。
*/
static void gpio_irq_enable(unsigned irq)
{
struct gpio_controller *__iomem g = get_irq_chipdata(irq);
u32 mask = __gpio_mask(irq_to_gpio(irq)); // 如果先前为下降沿中断,则使能为下降沿中断 if (irq_desc[irq].status & IRQT_FALLING)
__raw_writel(mask, &g->set_falling); // 如果先前为上升沿中断,则使能为上升沿中断 if (irq_desc[irq].status & IRQT_RISING)
__raw_writel(mask, &g->set_rising);
} /*
设置中断类型。
在dm644x平台上,中断有上升沿和下降沿两种触发方式。
*/
static int gpio_irq_type(unsigned irq, unsigned trigger)
{
struct gpio_controller *__iomem g = get_irq_chipdata(irq);
u32 mask = __gpio_mask(irq_to_gpio(irq)); if (trigger & ~(IRQT_FALLING | IRQT_RISING))
return -EINVAL; irq_desc[irq].status &= ~IRQT_BOTHEDGE;
irq_desc[irq].status |= trigger; __raw_writel(mask, (trigger & IRQT_FALLING)
? &g->set_falling : &g->clr_falling); // 设置为下降沿触发 __raw_writel(mask, (trigger & IRQT_RISING)
? &g->set_rising : &g->clr_rising); // 设置为上升沿触发 return ;
} /*
该结构体用于注册到所有irq的中断描述结构体中(struct irqdesc),
而所有中断描述结构体定义成一个全局数组irq_desc 。
*/
static struct irqchip gpio_irqchip = {
.unmask = gpio_irq_enable, /* 用于使能中断,
在enable_irq()等内核函数中会用到。*/
.mask = gpio_irq_disable,/* 用于禁止中断,
在disable_irq()等内核函数中会用到。*/
.type = gpio_irq_type, /* 用于设置中断类型,
在set_irq_type()内核函数中会用到。*/
}; /*
该函数将在下面的davinci_gpio_irq_setup中使用,将被注册到五个gpio bank中断的
irq_desc结构中,目的是处理所有级联的gpio中断。所谓级联的中断, 就是指有n个中断
共用同一个中断线。
在dm644x平台中,除了gpio0-gpio7外,其他63个gpio都共用五个gpiobank中断线,在这里,
gpio0-gpio7也被注册到gpiobank中断线,但实际上并不会使用,因为它们拥有自己的
中断线。其中,gpio0-gpio15共用IRQ_GPIOBNK0(56)中断线,gpio16-gpio31共用
IRQ_GPIOBNK1(57)中断线,gpio32-gpio47共用IRQ_GPIOBNK2(58)中断线,
gpio48-gpio63共用IRQ_GPIOBNK4(59)中断线,gpio64-gpio70共用
IRQ_GPIOBNK5(60)中断线,
因为寄存器是32位的,所以实际上只有三组寄存器,第一组包含bank0和bank1,
也就是gpio0-gpio31,第二组包含bank2和bank3,也就是gpio32-gpio63,
第三组包含bank4和bank5,也就是gpio64-gpio70,剩余了25个位没有使用。
*/
static void
gpio_irq_handler(unsigned irq, struct irqdesc *desc, struct pt_regs *regs)
{
struct gpio_controller *__iomem g = get_irq_chipdata(irq);
u32 mask = 0xffff; /* we only care about one bank */
// 如果bank中断线是寄数,则说明该中断的中断状态位在INTSTATn寄存器的高16位 if (irq & )
mask <<= ; /* temporarily mask (level sensitive) parent IRQ */
desc->chip->ack(irq);// 该ack函数会在arch/arm/mach-davinci/irq.c中注册。 while () {
u32 status;
struct irqdesc *gpio;
int n;
int res; /* ack any irqs */
/*gpio中断发生后,硬件会在INTSTATn寄存器中置位相应位,
以备程序查询,确定是哪个gpio*/
status = __raw_readl(&g->intstat) & mask;
if (!status)
break;
__raw_writel(status, &g->intstat); // 向该位写1清除 if (irq & )
status >>= ; /* now demux them to the right lowlevel handler */
// 从下面的davinci_gpio_irq_setup函数可以看出来以下程序的运作。 n = (int)get_irq_data(irq); // 获取该bank对应的第一个gpio号 gpio = &irq_desc[n]; // 获取该bank第一个gpio号对应的中断描述符 while (status) { // 该bank可能有多个gpio发生了中断 res = ffs(status); // 获取第一个发生了中断的位(1-32) n += res; /* 获得该gpio的中断线(系统实际上只有64(0-63)个中断线,
但那些共用的gpio的中断也有自己的断描述符和中断线(从64开始),
仅仅是为了管理,不能通过request_irq()函数来申请。*/
gpio += res; // 获得该gpio的中断描述符 /* 调用下面注册的do_simple_IRQ例程
其又会调用用户通过request_irq()
注册的中断例程
*/
desc_handle_irq(n - , gpio - , regs);
status >>= res;
}
}
desc->chip->unmask(irq); // 打开该irq中断线 /* now it may re-trigger */
} /*
* NOTE: for suspend/resume, probably best to make a sysdev (and class)
* with its suspend/resume calls hooking into the results of the set_wake()
* calls ... so if no gpios are wakeup events the clock can be disabled,
* with outputs left at previously set levels, and so that VDD3P3V.IOPWDN0
* can be set appropriately for GPIOV33 pins.
*/
/*
注册gpio中断例程到内核中,并初始化了一些寄存器。
该函数将会被board_evm.c(其浅析已经发表)中的evm_init()函数调用。具体调用过程如下:
start_kernel()-->setup_arch()-->init_machine = mdesc->init_machine
(init_machine是个全局函数指针变量,其指向的就是已经注册到机器描述符里evm_init());
调用函数指针init_machine()的例程是customize_machine(),其定义为
arch_initcall(customize_machine),所以,接下来的调用过程是:
start_kernel()-->do_basic_setup()-->do_initcalls()-->customize_machine()-->
init_machine()(也就是evm_init())-->davinci_gpio_irq_setup。
从上可以看出经历了两个过程,才调用davinci_gpio_irq_setup例程来初始化gpio中断。
*/
int __init davinci_gpio_irq_setup(void)
{
unsigned gpio, irq, bank, banks;
struct clk *clk; clk = clk_get(NULL, "gpio"); // 获取时钟 if (IS_ERR(clk)) {
printk(KERN_ERR "Error %ld getting gpio clock?\n",
PTR_ERR(clk));
return ;
} clk_enable(clk); // 使能gpio时钟并打开该模块电源 for (gpio = , irq = gpio_to_irq(), bank = (cpu_is_davinci_dm355() ?
IRQ_DM355_GPIOBNK0 : (cpu_is_davinci_dm6467() ?
IRQ_DM646X_GPIOBNK0 : IRQ_GPIOBNK0)); // dm644x的IRQ_GPIOBNK0(56) gpio < DAVINCI_N_GPIO; bank++) { // dm644x的DAVINCI_N_GPIO(71) struct gpio_controller *__iomem g = gpio2controller(gpio);
unsigned i; // 关该bank所有gpio的中断 __raw_writel(~, &g->clr_falling);
__raw_writel(~, &g->clr_rising); /* set up all irqs in this bank */
// 同一个bank的所有gpio共用一个中断例程gpio_irq_handler set_irq_chained_handler(bank, gpio_irq_handler);
set_irq_chipdata(bank, g);
set_irq_data(bank, (void *)irq); for (i = ; i < && gpio < DAVINCI_N_GPIO;
i++, irq++, gpio++) {
set_irq_chip(irq, &gpio_irqchip); /* 注册用于gpio中断禁止、设能
和类型选择的回调例程 */
set_irq_chipdata(irq, g); // 保存控制结构体(寄存器)的地址 set_irq_handler(irq, do_simple_IRQ);/* 为每个gpio中断设置同一个中
断例程do_simple_IRQ*/
set_irq_flags(irq, IRQF_VALID); // fiq中断有效 }
}
/*
一个共用bank中断线的gpio中断发生后的大致的流程是:
--> gpio_irq_handler --> do_simple_IRQ --> __do_irq -->
action->handler(用户使用request_irq()注册的中断例程)
*/
/* BINTEN -- per-bank interrupt enable. genirq would also let these
* bits be set/cleared dynamically.
*/
if (cpu_is_davinci_dm355())
banks = 0x3f;
else
banks = 0x1f; // 向BINTEN寄存器写入0x1f(共5个位,每个位控制1个bank),打开所有的bank中断 __raw_writel(banks, (void *__iomem)
IO_ADDRESS(DAVINCI_GPIO_BASE + 0x08)); printk(KERN_INFO "DaVinci: %d gpio irqs\n", irq - gpio_to_irq()); return ;
} gpio.h /*
* TI DaVinci GPIO Support
*
* Copyright (c) 2006 David Brownell
* Copyright (c) 2007, MontaVista Software, Inc. <source@mvista.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/ #ifndef __DAVINCI_GPIO_H
#define __DAVINCI_GPIO_H /*
* basic gpio routines
*
* board-specific init should be done by arch/.../.../board-XXX.c (maybe
* initializing banks together) rather than boot loaders; kexec() won't
* go through boot loaders.
*
* the gpio clock will be turned on when gpios are used, and you may also
* need to pay attention to PINMUX0 and PINMUX1 to be sure those pins are
* used as gpios, not with other peripherals.
*
* GPIOs are numbered 0..(DAVINCI_N_GPIO-1). For documentation, and maybe
* for later updates, code should write GPIO(N) or:
* - GPIOV18(N) for 1.8V pins, N in 0..53; same as GPIO(0)..GPIO(53)
* - GPIOV33(N) for 3.3V pins, N in 0..17; same as GPIO(54)..GPIO(70)
*
* For GPIO IRQs use gpio_to_irq(GPIO(N)) or gpio_to_irq(GPIOV33(N)) etc
* for now, that's != GPIO(N)
*/
#define GPIO(X) (X) /* 0 <= X <= 70 */
#define GPIOV18(X) (X) /* 1.8V i/o; 0 <= X <= 53 */
#define GPIOV33(X) ((X)+54) /* 3.3V i/o; 0 <= X <= 17 */ /*
寄存器都是32位到,每位对应一个gpio。
*/
struct gpio_controller {
u32 dir; // gpio方向设置寄存器 u32 out_data; // gpio设置为输出时,表示输出状态(0或1) u32 set_data; // gpio设置为输出时,用于输出高电平 u32 clr_data; // gpio设置为输出时,用于输出低电平 u32 in_data; // gpio设置为输入时,用于读取输入值 u32 set_rising; // gpio中断上升沿触发设置 u32 clr_rising; // gpio中断上升沿触发清除 u32 set_falling; // gpio中断下降沿触发设置 u32 clr_falling; // gpio中断下降沿触发清除 u32 intstat; // gpio中断状态位,由硬件设置,可读取,写1时清除。 }; /* The __gpio_to_controller() and __gpio_mask() functions inline to constants
* with constant parameters; or in outlined code they execute at runtime.
*
* You'd access the controller directly when reading or writing more than
* one gpio value at a time, and to support wired logic where the value
* being driven by the cpu need not match the value read back.
*
* These are NOT part of the cross-platform GPIO interface
*/
static inline struct gpio_controller *__iomem
__gpio_to_controller(unsigned gpio)
{
void *__iomem ptr; if (gpio >= DAVINCI_N_GPIO)
return NULL; if (gpio < )
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x10);
else if (gpio < )
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x38);
else if (gpio < )
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x60);
else
ptr = (void *__iomem)IO_ADDRESS(DAVINCI_GPIO_BASE + 0x88); return ptr;
} static inline u32 __gpio_mask(unsigned gpio)
{
return << (gpio % );
} /* The get/set/clear functions will inline when called with constant
* parameters, for low-overhead bitbanging. Illegal constant parameters
* cause link-time errors.
*
* Otherwise, calls with variable parameters use outlined functions.
*/
extern int __error_inval_gpio(void); extern void __gpio_set(unsigned gpio, int value);
extern int __gpio_get(unsigned gpio); /* Returns zero or nonzero; works for gpios configured as inputs OR
* as outputs.
*
* NOTE: changes in reported values are synchronized to the GPIO clock.
* This is most easily seen after calling gpio_set_value() and then immediatly
* gpio_get_value(), where the gpio_get_value() would return the old value
* until the GPIO clock ticks and the new value gets latched.
*/
extern int gpio_get_value(unsigned gpio);
extern void gpio_set_value(unsigned gpio, int value); /* powerup default direction is IN */
extern int gpio_direction_input(unsigned gpio);
extern int gpio_direction_output(unsigned gpio, int value); #include <asm-generic/gpio.h> /* cansleep wrappers */ extern int gpio_request(unsigned gpio, const char *tag);
extern void gpio_free(unsigned gpio); static inline int gpio_to_irq(unsigned gpio)
{
return DAVINCI_N_AINTC_IRQ + gpio;
} static inline int irq_to_gpio(unsigned irq)
{
return irq - DAVINCI_N_AINTC_IRQ;
} #endif /* __DAVINCI_GPIO_H */

gpio_request 原形代码【转】的更多相关文章

  1. gpio_request 原形代码

    http://blog.csdn.net/maopig/article/details/7428561 其原型为 int gpio_request(unsigned gpio, const char ...

  2. windows消息钩子注册底层机制浅析

    标 题: [原创]消息钩子注册浅析 作 者: RootSuLe 时 间: 2011-06-18,23:10:34 链 接: http://bbs.pediy.com/showthread.php?t= ...

  3. 自己学习smarty的一些代码 和记录

    http://www.yiibai.com/smarty/smarty_install.html  手册可以看这里 index.tpl <!DOCTYPE html> <html&g ...

  4. jstl标签库基础教程及其使用代码

    概述 在 JSP 页面中,使用标签库代替传统的 Java 片段语言来实现页面的显示逻辑已经不是新技术了,然而,由自定义标签很容易造成重复定义和非标准的实现.鉴于此,出现了 JSTL ( JSP Sta ...

  5. 基于等待队列及poll机制的按键驱动代码分析和测试代码

    按键驱动分析: #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> ...

  6. Linux下实现流水灯等功能的LED驱动代码及测试实例

    驱动代码: #include <linux/errno.h> #include <linux/kernel.h> #include <linux/module.h> ...

  7. Linux下简易蜂鸣器驱动代码及测试实例

    驱动代码: #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> ...

  8. LED字符设备驱动实例及测试代码

    驱动代码如下: #include <linux/kernel.h>//内核头文件 #include <linux/init.h>//__init等 #include <l ...

  9. 转:gpio_request

    今天再次学习SD卡驱动,遇到pgio_request这个函数,始终不知道其什么意思,看了几遍源代码才有了点感觉.现将其关键部分再此说明一下,以备自己以后复习,或是路客参考. 一般gpio_reques ...

随机推荐

  1. 自学Linux Shell9.2-基于Red Hat系统工具包存在两种方式之一:RPM包

    点击返回 自学Linux命令行与Shell脚本之路 9.2-基于Red Hat系统工具包存在两种方式之一:RPM包 本节主要介绍基于Red Had的系统(测试系统centos) 1. 工具包存在两种方 ...

  2. The 2018 ACM-ICPC China JiangSu Provincial Programming Contest快速幂取模及求逆元

    题目来源 The 2018 ACM-ICPC China JiangSu Provincial Programming Contest 35.4% 1000ms 65536K Persona5 Per ...

  3. 端午漫谈(附:Ubuntu18.04下轻量截图软件)

    先说声端午快乐- 有空就陪陪家人吧.今天陪外公吃了顿饭,陪老人家聊了会天,颇有点感触.发现技术真的是改变生活,小孩抖音自学跳舞,大人微信刷又刷,很多天海一方的老朋友都可以联系到了... 其实最有感触的 ...

  4. 在Android中通过Intent使用Bundle传递对象

    IntentBundle传递对象SerializableParcelable Android开发中有时需要在应用中或进程间传递对象,下面详细介绍Intent使用Bundle传递对象的方法.被传递的对象 ...

  5. SpringMvc的Url映射和传参案例

    Springmvc的基本使用,包括url映射.参数映射.页面跳转.ajax和文件上传 以前学习的时候写的代码案例,今天整理笔记的时候找到了,很久没有来园子了,发上来当个在线笔记用吧,免的时间长了又忘了 ...

  6. Java: 在不同windows主题下,JFrame窗口设置最佳高度的解决方案

    //设置窗口的大小,无论使用怎样的windows主题,都能灵活的应对,显示合适的窗口大小,一定要在JFrame.setVisible(true)之前调用, //替代传统的frame.setSize(w ...

  7. Python基础学习(四)

    菜鸟咀嚼python基础之继续. 一.返回函数 第三章已经简单操作了Python的高阶函数,目前继续练手函数作为返回值的特效:通常,高阶函数除了可以接收函数作为参数外,还可以把函数作为结果值返回. 案 ...

  8. Mysq中的流程控制语句的用法

    这篇博客主要是总结一下Mysq中的流程控制语句的用法,主要是:CASE,IF,IFNULL,NULLIF 1.case CASE value WHEN [compare-value] THEN res ...

  9. 走进HTTP协议之二 基本HTTP机制

    本系列第一节,我们回顾了与HTTP协议有关的基本术语和概念,本文将分析HTTP协议的基本原理与机制 HTTP协议的用途 HTTP协议用于客户端与服务器之间的通信,在通信线路两端,必定一端是客户端,另一 ...

  10. SIFT特征点检测学习一(转载)

    新手上路,先转载学习tornadomeet的博客:http://www.cnblogs.com/tornadomeet/archive/2012/08/16/2643168.html 特征点检测学习_ ...