[参考 http://hi.baidu.com/macroliu%5F/blog/item/8025218db64008a50e244477.html ]
参考文件:arch/arm/mach- s5pv210/include/mach/gpio.h , /drivers/gpio/gpiolib.c , arch/arm/plat-s3c/include/plat/gpio-core.h , arch/arm/mach-s5pv210/gpio.c .
另外, documentation/gpio.txt 文档是重要参考!
通常, GPIO分成若干个group, 每个group包含几个io port. 访问某个port时要指明哪个group哪个port, 不方便. kernel 处理方法是把所有io port整理成一个线性的空间, 即一组线性的数值, 每个io port对应于一个数值. (注意: 这里port指1个pin哦)
一,每组GPIO的数目:
#define S5PV210_GPIO_A0_NR (8)
#define S5PV210_GPIO_A1_NR (4)
#define S5PV210_GPIO_B_NR (8)
......
#define S5PV210_GPIO_ETC2_NR (8)
#define S5PV210_GPIO_ETC4_NR (6)
二,每组GPIO的起始号码
#define S5PV210_GPIO_NEXT(__gpio) /
((__gpio##_START) + (__gpio##_NR) + CONFIG_S3C_GPIO_SPACE + 1)
用 ## 粘贴符号来运算的,以A组的0起始,依次加每组的GPIO个数.
如: S5PV210_GPIO_A1_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_A0) =
S5PV210_GPIO_A0_START+ S5PV210_GPIO_A0_NR+ CONFIG_S3C_GPIO_SPACE + 1
全部的号码看这里:
enum s5p_gpio_number {
S5PV210_GPIO_A0_START = 0 ,
S5PV210_GPIO_A1_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_A0) , /*0+8+1 = 9*/
S5PV210_GPIO_B_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_A1), /*9+4+1 = 14*/
......
S5PV210_GPIO_ETC2_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_ETC1),/*438+8+1 = 447*/
S5PV210_GPIO_ETC4_START = S5PV210_GPIO_NEXT(S5PV210_GPIO_ETC2),/*447+8+1 = 456*/
/*总数是456+6+1 = 463*/
};
三,单个GPIO脚的号码
以单组的起始号
#define S5PV210_GPA0(_nr) (S5PV210_GPIO_A0_START + (_nr))
#define S5PV210_GPA1(_nr) (S5PV210_GPIO_A1_START + (_nr))
#define S5PV210_GPB(_nr) (S5PV210_GPIO_B_START + (_nr))
......
#define S5PV210_ETC2(_nr) (S5PV210_GPIO_ETC2_START + (_nr))
#define S5PV210_ETC4(_nr) (S5PV210_GPIO_ETC4_START + (_nr))
四,判断GPIO是否有效
比如: if (gpio_is_valid (S5PV210_GPB(0)))
static inline int gpio_is_valid(int number)
{
return ((unsigned)number) < ARCH_NR_GPIOS; /*好象现在改成直接返回0了*/
}
这是因为定义了:
#define ARCH_NR_GPIOS (S5PV210_ETC4(S5PV210_GPIO_ETC4_NR) + CONFIG_SAMSUNG_GPIO_EXTRA + 1)
同样类似的范围定义有: S5PV210_GPIO_END , S3C_GPIO_END.
五,申请分配GPIO -- 判断GPIO是否被占用, 若可用则占用它.
比如: if(gpio_request (S5PV210_GPH0(2), "GPH0")), 通过查看该port保存的记录标志是否为NULL来判断。gpio_desc[ARCH_NR_GPIOS]数值记录了每个io pin的情况.
注意: kernel代码的一个坏现象: 变量名与类型名经常取了相同的名字! 让人容易混淆. 比如, gpio_desc既是结构体名,又是变量名.
struct gpio_desc {
struct gpio_chip *chip; //该pin属于哪个gpio group? 此group的gpio_chip指针.
unsigned long flags; //该pin各个状态之bit标志, 比如是否FLAG_REQUESTED, 等等.
const char *label;
};
static struct gpio_desc gpio_desc [ARCH_NR_GPIOS];
gpiochip_is_requested ()仅判断port是否被占用?
六,释放GPIO
比如: gpio_free (S5PV210_GPH3(15)); 就是把对应port 的控制标志FLAG_REQUESTED清掉,之后可以再被request申请使用。
七,配置GPIO输入/输出 方向
比如: gpio_direction_output (S5PV210_GPH3(0), 1); gpio_direction_input .
八,输出output电平/读取input电平 -- gpio基本使用
比如输出一个高电平: gpio_set_value (S5PV210_GPH3(1), 1);
或者是得到输入的值: gpio_get_value (S5PV210_GPH3(8))
九,配置GPIO用途 -- 除in/out之外的其它功能
由于GPIO多复用,所以不管是把它当作GPIO使用时,还是当作中断亦或通讯口,需要配置它的用途。
比如: s3c_gpio_cfgpin (S5PV210_GPB(0), 0x2); 设置 S5PV210_GPB(0) 口的function为2, function 详情请参考芯片规格书。
API接口: s3c_gpio_cfgpin / s3c_gpio_setpull / s3c_gpio_setpin . 最终指向如下:
static struct s3c_gpio_cfg gpio_cfg = {
.cfg_eint = 0xf,
.set_config = s3c_gpio_setcfg_s3c64xx_4bit,
.set_pull = s3c_gpio_setpull_updown,
.get_pull = s3c_gpio_getpull_updown,
.set_pin = s3c_gpio_setpin_updown
};
疑问: s3c_gpio_setpin 功能与gpio_set_value 重复, 感觉没必要!???
疑问: 还没有设置port驱动电流GPxDRV 的函数???
十,上下拉
有的GPIO口可以内部配置成上拉或者下拉,这样就不需要外部再接电阻连线。配置成上拉时,驱动能力更强。配置上下拉对外部接口来说呈现的只是一种默认的电平,其本身可以对外输出高低由软件控制,就像I2C。
比如: s3c_gpio_setpull (S5PV210_GPB(0), S3C_GPIO_PULL_DOWN);
十一,配置成外中断
s3c_gpio_cfgpin(S5PV210_GPH1(5), S3C_GPIO_SFN(0xf)); //GPxCON 4-bit为0xF一般对应于INT功能. GPH1(5)对应EXT_INT[13].
set_irq_type(IRQ_EINT13, IRQ_TYPE_EDGE_BOTH);
具体应用的话还需要用request_irq注册对应的中断处理函数。
内核gpio代码的数据结构 :
** gpio_chip : 基本数据结构(gpiolib). A gpio_chip can help platforms abstract various sources of GPIOs so they can all be accessed through a common programing interface. Example sources would be SOC controllers, FPGAs, multifunction chips, dedicated GPIO expanders, and so on.
** s3c_gpio_chip : wrapper for specific implementation of gpio. This wrapper provides the necessary information for the Samsung specific gpios being registered with gpiolib.
* @chip: The chip structure to be exported via gpiolib.
* @base: The base pointer to the gpio configuration registers.
* @config: special function and pull-resistor control information.
* @pm_save: Save information for suspend/resume support.
** GPIO implemention framework (OPTIONAL) -- gpiolib: 上述gpio_xxxx函数是对外的基本API函数. 底层实现是s3c_gpiolib_xxxx : s3c_gpiolib_input, s3c_gpiolib_output, s3c_gpiolib_get, s3c_gpiolib_set. 其中s3c_gpiolib_input / s3c_gpiolib_output 是对应于s3c24xx芯片2-bits-per-port的GPxCON寄存器. 而PC110的GPxCON是4-bits-per-port, 故用samsung_gpiolib_4bit_ input / samsung_gpiolib_4bit_output 实现.
** s3c_gpio_cfg : 除in/out之外其它功能的config函数, 比如: set_config, set_pullupdown. API接口为s3c_gpio_xxxx.
GPIO驱动代码流程
** core_initcall(s5pv210_gpiolib_init) 内核初始化call.
** s5pv210_gpiolib_init -> samsung_gpiolib_add_4bit & s3c_gpiolib_add 注册所有port的数据结构并关联API接口函数.
** 把API接口函数export出去. 比如EXPORT_SYMBOL_GPL(gpio_request), 等等.
**** S5PC110 GPIO 及特性 ****
S5PC110 includes 237 multi-functional input/ output port pins and 142 memory port pins. There are 34 general port groups and 2 memory port groups as listed below:
GPA0: 8 in/out port - 2xUART with flow control
GPA1: 4 in/out port - 2xUART without flow control or 1xUART with flow control
GPB: 8 in/out port - 2x SPI --Fast I/O (3.3V I/O)
GPC0: 5 in/out port - I2S, PCM, AC97
GPC1: 5 in/out port - I2S, SPDIF, LCD_FRM
GPD0: 4 in/out port - PWM
GPD1: 6 in/out port - 3xI2C, PWM
GPE0,1: 13 in/out port - Camera I/F
GPF0,1,2,3: 30 in/out port - LCD I/F
GPG0,1,2,3: 28 in/out port - 4xMMC channel (Channel 0 and 2 support 4-bit and 8-bit mode, but channel 1, and channel 3 support only 4-bit mode) --Fast I/O (3.3V I/O)
GPH0,1,2,3: 32 in/out port - Key pad, External Wake-up (up-to 32-bit). (GPH* groups are in Alive region)
GPI: Low Power I2S, PCM (in/out port is not used), PDN configuration for power down is controlled by AUDIO_SS PDN Register.
GPJ0,1,2,3,4: 35 in/out port - Modem IF, CAMIF, CFCON, KEYPAD, SROM ADDR[22:16]
MP0_1,2,3: 20 in/out port - Control signals of EBI (SROM, NF, OneNAND)
MP0_4,5,6,7: 32 in/out memory port - EBI (For more information about EBI configuration, refer to Chapter 5, and 6)
MP1_0~8: 71 DRAM1 ports (in/out port is not used) --DRAM I/O (1.8V IO)
MP2_0~8: 71 DRAM2 ports (in/out port is not used) --DRAM I/O (1.8V IO)
ETC0, ETC1, ETC2, ETC4: 28 in/out ETC ports - JTAG, Operating Mode, RESET, CLOCK (ETC3 is reserved)
** GPIO分成3个type: Normal I/O, Fast I/O, DRAM I/O.
** GPIO分成2个part: alive-part , off-part. 区别在于sleep mode时是否有power. alive-part的port可作wake -up信号.
** GPIO有多种I/O Control Type.
** GPIO有多种Pad Type.
** Each Port Group has 2 types of control registers. One works in normal mode, and the other works in power down mode (STOP, DEEP-STOP, SLEEP mode). Normal registers (For example, GPA0 CON , GPA0 DAT , GPA0 PUD , and GPA0 DRV ) are the former, and power down registers (For example, GPA0CONPDN, and GPA0PUDPDN) are the latter.
gpio寄存器 基本都是GPx CON , GPx DAT , GPx PUD , GPx DRV . 另外, power down mode下对应的GPx CONPDN , GPx PUD PDN . 另外, 中断对应的GPx_ INT_CON , GPx_ INT_FLTCON n, GPx_ INT_MASK , GPx_ INT_PEND , INT_PRIORITY . 另外, alive part对应的EXT_INT_x_CON, EXT_INT_x_FLTCON, EXT_INT_x_MASK, EXT_INT_x_PEND. 最后, powd down mode的PDNEN.
** GPIO Interrupt : In interrupt function, it is important to understand the filter operation. S5PC110 uses two types of filters to detect interrupt : delay filter and digital filter . 改善稳定性. When you use interrupt function, set either delay or digital filter enabled in order to detect interrupt. GPIO Interrupt cannot use for wake-up source. For wake-up interrupt source, you can use External interrupt.
** External Interrupt : External Interrupt consists of 32 bits. EXT_INT[31:0] are used for wake-up source in Power down mode. In idle mode, all interrupts can be wake-up source; the other groups of external interrupts also can be the wake-up sources. EXT_INT[0] can be used PS_HOLD_CONTROL.
详解 s3c_gpio_cfgpin :
例子:
目的: 设置GPH3_4口为KP_ROW[3]功能。
实现: s3c_gpio_cfgpin(S5PV210_GPH3(1), S3C_GPIO_SFN(3))
分析:
S5PV210_GPH3(1) = S5PV210_GPIO_H3_START + 1 = 155+1 = 156 ;/*其实不用算出来是多少,对应名字就好*/
S3C_GPIO_SFN(3) = 0xfffffff3;
所以执行的是: s3c_gpio_cfgpin(156,0xfffffff3)
/*arch/arm/plat-samsung/gpio-config.c*/
int s3c_gpio_cfgpin(unsigned int pin, unsigned int config)
{
struct s3c_gpio_chip *chip = s3c_gpiolib_getchip(pin) ;
/****************************************************************************************************
s3c_gpiolib_getchip也就是去查找s3c_gpios[chip] ,
s3c_gpios这个数组的来源:
s5pv210_gpiolib_init时,执行samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit, ARRAY_SIZE(s5pv210_gpio_4bit));
其中执行了 s3c_gpiolib_track(s5pv210_gpio_4bit),在这里将s5pv210_gpio_4bit数组的内容,按照每个对应 ngpio 的大小,重新分配给 s3c_gpios。
===>这个方法不好, s3c_gpios依赖于 CONFIG_S3C_GPIO_TRACK宏, 若未定义则麻烦!???
所以s3c_gpios相当于s5pv210_gpio_4bit,在这个数组里找到 S5PV210_GPH3(0) 对应的部分:
.base = S5PV210_GPH3_BASE,
.config = &gpio_cfg_noint,
.chip = {
.base = S5PV210_GPH3(0),
.ngpio = S5PV210_GPIO_H3_NR,
.label = "GPH3",
****************************************************************************************************/
unsigned long flags;
int offset;
int ret;
if (!chip)
return -EINVAL;
offset = pin - chip->chip.base; /*S5PV210_GPH3(1)-S5PV210_GPH3(0)= 1*/
local_irq_save(flags);
ret = s3c_gpio_do_setcfg(chip, offset, config); /*(chip, 1, 0xfffffff3)*/
/****************************************************************************************************
(chip->config->set_config)(chip, off, config);
找到对应的是 arch/arm/mach-s5pv210/gpio.c
static struct s3c_gpio_cfg gpio_cfg = {
.cfg_eint = 0xf,
.set_config = s3c_gpio_setcfg_s3c64xx_4bit,
.set_pull = s3c_gpio_setpull_updown,
.get_pull = s3c_gpio_getpull_updown,
.set_pin = s3c_gpio_setpin_updown
};
所以:
(chip->config->set_config)(chip, off, config);
就是
s3c_gpio_setcfg_s3c64xx_4bit(chip, off, config); /*(chip, 1, 0xfffffff3)*/
看文件 arch/arm/plat-samsung/gpio-config.c
int s3c_gpio_setcfg_s3c64xx_4bit(struct s3c_gpio_chip *chip,
unsigned int off, unsigned int cfg)
{
void __iomem *reg = chip->base; /*reg的地址为S5PV210_GPH3_BASE = 0xE0200C60*/
unsigned int shift = (off & 7) * 4; /*(1&7)*4 = 4*/
u32 con;
if (off < 8 && chip->chip.ngpio > 8) /*由于当前base寄存器只可能设置8个GPIO,所以如果ngpio大于8则当前寄存器减4*/
reg -= 4;
if (s3c_gpio_is_cfg_special(cfg)) { /*看一下cfg有没有超出范围*/ ==> 此check没实际意义???
cfg &= 0xf;
cfg <<= shift; /*cfg = 3<<4 = 0x30*/
}
con = __raw_readl(reg);
con &= ~(0xf << shift); /*与上0xffffff0f*/
con |= cfg; /*或上0x30*/
__raw_writel(con, reg);
return 0;
}
此处执行的结果是:
将0xE0200C60寄存器的[7..4]赋值3,
也即设置GPH3_4口为KP_ROW[3]功能。
****************************************************************************************************/
local_irq_restore(flags);
return ret;
}