海思hi3521A内核中IO_ADDRESS(x) 分析

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

如有分析不妥的地方欢迎指正,共同进步。

你可能感兴趣的:(海思方案,海思,hi3521a,IO_ADDRESS(x))