我們知道默認外設I/O資源是不在Linux內核空間中的(如sram或硬件接口寄存器等),若需要訪問該外設I/O資源,必須先將其地址映射到內核空間中來,然後才能在內核空間中訪問它。
Linux內核訪問外設I/O內存資源的方式有兩種:動態映射(ioremap)和靜態映射(map_desc)。
一、動態映射(ioremap)方式
動態映射方式是大家使用了比較多的,也比較簡單。即直接通過內核提供的ioremap函數動態創建一段外設I/O內存資源到內核虛擬地址的映射表,從而可以在內核空間中訪問這段I/O資源。
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);
phys_addr:要映射的起始的IO地址
size:要映射的空間的大小
flags:要映射的IO空間和權限有關的標志
該函數返回映射後的內核虛擬地址(3G-4G). 接著便可以通過讀寫該返回的內核虛擬地址去訪問之這段I/O內存資源。
舉一個簡單的例子: (取自s3c2410的iis音頻驅動)
比如我們要訪問s3c2410平台上的I2S寄存器, 查看datasheet 知道IIS物理地址為0x55000000,
我們把它定義為宏S3C2410_PA_IIS,如下:
#define S3C2410_PA_IIS (0x55000000)
若要在內核空間(iis驅動)中訪問這段I/O寄存器(IIS)資源需要先建立到內核地址空間的映射:
our_card->regs = ioremap(S3C2410_PA_IIS, 0x100);
if (our_card->regs == NULL) {
err = -ENXIO;
goto exit_err;
}
創建好了之後,我們就可以通過readl(our_card->regs )或writel(value, our_card->regs)等IO接口函數去訪問它。
二、靜態映射(map_desc)方式
下面重點介紹靜態映射方式即通過map_desc結構體靜態創建I/O資源映射表。
內核提供了在系統啟動時通過map_desc結構體靜態創建I/O資源到內核地址空間的線性映射表(即page table)的方式,這種映射表是一種一一映射的關系。程序員可以自己定義該I/O內存資源映射後的虛擬地址。創建好了靜態映射表,在內核或驅動中訪問該I/O資源時則無需再進行ioreamp動態映射,可以直接通過映射後的I/O虛擬地址去訪問它。
下面詳細分析這種機制的原理並舉例說明如何通過這種靜態映射的方式訪問外設I/O內存資源。
內核提供了一個重要的結構體struct machine_desc ,這個結構體在內核移植中起到相當重要的作用,內核通過machine_desc結構體來控制系統體系架構相關部分的初始化。
machine_desc結構體的成員包含了體系架構相關部分的幾個最重要的初始化函數,包括map_io,init_irq, init_machine以及phys_io , timer成員等。
machine_desc結構體定義如下:
struct machine_desc {
/*
* Note! The first four elements are used
* by assembler code in head-armv.S
*/
unsigned int nr; /* architecture number */
unsigned int phys_io; /* start of physical io */
unsigned int io_pg_offst; /* byte offset for io
* page tabe entry */
const char *name; /* architecture name */
unsigned long boot_params; /* tagged list */
unsigned int video_start; /* start of video RAM */
unsigned int video_end; /* end of video RAM */
unsigned int reserve_lp0 :1; /* never has lp0 */
unsigned int reserve_lp1 :1; /* never has lp1 */
unsigned int reserve_lp2 :1; /* never has lp2 */
unsigned int soft_reboot :1; /* soft reboot */
void (*fixup)(struct machine_desc *,
struct tag *, char **,
struct meminfo *);
void (*map_io)(void);/* IO mapping function */
void (*init_irq)(void);
struct sys_timer *timer; /* system tick timer */
void (*init_machine)(void);
};