展锐sys/class/gpio 目录介绍

本文围绕Linux和Android系统下展锐GPIO展开,介绍了gpioX和gpiochipX目录下的节点属性,详细解析了创建export、gpiochipX的流程,包括相关函数和链表初始化。还阐述了export节点的使用及写入流程,以及计算gpio实际地址、查找ap_gpio base地址等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. gpioX 目录下的节点属性介绍

/sys/class/gpio/gpio211

-- active_low     // 获取dts中设置的flag,如果dts中配置的是FLAG_ACTIVE_LOW,则显示0

-- device

-- direction        // gpio 输入输出方向(输入:in, 输出:out)

-- edge             // 如果是中断,设置中断类型(上升沿、下降沿、高电平、低电平)

-- power           // 供电相关

-- subsystem

-- uevent          // uevent 通知相关

-- value            // gpio 当前值(0、1)

2. gpiochipX 目录下节点属性介绍 

/sys/class/gpio/gpiochip160

-- base // 当前chip的base 地址,用于计算gpio口号的

-- device

-- label // chip 的名字

-- ngpio // 当前chip 下可以有gpio 口的数量,当前chip 下的gpio号为base ~ base + ngpio

-- power

-- subsystem

-- uevent

3. 创建export 流程 

bsp/kernel/kernel4.14/drivers/gpio/gpiolib-sysfs.c

static CLASS_ATTR_WO(export); //创建只写export属性

static struct attribute *gpio_class_attrs[] = {
    &class_attr_export.attr,   // 定义export 属性节点
    &class_attr_unexport.attr, // 定义unexport 属性节点
    NULL,
};
ATTRIBUTE_GROUPS(gpio_class);

static struct class gpio_class = {
    .name =     "gpio",  // sys/class/gpio 目录名字
    .owner =    THIS_MODULE,

    .class_groups = gpio_class_groups,
};

3.1 CLASS_ATTR_WO(export) 展开

struct class_attribute class_attr_export = {
    .attr   = { .name = __stringify(export), .mode = S_IWUSR },
    .store  = export_store,
}

3.2 ATTRIBUTE_GROUPS(gpio_class) 展开

static const struct attribute_group gpio_class_group = {
    .attrs = gpio_class_attrs,
};

3.3 gpiolib-sysfs.c入口函数 gpiolib_sysfs_init 解析

bsp/kernel/kernel4.14/drivers/gpio/gpiolib-sysfs.c

static int __init gpiolib_sysfs_init(void)
{
    int     status;
    unsigned long   flags;
    struct gpio_device *gdev;

    status = class_register(&gpio_class); // 这个方法会创建sys/class/gpio 目录和export 和unexport 节点
    if (status < 0)
        return status;

    spin_lock_irqsave(&gpio_lock, flags);
    list_for_each_entry(gdev, &gpio_devices, list) { // 循环读取gpio_devices 链表,结果保存到gdev 中
        if (gdev->mockdev)
            continue;

        spin_unlock_irqrestore(&gpio_lock, flags);
        status = gpiochip_sysfs_register(gdev); // 注册gpiochipX 节点
        spin_lock_irqsave(&gpio_lock, flags);
    }
    spin_unlock_irqrestore(&gpio_lock, flags);

    return status;
}

postcore_initcall(gpiolib_sysfs_init);

4. 创建gpiochipX 流程

接着查看gpiochip_sysfs_register() 方法,分析创建gpioichipX 目录的流程

int gpiochip_sysfs_register(struct gpio_device *gdev)
{
    struct device   *dev;
    struct device   *parent;
    struct gpio_chip *chip = gdev->chip;

    if (!gpio_class.p)
        return 0;


    if (chip->parent)
        parent = chip->parent;
    else
        parent = &gdev->dev;

    // 会创建gpiochipX 目录和其下的base、lable、ngpio 等属性节点
    dev = device_create_with_groups(&gpio_class, parent,
                    MKDEV(0, 0),
                    chip, gpiochip_groups,
                    "gpiochip%d", chip->base);
    if (IS_ERR(dev))
        return PTR_ERR(dev);

    mutex_lock(&sysfs_lock);
    gdev->mockdev = dev;
    mutex_unlock(&sysfs_lock);

    return 0;
}

static struct attribute *gpiochip_attrs[] = {
    &dev_attr_base.attr,  // 创建base 属性节点,代表当前chip 的base 地址
    &dev_attr_label.attr, // 创建label 属性节点
    &dev_attr_ngpio.attr, // 创建ngpio 属性节点,代表当前chip 下gpio 的数量
    NULL,
};
ATTRIBUTE_GROUPS(gpiochip);

static ssize_t base_show(struct device *dev,
                   struct device_attribute *attr, char *buf)
{
    const struct gpio_chip  *chip = dev_get_drvdata(dev);

    return sprintf(buf, "%d\n", chip->base);
}
static DEVICE_ATTR_RO(base); // 这个会创建dev_attr_base 结构体,并且show 方法为base_show

static ssize_t label_show(struct device *dev,
                   struct device_attribute *attr, char *buf)
{
    const struct gpio_chip  *chip = dev_get_drvdata(dev);

    return sprintf(buf, "%s\n", chip->label ? : "");
}
static DEVICE_ATTR_RO(label); // 这个会创建dev_attr_label 结构体,并且show 方法为label_show

static ssize_t ngpio_show(struct device *dev,
                   struct device_attribute *attr, char *buf)
{
    const struct gpio_chip  *chip = dev_get_drvdata(dev);

    return sprintf(buf, "%u\n", chip->ngpio);
}
static DEVICE_ATTR_RO(ngpio); // 这个会创建dev_attr_ngpio 结构体,并且show 方法为ngpio_show

device_create_with_groups方法会按照gpio_gevices 链表创建gpiochipX 目录

4.1 ATTRIBUTE_GROUPS (gpiochip)展开

static const struct attribute_group gpiochip_group = {
    .attrs = gpiochip_attrs,
};

5. gpio_devices 链表初始化

接下来需要查看在哪儿初始化的gpio_devices 链表

bsp/kernel/kernel4.14/drivers/gpio/gpiolib.c

LIST_HEAD(gpio_devices);

static int gpiodev_add_to_list(struct gpio_device *gdev)
{
    struct gpio_device *prev, *next;

    if (list_empty(&gpio_devices)) {
        /* initial entry in list */
        list_add_tail(&gdev->list, &gpio_devices);
        return 0;
    }

    next = list_entry(gpio_devices.next, struct gpio_device, list);
    if (gdev->base + gdev->ngpio <= next->base) {
        /* add before first entry */
        list_add(&gdev->list, &gpio_devices);
        return 0;
    }

    prev = list_entry(gpio_devices.prev, struct gpio_device, list);
    if (prev->base + prev->ngpio <= gdev->base) {
        /* add behind last entry */
        list_add_tail(&gdev->list, &gpio_devices);
        return 0;
    }

    list_for_each_entry_safe(prev, next, &gpio_devices, list) {
        /* at the end of the list */
        if (&next->list == &gpio_devices)
            break;

        /* add between prev and next */
        if (prev->base + prev->ngpio <= gdev->base
                && gdev->base + gdev->ngpio <= next->base) {
            list_add(&gdev->list, &prev->list);
            return 0;
        }
    }

    dev_err(&gdev->dev, "GPIO integer space overlap, cannot add chip\n");
    return -EBUSY;
}

int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
                   struct lock_class_key *key)
{
    struct gpio_device *gdev;
    
    gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
    gdev->dev.bus = &gpio_bus_type;
    gdev->chip = chip;
    chip->gpiodev = gdev;
    ...
    if (chip->label)
        gdev->label = kstrdup(chip->label, GFP_KERNEL);
    else
        gdev->label = kstrdup("unknown", GFP_KERNEL);
    gdev->ngpio = chip->ngpio;
    ...
    gdev->base = base;
    ...
    status = gpiodev_add_to_list(gdev);
    ...
}

通过宏LIST_HEAD 初始化gpio_devices 链表,接着在方法gpiodev_add_to_list() 中通过list_add_tail() 添加数据到gpio_devices 链表中。

查看调用gpiochip_add_data_with_key()方法的地方

bsp/kernel/kernel4.14/include/linux/gpio/driver.h

#ifdef CONFIG_LOCKDEP
#define gpiochip_add_data(chip, data) ({        \
        static struct lock_class_key key;   \
        gpiochip_add_data_with_key(chip, data, &key);   \
    })
#else
#define gpiochip_add_data(chip, data) gpiochip_add_data_with_key(chip, data, NULL)
#endif

所以最终需要查看哪儿调用了gpiochip_add_data() 方法

5.1 gpiochip_add_data添加gpiochip 流程

比如我们看bsp/kernel/kernel4.14/drivers/gpio/gpio-sprd.c 驱动

bsp/kernel/kernel4.14/drivers/gpio/gpio-sprd.c

static int sprd_gpio_probe(struct platform_device *pdev)
{
    ...
    struct sprd_gpio *sprd_gpio;
    ...
    sprd_gpio = devm_kzalloc(&pdev->dev, sizeof(*sprd_gpio), GFP_KERNEL);
    ...
    sprd_gpio->irq = platform_get_irq(pdev, 0);
    ...

    sprd_gpio->chip.label = dev_name(&pdev->dev);
    sprd_gpio->chip.ngpio = SPRD_GPIO_NR;
    sprd_gpio->chip.base = -1;
    sprd_gpio->chip.parent = &pdev->dev;
    sprd_gpio->chip.of_node = pdev->dev.of_node;
    sprd_gpio->chip.request = sprd_gpio_request;
    sprd_gpio->chip.free = sprd_gpio_free;
    sprd_gpio->chip.get = sprd_gpio_get;
    sprd_gpio->chip.set = sprd_gpio_set;
    sprd_gpio->chip.direction_input = sprd_gpio_direction_input;
    sprd_gpio->chip.direction_output = sprd_gpio_direction_output;
    ...
    ret = devm_gpiochip_add_data(&pdev->dev, &sprd_gpio->chip, sprd_gpio);
    ...
}

在probe函数中调用了devm_gpiochip_add_data() 方法向gpio_devices 链表中添加了gpiochip 结构体。

还有两个probe 函数有调用devm_gpiochip_add_data() 方法

sprd_eic_probe

sprd_gpio_probe ----> gpiochip160

sprd_pmic_eic_probe ----> gpiochip144

6. export 节点介绍

在我们想控制某个gpio 口时,我们一般在代码中写如下代码,比如控制gpio11 

#define USB_EN_GPIO        11

gpio_request(USB_EN_GPIO, "usb_en");
gpio_direction_output(USB_EN_GPIO, 1);
gpio_export(USB_EN_GPIO, false);

编译和烧录镜像后在sys/class/gpio 目录下生成gpio11 的目录,目录结构如下

-rw-r--r-- 0        0              4096 1970-01-01 01:38 active_low
-rw-r--r-- 0        0              4096 1970-01-01 01:38 direction
drwxr-xr-x 0        0                   1970-01-01 00:00 power
lrwxrwxrwx 0        0                   1970-01-01 01:38 subsystem -> ../../../../class/gpio
-rw-r--r-- 0        0              4096 1970-01-01 00:00 uevent
-rw-r--r-- 0        0              4096 1970-01-01 01:38 value

然后就可以通过写入value 和 direction 控制gpio 口

还有一种比较方便的方法就是通过sys/class/gpio/export 来实现创建gpioX,比如我们想操作gpio11 ,可以通过如下方法生成gpio11 目录

echo 11 > sys/class/gpio/export

之后会生成sys/class/gpio/gpio11 目录
注意: 如果当前gpio 已经有request 的地方还没有释放,我们再写入export 节点,不会再创建gpioX 目录。

7. export节点写入流程

当我们使用echo 向sys/class/gpio/export 节点写值时会调用export_store() 方法

bsp/kernel/kernel4.14/drivers/gpio/gpiolib-sysfs.c

static ssize_t export_store(struct class *class,
                struct class_attribute *attr,
                const char *buf, size_t len)
{
    long            gpio;
    struct gpio_desc    *desc;
    int         status;

    status = kstrtol(buf, 0, &gpio);
    if (status < 0)
        goto done;

    desc = gpio_to_valid_desc(gpio); // 判断gpio 是否可以使用, 并返回gpio_desc 结构体
    /* reject invalid GPIOs */
    if (!desc) {
        pr_warn("%s: invalid GPIO %ld\n", __func__, gpio);
        return -EINVAL;
    }

    status = gpiod_request(desc, "sysfs"); // 申请当前gpio,命名为sysfs,如果当前gpio 在其他地方被申请使用,则申请会失败
    if (status < 0) {
        if (status == -EPROBE_DEFER)
            status = -ENODEV;
        goto done;
    }
    status = gpiod_export(desc, true); // export 出gpio口,创建gpioX 目录
    if (status < 0)
        gpiod_free(desc);
    else
        set_bit(FLAG_SYSFS, &desc->flags);

done:
    if (status)
        pr_debug("%s: status %d\n", __func__, status);
    return status ? : len;
}
static CLASS_ATTR_WO(export);

最终会通过gpiod_export() 方法将gpio export出来

bsp/kernel/kernel4.14/drivers/gpio/gpiolib-sysfs.c

int gpiod_export(struct gpio_desc *desc, bool direction_may_change)
{
    struct gpio_chip    *chip;
    struct gpio_device  *gdev;
    struct gpiod_data   *data;
    unsigned long       flags;
    int         status;
    const char      *ioname = NULL;
    struct device       *dev;
    int         offset;

    ...
    gdev = desc->gdev;
    chip = gdev->chip;

    mutex_lock(&sysfs_lock);

    ...
    spin_lock_irqsave(&gpio_lock, flags);
    if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
         test_bit(FLAG_EXPORT, &desc->flags)) {
        spin_unlock_irqrestore(&gpio_lock, flags);
        gpiod_dbg(desc, "%s: unavailable (requested=%d, exported=%d)\n",
                __func__,
                test_bit(FLAG_REQUESTED, &desc->flags),
                test_bit(FLAG_EXPORT, &desc->flags));
        status = -EPERM;
        goto err_unlock;
    }
    spin_unlock_irqrestore(&gpio_lock, flags);

    data = kzalloc(sizeof(*data), GFP_KERNEL);
    if (!data) {
        status = -ENOMEM;
        goto err_unlock;
    }

    data->desc = desc;
    mutex_init(&data->mutex);

    // 如果已经设置了输入和输出函数,则可以切换为输入和输出
    if (chip->direction_input && chip->direction_output)
        data->direction_can_change = direction_may_change;
    else
        data->direction_can_change = false;

    offset = gpio_chip_hwgpio(desc);
    if (chip->names && chip->names[offset])
        ioname = chip->names[offset];

    // 创建gpioX 目录及其属性节点
    dev = device_create_with_groups(&gpio_class, &gdev->dev,
                    MKDEV(0, 0), data, gpio_groups,
                    ioname ? ioname : "gpio%u",
                    desc_to_gpio(desc));
    if (IS_ERR(dev)) {
        status = PTR_ERR(dev);
        goto err_free_data;
    }

    set_bit(FLAG_EXPORT, &desc->flags);
    mutex_unlock(&sysfs_lock);
    return 0;

err_free_data:
    kfree(data);
err_unlock:
    mutex_unlock(&sysfs_lock);
    gpiod_dbg(desc, "%s: status %d\n", __func__, status);
    return status;
}

最终会通过device_create_with_groups() 方法创建一个gpioX 的目录,里面有direction、edg、value、active_low等属性节点

bsp/kernel/kernel4.14/drivers/gpio/gpiolib-sysfs.c

static struct attribute *gpio_attrs[] = {
    &dev_attr_direction.attr, // DEVICE_ATTR_RW(direction) 创建
    &dev_attr_edge.attr, // DEVICE_ATTR_RW(edge) 创建
    &dev_attr_value.attr, // DEVICE_ATTR_RW(value) 创建
    &dev_attr_active_low.attr, // DEVICE_ATTR_RW(active_low) 创建
    NULL,
};

static const struct attribute_group gpio_group = {
    .attrs = gpio_attrs, // gpioX 目录下的属性信息
    .is_visible = gpio_is_visible, // 控制哪些属性节点需要创建
};

static const struct attribute_group *gpio_groups[] = {
    &gpio_group,
    NULL
};

static umode_t gpio_is_visible(struct kobject *kobj, struct attribute *attr,
                   int n)
{
    struct device *dev = container_of(kobj, struct device, kobj);
    struct gpiod_data *data = dev_get_drvdata(dev);
    struct gpio_desc *desc = data->desc;
    umode_t mode = attr->mode;
    
    // 如果该gpio 有输入输出函数,则显示direction 属性节点
    bool show_direction = data->direction_can_change;

    if (attr == &dev_attr_direction.attr) {
        if (!show_direction)
            mode = 0;
    } else if (attr == &dev_attr_edge.attr) {
        // 如果该gpio 是中断模式,则创建edge 属性节点,用于设置中断条件(上拉、下拉、高、低触发中断)
        if (gpiod_to_irq(desc) < 0)
            mode = 0;
        if (!show_direction && test_bit(FLAG_IS_OUT, &desc->flags))
            mode = 0;
    }

    return mode;
}

7.1 gpio_to_valid_desc 方法

bsp/kernel/kernel4.14/drivers/gpio/gpiolib-sysfs.c

static struct gpio_desc *gpio_to_valid_desc(int gpio){
    // gpio_is_valid: 如果gpio 大于0 返回 1
    return gpio_is_valid(gpio) ? gpio_to_desc(gpio) : NULL;
}
bsp/kernel/kernel4.14/drivers/gpio/gpiolib.c

struct gpio_desc *gpio_to_desc(unsigned gpio)
{
    struct gpio_device *gdev;
    unsigned long flags;

    spin_lock_irqsave(&gpio_lock, flags);

    // 循环遍历gpio_devices 链表,获取注册的gpiochip 结构体
    list_for_each_entry(gdev, &gpio_devices, list) {
        // 如果该gpio 属于某个gpiochip,则返回该chip 下的第gpio - gdev->base 个gpio,条件是(base < gpio < base + ngpio)
        if (gdev->base <= gpio &&
            gdev->base + gdev->ngpio > gpio) {
            spin_unlock_irqrestore(&gpio_lock, flags);
            return &gdev->descs[gpio - gdev->base];
        }
    }

    spin_unlock_irqrestore(&gpio_lock, flags);

    if (!gpio_is_valid(gpio))
        WARN(1, "invalid GPIO %d\n", gpio);

    return NULL;
}

从上述代码可以看到,通过gpio获取gpio_desc 的条件为gpio需要大于0并且属于某一个chipcode。

8. 创建gpiochip 流程

gpiochip 目录是通过gpiochip_add_data_with_key() 方法将gpiochip 添加到gpio_devices的,我们再接着分析gpiochip_add_data_with_key() 方法

bsp/kernel/kernel4.14/drivers/gpio/gpiolib.c

int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
                   struct lock_class_key *key)
{
    ...
    if (base < 0) {
        // 如果base值小于0,重新计算当前gpiochip 的base 地址
        base = gpiochip_find_base(chip->ngpio); 
        if (base < 0) {
            status = base;
            spin_unlock_irqrestore(&gpio_lock, flags);
            goto err_free_label;
        }
        /*
         * TODO: it should not be necessary to reflect the assigned
         * base outside of the GPIO subsystem. Go over drivers and
         * see if anyone makes use of this, else drop this and assign
         * a poison instead.
         */
        chip->base = base;
    }
    gdev->base = base;

    status = gpiodev_add_to_list(gdev); // 将gpiochip 添加到gpio_devices 链表中
    if (status) {
        spin_unlock_irqrestore(&gpio_lock, flags);
        goto err_free_label;
    }

    spin_unlock_irqrestore(&gpio_lock, flags);

    // 循环遍历当前chip 下所有的gpio
    for (i = 0; i < chip->ngpio; i++) {
        struct gpio_desc *desc = &gdev->descs[i];

        desc->gdev = gdev;

        // 初始化当前gpio 的输入输出方向
        if (chip->get_direction) {
            /*
             * If we have .get_direction, set up the initial
             * direction flag from the hardware.
             */
            int dir = chip->get_direction(chip, i);

            if (!dir)
                set_bit(FLAG_IS_OUT, &desc->flags);
        
        // 如果当前chip 没有定义输入的函数,则将当前gpio设置为输出模式
        } else if (!chip->direction_input) {
            /*
             * If the chip lacks the .direction_input callback
             * we logically assume all lines are outputs.
             */
            set_bit(FLAG_IS_OUT, &desc->flags);
        }
    }
    ...
}

在这里我们已经获取到了每个需要注册的gpiochip,需要判断chip的基地址,如果没有设置,需要重新计算chip 的base 地址

8.1 gpiochip_find_base 计算chip的base地址

bsp/kernel/kernel4.14/drivers/gpio/gpiolib.c

static int gpiochip_find_base(int ngpio)
{
    struct gpio_device *gdev;
    int base = ARCH_NR_GPIOS - ngpio; // 首先从512 开始逐减计算chip 的base 地址

    // 反向循环遍历gpio_device,获取已经加入的gpiochip
    // 找到一段空的区间,然后减去需要的长度即是当前chip的base 地址
    list_for_each_entry_reverse(gdev, &gpio_devices, list) {
        /* found a free space? */
        if (gdev->base + gdev->ngpio <= base)
            break;
        else
            /* nope, check the space right before the chip */
            base = gdev->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;
    }
}

从adb shell cat sys/kernel/debug/gpio 中的信息可以看到

gpiochip5: GPIOs 144-159, parent: platform/sc27xx-eic, sc27xx-eic:
 gpio-144 (                    |vbus                ) in  hi IRQ
 gpio-145 (                    |Power Key           ) in  hi IRQ
 gpio-147 (                    |button              ) in  lo IRQ
 gpio-149 (                    |detect_mic          ) in  lo    
 gpio-152 (                    |detect_l            ) in  lo    
 gpio-153 (                    |bat-detect          ) in  hi IRQ
 gpio-154 (                    |Volume Up Key       ) in  lo IRQ
 gpio-156 (                    |detect_h            ) in  lo    
 gpio-157 (                    |detect_all          ) in  lo IRQ

gpiochip4: GPIOs 160-415, parent: platform/40280000.gpio, 40280000.gpio:
 gpio-160 (                    |ap_send_data        ) in  lo IRQ
 gpio-165 (                    |chip_en             ) out hi    
 gpio-168 (                    |avdd-gpios          ) out lo    
 gpio-185 (                    |wcn_power_on_1v2    ) out hi    
 gpio-186 (                    |sdiohal_gpio        ) in  lo IRQ
 gpio-192 (                    |dvdd-gpios          ) out lo    
 gpio-201 (                    |reset-gpios         ) out lo    
 gpio-204 (                    |reset-gpios         ) out lo    
 gpio-205 (                    |reset               ) out hi    
 gpio-206 (                    |power-down-gpios    ) out lo    
 gpio-207 (                    |int_ap              ) in  lo IRQ
 gpio-210 (                    |reset               ) out lo    
 gpio-211 (                    |sysfs               ) out hi    
 gpio-213 (                    |magnetic_reset      ) out hi    
 gpio-215 (                    |acceleremter_INT1   ) in  lo IRQ
 gpio-236 (                    |wcn_power_on        ) out hi    
 gpio-238 (                    |cd                  ) in  hi IRQ
 gpio-247 (                    |otg_power_gpio      ) out lo    
 gpio-248 (                    |tp_power_gpio       ) out hi    
 gpio-249 (                    |flash-chip-en-gpios ) out hi    
 gpio-250 (                    |power               ) in  hi IRQ
 gpio-251 (                    |avdd-gpios          ) out lo    
 gpio-301 (                    |flash-sync-gpios    ) in  lo    
 gpio-304 (                    |?                   ) in  hi IRQ
 gpio-305 (                    |?                   ) out hi    

gpiochip3: GPIOs 416-439, parent: platform/402100c0.gpio, eic-sync:

gpiochip2: GPIOs 440-463, parent: platform/402100a0.gpio, eic-async:

gpiochip1: GPIOs 464-487, parent: platform/40210080.gpio, eic-latch:

gpiochip0: GPIOs 488-511, parent: platform/40210000.gpio, eic-debounce:
 gpio-490 (                    |Volume Down Key     ) in  hi IRQ
 gpio-492 (                    |id                  ) in  hi IRQ

可以看到 eic-debounce 的chip base 地址为488 、40280000.gpio 的base 地址为160 ...... ,所以我们查看sys/class/gpio 下的目录为

- export

- gpiochip144

- gpiochip160

- gpiochip416

- gpiochip440

- gpiochip464

- gpiochip488

- unexport

由于eic-debounce 驱动最先被加载,所以它的base 地址最先分配,所以最大,加载顺序可以从串口log查看,驱动加载按顺序分别为

1. sprd_eic_probe

2. sprd_gpio_probe

3. sprd_pmic_eic_probe 

9. 计算gpio 的实际地址

如果我们想在在shell 中控制gpio90 口,通过向sys/class/gpio/export 节点写入gpio 口 创建gpioX 目录,然后通过gpioX 下的属性节点控制gpio 口

9.1 gpio dts 配置

比如我们在dtsi中有如下配置

power-gpio = <&ap_gpio 90 GPIO_ACTIVE_LOW>;

9.2 驱动中使用gpio

这里将ap_gpio 90 口设置为上升沿和下降沿中断

desc->gpiop = devm_gpiod_get(&pdev->dev, "power", GPIOD_IN);
if (IS_ERR(desc->gpiop)) {
    dev_err(&pdev->dev, "failed to get power detection GPIO\n");
    return PTR_ERR(desc->gpiop);
}

irq_power = gpiod_to_irq(desc->gpiop);
if (irq_power < 0) {
    dev_err(&pdev->dev, "failed to translate GPIO to IRQ\n");
    return irq_power;
}

ret = devm_request_threaded_irq(&pdev->dev, irq_power, NULL,
                sc27xx_fgu_power_detection,
                IRQF_ONESHOT | IRQF_TRIGGER_RISING |
                IRQF_TRIGGER_FALLING,
                "power-gpio", cm);
if (ret) {
    dev_err(&pdev->dev, "failed to request IRQ\n");
    return ret;
}

如果我们要向export 中写值,应该是写多少呢?答案是250

9.3 打印gpio 口使用情况

adb shell cat sys/kernel/debug/gpio

...
gpiochip4: GPIOs 160-415, parent: platform/40280000.gpio, 40280000.gpio:
 gpio-160 (                    |ap_send_data        ) in  lo IRQ
 gpio-165 (                    |chip_en             ) out hi    
 gpio-168 (                    |avdd-gpios          ) out lo    
 gpio-185 (                    |wcn_power_on_1v2    ) out hi    
 gpio-186 (                    |sdiohal_gpio        ) in  lo IRQ
 gpio-192 (                    |dvdd-gpios          ) out lo    
 gpio-201 (                    |reset-gpios         ) out lo    
 gpio-204 (                    |reset-gpios         ) out lo    
 gpio-205 (                    |reset               ) out hi    
 gpio-206 (                    |power-down-gpios    ) out lo    
 gpio-207 (                    |int_ap              ) in  lo IRQ
 gpio-210 (                    |reset               ) out lo    
 gpio-211 (                    |sysfs               ) out hi    
 gpio-213 (                    |magnetic_reset      ) out hi    
 gpio-215 (                    |acceleremter_INT1   ) in  lo IRQ
 gpio-236 (                    |wcn_power_on        ) out hi    
 gpio-238 (                    |cd                  ) in  hi IRQ
 gpio-247 (                    |otg_power_gpio      ) out lo    
 gpio-248 (                    |tp_power_gpio       ) out hi    
 gpio-249 (                    |flash-chip-en-gpios ) out hi    
 gpio-250 (                    |power               ) in  hi IRQ
 gpio-251 (                    |avdd-gpios          ) out lo    
 gpio-301 (                    |flash-sync-gpios    ) in  lo    
 gpio-304 (                    |?                   ) in  hi IRQ
 gpio-305 (                    |?                   ) out hi
...

从上可以看到gpio-250 的name 为 power,方向为输入, 当前为高电平,模式为IRQ 中断
为什么是gpio 250 呢,
我们在dts 中配置的是ap_gpio 90,因此我们需要操控的gpio base 地址应该是ap_gpio 的,然后offest 应该为90 ,而ap_gpio 的base 为160。
所以ap_gpio90 口的gpio 号为 base + offset = 160 + 90 = 250

9.4 ap_gpio base 地址查找

在源码中搜索ap_gpio,可以看到节点名应该是gpio@40280000,对应sys/kernel/debug/gpio 打印出来的值为40280000.gpio,所以ap_gpio 的base 地址为160
同理我们查看sys/class/gpio/gpiochip*/label 节点,可以看到gpiochip160/label 的值为40280000.gpio

ap_gpio: gpio@40280000 {
    compatible = "sprd,sharkle-gpio";
    reg = <0x40280000 0x1000>;
    gpio-controller;
    #gpio-cells = <2>;
    interrupt-controller;
    #interrupt-cells = <2>;
    interrupts = <GIC_SPI 35 IRQ_TYPE_LEVEL_HIGH>;
};

当前dts在sprd_gpio_probe()中使用了,并且在此方法中构建了gpiochip目录,可以查看此方法和 “8.1 gpiochip_find_base 计算chip的base地址“ 查看怎么计算的当前chip 的base 地址。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值