2. 解析设备树获取GPU资源

任务说明

本案例实现从设备树中解析Mali GPU驱动所需的三种核心资源:寄存器地址与长度、中断号、时钟信息。解析结果以结构体形式输出,供驱动后续的ioremap、request_irq、clk_get等操作直接使用。

设备树节点示例

以下为Mali GPU在设备树中的典型节点(dts片段),本案例代码将针对该节点进行解析:


gpu: mali@13000000 {
    compatible = "arm,mali-bifrost";
    reg = <0x13000000 0x10000>;
    interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
    clocks = <&clk_gpu &clk_gpu_axi>;
    clock-names = "gpu", "axi";
};

核心数据结构


struct mali_gpu_resources {
    phys_addr_t reg_start;
    u32 reg_size;
    int irq;
    struct clk *clk_gpu;
    struct clk *clk_axi;
};

解析实现


#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/of_clk.h>
#include <linux/clk.h>
#include <linux/platform_device.h>

static int mali_parse_dt_resources(struct platform_device *pdev,
                                   struct mali_gpu_resources *res)
{
    struct device_node *np = pdev->dev.of_node;
    struct resource r;
    int ret;

    if (!np)
        return -ENODEV;

    /* 1. 解析寄存器地址与长度 */
    ret = of_address_to_resource(np, 0, &r);
    if (ret) {
        dev_err(&pdev->dev, "failed to parse reg\n");
        return ret;
    }
    res->reg_start = r.start;
    res->reg_size = resource_size(&r);

    /* 2. 解析中断号 */
    res->irq = irq_of_parse_and_map(np, 0);
    if (!res->irq) {
        dev_err(&pdev->dev, "failed to parse irq\n");
        return -EINVAL;
    }

    /* 3. 解析时钟(需与clock-names顺序对应) */
    res->clk_gpu = of_clk_get_by_name(np, "gpu");
    if (IS_ERR(res->clk_gpu)) {
        dev_err(&pdev->dev, "failed to get gpu clock\n");
        return PTR_ERR(res->clk_gpu);
    }

    res->clk_axi = of_clk_get_by_name(np, "axi");
    if (IS_ERR(res->clk_axi)) {
        dev_err(&pdev->dev, "failed to get axi clock\n");
        clk_put(res->clk_gpu);
        return PTR_ERR(res->clk_axi);
    }

    return 0;
}

调用与资源释放


static int mali_probe(struct platform_device *pdev)
{
    struct mali_gpu_resources res;
    int ret;

    memset(&res, 0, sizeof(res));
    ret = mali_parse_dt_resources(pdev, &res);
    if (ret)
        return ret;

    /* 此处可进行 ioremap(res.reg_start, res.reg_size) 等后续操作 */
    dev_info(&pdev->dev, "GPU reg: 0x%llx size: %u irq: %d\n",
             (u64)res.reg_start, res.reg_size, res.irq);

    return 0;
}

static void mali_resources_release(struct mali_gpu_resources *res)
{
    if (!IS_ERR_OR_NULL(res->clk_gpu))
        clk_put(res->clk_gpu);
    if (!IS_ERR_OR_NULL(res->clk_axi))
        clk_put(res->clk_axi);
}

验证方法

将上述代码编译进内核模块,在目标板上加载模块。若解析成功,dmesg中应输出寄存器起始地址0x13000000、长度0x10000及中断号45。若设备树节点缺失或属性错误,应返回对应错误码并打印错误信息。可通过手动修改设备树中reg或interrupts值来验证解析逻辑的健壮性。

设备树节点 mali@13000000 reg: 0x13000000 size 0x10000 interrupts: 45 clocks: gpu, axi of_address_to_resource 解析函数 1. reg → 地址/长度 2. irq_of_parse_and_map 3. of_clk_get_by_name 填充 mali_gpu_resources reg_start = 0x13000000 reg_size = 0x10000 irq = 45 clk_gpu = clk指针 clk_axi = clk指针 后续使用 驱动后续操作 ioremap(reg_start, reg_size) request_irq(irq, handler) clk_prepare_enable(clk_gpu) 错误处理 -ENODEV: 无设备树节点 -EINVAL: 中断解析失败 设备树输入 解析函数 资源结构体 错误路径/后续使用

判定标准

代码通过编译且加载后满足以下条件即判定为通过:

  • dmesg输出中包含"GPU reg: 0x13000000 size: 65536 irq: 45"(数值与设备树一致);
  • 当设备树中reg属性被修改为其他地址时,输出随之变化;
  • 删除clock-names中的"axi"后,模块加载返回-ENOENT且打印"failed to get axi clock"。