上文中 虽然讲述了如何实现IO空间的静态映射方法,但调用s3c_gpio_cfgpin、s3c_gpio_setpull、gpio_direction_output就可以访问IO空间,这又是如何实现的呢?本文就来讲述一下实现过程。
回到smdkv210_map_io函数中
staticvoid __init smdkv210_map_io(void)
{
s5p_init_io(NULL, 0, S5P_VA_CHIPID);
s3c24xx_init_clocks(24000000);
s5pv210_gpiolib_init();
s3c24xx_init_uarts(smdkv210_uartcfgs,
ARRAY_SIZE(smdkv210_uartcfgs));
#ifndefCONFIG_S5P_HIGH_RES_TIMERS
s5p_set_timer_source(S5P_PWM2, S5P_PWM4);
#endif
s5p_reserve_bootmem(s5pv210_media_devs,
ARRAY_SIZE(s5pv210_media_devs),
S5P_RANGE_MFC);
}
在执行完s5p_init_io(NULL, 0, S5P_VA_CHIPID)之后,调用了s5pv210_gpiolib_init()函数,这个就是解答疑问的关键点。
__init int s5pv210_gpiolib_init(void)
{
structs3c_gpio_chip *chip = s5pv210_gpio_4bit;
intnr_chips = ARRAY_SIZE(s5pv210_gpio_4bit);
int gpioint_group = 0;
int i = 0;
for (i = 0; i < nr_chips; i++, chip++) {
if (chip->config == NULL) {
chip->config= &gpio_cfg;
chip->group = gpioint_group++;
}
if (chip->base == NULL)
chip->base= S5PV210_BANK_BASE(i);
}
samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit,nr_chips);
s5p_register_gpioint_bank(IRQ_GPIOINT, 0,S5P_GPIOINT_GROUP_MAXNR);
return 0;
}
需要说明的是GPIO被分成了若干组,GPA0、GPA0、GPB、GPC0……,其中多作为外部中断IO组:GPH0、GPH1、GPH2、GPH3。
s5pv210_gpio_4bit结构体数组gpio分组的数据:
staticstruct s3c_gpio_chip s5pv210_gpio_4bit[] = {
{
.chip ={
.base = S5PV210_GPA0(0),
.ngpio = S5PV210_GPIO_A0_NR,
.label = "GPA0",
},
}, {
.chip ={
.base = S5PV210_GPA1(0),
.ngpio = S5PV210_GPIO_A1_NR,
.label = "GPA1",
},
}, {
.chip ={
.base = S5PV210_GPB(0),
.ngpio = S5PV210_GPIO_B_NR,
.label = "GPB",
},
}, {
.chip ={
.base = S5PV210_GPC0(0),
.ngpio = S5PV210_GPIO_C0_NR,
.label = "GPC0",
},
}, {
.chip ={
.base = S5PV210_GPC1(0),
.ngpio = S5PV210_GPIO_C1_NR,
.label = "GPC1",
},
}, {
.chip ={
.base = S5PV210_GPD0(0),
.ngpio = S5PV210_GPIO_D0_NR,
.label = "GPD0",
},
}, {
.chip ={
.base = S5PV210_GPD1(0),
.ngpio = S5PV210_GPIO_D1_NR,
.label = "GPD1",
},
}, {
.chip ={
.base = S5PV210_GPE0(0),
.ngpio = S5PV210_GPIO_E0_NR,
.label = "GPE0",
},
}, {
.chip ={
.base = S5PV210_GPE1(0),
.ngpio = S5PV210_GPIO_E1_NR,
.label = "GPE1",
},
}, {
.chip ={
.base = S5PV210_GPF0(0),
.ngpio = S5PV210_GPIO_F0_NR,
.label = "GPF0",
},
}, {
.chip ={
.base = S5PV210_GPF1(0),
.ngpio = S5PV210_GPIO_F1_NR,
.label = "GPF1",
},
}, {
.chip ={
.base = S5PV210_GPF2(0),
.ngpio = S5PV210_GPIO_F2_NR,
.label = "GPF2",
},
}, {
.chip ={
.base = S5PV210_GPF3(0),
.ngpio = S5PV210_GPIO_F3_NR,
.label = "GPF3",
},
}, {
.chip ={
.base = S5PV210_GPG0(0),
.ngpio = S5PV210_GPIO_G0_NR,
.label = "GPG0",
},
}, {
.chip ={
.base = S5PV210_GPG1(0),
.ngpio = S5PV210_GPIO_G1_NR,
.label = "GPG1",
},
}, {
.chip ={
.base = S5PV210_GPG2(0),
.ngpio = S5PV210_GPIO_G2_NR,
.label = "GPG2",
},
}, {
.chip ={
.base = S5PV210_GPG3(0),
.ngpio = S5PV210_GPIO_G3_NR,
.label = "GPG3",
},
}, {
.config =&gpio_cfg_noint,
.chip ={
.base = S5PV210_GPI(0),
.ngpio = S5PV210_GPIO_I_NR,
.label = "GPI",
},
}, {
.chip ={
.base = S5PV210_GPJ0(0),
.ngpio = S5PV210_GPIO_J0_NR,
.label = "GPJ0",
},
}, {
.chip ={
.base = S5PV210_GPJ1(0),
.ngpio = S5PV210_GPIO_J1_NR,
.label = "GPJ1",
},
}, {
.chip ={
.base = S5PV210_GPJ2(0),
.ngpio = S5PV210_GPIO_J2_NR,
.label = "GPJ2",
},
}, {
.chip ={
.base = S5PV210_GPJ3(0),
.ngpio = S5PV210_GPIO_J3_NR,
.label = "GPJ3",
},
}, {
.chip ={
.base = S5PV210_GPJ4(0),
.ngpio = S5PV210_GPIO_J4_NR,
.label = "GPJ4",
},
}, {
.config =&gpio_cfg_noint,
.chip ={
.base = S5PV210_MP01(0),
.ngpio = S5PV210_GPIO_MP01_NR,
.label = "MP01",
},
}, {
.config =&gpio_cfg_noint,
.chip ={
.base = S5PV210_MP02(0),
.ngpio = S5PV210_GPIO_MP02_NR,
.label = "MP02",
},
}, {
.config =&gpio_cfg_noint,
.chip ={
.base = S5PV210_MP03(0),
.ngpio = S5PV210_GPIO_MP03_NR,
.label = "MP03",
},
}, {
.config =&gpio_cfg_noint,
.chip ={
.base = S5PV210_MP04(0),
.ngpio = S5PV210_GPIO_MP04_NR,
.label = "MP04",
},
}, {
.config =&gpio_cfg_noint,
.chip ={
.base = S5PV210_MP05(0),
.ngpio = S5PV210_GPIO_MP05_NR,
.label = "MP05",
},
}, {
.base =(S5P_VA_GPIO + 0xC00),
.config =&gpio_cfg_noint,
.irq_base = IRQ_EINT(0),
.chip ={
.base = S5PV210_GPH0(0),
.ngpio = S5PV210_GPIO_H0_NR,
.label = "GPH0",
.to_irq = samsung_gpiolib_to_irq,
},
}, {
.base =(S5P_VA_GPIO + 0xC20),
.config =&gpio_cfg_noint,
.irq_base = IRQ_EINT(8),
.chip ={
.base = S5PV210_GPH1(0),
.ngpio = S5PV210_GPIO_H1_NR,
.label = "GPH1",
.to_irq = samsung_gpiolib_to_irq,
},
}, {
.base =(S5P_VA_GPIO + 0xC40),
.config =&gpio_cfg_noint,
.irq_base = IRQ_EINT(16),
.chip ={
.base = S5PV210_GPH2(0),
.ngpio = S5PV210_GPIO_H2_NR,
.label = "GPH2",
.to_irq = samsung_gpiolib_to_irq,
},
}, {
.base =(S5P_VA_GPIO + 0xC60),
.config =&gpio_cfg_noint,
.irq_base = IRQ_EINT(24),
.chip ={
.base = S5PV210_GPH3(0),
.ngpio = S5PV210_GPIO_H3_NR,
.label = "GPH3",
.to_irq = samsung_gpiolib_to_irq,
},
},
};
对于每一组GPIO都设置有struct s3c_gpio_cfg函数为主的结构体config
structs3c_gpio_cfg {
unsigned int cfg_eint;
s3c_gpio_pull_t (*get_pull)(struct s3c_gpio_chip *chip, unsigned offs);
int (*set_pull)(structs3c_gpio_chip *chip, unsigned offs,
s3c_gpio_pull_t pull);
unsigned (*get_config)(struct s3c_gpio_chip*chip, unsigned offs);
int (*set_config)(struct s3c_gpio_chip *chip, unsigned offs,
unsigned config);
};
当默认没有时需要赋值:
if(chip->config == NULL) {
chip->config = &gpio_cfg;
}
当然一些在s5pv210_gpio_4bit结构体数组中有些已经包含了,例如:
.config = &gpio_cfg_noint
他们的实现分别如下:
staticstruct s3c_gpio_cfg gpio_cfg = {
.set_config =s3c_gpio_setcfg_s3c64xx_4bit,
.set_pull =s3c_gpio_setpull_updown,
.get_pull =s3c_gpio_getpull_updown,
};
staticstruct s3c_gpio_cfg gpio_cfg_noint ={
.set_config =s3c_gpio_setcfg_s3c64xx_4bit,
.set_pull =s3c_gpio_setpull_updown,
.get_pull =s3c_gpio_getpull_updown,
};
具体实现:
1)s3c_gpio_setcfg_s3c64xx_4bit
int s3c_gpio_setcfg_s3c64xx_4bit(structs3c_gpio_chip *chip,
unsigned int off, unsigned int cfg)
{
void __iomem *reg = chip->base;
unsigned int shift = (off & 7) * 4;
u32 con;
if (off < 8 &&chip->chip.ngpio > 8)
reg -= 4;
if (s3c_gpio_is_cfg_special(cfg)) {
cfg &= 0xf;
cfg <<= shift;
}
con =__raw_readl(reg);
con &= ~(0xf << shift);
con |= cfg;
__raw_writel(con, reg);
return 0;
}
2)s3c_gpio_setpull_updown
ints3c_gpio_setpull_updown(struct s3c_gpio_chip *chip,
unsigned int off, s3c_gpio_pull_t pull)
{
void __iomem *reg = chip->base + 0x08;
int shift = off * 2;
u32 pup;
pup =__raw_readl(reg);
pup &= ~(3 << shift);
pup |= pull << shift;
__raw_writel(pup,reg);
return 0;
}
函数的实现过程都是使用__raw_readl 、__raw_writel原始的操作方法操作IO端口的虚拟地址。
同样的,对每一组gpio需要在之前映射好的gpio虚拟地址中找到自己位置,这样后来者才能够直接、简单、准确操作每一组gpio的寄存器和每个gpio的配置。
第一种在定义数组是直接就给出了GPIO的虚拟地址
.base =(S5P_VA_GPIO + 0xC40),
第二种就是通过如下方式实现:
if(chip->base == NULL)
chip->base =S5PV210_BANK_BASE(i);
其中#defineS5PV210_BANK_BASE(bank_nr) (S5P_VA_GPIO +((bank_nr) * 0x20))
很好理解:
S5P_VA_GPIO就是整个GPIO空间的虚拟地址的开始。虚拟地址与物理地址的连续线性映射,就可以通过每组GPIO的物理地址的偏移加上S5P_VA_GPIO得到自己的虚拟空间地址。
注册所有的io port的数据结构,并关联API接口函数, 导出API
void__init samsung_gpiolib_add_4bit_chips(struct s3c_gpio_chip *chip,
int nr_chips)
{
for (; nr_chips > 0; nr_chips--, chip++){
samsung_gpiolib_add_4bit(chip);
s3c_gpiolib_add(chip);
}
}
à
void__init samsung_gpiolib_add_4bit(struct s3c_gpio_chip *chip)
{
chip->chip.direction_input= samsung_gpiolib_4bit_input;
chip->chip.direction_output= samsung_gpiolib_4bit_output;
chip->pm =__gpio_pm(&s3c_gpio_pm_4bit);
}
à
static intsamsung_gpiolib_4bit_input(struct gpio_chip *chip,
unsigned int offset)
{
struct s3c_gpio_chip *ourchip =to_s3c_gpio(chip);
void __iomem *base = ourchip->base;
unsigned long con;
con = __raw_readl(base + GPIOCON_OFF);
con &= ~(0xf < __raw_writel(con, base + GPIOCON_OFF); gpio_dbg("%s: %p: CON now%08lx\n", __func__, base, con); return 0; } static intsamsung_gpiolib_4bit_output(struct gpio_chip *chip, unsigned int offset, int value) { struct s3c_gpio_chip *ourchip =to_s3c_gpio(chip); void __iomem *base = ourchip->base; unsigned long con; unsigned long dat; con = __raw_readl(base + GPIOCON_OFF); con &= ~(0xf < con |= 0x1 << con_4bit_shift(offset); dat = __raw_readl(base + GPIODAT_OFF); if (value) dat |= 1 << offset; else dat &= ~(1 << offset); __raw_writel(dat, base + GPIODAT_OFF); __raw_writel(con, base + GPIOCON_OFF); __raw_writel(dat, base + GPIODAT_OFF); gpio_dbg("%s: %p: CON %08lx, DAT%08lx\n", __func__, base, con, dat); return 0; } 在s3c_gpiolib_add函数中也实现了: __init void s3c_gpiolib_add(structs3c_gpio_chip *chip) { …… if (!gc->direction_input) gc->direction_input =s3c_gpiolib_input; if (!gc->direction_output) gc->direction_output =s3c_gpiolib_output; if (!gc->set) gc->set = s3c_gpiolib_set; if (!gc->get) gc->get = s3c_gpiolib_get; …… } 再倒回来看看下面的几个方法: .set_config = s3c_gpio_setcfg_s3c64xx_4bit, .set_pull = s3c_gpio_setpull_updown, .get_pull = s3c_gpio_getpull_updown, chip->chip.direction_input= samsung_gpiolib_4bit_input; chip->chip.direction_output= samsung_gpiolib_4bit_output; 1) 导出API : EXPORT_SYMBOL_GPL(gpio_direction_input); 关联API接口函数 intgpio_direction_input(unsigned gpio) { …… status = chip->direction_input(chip,gpio); …… } 导出API : EXPORT_SYMBOL_GPL(gpio_direction_output); 关联API接口函数 intgpio_direction_output(unsigned gpio, int value) { …… status = chip->direction_output(chip,gpio, value); …… } 导出API EXPORT_SYMBOL(s3c_gpio_cfgpin); 关联API接口函数 ints3c_gpio_cfgpin(unsigned int pin, unsigned int config) { …… ret = s3c_gpio_do_setcfg(chip, offset,config); …… } à static inline ints3c_gpio_do_setcfg(struct s3c_gpio_chip *chip, unsigned int off, unsigned int config) { return(chip->config->set_config)(chip, off, config); } 到此,把内核调用s3c_gpio_cfgpin、gpio_direction_input、gpio_direction_output的实现流程基本上理完了。 下面再讲述两个函数: gpio_set_value和gpio_get_value 这两个函数也是经常用到,虽然gpio_set_value与gpio_direction_output很类似。不过省去了设置输出的功能,只设置DATA寄存器的,而不改变io口的方向。 #definegpio_get_value __gpio_get_value #definegpio_set_value __gpio_set_value à int __gpio_get_value(unsignedgpio) { structgpio_chip *chip; intvalue; chip= gpio_to_chip(gpio); WARN_ON(chip->can_sleep); value = chip->get ? chip->get(chip,gpio - chip->base) : 0; trace_gpio_value(gpio,1, value); returnvalue; } EXPORT_SYMBOL_GPL(__gpio_get_value); void __gpio_set_value(unsignedgpio, int value) { structgpio_chip *chip; chip= gpio_to_chip(gpio); WARN_ON(chip->can_sleep); trace_gpio_value(gpio,0, value); chip->set(chip, gpio - chip->base,value); } EXPORT_SYMBOL_GPL(__gpio_set_value); chip->set和chip->get的赋值是在s3c_gpiolib_add: if (!gc->set) gc->set = s3c_gpiolib_set; if (!gc->get) gc->get = s3c_gpiolib_get; 这个上面已经出现过。 函数原型: staticvoid s3c_gpiolib_set(struct gpio_chip *chip, unsigned offset, int value) { struct s3c_gpio_chip *ourchip =to_s3c_gpio(chip); void __iomem *base = ourchip->base; unsigned long flags; unsigned long dat; s3c_gpio_lock(ourchip, flags); dat = __raw_readl(base + 0x04); dat &= ~(1 << offset); if (value) dat |= 1 << offset; __raw_writel(dat, base + 0x04); s3c_gpio_unlock(ourchip, flags); } staticint s3c_gpiolib_get(struct gpio_chip *chip, unsigned offset) { struct s3c_gpio_chip *ourchip =to_s3c_gpio(chip); unsigned long val; val = __raw_readl(ourchip->base + 0x04); val >>= offset; val &= 1; return val; } 到此,对于GPIO常用的操作和实现流程都已经讲述完毕!2.4.2 关联API接口函数,导出API