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*()
(其中 *
是 b
、w
或 l
)可用于直接 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()
(写入内存屏障)待订购。
我使用的是 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*()
(其中 *
是 b
、w
或 l
)可用于直接 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()
(写入内存屏障)待订购。