gpio编号到描述符的转换

前言

最近在调试一个新板子的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编号在数组中找到描述符 */
}


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章