参考文件: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 inAliveregion)
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, GPA0CON, GPA0DAT, GPA0PUD, and GPA0DRV) are the former, and power down registers (For example, GPA0CONPDN, and GPA0PUDPDN) are the latter.
gpio寄存器基本都是GPxCON, GPxDAT, GPxPUD, GPxDRV. 另外, power down mode下对应的GPxCONPDN, GPxPUDPDN. 另外, 中断对应的GPx_INT_CON, GPx_INT_FLTCONn, 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 thefilteroperation. S5PC110 uses two types of filters to detect interrupt :delay filteranddigital 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;
}