日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 运维知识 > linux >内容正文

linux

linux设备驱动归纳总结(五):3.操作硬件——IO静态映射【转】

發(fā)布時(shí)間:2023/12/13 linux 26 豆豆
生活随笔 收集整理的這篇文章主要介紹了 linux设备驱动归纳总结(五):3.操作硬件——IO静态映射【转】 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文轉(zhuǎn)載自:http://blog.chinaunix.net/uid-25014876-id-83299.html

linux設(shè)備驅(qū)動(dòng)歸納總結(jié)(五):3.操作硬件——IO靜態(tài)映射

?

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

有時(shí)候會(huì)覺得,每次訪問硬件都要先通過ioremap來獲取虛擬地址,其實(shí)有沒有一種一勞永逸的方法,只要一次的操作,以后就能通過這個(gè)地址來訪問硬件。答案是“有”,這就是接下來要介紹的IO內(nèi)存靜態(tài)映射。

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

?

一、靜態(tài)IO是怎么建立的

?

Io靜態(tài)映射發(fā)生在內(nèi)核啟動(dòng)的時(shí)候,接下來通過內(nèi)核源代碼來分析,如果你的開發(fā)板是mini2440或者時(shí)候mini2440的內(nèi)核配置文件,可以跟著我同樣修改。注意:我的開發(fā)板只是使用mini2440的配置文件,外圍電路跟mini2440不一樣。

?

:以下代碼在內(nèi)核目錄linux-2.6.29/arch/arm/mach-s3c2440/mach-mini2440.c

靜態(tài)映射的建立方法,是在內(nèi)核啟動(dòng)的時(shí)候,讀取struct map_desc結(jié)構(gòu)體里面的成員:

/*arch/arm/include/asm/mach/map.h*/

14 struct map_desc {

15 unsigned long virtual; //存放以后需要操作的虛擬地址,由自己定義

16 unsigned long pfn; //需要操作的硬件的物理地址對(duì)應(yīng)的頁幀號(hào),即物理地址右移12

17 unsigned long length; //需要映射的大小

18 unsigned int type; //類型

19 };

這里要說明兩個(gè)成員:

1)物理地址的頁幀號(hào)pfn:如果你了解linux的頁式管理,那你應(yīng)該知道,一個(gè)頁的大小是4096B2 << 12),所以一個(gè)地址的31-12位是用來表示一個(gè)地址對(duì)應(yīng)的頁幀號(hào)。對(duì)應(yīng)的,一個(gè)物理地址,只要右移12位就能得到對(duì)應(yīng)的頁幀號(hào),也可以使用函數(shù):

#define __phys_to_pfn(paddr) ((paddr) >> PAGE_SHIFT) //其實(shí)也是右移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是我們常用的表示該地址不放在緩沖區(qū)cached。要知道,為了方便內(nèi)存的訪問,內(nèi)核會(huì)將一些經(jīng)常使用的內(nèi)存數(shù)據(jù)放在cached中,但是這樣在訪問寄存器時(shí)就不行了,如果寄存器改變了,內(nèi)核讀取數(shù)據(jù)是從cached中讀取數(shù)據(jù),而不在寄存器讀取,這樣的做法是不合理的。

?

首先,我們需要往這個(gè)結(jié)構(gòu)體中填充我們需要訪問的地址。

本來這個(gè)結(jié)構(gòu)體是空的。

/*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 };

?

填充結(jié)構(gòu)體后,我們再看看啟動(dòng)時(shí)通過調(diào)用什么函數(shù):

/*linux-2.6.29/arch/arm/mach-s3c2440/mach-mini2440.c*/

264 static void __init mini2440_map_io(void)

265 {?//就是這個(gè)函數(shù),將我剛才修改的結(jié)構(gòu)體的成員進(jìn)行靜態(tài)映射

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 }

這個(gè)函數(shù)里面有一個(gè)重要的函數(shù)——iotable_init(),其實(shí)大部分的工作都由這個(gè)函數(shù)來完成,實(shí)現(xiàn)靜態(tài)映射。

?

既然修改了內(nèi)核,就需要重新編譯內(nèi)核:

make bzImage

?

通過上面這幾步,我們就實(shí)現(xiàn)了這樣的一個(gè)操作,可以通過虛擬地址0xeeee0000來訪問一頁的物理地址。既然知道了虛擬地址和物理地址之間的關(guān)系,就不需要再用ioremap了。

?

為了更好的規(guī)范,我們使用一個(gè)頭文件來定義寄存器的訪問地址,方便編程時(shí)使用:

/*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 //我們已經(jīng)知道靜態(tài)映射的虛擬地址

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 */

然后再修改一下前一節(jié)的2nd函數(shù),去掉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) //模塊初始化函數(shù)

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) //模塊卸載函數(shù)

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相關(guān)函數(shù),其他部分都沒有改動(dòng),效果還是一樣,加載燈亮,卸載燈滅。

?

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

?

二、總結(jié)

?

這節(jié)介紹的內(nèi)容確實(shí)是少:

1)內(nèi)核中使用什么數(shù)據(jù)結(jié)構(gòu)來管理靜態(tài)映射IO。

2)內(nèi)核時(shí)候什么函數(shù)在啟動(dòng)過程實(shí)現(xiàn)靜態(tài)IO映射。

3)如何編寫驅(qū)動(dòng)函數(shù)來使用靜態(tài)映射IO。

?

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

轉(zhuǎn)載于:https://www.cnblogs.com/zzb-Dream-90Time/p/6248709.html

總結(jié)

以上是生活随笔為你收集整理的linux设备驱动归纳总结(五):3.操作硬件——IO静态映射【转】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。