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

Memory ARM:我可以直接访问ioremap_nocache()返回的范围吗[不使用ioread*()/iowrite*()]?,memory,linux-kernel,arm,linux-device-driver,embedded-linux,Memory,Linux Kernel,Arm,Linux Device Driver,Embedded Linux,我使用的是TI AM3358 SoC,运行的是ARM Cortex-A8处理器,运行的是Linux 3.12。我在设备树中启用了GPMC节点的子设备,它探测我的驱动程序,在那里我使用设备树节点提供的资源调用ioremap\u nocache(),以获取未缓存的区域 我之所以不请求缓存,是因为它不是一个连接到GPMC总线的实际内存设备,GPMC总线当然会受益于处理器缓存,而是一个FPGA设备。因此,访问需要始终通过实际的电线 当我这样做时: u16 __iomem *addr = ioremap_

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

我之所以不请求缓存,是因为它不是一个连接到GPMC总线的实际内存设备,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]);
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];
我看到8次访问是使用逻辑分析仪在线路上完成的。但是,当我这样做时:

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]);
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); })
\uuuu raw\u read*()
\uuu raw\u write*()
(其中
*
b
w
l
)可用于直接读/写。它们执行这些操作所需的单一指令,将地址指针强制转换为
volatile
指针

\uuuu raw\u writew()
示例(存储寄存器,半字):


不过,请注意,这两个函数不会插入任何屏障,因此您应该调用
rmb()
(读取内存屏障)和
wmb()
(写入内存屏障),只要您希望对内存访问进行排序。

您需要检查生成的代码。对于编译器来说,这是一个非常简单的优化。在必要的地方添加volatile。是的,可能就是这样。我会看一看,然后返回结果。您看过
ioread16
的定义了吗?这是专门设计的,既有正确的排序语义,又不会被优化掉。任何启用了优化功能的编译器都会放弃对从未使用过的变量的简单赋值。是的,我看到ARM的
readw()
执行“易失性”加载,然后是数据内存屏障,因此这一定会有所帮助;-)