1. 程式人生 > >LInux-3.0.8中基於S5PV210的GPIO模塊代碼追蹤和分析

LInux-3.0.8中基於S5PV210的GPIO模塊代碼追蹤和分析

clas deb down then rect drivers 基於 lee 使用

  編寫按鍵驅動時,想知道內核是如何管理GPIO的,所以開始追蹤代碼,中間走了一些彎路,現記錄於此。

  追蹤代碼之前,我猜測:第一,這部分代碼應該在系統set up階段執行;第二,GPIO的代碼應該在machine或者platform或者vendor相關的目錄下。事實證明,第一點是正確的,第二點基本是錯誤的,因為內核依靠對GPIO的抽象來管理之,這層抽象層給具體的machine留出了一些它們需要是實現的接口,這與其他的設備驅動框架在使用上是很類似的,當然,GPIO也是一種設備啊... ...所以,管理GPIO的多數代碼位於drivers/gpio/目錄下。

  好了,開始走讀代碼,那麽對於S5PV210這塊SoC,GPIO子系統的入口函數在哪裏呢?在drivers/gpio/gpio-s5pv210.c中,入口函數的實現如下圖所示:

技術分享圖片

  入口函數沒幾行代碼,但是下面所有的內容都要從它開始,所以一點一點來吧,這裏先羅列一下下面要敘述的內容:

1. struct s3c_gpio_chip的內容,以及重要的數組s5pv210_gpio_4bit;

2. 上述結構體中的成員config和base;

3. samsung_gpiolib_add_4bit_chips()的分析,這部分比較長。

  第一部分,struct s3c_gpio_chip的介紹以及重要的數組s5pv210_gpio_4bit

  技術分享圖片

  從面向對象的角度來看,s3c_gpio_chip是gpio_chip的子類,但是又新增了一些新的屬性和操作。雖說它的名字是chip,但是一個s3c_gpio_chip描述的是一個GPIO bank,如GPA0、GPA1、GPB等等, 這一點在接著要介紹的數組s5pv210_gpio_4bit中得以清晰體現。

  base成員表示的是當前bank的控制寄存器的起始虛擬地址,註意是虛擬地址。

  config成員的類型是struct s3c_gpio_cfg,具體的結構是:

 1 struct s3c_gpio_cfg {
 2     unsigned int    cfg_eint;
 3 
 4     s3c_gpio_pull_t    (*get_pull)(struct s3c_gpio_chip *chip, unsigned offs);
 5     int        (*set_pull)(struct s3c_gpio_chip *chip, unsigned offs,
6 s3c_gpio_pull_t pull); 7 8 unsigned (*get_config)(struct s3c_gpio_chip *chip, unsigned offs); 9 int (*set_config)(struct s3c_gpio_chip *chip, unsigned offs, 10 unsigned config); 11 };

  可見config成員用於控制和查看當前bank的某個pin的上下拉電阻的狀態,以及設置和查看某引腳上的復用功能,這個成員作為子類struct s3c_gpio_cfg的新成員,說明以上這兩種功能在不同的machine以及GPIO IP上的差別是不能忽略的。

  struct s3c_gpio_cfg中很重要的一個成員就是它的父類struct gpio_chip,它內容很多,不過看成員名字就基本能明白含義:

 1 struct gpio_chip {
 2     const char        *label;
 3     struct device        *dev;
 4     struct module        *owner;
 5 
 6     int            (*request)(struct gpio_chip *chip,
 7                         unsigned offset);
 8     void            (*free)(struct gpio_chip *chip,
 9                         unsigned offset);
10 
11     int            (*direction_input)(struct gpio_chip *chip,
12                         unsigned offset);
13     int            (*get)(struct gpio_chip *chip,
14                         unsigned offset);
15     int            (*direction_output)(struct gpio_chip *chip,
16                         unsigned offset, int value);
17     int            (*set_debounce)(struct gpio_chip *chip,
18                         unsigned offset, unsigned debounce);
19 
20     void            (*set)(struct gpio_chip *chip,
21                         unsigned offset, int value);
22 
23     int            (*to_irq)(struct gpio_chip *chip,
24                         unsigned offset);
25 
26     void            (*dbg_show)(struct seq_file *s,
27                         struct gpio_chip *chip);
28     int            base;
29     u16            ngpio;
30     const char        *const *names;
31     unsigned        can_sleep:1;
32     unsigned        exported:1;
33 
34 #if defined(CONFIG_OF_GPIO)
35     /*
36      * If CONFIG_OF is enabled, then all GPIO controllers described in the
37      * device tree automatically may have an OF translation
38      */
39     struct device_node *of_node;
40     int of_gpio_n_cells;
41     int (*of_xlate)(struct gpio_chip *gc, struct device_node *np,
42                 const void *gpio_spec, u32 *flags);
43 #endif
44 };

  這個結構體中的request free等函數指針與gpio的request、free、direction_input、direction_output等函數的實現有關系。

  接著看一下數組s5pv210_gpio_4bit,首先,這個名字中包含4bit,意思是每個bank的gpio控制寄存器中,每4bit控制一個gpio pin。這個數組很長,我們只取出個別元素看一下:

 1 static struct s3c_gpio_chip s5pv210_gpio_4bit[] = {
 2     {
 3         .chip    = {
 4             .base    = S5PV210_GPA0(0),
 5             .ngpio    = S5PV210_GPIO_A0_NR,
 6             .label    = "GPA0",
 7         },
 8     }, {
 9         .chip    = {
10             .base    = S5PV210_GPA1(0),
11             .ngpio    = S5PV210_GPIO_A1_NR,
12             .label    = "GPA1",
13         },
14     }, {
15         .chip    = {
16             .base    = S5PV210_GPB(0),
17             .ngpio    = S5PV210_GPIO_B_NR,
18             .label    = "GPB",
19         },
20     },
21 ... ....
22 {
23         .base    = (S5P_VA_GPIO + 0xC40),
24         .config    = &gpio_cfg_noint,
25         .irq_base = IRQ_EINT(16),
26         .chip    = {
27             .base    = S5PV210_GPH2(0),
28             .ngpio    = S5PV210_GPIO_H2_NR,
29             .label    = "GPH2",
30             .to_irq = samsung_gpiolib_to_irq,
31         },
32     }, {
33         .base    = (S5P_VA_GPIO + 0xC60),
34         .config    = &gpio_cfg_noint,
35         .irq_base = IRQ_EINT(24),
36         .chip    = {
37             .base    = S5PV210_GPH3(0),
38             .ngpio    = S5PV210_GPIO_H3_NR,
39             .label    = "GPH3",
40             .to_irq = samsung_gpiolib_to_irq,
41         },
42     },
43 };

  這個數組描述了一些S5PV210上的gpio,但是對比數據手冊,有一些gpio並沒有出現在這裏,但是這已經足夠多了,可以說這個數組描述了系統中多數能夠使用的gpio,並初始化了一些信息,比如當前bank的第一個pin的編號、這個bank中所有pin的數量等等,這些都是宏定義,具體的需要看mach-s5pv210相關的頭文件,由於涉及到的宏太多,而且沒什麽難度,這裏就不記錄了。

  第二部分,struct s3c_gpio_chip中的config成員和base成員初始化。

  這一部分的處理在s5pv210_gpiolib_init函數中,可以看到s5pv210_gpio_4bit中很多元素的config成員沒有初始化,也就是NULL,那麽這裏就需要給其賦值為gpio_cfg,這個變量就在當前文件中,定義如下:

1 static struct s3c_gpio_cfg gpio_cfg = {
2     .set_config    = s3c_gpio_setcfg_s3c64xx_4bit,
3     .set_pull    = s3c_gpio_setpull_updown,
4     .get_pull    = s3c_gpio_getpull_updown,
5 };

  下面就看看這三個函數吧,看名字應該就能知道其功能了。

#ifdef CONFIG_S3C_GPIO_CFG_S3C64XX
int s3c_gpio_setcfg_s3c64xx_4bit(struct s3c_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;
}

  代碼邏輯很簡單,獲取當前bank的控制寄存器的虛擬地址,根據offset計算要控制的引腳在控制寄存器中的起始位,然後將要設置的cfg值寫入到控制寄存器,完成工作。不過這裏有個宏CONFIG_S3C_GPIO_CFG_S3C64XX,查看相關的Kconfig就可知道,一旦選中s5p相關的平臺,那麽S3C_GPIO_CFG_S3C64XX這個宏一定會定義。

LInux-3.0.8中基於S5PV210的GPIO模塊代碼追蹤和分析