现在的位置: 首页 > 综合 > 正文

GPIO调用方法的实现流程

2018年08月23日 ⁄ 综合 ⁄ 共 12628字 ⁄ 字号 评论关闭
文章目录

GPIO调用方法的实现流程

上文中<IO空间的静态映射基本过程>虽然讲述了如何实现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;

}

2.1 s5pv210_gpio_4bit

需要说明的是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,

        },

    },

};

2.2 chip->config = &gpio_cfg

对于每一组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端口的虚拟地址。

2.3 chip->base = S5PV210_BANK_BASE(i)

同样的,对每一组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得到自己的虚拟空间地址。

 

2.4 samsung_gpiolib_add_4bit_chips(s5pv210_gpio_4bit,nr_chips)

注册所有的io port的数据结构,并关联API接口函数, 导出API

2.4.1 注册过程

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 <<con_4bit_shift(offset));

    __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_4bit_shift(offset));

    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;

……

}

2.4.2 关联API接口函数,导出API

再倒回来看看下面的几个方法:

.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常用的操作和实现流程都已经讲述完毕!

抱歉!评论已关闭.