本文轉載自:http://blog.chinaunix.net/uid-25014876-id-83299.html
linux設備驅動歸納總結(五):3.操作硬件——IO靜態映射
?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
有時候會覺得,每次訪問硬件都要先通過ioremap來獲取虛擬地址,其實有沒有一種一勞永逸的方法,只要一次的操作,以后就能通過這個地址來訪問硬件。答案是“有”,這就是接下來要介紹的IO內存靜態映射。
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
?
一、靜態IO是怎么建立的
?
Io靜態映射發生在內核啟動的時候,接下來通過內核源代碼來分析,如果你的開發板是mini2440或者時候mini2440的內核配置文件,可以跟著我同樣修改。注意:我的開發板只是使用mini2440的配置文件,外圍電路跟mini2440不一樣。
?
注:以下代碼在內核目錄linux-2.6.29/arch/arm/mach-s3c2440/mach-mini2440.c。
靜態映射的建立方法,是在內核啟動的時候,讀取struct map_desc結構體里面的成員:
/*arch/arm/include/asm/mach/map.h*/
14 struct map_desc {
15 unsigned long virtual; //存放以后需要操作的虛擬地址,由自己定義
16 unsigned long pfn; //需要操作的硬件的物理地址對應的頁幀號,即物理地址右移12
17 unsigned long length; //需要映射的大小
18 unsigned int type; //類型
19 };
這里要說明兩個成員:
1)物理地址的頁幀號pfn:如果你了解linux的頁式管理,那你應該知道,一個頁的大小是4096B(2 << 12),所以一個地址的31-12位是用來表示一個地址對應的頁幀號。對應的,一個物理地址,只要右移12位就能得到對應的頁幀號,也可以使用函數:
#define __phys_to_pfn(paddr) ((paddr) >> PAGE_SHIFT) //其實也是右移12位
2)類型type:有下面這些類型定義:
/*include/asm/mach/map.h*/
21 /* types 0-3 are defined in asm/io.h */
22 #define MT_UNCACHED 4
23 #define MT_CACHECLEAN 5
24 #define MT_MINICLEAN 6
25 #define MT_LOW_VECTORS 7
26 #define MT_HIGH_VECTORS 8
27 #define MT_MEMORY 9
28 #define MT_ROM 10
其中,MT_UNCACHED是我們常用的,表示該地址不放在緩沖區cached中。要知道,為了方便內存的訪問,內核會將一些經常使用的內存數據放在cached中,但是這樣在訪問寄存器時就不行了,如果寄存器改變了,內核讀取數據是從cached中讀取數據,而不在寄存器讀取,這樣的做法是不合理的。
?
首先,我們需要往這個結構體中填充我們需要訪問的地址。
本來這個結構體是空的。
/*arch/arm/mach-s3c2440/mach-mini2440.c*/
45 static struct map_desc mini2440_iodesc[] __initdata = {
46 };
修改成:
45 static struct map_desc mini2440_iodesc[] __initdata = {
46 {
47 .virtual = 0xeeee0000,
48 .pfn = __phys_to_pfn(0x56000000), //0x56000
49 .length = SZ_4K, //這里我直接映射一頁
50 .type = MT_UNCACHED
51 },
52 };
?
填充結構體后,我們再看看啟動時通過調用什么函數:
/*linux-2.6.29/arch/arm/mach-s3c2440/mach-mini2440.c*/
264 static void __init mini2440_map_io(void)
265 {?//就是這個函數,將我剛才修改的結構體的成員進行靜態映射
266 s3c24xx_init_io(mini2440_iodesc, ARRAY_SIZE(mini2440_iodesc));
267 s3c24xx_init_clocks(12000000);
268 s3c24xx_init_uarts(mini2440_uartcfgs, ARRAY_SIZE(mini2440_uartcfgs));
269 }
這個函數里面有一個重要的函數——iotable_init(),其實大部分的工作都由這個函數來完成,實現靜態映射。
?
既然修改了內核,就需要重新編譯內核:
make bzImage
?
通過上面這幾步,我們就實現了這樣的一個操作,可以通過虛擬地址0xeeee0000來訪問一頁的物理地址。既然知道了虛擬地址和物理地址之間的關系,就不需要再用ioremap了。
?
為了更好的規范,我們使用一個頭文件來定義寄存器的訪問地址,方便編程時使用:
/*5th_mm_3/1st/test_map_io.h*/
1 #ifndef _TEST_H
2 #define _TEST_H
3
4 typedef volatile unsigned long * s3c_reg_t;
5
6 #define S3C2440_VA 0xeeee0000 //我們已經知道靜態映射的虛擬地址
7
8 #define S3C2440_BASE(x) (S3C2440_VA + (x))
9 #define S3C2440_GPEBASE S3C2440_BASE(0x40)
10 #define S3C2440_GPECON S3C2440_BASE(0x40) //這就是我們要操作的寄存器
11 #define S3C2440_GPEDAT S3C2440_BASE(0x44)
12 #define S3C2440_GPEUP S3C2440_BASE(0x48)
13
14
15 #endif /* _TEST_H */
然后再修改一下前一節的2nd函數,去掉ioremap部分:
1 #include
2 #include
3
4 #include
5 #include "test_map_io.h"
6
7 s3c_reg_t *GPECON, *GPEDAT, *GPEUP;
8 unsigned long reg;
9
10 void led_device_init(void)
11 {
12 GPECON = (s3c_reg_t *)S3C2440_GPECON;
13 GPEDAT = (s3c_reg_t *)S3C2440_GPEDAT;
14 GPEUP = (s3c_reg_t *)S3C2440_GPEUP;
15 }
16
17 void led_configure(void)
18 {
19 reg = ioread32(GPECON);
20 reg &= ~(3 << 24);
21 reg |= (1 << 24);
22 iowrite32(reg, GPECON);
23
24 reg = ioread32(GPEUP);
25 reg &= ~(3 << 12);
26 iowrite32(reg, GPEUP);
27 }
28
29 void led_on(void)
30 {
31 reg = ioread32(GPEDAT);
32 reg &= ~(1 << 12);
33 iowrite32(reg, GPEDAT);
34 }
35
36 void led_off(void)
37 {
38 reg = ioread32(GPEDAT);
39 reg |= (1 << 12);
40 iowrite32(reg, GPEDAT);
41 }
42
43 static int __init test_init(void) //模塊初始化函數
44 {
45 led_device_init();
46 led_configure();
47 led_on();
48 printk("hello led!\n");
49 return 0;
50 }
51
52 static void __exit test_exit(void) //模塊卸載函數
53 {
54 led_off();
55 printk("bye\n");
56 }
57
58 module_init(test_init);
59 module_exit(test_exit);
60
61 MODULE_LICENSE("GPL");
62 MODULE_AUTHOR("xoao bai");
63 MODULE_VERSION("v0.1");
除了紅筆部分,和刪除的ioremap相關函數,其他部分都沒有改動,效果還是一樣,加載燈亮,卸載燈滅。
?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
?
二、總結
?
這節介紹的內容確實是少:
1)內核中使用什么數據結構來管理靜態映射IO。
2)內核時候什么函數在啟動過程實現靜態IO映射。
3)如何編寫驅動函數來使用靜態映射IO。
?
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx