ioremap_nocache 是 Linux 内核中用于将物理地址映射到内核虚拟地址空间的函数,特别是用于 I/O 内存映射,并且禁用缓存。
一、基本语法
void __iomem *ioremap_nocache(phys_addr_t phys_addr, size_t size);
二、基本功能
将物理内存地址映射到内核虚拟地址空间
禁用 CPU 缓存
返回可用于 I/O 访问的虚拟地址
三、常见使用场景
// 设备驱动中的使用示例
struct my_device {
void __iomem *base; // 映射后的虚拟地址
resource_size_t phys_addr; // 物理地址
resource_size_t size; // 映射大小
};
static int my_probe(struct platform_device *pdev) {
struct my_device *dev;
struct resource *res;
// 获取设备资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
return -ENOENT;
}
// 映射 I/O 内存
dev->base = ioremap_nocache(res->start, resource_size(res));
if (!dev->base) {
return -ENOMEM;
}
// 使用映射后的地址访问硬件
writel(0x1234, dev->base + SOME_REGISTER_OFFSET);
return 0;
}
注意:使用完后必须使用iounmap解除映射
static void my_remove(struct platform_device *pdev) {
struct my_device *dev = platform_get_drvdata(pdev);
if (dev->base) {
iounmap(dev->base);
dev->base = NULL;
}
}
四、相关的内存访问函数
// 32位读写操作
u32 readl(const volatile void __iomem *addr);
void writel(u32 value, volatile void __iomem *addr);
// 16位读写操作
u16 readw(const volatile void __iomem *addr);
void writew(u16 value, volatile void __iomem *addr);
// 8位读写操作
u8 readb(const volatile void __iomem *addr);
void writeb(u8 value, volatile void __iomem *addr);
五、完整的驱动示例
struct my_device {
void __iomem *base;
struct device *dev;
int irq;
};
static int my_driver_probe(struct platform_device *pdev) {
struct my_device *mydev;
struct resource *res;
int ret;
// 分配设备结构体
mydev = devm_kzalloc(&pdev->dev, sizeof(*mydev), GFP_KERNEL);
if (!mydev)
return -ENOMEM;
// 获取内存资源
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENOENT;
// 映射 I/O 内存
mydev->base = ioremap_nocache(res->start, resource_size(res));
if (!mydev->base)
return -ENOMEM;
// 初始化设备
mydev->dev = &pdev->dev;
platform_set_drvdata(pdev, mydev);
// 读写寄存器示例
writel(0x1234, mydev->base + 0x00); // 写寄存器
u32 val = readl(mydev->base + 0x04); // 读寄存器
return 0;
}
static int my_driver_remove(struct platform_device *pdev) {
struct my_device *mydev = platform_get_drvdata(pdev);
// 解除映射
if (mydev->base)
iounmap(mydev->base);
return 0;
}
六、使用场景
- 硬件设备驱动开发
- 访问内存映射的 I/O 设备
- 需要直接访问硬件寄存器
- DMA 操作的内存区域
这个函数在设备驱动开发中非常重要,特别是在需要直接访问硬件寄存器的场景下。禁用缓存确保了对硬件的访问是直接的,不会有缓存导致的不一致问题。