天天看點

uboot的GPIO驅動分析--基于全志的A10晶片【轉】

(2013-12-28 10:47:31)

uboot的GPIO驅動分析--基于全志的A10晶片【轉】

轉載▼

<col>

标簽:

分類:​​uboot的驅動分析​​

uboot的GPIO相當簡單,其就是三層結構。分别為:

1、頂層接口層,其隻定義了通用的接口,并不負責實作,實作是我們具體根據具體的晶片來實作的。

2、中間接口實作層,用具體的闆子的GPIO來實作頂層的接口

3、 底層具體晶片GPIO的實作層 。

現在具體分析:

頂層接口層

int gpio_request(unsigned gpio, const char *label);//申請GPIO資源

int gpio_free(unsigned gpio); //釋放申請的GPIO資源

int gpio_direction_input(unsigned gpio); //設定GPIO為輸入模式

int gpio_direction_output(unsigned gpio, int value);//設定GPIO為輸出模式

int gpio_get_value(unsigned gpio); //得到GPIO的值

int gpio_set_value(unsigned gpio, int value);//設定GPIO的值

說明:unsigned gpio為邏輯号,雖然和實際的實體GPIO位址有一定的關系,但并不是實際的實體GPIO位址。

中間接口實作層:

用具體的晶片的GPIO來實作其頂層接口

int gpio_request(unsigned gpio, const char *label)

{

 return 0;

}

int gpio_free(unsigned gpio)

int gpio_direction_input(unsigned gpio)

 sunxi_gpio_set_cfgpin(gpio,SUNXI_GPIO_INPUT);

 return sunxi_gpio_input(gpio);

int gpio_direction_output(unsigned gpio, int value)

 sunxi_gpio_set_cfgpin(gpio,SUNXI_GPIO_OUTPUT);

 return sunxi_gpio_output(gpio, value);

int gpio_get_value(unsigned gpio)

int gpio_set_value(unsigned gpio, int value)

底層具體晶片GPIO的實作層:

在實作的時候,其用了一個小技巧,其目的是把GPIO的實體寄存器放到結構體裡面來,進而把實體的位址操作轉換為資料結構的操作。

其實作如下:

把SUNXI_PIO_BASE 強制轉換為sunxi_gpio_reg *指針來實作。

#define SUNXI_PIO_BASE 0x01c20800

struct sunxi_gpio {

 u32 cfg[4];

 u32 dat;

 u32 drv[2];

 u32 pull[2];

};

struct sunxi_gpio_int {

 u32 cfg[3];

 u32 ctl;

 u32 sta;

 u32 deb;

struct sunxi_gpio_reg {

 struct sunxi_gpio gpio_bank[9];

 u8 res[0xbc];

 struct sunxi_gpio_int gpio_int;

我們實作具體的晶片的GPIO的操作的思想是:

使用邏輯符号unsigned gpio,通過SUNXI_PIO_BASE強制轉換為sunxi_gpio_reg *指針的指針來操作相關寄存器。

但是邏輯符号unsigned gpio要通過SUNXI_PIO_BASE強制轉換為sunxi_gpio_reg *指針的指針來操作相關寄存器,必須要解決一個問題,即如何在衆多的寄存器的中,找到指定的那個寄存器,并且在該寄存器上找到指定的那些相關位。

即gpio----&gt;bank------&gt;bank中的offset

這個映射關系和具體的晶片有關。

這裡隻讨論全志的a10晶片。

先看邏輯符号gpio的定義:

#define SUNXI_GPIO_A_NR 32

#define SUNXI_GPIO_B_NR 32

#define SUNXI_GPIO_C_NR 32

#define SUNXI_GPIO_D_NR 32

#define SUNXI_GPIO_E_NR 32

#define SUNXI_GPIO_F_NR 32

#define SUNXI_GPIO_G_NR 32

#define SUNXI_GPIO_H_NR 32

#define SUNXI_GPIO_I_NR 32

#define SUNXI_GPIO_NEXT(__gpio) \

 ((__gpio##_START) + (__gpio##_NR) + 0)

enum sunxi_gpio_number {

 SUNXI_GPIO_A_START = 0,

 SUNXI_GPIO_B_START =SUNXI_GPIO_NEXT(SUNXI_GPIO_A),

 SUNXI_GPIO_C_START =SUNXI_GPIO_NEXT(SUNXI_GPIO_B),

 SUNXI_GPIO_D_START =SUNXI_GPIO_NEXT(SUNXI_GPIO_C),

 SUNXI_GPIO_E_START =SUNXI_GPIO_NEXT(SUNXI_GPIO_D),

 SUNXI_GPIO_F_START =SUNXI_GPIO_NEXT(SUNXI_GPIO_E),

 SUNXI_GPIO_G_START =SUNXI_GPIO_NEXT(SUNXI_GPIO_F),

 SUNXI_GPIO_H_START =SUNXI_GPIO_NEXT(SUNXI_GPIO_G),

 SUNXI_GPIO_I_START =SUNXI_GPIO_NEXT(SUNXI_GPIO_H),

#define SUNXI_GPA(_nr) (SUNXI_GPIO_A_START + (_nr))

#define SUNXI_GPB(_nr) (SUNXI_GPIO_B_START + (_nr))

#define SUNXI_GPC(_nr) (SUNXI_GPIO_C_START + (_nr))

#define SUNXI_GPD(_nr) (SUNXI_GPIO_D_START + (_nr))

#define SUNXI_GPE(_nr) (SUNXI_GPIO_E_START + (_nr))

#define SUNXI_GPF(_nr) (SUNXI_GPIO_F_START + (_nr))

#define SUNXI_GPG(_nr) (SUNXI_GPIO_G_START + (_nr))

#define SUNXI_GPH(_nr) (SUNXI_GPIO_H_START + (_nr))

#define SUNXI_GPI(_nr) (SUNXI_GPIO_I_START + (_nr))

從這些定義可以得出結論:

A到I這九大組的gpio是從0開始的,每個為32位的标記,(當然基本上每組的gpio号都是用不完的,但為了方面就定義每個組的大小都為32)

即第x組的第y個gpio的索引為x*32+y

邏輯索引gpio通過SUNXI_PIO_BASE 強制轉換為sunxi_gpio_reg *指針的指針,來指向到具體寄存器的映射如下:

#define GPIO_BANK(pin) ((pin) &gt;&gt; 5)

#define GPIO_NUM(pin) ((pin) &amp; 0x1f)

是以寫邏輯gpio指定的GPIO可以這樣:

static int sunxi_gpio_output(u32 pin, u32 val)

 u32 bank = GPIO_BANK(pin);

 u32 num = GPIO_NUM(pin);

 struct sunxi_gpio *pio =

    &amp;((struct sunxi_gpio_reg*)SUNXI_PIO_BASE)-&gt;gpio_bank[bank];//通過組号索引得到該gpio的多個寄存器的首位址

 dat = readl(&amp;pio-&gt;dat); //讀其輸入輸出寄存器

 if (val)

  dat |= 0x1 &lt;&lt; num;

 else

  dat &amp;= ~(0x1 &lt;&lt; num);

 writel(dat, &amp;pio-&gt;dat);//寫入其輸入輸入寄存器

#define GPIO_CFG_INDEX(pin) (((pin) &amp; 0x1f) &gt;&gt; 3)

#define GPIO_CFG_OFFSET(pin) ((((pin) &amp; 0x1f) &amp; 0x7)&lt;&lt; 2)

是以配置邏輯gpio指定的GPIO可以這樣:

int sunxi_gpio_set_cfgpin(u32 pin, u32 val)

 u32 cfg;

 u32 index = GPIO_CFG_INDEX(pin);//為配置寄存器的下标索引

 u32 offset = GPIO_CFG_OFFSET(pin);//在配置寄存器的偏移

 cfg = readl(&amp;pio-&gt;cfg[0] + index);//&amp;pio-&gt;cfg[0]為該組的多個配置寄存器的首位址,通過index索引得到其配置寄存器的位址

 cfg &amp;= ~(0xf &lt;&lt; offset);

 cfg |= val &lt;&lt; offset; //這兩行為設定配置寄存器的值

 writel(cfg, &amp;pio-&gt;cfg[0] + index);//寫入指定的配置寄存器

#define GPIO_PULL_INDEX(pin) (((pin) &amp; 0x1f) &gt;&gt; 4)

#define GPIO_PULL_OFFSET(pin) ((((pin) &amp; 0x1f) &amp; 0xf)&lt;&lt; 1)

上拉的設定和配置寄存器的一樣,這裡就不多做說明了。