1. 初始化GPU硬件抽象层

初始化GPU硬件抽象层 学习卡片

任务说明

本案例针对SA8155(Qualcomm Snapdragon 8155)平台,完成GPU硬件抽象层(HAL)的初始化代码。核心目标为:定义GPU寄存器基地址与中断号,编写初始化函数完成寄存器映射与中断注册,并提供验证初始化是否成功的方法。

硬件资源定义

SA8155 GPU(Adreno 640)的寄存器基地址与中断号定义如下,代码中直接使用这些常量:

// gpu_hal.h
#ifndef GPU_HAL_H
#define GPU_HAL_H

#include <stdint.h>
#include <stdbool.h>

#define GPU_REG_BASE_ADDR     0x3D000000UL  // GPU寄存器物理基地址
#define GPU_REG_BLOCK_SIZE    0x100000UL    // 映射大小(1MB)
#define GPU_IRQ_NUMBER        181           // GPU中断号(SA8155)

// 关键寄存器偏移
#define GPU_STATUS_OFFSET     0x00000000UL  // 状态寄存器
#define GPU_POWER_CTRL_OFFSET 0x00000004UL  // 电源控制寄存器
#define GPU_INIT_DONE_MASK    0x00000001UL  // 初始化完成标志位

// HAL上下文结构
typedef struct {
    uintptr_t reg_base_virt;   // 虚拟地址映射
    uint32_t  reg_block_size;
    int       irq_number;
    bool      initialized;
} gpu_hal_t;

// 初始化GPU HAL
int gpu_hal_init(gpu_hal_t *hal);
// 验证初始化状态
bool gpu_hal_is_initialized(gpu_hal_t *hal);
// 反初始化
void gpu_hal_deinit(gpu_hal_t *hal);

#endif // GPU_HAL_H

初始化实现

以下代码完成寄存器物理地址到虚拟地址的映射、中断号保存,并读取状态寄存器验证硬件可访问性。该实现基于Linux内核驱动模型,使用ioremap进行地址映射,request_irq注册中断处理函数。

// gpu_hal.c
#include <linux/io.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include "gpu_hal.h"

static irqreturn_t gpu_irq_handler(int irq, void *dev_id)
{
    // 中断处理:清除中断标志并记录状态
    gpu_hal_t *hal = (gpu_hal_t *)dev_id;
    uint32_t status = ioread32((void __iomem *)(hal->reg_base_virt + GPU_STATUS_OFFSET));
    // 实际项目中在此处理具体中断事件
    return IRQ_HANDLED;
}

int gpu_hal_init(gpu_hal_t *hal)
{
    if (!hal)
        return -EINVAL;

    hal->reg_block_size = GPU_REG_BLOCK_SIZE;
    hal->irq_number = GPU_IRQ_NUMBER;
    hal->initialized = false;

    // 1. 映射寄存器物理地址到虚拟地址
    hal->reg_base_virt = (uintptr_t)ioremap(GPU_REG_BASE_ADDR, GPU_REG_BLOCK_SIZE);
    if (!hal->reg_base_virt) {
        return -ENOMEM;
    }

    // 2. 读取状态寄存器验证映射有效
    uint32_t status = ioread32((void __iomem *)(hal->reg_base_virt + GPU_STATUS_OFFSET));
    if (status == 0xFFFFFFFF) {
        // 全1表示总线错误,映射无效
        iounmap((void __iomem *)hal->reg_base_virt);
        return -EIO;
    }

    // 3. 注册中断
    if (request_irq(hal->irq_number, gpu_irq_handler, IRQF_SHARED,
                    "gpu_hal", hal)) {
        iounmap((void __iomem *)hal->reg_base_virt);
        return -EBUSY;
    }

    // 4. 设置电源控制寄存器,使GPU退出复位状态
    iowrite32(0x1, (void __iomem *)(hal->reg_base_virt + GPU_POWER_CTRL_OFFSET));

    // 5. 确认初始化完成标志
    status = ioread32((void __iomem *)(hal->reg_base_virt + GPU_STATUS_OFFSET));
    if (!(status & GPU_INIT_DONE_MASK)) {
        free_irq(hal->irq_number, hal);
        iounmap((void __iomem *)hal->reg_base_virt);
        return -ETIMEDOUT;
    }

    hal->initialized = true;
    return 0;
}

bool gpu_hal_is_initialized(gpu_hal_t *hal)
{
    if (!hal)
        return false;
    return hal->initialized;
}

void gpu_hal_deinit(gpu_hal_t *hal)
{
    if (!hal)
        return;

    if (hal->initialized) {
        free_irq(hal->irq_number, hal);
        iounmap((void __iomem *)hal->reg_base_virt);
        hal->initialized = false;
    }
}

调用示例

在驱动入口函数中调用初始化,并检查返回值:

// 使用示例
static gpu_hal_t gpu_hal;

static int __init gpu_driver_init(void)
{
    int ret = gpu_hal_init(&gpu_hal);
    if (ret) {
        pr_err("GPU HAL init failed: %d\n", ret);
        return ret;
    }

    if (gpu_hal_is_initialized(&gpu_hal)) {
        pr_info("GPU HAL initialized successfully\n");
    }
    return 0;
}

static void __exit gpu_driver_exit(void)
{
    gpu_hal_deinit(&gpu_hal);
}

module_init(gpu_driver_init);
module_exit(gpu_driver_exit);
MODULE_LICENSE("GPL");

验证方法

将上述代码编译为内核模块(需Linux内核源码树),在SA8155目标板上加载模块:

# 编译
make -C /path/to/kernel M=$(pwd) modules

# 加载模块
insmod gpu_hal.ko

# 观察内核日志
dmesg | tail

判定标准:

  1. 模块加载无报错,dmesg输出“GPU HAL initialized successfully”;
  2. 若寄存器地址或中断号错误,模块加载返回错误码(-EIO或-EBUSY),dmesg显示对应错误信息;
  3. 卸载模块(rmmod)无异常,中断与映射资源正确释放。
预期输出(确定性结果):当寄存器基地址0x3D000000处硬件状态寄存器bit0为1时,初始化函数返回0,模块加载成功并打印成功日志。若地址无效,ioread32返回0xFFFFFFFF,函数返回-EIO。
GPU HAL初始化流程与验证观测点 驱动入口 gpu_hal_init() ioremap() 读状态寄存器 值=0xFFFFFFFF? 返回 -EIO request_irq() 注册失败? 返回 -EBUSY 写电源控制寄存器 检查INIT_DONE位 bit0=1? 返回 -ETIMEDOUT 返回 0 (成功) 验证观测点 1. dmesg输出成功日志 2. 模块加载无错误码 3. 卸载时资源正确释放