GPIO驱动分析 & GPIO使用方法 -- S5PC110、S5PC210

[参考 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;
}

你可能感兴趣的:(GPIO驱动分析 & GPIO使用方法 -- S5PC110、S5PC210)