ARM:我可以直接访问 ioremap_nocache() 返回的范围 [而不使用 ioread*()/iowrite*()] 吗?

ARM: May I do direct memory accesses to a range returned by ioremap_nocache() [without using ioread*()/iowrite*()]?

我使用的是 TI AM3358 SoC,运行 ARM Cortex-A8 处理器,运行 Linux 3.12。我在设备树中启用了 GPMC 节点的子设备,它探测我的驱动程序,并在那里我使用设备树节点提供的资源调用 ioremap_nocache() 以获取未缓存的区域。

我请求无缓存的原因是它不是连接到 GPMC 总线的实际内存设备,它当然会受益于处理器缓存,而是 FPGA 设备。因此访问需要始终通过实际线路。

当我这样做时:

u16 __iomem *addr = ioremap_nocache(...);

iowrite16(1, &addr[0]);
iowrite16(1, &addr[1]);
iowrite16(1, &addr[2]);
iowrite16(1, &addr[3]);
ioread16(&addr[0]);
ioread16(&addr[1]);
ioread16(&addr[2]);
ioread16(&addr[3]);

我看到 8 次访问是使用逻辑分析仪在线路上完成的。但是,当我这样做时:

u16 v;

addr[0] = 1;
addr[1] = 1;
addr[2] = 1;
addr[3] = 1;
v = addr[0];
v = addr[1];
v = addr[2];
v = addr[3];

我看到了四个写访问,但没有看到后续的读访问。

我错过了什么吗?知道整个 GPMC 范围应该像内存一样可寻址,ioread16() 和直接内存访问之间有什么区别?

此行为是否是可以避免的任何编译器优化的结果?我还没有查看生成的说明,但在那之前,也许有足够经验的人有一些有趣的回复。

ioread*()iowrite*(),在 ARM 上,执行数据内存屏障,然后执行 volatile 访问,例如:

#define readb(c)                ({ u8  __v = readb_relaxed(c); __iormb(); __v; })
#define readw(c)                ({ u16 __v = readw_relaxed(c); __iormb(); __v; })
#define readl(c)                ({ u32 __v = readl_relaxed(c); __iormb(); __v; })

#define writeb(v,c)             ({ __iowmb(); writeb_relaxed(v,c); })
#define writew(v,c)             ({ __iowmb(); writew_relaxed(v,c); })
#define writel(v,c)             ({ __iowmb(); writel_relaxed(v,c); })

__raw_read*()__raw_write*()(其中 *bwl)可用于直接 reads/writes。它们执行这些操作所需的精确指令,将地址指针转换为 volatile 指针。

__raw_writew() 示例(存储寄存器,半字):

#define __raw_writew __raw_writew
static inline void __raw_writew(u16 val, volatile void __iomem *addr)
{
        asm volatile("strh %1, %0"
                     : "+Q" (*(volatile u16 __force *)addr)
                     : "r" (val));
}

但是请注意,这两个函数不会插入任何屏障,因此您应该在需要内存访问的任何地方调用 rmb()(读取内存屏障)和 wmb()(写入内存屏障)待订购。