靜態映射和動態映射

在linux中去操作系統寄存器時必須經過映射,即
1,靜態映射IO__ADDRESS();//這只是一個工具,使用前必須先初始化靜態映射表
2,動態映射ioremap();
無論靜態映射還是動態映射目的都是將外設地址映射到虛擬地址的空間(3G-4G)中分配給用作外設映射的虛擬地址空間(開啓mmu後cpu看到的都是虛擬地址,訪問外設時同樣需要映射到虛擬地址空間)
一般寫寄存器讀寄存器時:

#define __REG32_DEV(x)        (*((volatile unsigned long *)((x) - REGS_PHYS_BASE + REGS_VIRT_BASE)))

chip_id_reg = __REG32_DEV(0X18000000);

我們在使用 (*((volatile unsigned long *) ((addr))這樣獲取系統寄存器的值時,其中的addr必須是經過映射的虛擬地址,而不能直接使用裸的物理地址,這樣我們就要使用IO_ADDRESS來進行映射。
上面的__REG32_DEV(x) 是我們自己定義的,我們來看看內核中原生的讀寫接口:

readl(addr);wrtel(b,addr);
#define readl(addr)             (*(volatile unsigned int *)(addr))
#define writel(b,addr)          (*(volatile unsigned int *)(addr)) = (b)

再看看他們的使用:
readl(IO_ADDRESS(offset));看到其中的addr也是必須要使用IO__ADDRESS()來進行地址映射的。
writel也是一樣的道理。
但是再回頭看看上面的定義:

#define __REG32_DEV(x)  (*((volatile unsigned long *)((x) - REGS_PHYS_BASE + \
									REGS_VIRT_BASE)))

好像沒有調用IO_ADDRESS()來進行映射,那我們分析下這裏(x) - REGS_PHYS_BASE + \ REGS_VIRT_BASE
看下(x) - REGS_PHYS_BASE + REGS_VIRT_BASE是不是和IO_ADDRESS(x)是一樣的效果
看下IO_ADDRESS(X)實現:
#define IO_ADDRESS(x) (((x) & 0x000fffff) | (((x) >> 4) & 0x0ff00000) | IO_BASE)
好像和(x) - REGS_PHYS_BASE + REGS_VIRT_BASE是不是有點類似?
其實是一樣的,(x) - REGS_PHYS_BASE + REGS_VIRT_BASE表示:
裸地址 - 系統IO基地址 + 虛擬地址基地址。
所以我們的
(x) - REGS_PHYS_BASE + REGS_VIRT_BASE和IO_ADDRESS(x)的效果是一樣的,
完全可以:
#define IO_ADDRESS(x) (x) - REGS_PHYS_BASE + REGS_VIRT_BASE和IO_ADDRESS(x)

總結一下:
總之要在帶mmu的系統中操作寄存器值就必須進行地址映射
無論使用radl(addr),還是writrl(b,addr),或者自己實現一個宏:
#define __REG32_DEV(addr) (*((volatile unsigned long *)(addr)來進行寄存器的操作
其中的addr都必須經過地址映射到虛擬地址空間
映射的方法有:
IO_ADDRESS()使用該宏時,必須先在內核中實現一個映射表纔可以使用該宏或者使用自己的宏
ioremap()
或者直接自己實現上述函數:addr = (addr) - REGS_PHYS_BASE + REGS_VIRT_BASE)))

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章