前言
最近在調試一個新板子的LED驅動,LED燈用到了特定的GPIO引腳,而驅動中有一項工作就是需要佔用某個GPIO資源並設置爲輸出引腳,調用的接口類似如下:
#define GPIOAO_3 (3) /* GPIOAO_3這個引腳的id爲3 */
gpio_request(GPIOAO_3, OWNER_NAME); /* 佔用GPIOAO_3這個GPIO的資源 */
gpio_direction_output(GPIOAO_3, 0); /* 將GPIOAO_3這個GPIO設置爲輸出引腳並拉低 */
上面的代碼比較常見,應該很多同學都看過類似的,現在問題就來了,爲什麼GPIOAO_3這個GPIO定義爲3呢?這個就是我今天要討論的問題。
正文
前面提出的問題,下面我們會跟着代碼來一步步找出答案。每個平臺都有自己的GPIO驅動,下面我用到的芯片平臺是amlogic s805。雖然代碼有差異,但是基本架構是不變的,一般都從probe函數開始,並調用gpiochip_add()函數將自己的驅動註冊進系統,在這個過程就決定了GPIO的編號到描述符的映射關係。
1、
代碼路徑:
amlogic_gpio_probe:arch\arm\mach-meson8b\gpio.c
函數調用:
struct amlogic_gpio_desc amlogic_pins[]=
{
PIN_AOMAP(GPIOAO_0,6,0,6,16,6,0),
PIN_AOMAP(GPIOAO_1,6,1,6,17,6,1),
PIN_AOMAP(GPIOAO_2,6,2,6,18,6,2),
PIN_AOMAP(GPIOAO_3,6,3,6,19,6,3),
PIN_AOMAP(GPIOAO_4,6,4,6,20,6,4),
...
};
static int amlogic_gpio_probe(struct platform_device *pdev)
{
...
amlogic_gpio_chip.base=0; /* base這個變量值很重要,奠定了後續gpio編號的起始值 */
amlogic_gpio_chip.ngpio=ARRAY_SIZE(amlogic_pins); /* amlogic_pins是一個數組,裏面按順序列出了平臺所有的GPIO引腳 */
gpiochip_add(&amlogic_gpio_chip); /* 註冊進系統 */
...
return 0;
}
2、
代碼路徑:
gpiochip_add:drivers\gpio\gpiolib.c
函數調用:
int gpiochip_add(struct gpio_chip *chip)
{
...
int base = chip->base;
if ((!gpio_is_valid(base) || !gpio_is_valid(base + chip->ngpio - 1)) /* 判斷編號和GPIO數是否超過了規定的範圍,我這裏是0~256 */
&& base >= 0) {
status = -EINVAL;
goto fail;
}
spin_lock_irqsave(&gpio_lock, flags);
if (base < 0) { /* 我這個平臺的base值是0,不會走到下面的分支 */
base = gpiochip_find_base(chip->ngpio); -------------------- 2.1小節
if (base < 0) {
status = base;
goto unlock;
}
chip->base = base;
}
status = gpiochip_add_to_list(chip); /* 將自己平臺的GPIO註冊進系統的鏈表 */
if (status == 0) {
chip->desc = &gpio_desc[chip->base]; /* GPIO編號和描述符是一個線性映射的關係,而gpio_desc這個數組就保存這個映射關係,並從base這個偏移位置開始保存每個GPIO的描述符信息 */
for (id = 0; id < chip->ngpio; id++) { /* 這個for循環裏就爲每一個GPIO描述符賦值 */
struct gpio_desc *desc = &chip->desc[id]; /* 描述符的首地址指向gpio_desc數組偏移base的位置 */
desc->chip = chip;
desc->flags = !chip->direction_input
? (1 << FLAG_IS_OUT)
: 0;
}
}
...
return status;
}
2.1、
如果你的平臺的base小於0,會走到下面的函數去爲這組GPIO引腳自動分配一個base值
代碼路徑:
gpiochip_add:drivers\gpio\gpiolib.c
函數調用:
static int gpiochip_find_base(int ngpio)
{
struct gpio_chip *chip;
int base = ARCH_NR_GPIOS - ngpio; /* ARCH_NR_GPIOS一般是平臺中GPIO數量的最大值 */
list_for_each_entry_reverse(chip, &gpio_chips, list) { /* 倒序遍歷鏈表(因爲GPIO引腳可能分爲了好幾個bank),目的是找出gpio_desc數組中已經被佔用的位置 */
/* found a free space? */
if (chip->base + chip->ngpio <= base) /* 如果已經被佔用的數組位置和準備分配的位置不重合,自然就直接跳出循環了 */
break;
else
/* nope, check the space right before the chip */
base = chip->base - ngpio;
}
if (gpio_is_valid(base)) { /* 檢查是否在規定的範圍內 */
pr_debug("%s: found new base at %d\n", __func__, base);
return base;
} else {
pr_err("%s: cannot find free range\n", __func__);
return -ENOSPC;
}
}
3、
從上面兩步可以看出來,GPIO編號和描述符之間的關係是由gpio_desc這個數組決定的。所以我們想通過GPIO編號找到對應的描述符,就必須知道這個描述符在數組中的偏移位置,而偏移多少可以根據GPIO在amlogic_pins這個數組的位置加上base值得到。
代碼路徑:
drivers\gpio\gpiolib.c
函數調用關係:
gpio_request
gpiod_request
gpio_to_desc
static struct gpio_desc *gpio_to_desc(unsigned gpio)
{
if (WARN(!gpio_is_valid(gpio), "invalid GPIO %d\n", gpio))
return NULL;
else
return &gpio_desc[gpio]; /* 就是根據傳遞過來的GPIO編號在數組中找到描述符 */
}