1.前言
由于要操作寄存器,涉及到用户态和内核态映射问题,由物理地址映射到虚拟地址。海思部分型号sdk提供io.h 操作IO_ADDRESS(x),但是有些型号sdk没有提供该定义,对于移植驱动有些问题。所以要造轮子,研究一下。
2.源码
路径为:Hi3521A_SDK_V100\osdrv\opensource\kernel\linux-3.10.y\arch\arm\mach-hi3521a\include\mach\io.h
#ifndef __ASM_ARM_ARCH_IO_H
#define __ASM_ARM_ARCH_IO_H
#define IO_SPACE_LIMIT 0xffffffff
#define __io(a) __typesafe_io(a)
#define __mem_pci(a) (a)
/* phys_addr virt_addr
* 0x1000_0000 <-----> 0xFE00_0000
*/
#define HI3521A_IOCH1_VIRT (0xFE000000)
#define HI3521A_IOCH1_PHYS (0x10000000)
#define HI3521A_IOCH1_SIZE (0x00400000)
/* phys_addr virt_addr
* 0x1200_0000 <-----> 0xFE40_0000
*/
#define HI3521A_IOCH2_VIRT (0xFE400000)
#define HI3521A_IOCH2_PHYS (0x12000000)
#define HI3521A_IOCH2_SIZE (0x00230000)
/* phys_addr virt_addr
* 0x1301_0000 <-----> 0xFE70_0000
*/
#define HI3521A_IOCH3_VIRT (0xFE700000)
#define HI3521A_IOCH3_PHYS (0x13000000)
#define HI3521A_IOCH3_SIZE (0x00160000)
#define IO_OFFSET_LOW (0xEB700000)
#define IO_OFFSET_MID (0xEC400000)
#define IO_OFFSET_HIGH (0xEE000000)
#define IO_ADDRESS_LOW(x) ((x) + IO_OFFSET_LOW)
#define IO_ADDRESS_MID(x) ((x) + IO_OFFSET_MID)
#define IO_ADDRESS_HIGH(x) ((x) + IO_OFFSET_HIGH)
#define __IO_ADDRESS_HIGH(x) ((x >= HI3521A_IOCH2_PHYS) ? IO_ADDRESS_MID(x) \
: IO_ADDRESS_HIGH(x))
#define IO_ADDRESS(x) ((x) >= HI3521A_IOCH3_PHYS ? IO_ADDRESS_LOW(x) \
: __IO_ADDRESS_HIGH(x))
#endif
3.分析
3.1 内核态寄存器划分
根据用户指南文档手册查看对应物理地址,主要把内核态寄存器分为三个区间进行操作。
#define HI3521A_IOCH1_PHYS (0x10000000)
0x1000_0000 0x1000_FFFF FMC寄存器 64KB
#define HI3521A_IOCH2_PHYS (0x12000000)
0x1200_0000 0x1200_FFFF Timer0/Timer1寄存器 64KB
#define HI3521A_IOCH3_PHYS (0x13000000)
0x1300_0000 0x1300_FFFF 保留
3.2 地址映射到用户态
用户空间 0x8000_0000 0xFFFF_FFFF DDR存储地址空间 2GB
IO_ADDRESS作用就是对应内核空间物理地址映射到用户空间地址上,用户态可以间接读写内核态寄存器的值。
通过三个区间范围最大值为32M空间,故把内核态映射到用户态高地址空间0xFE000000-0xFFFFFFFF,同时分析每个区间寄存器最大地址,然后确定虚拟地址的开始地址值和偏移量,注意最大不要超过4G。
至此我们就能得出各个虚拟地址开始地址和偏移量。
3.3 宏引用
- HI3521A_IOCH1_VIRT 、HI3521A_IOCH1_PHYS、HI3521A_IOCH1_SIZE使用出处
arch\arm\mach-hi3521a\core.c中 hi3521a_io_desc -->hi3521a_timer_init
- __phys_to_pfn 定义
路径:arch\arm\include\asm\memory.h
/*
* Convert a physical address to a Page Frame Number and back
*/
#define __phys_to_pfn(paddr) ((unsigned long)((paddr) >> PAGE_SHIFT))
#define __pfn_to_phys(pfn) ((phys_addr_t)(pfn) << PAGE_SHIFT)
/*
* Convert a page to/from a physical address
*/
#define page_to_phys(page) (__pfn_to_phys(page_to_pfn(page)))
#define phys_to_page(phys) (pfn_to_page(__phys_to_pfn(phys)))
路径:arch\arm\include\asm\page.h
#define PAGE_SHIFT 12
#define PAGE_SIZE (_AC(1,UL) << PAGE_SHIFT)
#define PAGE_MASK (~(PAGE_SIZE-1))
路径:include\asm-generic\memory_model.h
/*
* Note: section's mem_map is encorded to reflect its start_pfn.
* section[i].section_mem_map == mem_map's address - start_pfn;
*/
#define __page_to_pfn(pg) \
({ const struct page *__pg = (pg); \
int __sec = page_to_section(__pg); \
(unsigned long)(__pg - __section_mem_map_addr(__nr_to_section(__sec))); \
})
#define __pfn_to_page(pfn) \
({ unsigned long __pfn = (pfn); \
struct mem_section *__sec = __pfn_to_section(__pfn); \
__section_mem_map_addr(__sec) + __pfn; \
})
#endif /* CONFIG_FLATMEM/DISCONTIGMEM/SPARSEMEM */
#define page_to_pfn __page_to_pfn
#define pfn_to_page __pfn_to_page
如有分析不妥的地方欢迎指正,共同进步。