of_get_named_gpio()函数解析

解析树莓派GPIO获取与设备节点关联问题:递归搜索与结构定义
文章讲述了在嵌入式系统开发中,如何使用LinuxGPIOAPI从设备节点中正确获取GPIO信息,以及在编写代码时需要注意的递归搜索和结构组织问题。重点讨论了of_get_named_gpio和of_parse_phandle_with_args函数的使用和结构定义的变化对GPIO访问的影响。

why to do

今天做试验,发现节点中的信息读不到,就想看一下,这函数是否递归搜索。
这个函数

	/* 提取GPIO */
	for (i = 0; i < KEY_NUM; i++) {
   
   
		imx6uirq.irqkeydesc[i].gpio = of_get_named_gpio(imx6uirq.nd ,"key-gpio", i);
		if (imx6uirq.irqkeydesc[i].gpio < 0) {
   
   
			printk("can't get key%d\r\n", i);
		}
	}

正确写法

interrupt_keys: interrupt_keys{
   
   
	#address-cells = <1>;
	#size-cells = <1>;
	compatible = "atkalpha-key";
	pinctrl-names = "default";
	pinctrl-0 = <&pinctrl_gpio_keys>;
	key-gpio = <&gpio1 18 GPIO_ACTIVE_LOW>; /* KEY0 */
	interrupt-parent = <&gpio1>;
	interrupts = <18 IRQ_TYPE_EDGE_BOTH>; /* FALLING RISING */
	status = "okay";
	
<think> 我们之前已经详细解释了of_get_named_gpiogpio_request,但为了符合用户要求,需要再次回答,但注意不能完全重复,可以换角度或补充细节。 此次回答将更侧重于使用场景、内部机制和常见陷阱。 ### 详解 of_get_named_gpiogpio_request #### **1. 函数背景与定位** - **of_get_named_gpio**: 属于Linux设备树(Device Tree)解析API,用于从设备树节点中提取GPIO配置信息。 - **gpio_request**: GPIO子系统核心API,用于声明对GPIO引脚的使用权,避免资源冲突。 #### **2. 函数深度解析** ##### (1) of_get_named_gpio - **内部实现流程**: ```mermaid graph LR A[设备树节点] --> B[查找属性propname] B --> C{属性存在?} C -- 存在 --> D[解析属性值中的GPIO描述符] D --> E[转换为全局GPIO编号] C -- 不存在 --> F[返回-ENOENT] ``` - **关键数据结构**: ```c // GPIO描述符在设备树中的存储格式 // 每个GPIO描述符由3个或4个整数组成: // <gpio_controller_phandle, gpio_pin_number, gpio_flags> // 示例:<&gpio1 18 GPIO_ACTIVE_LOW> ``` ##### (2) gpio_request - **资源管理机制**: - 内核维护全局`gpio_desc`数组,每个GPIO有状态标志: ```c struct gpio_desc { struct gpio_chip *chip; unsigned long flags; // 包含FLAG_REQUESTED等状态 const char *label; }; ``` - `gpio_request`操作: ```c int gpio_request(unsigned gpio, const char *label) { struct gpio_desc *desc = gpio_to_desc(gpio); if (test_and_set_bit(FLAG_REQUESTED, &desc->flags)) return -EBUSY; // 已被申请 desc->label = label; return 0; } ``` #### **3. 进阶使用技巧** - **同时获取多个GPIO**: ```c // 使用of_gpio_count获取属性中GPIO数量 int num_gpios = of_gpio_count(np); int *gpios = kmalloc_array(num_gpios, sizeof(int), GFP_KERNEL); for (int i = 0; i < num_gpios; i++) { gpios[i] = of_get_named_gpio(np, "gpios", i); // 错误处理省略 } ``` - **自动释放机制**: ```c // 使用设备资源管理(devres)版本 int devm_gpio_request(struct device *dev, unsigned gpio, const char *label); // 驱动卸载时自动调用gpio_free ``` #### **4. 典型错误案例** - **案例1: 忽略电平标志** ```dts // 设备树:led-gpios = <&gpio1 5 GPIO_ACTIVE_LOW>; ``` 驱动代码错误: ```c gpio_direction_output(gpio_num, 0); // 意图点亮LED,但实际输出低电平,由于ACTIVE_LOW,LED不会亮 ``` 正确做法: ```c // 使用gpiod_get时自动处理极性,但此处需手动处理: gpio_direction_output(gpio_num, 1); // 输出高电平,实际LED电压为低,点亮 ``` - **案例2: 跨控制器GPIO申请** ```dts // 设备树:两个GPIO来自不同控制器 cross-gpios = <&gpio0 3 GPIO_ACTIVE_HIGH>, <&gpio1 8 GPIO_ACTIVE_HIGH>; ``` 驱动代码: ```c // 必须分别申请和释放 gpio_request(of_get_named_gpio(np, "cross-gpios", 0), "gpio0-pin3"); gpio_request(of_get_named_gpio(np, "cross-gpios", 1), "gpio1-pin8"); ``` #### **5. 与gpiod_* API的对比** | 传统GPIO API | 推荐GPIO Descriptor API | |-----------------------|-----------------------------| | `of_get_named_gpio` | `gpiod_get_index` | | `gpio_request` | 自动在gpiod_get中申请 | | `gpio_direction_*` | `gpiod_direction_*` | | 手动处理电平极性 | 自动根据设备树标志转换电平 | **迁移示例**: ```c // 传统方式 int gpio = of_get_named_gpio(np, "led-gpio", 0); gpio_request(gpio, "led"); gpio_direction_output(gpio, 1); // 新方式 struct gpio_desc *led_desc = gpiod_get_index(dev, "led", 0, GPIOD_OUT_HIGH); gpiod_set_value(led_desc, 0); // 即使设备树有GPIO_ACTIVE_LOW也会正确点亮 ``` §§相关问题§§ 1. 在Linux设备树中,同一个GPIO属性包含多个GPIO引脚描述时,`of_gpio_count`函数如何计算引脚数量? 2. 使用`gpio_request`申请成功后,若驱动未调用`gpio_free`直接卸载模块,会导致什么后果? 3. 在设备树中配置`GPIO_ACTIVE_LOW`标志后,驱动使用`gpio_set_value`和`gpiod_set_value`有何行为差异? 4. 如何在内核中跟踪调试GPIO资源泄漏问题? </think> ### 详解 of_get_named_gpiogpio_request #### **1. 核心功能对比** | 函数 | 作用域 | 主要功能 | 资源管理 | |--------------------|----------------|----------------------------------|--------------| | `of_get_named_gpio` | 设备树解析 | 从设备树获取GPIO编号 | 不占用资源 | | `gpio_request` | GPIO子系统 | 申请GPIO使用权并标记为占用 | 占用资源 | #### **2. of_get_named_gpio 深度解析** ```c // 内核实现简化版 int of_get_named_gpio(struct device_node *np, const char *propname, int index) { // 1. 查找设备树属性 struct property *prop = of_find_property(np, propname, NULL); // 2. 获取GPIO描述符 struct gpio_desc *desc = of_get_named_gpiod_flags(np, propname, index, NULL); // 3. 转换为全局GPIO编号 return desc ? desc_to_gpio(desc) : -ENOENT; } ``` - **关键特性**: - 不实际占用GPIO资源 - 支持多索引访问(如`enable-gpios[0]`, `enable-gpios[1]`) - 自动解析设备树标志位(`GPIO_ACTIVE_LOW`等) #### **3. gpio_request 工作机制** ```mermaid graph TD A[调用gpio_request] --> B{检查GPIO状态} B -->|未占用| C[标记FLAG_REQUESTED] C --> D[设置label标识] D --> E[返回成功0] B -->|已占用| F[返回-EBUSY] ``` - **资源管理原理**: - 内核维护全局`gpio_desc[]`数组 - 每个GPIO包含状态标志位: ```c struct gpio_desc { unsigned long flags; // 包含FLAG_REQUESTED等状态 const char *label; // 申请时设置的标识 // ... }; ``` #### **4. 完整开发流程示例** ```c // 设备树节点 // mydev: mydev@0 { // compatible = "vendor,mydev"; // irq-gpios = <&gpio1 12 GPIO_ACTIVE_HIGH>; // }; static int mydev_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; int gpio, irq, ret; // 1. 获取GPIO编号(不占用资源) gpio = of_get_named_gpio(np, "irq-gpios", 0); if (gpio < 0) { dev_err(dev, "获取GPIO失败: %d\n", gpio); return gpio; } // 2. 申请GPIO使用权(占用资源) ret = devm_gpio_request(dev, gpio, "mydev-irq"); if (ret) { dev_err(dev, "GPIO %d申请失败: %d\n", gpio, ret); return ret; } // 3. 配置为输入模式 ret = gpio_direction_input(gpio); if (ret) { dev_err(dev, "GPIO方向设置失败\n"); return ret; } // 4. 转换为中断号 irq = gpio_to_irq(gpio); ret = devm_request_irq(dev, irq, mydev_irq_handler, IRQF_TRIGGER_RISING, "mydev_irq", NULL); // ... 错误处理省略 return 0; } ``` #### **5. 资源管理最佳实践** - **手动管理**(传统方式): ```c gpio_request(gpio, "label"); // ... 使用GPIO ... gpio_free(gpio); // 必须显式释放 ``` - **自动管理**(推荐方式): ```c devm_gpio_request(dev, gpio, "label"); // 无需显式free,随设备自动释放 ``` 释放时机: - 驱动卸载时(`remove`) - 探测失败时(`probe`错误处理) - 设备断开时(热插拔) #### **6. 特殊场景处理** **场景1:共享GPIO** ```c // 多个驱动使用同一GPIO(需协调) if (gpio_is_valid(shared_gpio)) { if (!gpio_requested(shared_gpio)) { ret = gpio_request(shared_gpio, "shared-gpio"); } // 使用引用计数管理 } ``` **场景2:电平极性处理** ```dts // 设备树:power-enable-gpios = <&gpio2 8 GPIO_ACTIVE_LOW>; ``` ```c // 驱动代码需反转逻辑 gpio_direction_output(gpio, 1); // 实际输出低电平(使能) ``` #### **7. 调试与错误排查** - **常见错误码**: | 错误码 | 含义 | 常见原因 | |------------|-----------------------|-----------------------------| | `-ENOENT` | 属性不存在 | 设备树未定义该GPIO属性 | | `-EINVAL` | 无效参数 | 索引超出范围/无效phandle | | `-EBUSY` | GPIO已被占用 | 未释放前重复申请 | | `-EPROBE_DEFER` | 依赖未就绪 | GPIO控制器驱动未加载 | - **调试命令**: ```bash # 查看GPIO状态 cat /sys/kernel/debug/gpio # 查看设备树GPIO映射 dtc -I fs /sys/firmware/devicetree/base | grep gpio ```
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

清欢_小铭

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值