5、映射的思考

学习了 GPIO_newbule 之后,一血关于映射上面的问题总结。

1、映射方法

    实现映射的方法是通过:ioremap ,IO_ADDRESS 这两种方法,实现物理和虚拟地址的映射,它们返回的结果就是虚拟地址了,但是这两种方法的区别是:

ioremap : 动态映射,一般是在外围的控制器的地址。当 映射的时间,是当加载相对应函数的是,才完成映射的操作。

IO_ADDRESS  : 静态映射,一般是寄存器资源映射上,映射的时间是,当系统启动的时候,就完成映射的操作,

1.1、IO_ADDRESS  的映射

   物理地址 = 基址 + 偏移地址,所以使用 IO_ADDRESS   进行地址映射的时候,存在两种映射,既直接使用物理地址映射和先对基址映射之后,再加上偏移地址,这两种映射的最终地址,都是一样的。

方法一:

#define IOConfig         IO_ADDRESS(0x200f0000) //IO复用寄存器

#define HW_REG(reg) *((volatile unsigned int *)(reg))

HW_REG(IOConfig + 0x138) = 0;

    上面的代码,是实现 200f_0000 基址地址的映射之后,再加上偏移地址。

方法二:

writel(0, IO_ADDRESS(0x200F0138));

    实现是对物理地址做完映射之后,进行写入 0 的操作。

   两种方法,经过验证,最终得到的虚拟地址是完全一样的,本平台使用的是 海思的 HI3518平台,所以关于 IO_ADDRESS 的定义是位于

Io.h (archarmmach-hi3518includemach)  这里,里面看到 IO_ADDRESS 定义为:

#define __io(a)        __typesafe_io(a)
#define __mem_pci(a)    (a)

#define HI3518_IOCH1_PHYS 0x10000000 /* 0x1000_0000 ~ 0x1020_0000 */
#define HI3518_IOCH2_PHYS 0x20000000 /* 0x2000_0000 ~ 0x2020_0000 */
#define HI3518_IOCH1_SIZE 0x200000
#define HI3518_IOCH2_SIZE 0x700000

#define HI3518_IOCH1_VIRT 0xFE000000
#define HI3518_IOCH2_VIRT (HI3518_IOCH1_VIRT + HI3518_IOCH1_SIZE)

/*
*         physical addr       <--->         virtual addr
* [0x1000_0000 ~ 0x1020_0000) <---> [0xFE00_0000 ~ 0xFE20_0000)
* [0x2000_0000 ~ 0x2070_0000) <---> [0xFE20_0000 ~ 0xFE90_0000)
*/
#define IO_IOCH1_OFFSET        (HI3518_IOCH1_VIRT - HI3518_IOCH1_PHYS)
#define IO_IOCH2_OFFSET        (HI3518_IOCH2_VIRT - HI3518_IOCH2_PHYS)

#define IO_ADDRESS(x) ((x) >= HI3518_IOCH2_PHYS ? (x) + IO_IOCH2_OFFSET
                        : (x) + IO_IOCH1_OFFSET)

1.3、计算虚拟地址

当IO_ADDRESS(0X200F0000) = (0X200F0000) + IO_IOCH2_OFFSET
    = (0X200F0000) + (HI3518_IOCH2_VIRT - HI3518_IOCH2_PHYS)
    = (0X200F0000) + (HI3518_IOCH2_VIRT - 0x20000000)
    = (0X200F0000) + (0xFE000000 + 0x200000) - 0x20000000
    = FE2F0000  
   
所以,IO_ADDRESS(0X200F0000) + 0x138 = FE2F0138

当当IO_ADDRESS(200F0138) = (200F0138) + IO_IOCH2_OFFSET
    = 200F0138 + (HI3518_IOCH2_VIRT - HI3518_IOCH2_PHYS)
    = 200F0138 + (HI3518_IOCH1_VIRT + HI3518_IOCH1_SIZE) - 0x20000000
    = 200F0138 + 0xFE000000 + 0x200000 - 0x20000000
    = FE2F0138

   可见最终的虚拟地址,都一样的。

1.4、虚拟地址的操作

    对已经完成虚拟地址操作之后,需要对这个地址操作的时候,有以下方法:

void writeb(unsigned value, address);   // 8位

void writew(unsigned value, address);  // 16 位

void writel(unsigned value, address); // 32 位

    这些写入函数,完成写 value 到 address 的地址,address 地址都是已经完成映射的虚拟地址

unsigned readb(address);

unsigned readw(address);

unsigned readl(address);

    读出函数,是将虚拟地址 address 处,读出寄存器的值。

#define HW_REG(reg) *((volatile unsigned int *)(reg))

HW_REG(IOConfig + 0x138) = 0;

    通过,宏代码的方法实现寄存器的写入

#define GPIO0_6_DIR        IO_ADDRESS(0x20140000+ 0x400)

flag = HW_REG(GPIO0_6_DIR);

    完成寄存器数据的读出。

1.5、通过报错的方法寻找头文件

    笔者,想寻找关于 IO_ADDRESS 是在那个头文件定义的,经过朋友指点才找到,但是还有通过重复定义的方法,编译器会提供相关的信息,其实就可以找到。

    在函数里面,自己定义 IO_ADDRESS ,而头文件包含的地方,也是存在 IO_ADDRESS  的,所以就出现了重复定义的错误,当编译的时候,就会报错:

/home/carlos/3516C/driver/gpio_newBlue/gpio.c:168: warning: "IO_ADDRESS" redefined
arch/arm/mach-hi3518/include/mach/io.h:25: note: this is the location of the previous definition

    可见,与真正的头文件是在,arch/arm/mach-hi3518/include/mach/io.h,达到我们的目的

原文地址:https://www.cnblogs.com/qxj511/p/5468901.html