linux核心訪問外設i/o資源的方式
我們知道缺省外設i/o資源是不在linux核心空間中的(如sram或硬體介面暫存器等),若需要訪問該外設i/o資源,必須先將其位址對映到核心空間中來,然後才能在核心空間中訪問它。
linux核心訪問外設i/o記憶體資源的方式有兩種:動態對映(ioremap)和靜態對映(map_desc)。
一、動態對映(ioremap)方式
ioremap巨集定義在asm/io.h內:
#define ioremap(cookie,size) __ioremap(cookie,size,0)
__ioremap函式原型為(arm/mm/ioremap.c):
void __iomem * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags);
size:要對映的空間的大小
flags:要對映的io空間和許可權有關的標誌
該函式返回對映後的核心虛擬位址(3g-4g). 接著便可以通過讀寫該返回的核心虛擬位址去訪問之這段i/o記憶體資源。
舉乙個簡單的例子: (取自s3c2410的iis音訊驅動)
#define s3c2410_pa_iis (0x55000000)
若要在核心空間(iis驅動)中訪問這段i/o暫存器(iis)資源需要先建立到核心位址空間的對映:
our_card->regs = ioremap(s3c2410_pa_iis, 0x100);
if (our_card->regs == null)
iotable_init核心提供,定義如下:
由上知道,s3c2410_map_io最終呼叫iotable_init建立對映表。
iotable_init函式的引數有兩個:乙個是map_desc型別的結構體,另乙個是該結構體的數量nr。這裡最關鍵的就是struct map_desc。map_desc結構體定義如下:
/* include/asm-arm/mach/map.h */
struct map_desc ;
這樣的話我們就知道了建立i/o對映表的大致流程為:只要定義相應i/o資源的map_desc結構體,並將該結構體傳給iotable_init函式執行,就可以建立相應的i/o資源到核心虛擬位址空間的對映表了。
我們來看看s3c2410是怎麼定義map_desc結構體的(即上面s3c2410_map_io函式內的s3c2410_iodesc)。
/* arch/arm/mach-s3c2410/s3c2410.c */
static struct map_desc s3c2410_iodesc __initdata = ;
iodesc_ent巨集如下:
#define iodesc_ent(x)
展開後等價於:
static struct map_desc s3c2410_iodesc __initdata = ,
…… };
s3c24xx_pa_ lcd和s3c24xx_va_ lcd為定義在map.h內的lcd暫存器的實體地址和虛擬位址。在這裡map_desc 結構體的virtual成員被初始化為s3c24xx_va_ lcd,pfn成員值通過__phys_to_pfn核心函式計算,只需要傳遞給它該i/o資源的實體地址就行。length為對映資源的大小。mt_device為i/o型別,通常定義為mt_device。
這裡最重要的即virtual 成員的值s3c24xx_va_ lcd,這個值即該i/o資源對映後的核心虛擬位址,建立對映表成功後,便可以在核心或驅動中直接通過該虛擬位址訪問這個i/o資源。
s3c24xx_va_ lcd以及s3c24xx_pa_ lcd定義如下:
/* include/asm-arm/arch-s3c2410/map.h */
/* lcd controller */
#define s3c24xx_va_lcd s3c2410_addr(0x00600000) //lcd對映後的虛擬位址
#define s3c2410_pa_lcd (0x4d000000) //lcd暫存器實體地址
#define s3c24xx_sz_lcd sz_1m //lcd暫存器大小
s3c2410_addr 定義如下:
#define s3c2410_addr(x) ((void __iomem *)0xf0000000 + (x))
這裡就是一種線性偏移關係,即s3c2410建立的i/o靜態對映表會被對映到0xf0000000之後。(這個線性偏移值可以改,也可以你自己在virtual成員裡手動定義乙個值,只要不和其他io資源對映位址衝突,但最好是在0xf0000000之後。)
io_address巨集定義如下:
/* include/asm/arch-versatile/hardware.h */
/* macro to get at io space when running virtually */
#define io_address(x) (((x) & 0x0fffffff) + (((x) >> 4) & 0x0f000000) + 0xf0000000) )
s3c2410_iodesc這個對映表建立成功後,我們在核心中便可以直接通過s3c24xx_va_ lcd訪問lcd的暫存器資源。
如:s3c2410 lcd驅動的probe函式內
/* stop the video and unset envid if set */
info->regs.lcdcon1 &= ~s3c2410_lcdcon1_envid;
lcdcon1 = readl(s3c2410_lcdcon1); //read對映後的暫存器虛擬位址
writel(lcdcon1 & ~s3c2410_lcdcon1_envid, s3c2410_lcdcon1); //write對映後的虛擬位址
/* include/asm/arch-s3c2410/regs-lcd.h */
#define s3c2410_lcdreg(x) ((x) + s3c24xx_va_lcd)
/* lcd control registers */
#define s3c2410_lcdcon1 s3c2410_lcdreg(0x00)
到此,我們知道了通過map_desc結構體建立i/o記憶體資源靜態對映表的原理了。總結一下發現其實過程很簡單,一通過定義map_desc結構體建立靜態對映表,二在核心中通過建立對映後虛擬位址訪問該io資源。
三、i/o靜態對映方式應用例項
i/o靜態對映方式通常是用在暫存器資源的對映上,這樣在編寫核心**或驅動時就不需要再進行ioremap,直接使用對映後的核心虛擬位址訪問。同樣的io資源只需要在核心初始化過程中對映一次,以後就可以一直使用。
暫存器資源對映的例子上面講原理時已經介紹得很清楚了,這裡我舉乙個sram的例項介紹如何應用這種i/o靜態對映方式。當然原理和操作過程同暫存器資源是一樣的,可以把sram看成是大號的i/o暫存器資源。
比如我的板子在0x30000000位置有一塊64kb大小的sram。我們現在需要通過靜態對映的方式去訪問該sram。我們要做的事內容包括修改kernel**,新增sram資源相應的map_desc結構,建立sram到核心位址空間的靜態對映表。寫乙個sram module,在sram module 內直接通過靜態對映後的核心虛擬位址訪問該sram。
第一步:建立sram靜態對映表
在我板子的map_des結構體陣列(***_io_desc)內新增sram資源相應的map_desc。如下:
static struct map_desc ***_io_desc __initdata = ,,
};巨集***_sram_base為我板子上sram的實體地址,定義為0x30000000。我的kernel是通過io_address的方式計算核心虛擬位址的,這點和之前介紹的s3c2410有點不一樣。不過原理都是相同的,為乙個線性偏移, 範圍在0xf0000000之後。
sram module**如下:
/* sram testing module */
…… static void sram_test(void)
static int __init sram_init(void)
sram_test();
return 0;
} static void __exit sram_exit(void)
module_init(sram_init);
module_exit(sram_exit);
在開發板上執行結果如下:
/ # insmod bin/sram.ko
request sram mem region ............
hello,sram! ß 這句即列印的sram內的字串
/ # rmmod sram
release sram mem region success!
sram is close
實驗發現可以通過對映後的位址正常訪問sram。
核心訪問外設I O資源方式
參考 很多預設的外設i o資源是不在linux核心空間中,如sram 硬體暫存器,如果要訪問這些資源,就必須將它位址對映到核心空間。linux核心空間訪問外設i o資源有兩種方式 動態對映 ioremap 和靜態對映 map desc 一 動態對映 動態對映方式接觸應該比較多,即通過核心提供的ior...
Linux 核心訪問外設IO資源的方式
首先介紹一下i o埠和i o記憶體。1.i o埠 當乙個暫存器或記憶體位於i o空間時,稱其為i o埠。2.i o記憶體 當乙個暫存器或記憶體位於記憶體空間時,稱其為i o記憶體。再來看一下i o暫存器和常規記憶體的區別 i o暫存器具有邊際效應 side effect 而記憶體操作則沒有,記憶體寫...
外設I O資源的訪問方式
華清遠見嵌入式學院 講師。我們知道缺省外設i o資源是不在linux核心空間中的 如sram或硬體介面暫存器等 若需要訪問該外設i o資源,必須先將其位址對映到核心空間中來,然後才能在核心空間中訪問它。linux核心訪問外設i o記憶體資源的方式有兩種 動態對映 ioremap 和靜態對映 map ...