linux驅動開發之地址映射(mmu)

在編寫驅動之前,我們需要先簡單瞭解一下 MMU 這個神器, MMU 全稱叫做 Memory Manage Unit,也就是內存管理單元。在老版本的 Linux 中要求處理器必須有 MMU,但是現在Linux 內核已經支持無 MMU 的處理器了。 MMU 主要完成的功能如下:
①、完成虛擬空間到物理空間的映射。
②、內存保護,設置存儲器的訪問權限,設置虛擬存儲空間的緩衝特性。
我們重點來看一下第①點,也就是虛擬空間到物理空間的映射,也叫做地址映射。首先了解兩個地址概念:虛擬地址(VA,Virtual Address)、物理地址(PA, Physcical Address)。對於 32 位的處理器來說,虛擬地址範圍是 2^32=4GB,開發板上有 512MB 的 DDR3,這 512MB 的內存就是物理內存,經過 MMU 可以將其映射到整個 4GB 的虛擬空間,如圖
在這裏插入圖片描述
物理內存只有 512MB,虛擬內存有 4GB,那麼肯定存在多個虛擬地址映射到同一個物理地址上去,虛擬地址範圍比物理地址範圍大的問題處理器自會處理.
Linux 內核啓動的時候會初始化 MMU,設置好內存映射,設置好以後 CPU 訪問的都是虛擬 地 址 。 比 如 I.MX6ULL 的 GPIO1_IO03 引 腳 的 復 用 寄 存 器IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 的地址爲 0X020E0068。如果沒有開啓 MMU 的話直接向 0X020E0068 這個寄存器地址寫入數據就可以配置 GPIO1_IO03 的複用功能。現在開啓了 MMU,並且設置了內存映射,因此就不能直接向 0X020E0068 這個地址寫入數據了。我們必須得到 0X020E0068 這個物理地址在 Linux 系統裏面對應的虛擬地址,這裏就涉及到了物理內存和虛擬內存之間的轉換,需要用到兩個函數: ioremap 和 iounmap。

1、 ioremap 函數

ioremap 函 數 用 於 獲 取 指 定 物 理 地 址 空 間 對 應 的 虛 擬 地 址 空 間 , 定 義 在arch/arm/include/asm/io.h 文件中,定義如下:.

1 #define ioremap(cookie,size) __arm_ioremap((cookie), (size),
MT_DEVICE)
2 3
void __iomem * __arm_ioremap(phys_addr_t phys_addr, size_t size,
unsigned int mtype)
4 {
5 return arch_ioremap_caller(phys_addr, size, mtype,
__builtin_return_address(0));
6 }

ioremap 是個宏,有兩個參數: cookie 和 size,真正起作用的是函數__arm_ioremap,此函數有三個參數和一個返回值,這些參數和返回值的含義如下:
phys_addr:要映射給的物理起始地址。
size:要映射的內存空間大小。
mtype: ioremap 的類型,可以選擇 MT_DEVICE、 MT_DEVICE_NONSHARED、
MT_DEVICE_CACHED 和 MT_DEVICE_WC, ioremap 函數選擇 MT_DEVICE。
返回值: __iomem 類型的指針,指向映射後的虛擬空間首地址。
假如我們要獲取 I.MX6ULL 的 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 寄存器對應的虛擬地址,使用如下代碼即可:

#define SW_MUX_GPIO1_IO03_BASE (0X020E0068)
static void __iomem* SW_MUX_GPIO1_IO03;
SW_MUX_GPIO1_IO03 = ioremap(GPIO1_GDIR_BASE, 4);

宏 SW_MUX_GPIO1_IO03_BASE 是寄存器物理地址, SW_MUX_GPIO1_IO03 是映射後的虛擬地址。對於 I.MX6ULL 來說一個寄存器是 4 字節(32 位)的,因此映射的內存長度爲 4。映射完成以後直接對 SW_MUX_GPIO1_IO03 進行讀寫操作即可。

2、 iounmap 函數

卸載驅動的時候需要使用 iounmap 函數釋放掉 ioremap 函數所做的映射, iounmap 函數原型如下:

void iounmap (volatile void __iomem *addr)

iounmap 只有一個參數 addr,此參數就是要取消映射的虛擬地址空間首地址。假如我們現在要取消掉 IOMUXC_SW_MUX_CTL_PAD_GPIO1_IO03 寄存器的地址映射,使用如下代碼即可:

iounmap(SW_MUX_GPIO1_IO03);

I/O 內存訪問函數

這裏說的 I/O 是輸入/輸出的意思,並不是我們學習單片機的時候講的 GPIO 引腳。這裏涉及到兩個概念: I/O 端口和 I/O 內存。當外部寄存器或內存映射到 IO 空間時,稱爲 I/O 端口。
當外部寄存器或內存映射到內存空間時,稱爲 I/O 內存。但是對於 ARM 來說沒有 I/O 空間這個概念,因此 ARM 體系下只有 I/O 內存(可以直接理解爲內存)。使用 ioremap 函數將寄存器的物理地址映射到虛擬地址以後,我們就可以直接通過指針訪問這些地址,但是 Linux 內核不建議這麼做,而是推薦使用一組操作函數來對映射後的內存進行讀寫操作。
1、讀操作函數
讀操作函數有如下幾個:

1 u8 readb(const volatile void __iomem *addr)
2 u16 readw(const volatile void __iomem *addr)
3 u32 readl(const volatile void __iomem *addr)

readb、 readw 和 readl 這三個函數分別對應 8bit、 16bit 和 32bit 讀操作,參數 addr 就是要讀取寫內存地址,返回值就是讀取到的數據。
2、寫操作函數
寫操作函數有如下幾個:

1 void writeb(u8 value, volatile void __iomem *addr)
2 void writew(u16 value, volatile void __iomem *addr)
3 void writel(u32 value, volatile void __iomem *addr)

writeb、 writew 和 writel 這三個函數分別對應 8bit、 16bit 和 32bit 寫操作,參數 value 是要寫入的數值, addr 是要寫入的地址

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