kernel - gpio子系统

本文深入探讨了Linux内核中GPIO的注册过程,详细解释了devm_gpiochip_add_data和gpiochip_add_data函数的实现细节。同时,文章剖析了通过设备树获取GPIO的过程,包括of_get_named_gpio_flags和of_get_named_gpiod_flags等关键函数的作用,以及of_find_gpiochip_by_xlate和gpiochip_find的使用方法。

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

gpio_chip注册

int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip,
               void *data)              
{
    ... 
    ret = gpiochip_add_data(chip, data);
    if (ret < 0) {
        devres_free(ptr);
        return ret;
    } 
    ...
}
EXPORT_SYMBOL_GPL(devm_gpiochip_add_data);
  • 实际调用gpiochip_add_data
int gpiochip_add_data(struct gpio_chip *chip, void *data)
{
    ......

    /*
     * First: allocate and populate the internal stat container, and
     * set up the struct device.
     */
    gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
    if (!gdev)
        return -ENOMEM;
    gdev->dev.bus = &gpio_bus_type;
    gdev->chip = chip;
    chip->gpiodev = gdev;
    if (chip->parent) {
        gdev->dev.parent = chip->parent;
        gdev->dev.of_node = chip->parent->of_node;
    }

    ......
    
    status = gpiodev_add_to_list(gdev);
    if (status) {
        spin_unlock_irqrestore(&gpio_lock, flags);
        goto err_free_label;
    }

    ......

    status = of_gpiochip_add(chip);
   
    ......
}
  • 创建并初始化gdev,然后调用gpiodev_add_to_list注册到链表
  • 调用of_gpiochip_add,初始化设备树相关数据,比如of_xlate函数指针

通过设备树获取gpio流程

int of_get_named_gpio_flags(struct device_node *np, const char *list_name,
                int index, enum of_gpio_flags *flags)                                                                                                           
{
    struct gpio_desc *desc;

    desc = of_get_named_gpiod_flags(np, list_name, index, flags);                                                                                               

    if (IS_ERR(desc))
        return PTR_ERR(desc);
    else
        return desc_to_gpio(desc);
}
  • 通过of_get_named_gpiod_flags得到gpio_desc
  • 调用desc_to_gpio得到gpio编号
struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
             const char *propname, int index, enum of_gpio_flags *flags)
{
    struct of_phandle_args gpiospec;
    struct gpio_chip *chip;
    struct gpio_desc *desc;
    int ret;

    ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index,
                     &gpiospec);
    if (ret) {
        pr_debug("%s: can't parse '%s' property of node '%pOF[%d]'\n",
            __func__, propname, np, index);
        return ERR_PTR(ret);
    }  

    chip = of_find_gpiochip_by_xlate(&gpiospec);
    if (!chip) {
        desc = ERR_PTR(-EPROBE_DEFER);
        goto out;
    }  

    desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, flags);                                                                                                
    if (IS_ERR(desc))
        goto out;

    pr_debug("%s: parsed '%s' property of node '%pOF[%d]' - status (%d)\n",
         __func__, propname, np, index,
         PTR_ERR_OR_ZERO(desc));

out:   
    of_node_put(gpiospec.np);

    return desc;
}
  • 调用of_parse_phandle_with_args获取gpio相关参数gpiospec
  • 根据gpiospec,调用of_find_gpiochip_by_xlate得到gpiochip
  • 能过调用of_xlate_and_get_gpiod_flags得到gpio_desc
static int of_gpiochip_match_node_and_xlate(struct gpio_chip *chip, void *data)
{
    struct of_phandle_args *gpiospec = data;

    return chip->gpiodev->dev.of_node == gpiospec->np &&
                chip->of_xlate &&
                chip->of_xlate(chip, gpiospec, NULL) >= 0;
}

static struct gpio_chip *of_find_gpiochip_by_xlate(
                    struct of_phandle_args *gpiospec)
{
    return gpiochip_find(gpiospec, of_gpiochip_match_node_and_xlate);
}

struct gpio_chip *gpiochip_find(void *data,
                int (*match)(struct gpio_chip *chip,
                         void *data))
{
    struct gpio_device *gdev;
    struct gpio_chip *chip = NULL;
    unsigned long flags;

    spin_lock_irqsave(&gpio_lock, flags);
    list_for_each_entry(gdev, &gpio_devices, list)
        if (gdev->chip && match(gdev->chip, data)) {
            chip = gdev->chip;
            break;
        }

    spin_unlock_irqrestore(&gpio_lock, flags);

    return chip;
}
  • of_find_gpiochip_by_xlate调用gpiochip_find,传入参数of_gpiochip_match_node_and_xlate
  • gpiochip_find函数中遍历gpio_devices,根据传入的匹配配置of_gpiochip_match_node_and_xlate,得到对应的chip
  • of_gpiochip_match_node_and_xlate函数中调用chip->of_xlate函数指针,该指针在of_gpiochip_add赋值为of_gpio_simple_xlate
int of_gpio_simple_xlate(struct gpio_chip *gc,        
             const struct of_phandle_args *gpiospec, u32 *flags)                                                                                                
{
    ......

    if (flags)
        *flags = gpiospec->args[1];

    return gpiospec->args[0];
}
  • 该函数会得到设备树中指定的gpio flag
  • 函数返回值为gpio的编号
static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip,
                    struct of_phandle_args *gpiospec,
                    enum of_gpio_flags *flags)
{
    int ret;

    if (chip->of_gpio_n_cells != gpiospec->args_count)
        return ERR_PTR(-EINVAL);

    ret = chip->of_xlate(chip, gpiospec, flags);
    if (ret < 0)
        return ERR_PTR(ret);

    return gpiochip_get_desc(chip, ret);
}
  • 通过调用chip->of_xlate得到硬件编号
  • 调用函数gpiochip_get_desc根据chip和硬件编号得到gpio管脚号

转载于:https://www.cnblogs.com/qzhang1535/p/11573649.html

<think>好的,我现在需要帮助用户了解如何在Linux Kernel 4.19中配置GPIO模式。首先,我需要回顾一下自己关于Linux内核GPIO子系统的知识。GPIO(通用输入输出)在嵌入式系统中非常重要,用于控制硬件设备。 用户提到的是Kernel 4.19,这个版本的内核可能使用较新的GPIO接口,比如基于描述符的API,而不是旧的基于编号的方法。我需要确认这一点,避免提供过时的信息。 首先,配置GPIO通常包括几个步骤:申请GPIO、设置方向(输入或输出)、读写值、释放GPIO。在设备树(Device Tree)中定义GPIO是常见做法,特别是在使用设备树的内核版本中。所以,用户可能需要了解如何在设备树中添加GPIO节点,然后在驱动代码中获取这些GPIO。 另外,用户可能需要知道如何通过sysfs接口进行配置,虽然sysfs在较新的内核中可能被逐步淘汰,转而使用chardev(比如libgpiod)。但如果是内核配置,可能更关注驱动层面的代码,而不是用户空间的工具。 接下来,我需要考虑用户可能的场景。用户可能是在开发一个内核驱动,需要控制某个GPIO引脚,比如控制LED或读取按钮状态。因此,回答需要涵盖设备树配置、驱动代码中的GPIO操作,以及必要的头文件和API函数。 同时,需要确保示例代码符合内核4.19的API。例如,使用gpiod_get()而不是gpio_request(),因为新的API更推荐基于描述符的函数。此外,错误处理也是关键,比如检查返回值,正确释放资源。 可能的问题点包括:设备树中GPIO引脚的正确语法,如何在代码中引用这些引脚,以及编译和加载模块的步骤。用户可能还需要知道如何验证配置是否生效,比如通过GPIO的状态变化或使用示波器测量信号。 另外,需要提醒用户注意GPIO编号和引脚名称的对应关系,不同硬件平台可能有不同的映射方式,参考具体的板级支持包(BSP)文档很重要。例如,树莓派和BeagleBone Black的GPIO编号方式不同。 最后,应该生成相关问题,帮助用户深入理解,比如如何调试GPIO配置,如何处理多个GPIO,或者如何在中断上下文中使用GPIO。这些相关问题可以引导用户进一步探索。</think>### Linux Kernel 4.19 GPIO模式配置教程 在Linux Kernel 4.19中配置GPIO模式主要通过设备树(Device Tree)和GPIO子系统API实现,以下是详细步骤: --- #### 一、设备树配置 1. **定位GPIO控制器节点** 在`.dts`文件中查找类似内容: ```dts gpio0: gpio@e6050000 { compatible = "renesas,gpio-r8a7795"; reg = <0xe6050000 0x50>; #gpio-cells = <2>; }; ``` 2. **定义GPIO使用属性** 在需要使用GPIO的设备节点中添加: ```dts my_device { compatible = "custom,my-device"; led-gpio = <&gpio0 15 GPIO_ACTIVE_HIGH>; // GPIO0组第15引脚,高电平有效 }; ``` --- #### 二、驱动代码实现 ```c #include <linux/gpio/consumer.h> // 新版GPIO API头文件 struct gpio_desc *gpio_led; // GPIO描述符指针 // 初始化GPIO static int __init my_driver_init(void) { // 获取设备树中定义的GPIO gpio_led = gpiod_get(&pdev->dev, "led", GPIOD_OUT_LOW); if (IS_ERR(gpio_led)) { pr_err("无法获取GPIO\n"); return PTR_ERR(gpio_led); } // 设置GPIO电平 gpiod_set_value(gpio_led, 1); return 0; } // 释放资源 static void __exit my_driver_exit(void) { gpiod_put(gpio_led); } module_init(my_driver_init); module_exit(my_driver_exit); ``` --- #### 三、关键API说明 | API函数 | 功能描述 | |-----------------------|---------------------------------| | `gpiod_get()` | 获取GPIO描述符 | | `gpiod_direction_output()` | 设置为输出模式 | | `gpiod_set_value()` | 设置输出电平 | | `gpiod_get_value()` | 读取输入电平(输入模式时使用) | --- #### 四、验证配置 1. 编译驱动后插入内核: ```bash insmod my_driver.ko ``` 2. 通过sysfs检查GPIO状态: ```bash cat /sys/kernel/debug/gpio # 查看GPIO使用状态 ``` 3. 使用示波器测量实际引脚电平变化 --- #### 五、注意事项 1. 新旧API差异:Kernel 4.19已废弃`gpio_request()`,推荐使用基于描述符的API[^1] 2. 引脚复用:需确保GPIO未被其他功能(如I2C、SPI)占用 3. 电平标准:注意硬件设计中的电压匹配(3.3V/1.8V)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值