内核模块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
相关产品推荐
相关产品推荐

