第一: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);
?
審核編輯:湯梓紅
評論
查看更多