zoukankan      html  css  js  c++  java
  • 【Linux开发】linux设备驱动归纳总结(五):3.操作硬件——IO静态映射

    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的页式管理,那你应该知道,一个页的大小是4096B2 << 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! ");

    49 return 0;

    50 }

    51

    52 static void __exit test_exit(void) //模块卸载函数

    53 {

    54 led_off();

    55 printk("bye ");

    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

    源代码: 5th_mm_3.rar   

  • 相关阅读:
    大神的文章
    分布式锁
    分布式事务
    事务两三事
    spring框架笔记
    三个缓存数据库Redis、Memcache、MongoDB
    面向对象面试题
    SSM面试
    单例模式
    Spring Cloud面试题
  • 原文地址:https://www.cnblogs.com/huty/p/8518578.html
Copyright © 2011-2022 走看看