1. 初始化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
判定标准:
- 模块加载无报错,dmesg输出“GPU HAL initialized successfully”;
- 若寄存器地址或中断号错误,模块加载返回错误码(-EIO或-EBUSY),dmesg显示对应错误信息;
- 卸载模块(rmmod)无异常,中断与映射资源正确释放。
预期输出(确定性结果):当寄存器基地址0x3D000000处硬件状态寄存器bit0为1时,初始化函数返回0,模块加载成功并打印成功日志。若地址无效,ioread32返回0xFFFFFFFF,函数返回-EIO。