针对该gpio.c的硬件手册是SPRUE25.pdf,可到TI的网站(www.ti.com)上下载,或直接在谷歌里搜索。
以下是两个文件gpio.c和gpio.h的浅析。
gpio.c
/*
* 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获取设置值。
逻辑上各个gpio都有自己的中断号,这样做的目的是使得驱动程序能通过request_irq()函数来申请该gpio的中断,而实际上除了gpio0-gpio7外,其他gpio都是共享四个gpiobank中断号的。通过gpio_irq_handler例程实现了共享中断号的目的。
*/
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 0;
}
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 0;
}
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 0;
}
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 exp