第一:linux系统中内存分配关注问题
在编写Linux驱动过程中,不可避免涉及外设操作,而外设地址空间与DDR地址空间一般不连续,在linux上电时,并不会为外设地址空间建立页表,又因为linux访问内存使用的都是虚拟地址,因此如果想访问外设的寄存器(一般包括数据寄存器、控制寄存器与状态寄存器),需要在驱动初始化中将外设所处的物理地址映射为虚拟地址,linux为应对该问题提供了较多接口,包括:ioremap/ioremap_wc/devm_ioremap/devm_ioremap_resource等,以应对不同的场景需求,本文即阐述这些接口的使用,以及需要注意的区别。
第二:场景的应用背景
在系统运行时,外设IO资源的物理地址是已知的,由硬件的设计决定(参考SOC的datesheet,一般会有memorymap)。驱动程序不能通过物理地址访问IO资源,必须将其映射到内核态的虚拟地址空间(通过页表)[1],然后根据映射所得到的内核虚拟地址范围,通过线性偏移(virt_addr = virt_base + phy_addr - phy_base)访问这些IO内存资源。
代码路径:arch/arm/include/asm/io.h /* 349 * ioremap() and friends. 350 * 351 * ioremap() takes a resource address, and size. Due to the ARM memory 352 * types, it is important to use the correct ioremap() function as each 353 * mapping has specific properties. 354 * 355 * Function Memory type Cacheability Cache hint 356 * ioremap() Device n/a n/a 357 * ioremap_nocache() Device n/a n/a 358 * ioremap_cache() Normal Writeback Read allocate 359 * ioremap_wc() Normal Non-cacheable n/a 360 * ioremap_wt() Normal Non-cacheable n/a 361 * 362 * All device mappings have the following properties: 363 * - no access speculation 364 * - no repetition (eg, on return from an exception) 365 * - number, order and size of accesses are maintained 366 * - unaligned accesses are "unpredictable" 367 * - writes may be delayed before they hit the endpoint device 368 * 369 * ioremap_nocache() is the same as ioremap() as there are too many device 370 * drivers using this for device registers, and documentation which tells 371 * people to use it for such for this to be any different. This is not a 372 * safe fallback for memory-like mappings, or memory regions where the 373 * compiler may generate unaligned accesses - eg, via inlining its own 374 * memcpy. 375 * 376 * All normal memory mappings have the following properties: 377 * - reads can be repeated with no side effects 378 * - repeated reads return the last value written 379 * - reads can fetch additional locations without side effects 380 * - writes can be repeated (in certain cases) with no side effects 381 * - writes can be merged before accessing the target 382 * - unaligned accesses can be supported 383 * - ordering is not guaranteed without explicit dependencies or barrier 384 * instructions 385 * - writes may be delayed before they hit the endpoint memory 386 * 387 * The cache hint is only a performance hint: CPUs may alias these hints. 388 * Eg, a CPU not implementing read allocate but implementing write allocate 389 * will provide a write allocate mapping instead. 390 */
ioremap函数组共有五个接口,根据函数实现可以分为三类:
ioremap & ioremap_nocache实现相同,使用场景为映射device memory类型内存;
ioremap_cached,使用场景为映射normal memory类型内存,且映射后的虚拟内存支持cache;
ioremap_wc & ioremap_wt实现相同,使用场景为映射normal memory类型内训,且映射后的虚拟内存不支持cache。
第三:何为memory type?
内存类型(memory type)分为设备(device)类型与一般(normal)类型。
第四:ioremap & ioremap_nocache
代码路径:arch/arm/include/asm/io.h void __iomem *ioremap(resource_size_t res_cookie, size_t size); #define ioremap ioremap #define ioremap_nocache ioremap
ioremap用来映射memory type为device memory的设备,同时不使用cache(device memory本身就没有cacheable这个属性),即CPU的读写操作直接操作设备内存。ioremap_nocache的实现与ioremap完全相同,保留该符号是因为向后兼容使用ioremap_nocache接口的驱动程序。
API接口中的res_cookie参数是需要映射的物理地址,size参数是需要映射的内存大小,单位是Byte。不需要考虑物理地址的页对齐问题,底层通过PAGE_ALIGN接口完成了页对齐。
第五:ioremap_cached
/* * Do not use ioremap_cached in new code. Provided for the benefit of * the pxa2xx-flash MTD driver only. */ void __iomem *ioremap_cached(resource_size_t res_cookie, size_t size);
ioremap_cached用来映射memory type为normal memory的设备,同时使用cache,这会提高内存的访问速度,提高系统的性能。
第六:ioremap_wc & ioremap_wt
void __iomem *ioremap_wc(resource_size_t res_cookie, size_t size); #define ioremap_wc ioremap_wc #define ioremap_wt ioremap_wc
ioremap_wc用来映射memory type为normal memory的设备,同时不使用cache。
第七:解映射API
代码路径:arch/arm/include/asm/io.h void iounmap(volatile void __iomem *iomem_cookie);
不论采用以上哪种映射方式,解映射为统一接口,其中iomem_cookie参数为映射后的虚拟地址。
第八:IO资源读写过程
我们已经知道如何映射和解映射虚拟内存,内存映射是为了访问,理论上这时候可以像读写RAM那样直接通过虚拟地址指针读写IO内存资源,但是为了保证驱动程序跨平台的可移植性【这一点还不太理解,直接指针操作怎么影响的移植性,望大家指教】,我们应该采用linux中特定的接口函数来访问IO内存[1]。常用接口如下:
代码路径:arch/arm/include/asm/io.h 299 #define readb(c) ({ u8 __v = readb_relaxed(c); __iormb(); __v; }) 300 #define readw(c) ({ u16 __v = readw_relaxed(c); __iormb(); __v; }) 301 #define readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; }) 303 #define writeb(v,c) ({ __iowmb(); writeb_relaxed(v,c); }) 304 #define writew(v,c) ({ __iowmb(); writew_relaxed(v,c); }) 305 #define writel(v,c) ({ __iowmb(); writel_relaxed(v,c); }) 316 static inline void memset_io(volatile void __iomem *dst, unsigned c, size_t count); 324 static inline void memcpy_fromio(void *to, const volatile void __iomem *from, size_t count); 332 static inline void memcpy_toio(volatile void __iomem *to, const void *from, size_t count);
审核编辑:汤梓红
评论
查看更多