You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

内核模块ioremap映射物理寄存器后iowrite32写入失效、回读值不符问题排查

内核模块外设寄存器写入异常问题

问题背景

  • 同类问题在社区已出现数十次,但始终无法定位本次问题根源
  • 开发需求:编写内核模块,需从预留内存区间读取外部设备写入的数据,同时向外设配套控制寄存器写入配置值
  • 异常现象:执行写入操作后回读校验,得到的值与写入值完全不同,写入仿佛未生效
  • 已验证前提:
    • 设备树reg属性的地址偏移、大小可被内核正确识别
    • /proc/iomem中可查询到该段预留内存的记录
    • 使用*devmem工具操作同一物理地址时,写入、回读校验均可正常工作

相关实现代码

物理地址到虚拟地址映射实现

// Read the control memory and map to virtual address
res_ctrl = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res_ctrl) {
    dev_err(&pdev->dev, "can't get device resources\n");
    return -ENOENT;
}
p3chvideo_device->pchv_ctrl.paddr = res_ctrl->start;
p3chvideo_device->pchv_ctrl.size = resource_size(res_ctrl);
struct resource* res_d = request_mem_region(p3chvideo_device->pchv_ctrl.paddr, p3chvideo_device->pchv_ctrl.size, "p3chv");
p3chvideo_device->pchv_ctrl.vaddr = ioremap_nocache(p3chvideo_device->pchv_ctrl.paddr, p3chvideo_device->pchv_ctrl.size); 
if (!p3chvideo_device->pchv_ctrl.vaddr) {
    pr_info("Control buffer allocated vaddr: 0x%0llX paddr: 0x%0llX (size: 0x%0llX)\n", 
        p3chvideo_device->pchv_ctrl.vaddr, 
        p3chvideo_device->pchv_ctrl.paddr, 
        p3chvideo_device->pchv_ctrl.size);
    return -EADDRNOTAVAIL;
}
//p3chvideo_device->pchv_ctrl.vaddr = devm_ioremap(&pdev->dev, p3chvideo_device->pchv_ctrl.paddr, p3chvideo_device->pchv_ctrl.size);//ioremap(p3chvideo_device->pchv_ctrl.paddr, p3chvideo_device->pchv_ctrl.size);
pr_info("Control buffer allocated vaddr: 0x%0llX paddr: 0x%0llX (size: 0x%0llX)\n", 
    p3chvideo_device->pchv_ctrl.vaddr, 
    p3chvideo_device->pchv_ctrl.paddr, 
    p3chvideo_device->pchv_ctrl.size);

寄存器写入操作实现

static void buffer_loaded_enable_interrupt(void) {
    pr_info("buffer loaded enable interrupt 0x%0llX 0x%0X\n", 
        p3chvideo_device->pchv_ctrl.vaddr + IRQ_ENABLE_BUFFER_LOADED, 
        (u32)(1 << 0));
    // Clear buffer Loaded Interrupt
    //wmb();
    pr_info("Stored value before: 0x%0X", 
        readl(p3chvideo_device->pchv_ctrl.vaddr + IRQ_ENABLE_BUFFER_LOADED));
    //*(p3chvideo_device->pchv_ctrl.vaddr + IRQ_ENABLE_BUFFER_LOADED) = (u32)(1 << 0);
    iowrite32((u32)(1 << 0), p3chvideo_device->pchv_ctrl.vaddr + IRQ_ENABLE_BUFFER_LOADED);
    udelay(100);
    pr_info("Stored value: 0x%0X", 
        readl(p3chvideo_device->pchv_ctrl.vaddr + IRQ_ENABLE_BUFFER_LOADED));
}

核心疑问

上述实现遗漏了哪个关键环节,导致寄存器写入操作无法生效?

问题根源排查与修复

  • 校验pchv_ctrl.vaddr的类型定义。若定义为u32 __iomem *,指针加法会按4字节步长计算偏移,导致实际写入地址与预期字节偏移不符。需将vaddr类型改为void __iomem *,或手动将偏移量转换为字节单位计算地址。
  • 确认设备树reg条目的索引映射。若reg属性包含多个条目(如第一个为预留内存、第二个为控制寄存器),当前代码通过index 0获取的是预留内存资源,而非控制寄存器资源。需调整platform_get_resource的第三个参数为控制寄存器对应的索引。
  • 补充request_mem_region返回值校验。若该物理地址已被其他驱动(如内核原生驱动、reserved-memory子系统)申请,request_mem_region会返回NULL,当前代码未校验该结果继续执行,会导致内存区域冲突。需添加返回值判断,失败时直接返回错误。
  • 更换MMIO映射接口。ioremap_nocache映射的是普通非缓存内存,不适用于MMIO寄存器场景,可能导致CPU合并写入、延迟总线触发。需改用通用的ioremap接口,自动适配设备内存的页属性。
  • 检查reserved-memory节点配置。若预留内存通过reserved-memory节点实现,未添加no-map属性会导致内核自动映射该段内存并启用缓存,再次调用ioremap会导致页表冲突,写入被缓存拦截。需在reserved-memory节点中添加no-map;属性。

内容的提问来源于stack exchange,提问作者user1273813

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.24 01:54:00