Linux 设备树(Device Tree)基础(驱动开发必备知识)

Linux 设备树(Device Tree)基础(驱动开发必备知识)

在嵌入式 Linux 开发中,设备树(Device Tree,简称 DT)是一个至关重要的概念,它用来描述硬件平台的配置信息。设备树与内核代码解耦,提供了一种平台无关的硬件描述方式,使得相同的内核可以支持多种硬件平台。本文将介绍设备树的基础知识,并通过实际示例,阐述如何在 驱动开发 中正确解析和使用设备树中的硬件资源。

1. 什么是设备树?为什么需要它?

设备树是一个数据结构,主要用于描述硬件的配置信息,它告诉操作系统如何配置硬件资源,内核会通过解析设备树来识别和初始化设备,而不需要硬编码硬件配置信息。

设备树的优点:

  • 硬件描述解耦:设备树把硬件的描述信息从内核代码中分离出来,使得同一个内核能够适配多种不同的硬件平台。
  • 简化驱动开发:驱动程序无需硬编码硬件参数,可以通过设备树动态获取硬件资源(如地址、中断号、DMA通道等)。
  • 提高可移植性:设备树提供了一种统一的硬件描述方式,帮助内核在不同的硬件平台上运行时,只需要修改设备树而无需修改内核代码。

2. 设备树文件的基本结构

设备树的配置文件通常是 .dts(Device Tree Source)文件,包含了多个节点,每个节点代表一个硬件设备。每个节点可以包含多个属性(如 compatible、reg、interrupts 等),用于描述该设备的具体特性。

设备树的基本结构

/dts-v1/;

/ {
    compatible = "vendor,device";
    model = "My Embedded System";
    
    cpu {
        device_type = "cpu";
        reg = <0x0>;
        compatible = "arm,cortex-a9";
    };

    memory {
        device_type = "memory";
        reg = <0x80000000 0x20000000>; // 内存起始地址和大小
    };

    uart1: serial@1000 {
        compatible = "vendor,uart";
        reg = <0x1000 0x1000>;  // 寄存器地址
        interrupts = <0x1 0x23>; // 中断号
        status = "okay";         // 设备启用
    };
};

核心字段解析:

  • / 根节点:表示设备树的根节点,包含了系统的基本信息。
  • compatible:该属性指定设备的兼容信息,驱动通过它来判断是否支持该设备。
  • reg:设备的寄存器基地址和大小。
  • interrupts:设备的中断号和中断类型。
  • status:设备的启用状态,“okay” 表示设备启用,“disabled” 表示禁用。

3. 设备树与驱动开发的结合

设备树在驱动开发中起到桥梁作用,它让驱动程序能够动态获取硬件资源,避免了硬编码。驱动程序通常会通过设备树解析出设备的地址、大小、中断号等信息,然后配置设备。

驱动程序如何解析设备树

驱动程序通常会通过以下函数解析设备树:

  • of_property_read_u32():读取设备树中 reg 属性的值。
  • of_irq_get() 或 irq_of_parse_and_map():解析设备树中的中断号。
  • of_find_device_by_node():查找设备节点。

设备树解析的实际示例:UART 驱动

假设我们要开发一个 UART 驱动,设备树中描述了一个串口设备,驱动程序需要从设备树中获取其寄存器地址、大小和中断号。

设备树描述(.dts 文件)

/ {
    compatible = "vendor,device";
    model = "My Embedded System";

    uart1: serial@1000 {
        compatible = "vendor,uart";
        reg = <0x1000 0x1000>;      // 寄存器地址
        reg_size = <0x1000>;        // 寄存器大小
        interrupts = <0x1 0x23>;    // 中断号
        status = "okay";            // 设备启用
    };
};

驱动代码

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_device.h>

static int my_uart_probe(struct platform_device *pdev)
{
    struct device *dev = &pdev->dev;
    void __iomem *base;
    int irq;
    u32 reg_start, reg_size;

    // 通过设备树获取设备的寄存器地址和大小
    if (of_property_read_u32(dev->of_node, "reg", &reg_start)) {
        dev_err(dev, "Failed to get 'reg' property\n");
        return -EINVAL;
    }

    if (of_property_read_u32(dev->of_node, "reg_size", &reg_size)) {
        dev_err(dev, "Failed to get 'reg_size' property\n");
        return -EINVAL;
    }

    base = ioremap(reg_start, reg_size);
    if (!base) {
        dev_err(dev, "Failed to remap memory\n");
        return -ENOMEM;
    }

    // 获取设备的中断号
    irq = irq_of_parse_and_map(dev->of_node, 0);
    if (irq < 0) {
        dev_err(dev, "Failed to get interrupt\n");
        iounmap(base);
        return irq;
    }

    // 设备初始化代码,例如配置 UART 寄存器

    dev_info(dev, "UART base: %p, IRQ: %d\n", base, irq);

    return 0;
}

static int my_uart_remove(struct platform_device *pdev)
{
    // 设备卸载时的清理工作
    iounmap(devm_kzalloc(&pdev->dev, sizeof(void *), GFP_KERNEL));

    return 0;
}

static const struct of_device_id my_uart_of_match[] = {
    { .compatible = "vendor,uart", },
    {},
};
MODULE_DEVICE_TABLE(of, my_uart_of_match);

static struct platform_driver my_uart_driver = {
    .driver = {
        .name = "my_uart",
        .of_match_table = my_uart_of_match,
    },
    .probe = my_uart_probe,
    .remove = my_uart_remove,
};

module_platform_driver(my_uart_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Issac");
MODULE_DESCRIPTION("Simple UART driver using device tree");

驱动解析过程:

  1. 获取设备的寄存器地址:使用 of_property_read_u32() 读取设备树中 reg 属性的值,得到 UART 的寄存器基地址。
  2. 获取设备的寄存器大小:同样通过 of_property_read_u32() 获取 reg_size,如果设备树未提供该属性,通常可以通过 reg 来推测。
  3. 映射设备寄存器:使用 ioremap() 将物理地址映射到内核虚拟地址空间,方便驱动程序对设备寄存器进行操作。
  4. 获取设备的中断号:使用 irq_of_parse_and_map() 函数解析设备树中的中断信息,返回中断号。

设备树4. 总结

设备树是嵌入式 Linux 系统中非常重要的硬件描述工具,尤其对于驱动开发者而言,理解设备树的作用至关重要。它通过动态解析硬件设备的信息,避免了硬编码的复杂性,使得驱动程序更加灵活和可移植。

本文通过一个简单的 UART 驱动例子,演示了如何在驱动程序中解析设备树并获取硬件资源。掌握设备树的基本用法和解析技巧,对于驱动开发和系统移植具有重要意义。

希望这篇文章能够帮助你更好地理解设备树在驱动开发中的应用,并能在实际开发中灵活运用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值