linux驱动开发一般不会直接操作寄存器,主要是学习一下地址映射。
什么是地址映射?
将逻辑地址(虚拟空间地址)转换为真实的物理地址。
虚拟空间地址是虚拟出来扩充内存空间的,并不是扩充真实的物理地址。对于真实的物理地址,运行程序必须将程序一次性放入,这样就导致大内存程序无法在小内存地址运行,只能等待。大大降低了处理器的利用率。实际上程序运行并不需要同时把程序加载进来,虚拟地址就解决了这样一个问题,当程序运行时把需要运行的程序和数据加载进来,暂时不需要的放入交换区(硬盘–外存),这样就可以实现运行大于实际物理内存的程序。打个比方,对于32位的处理器,虚拟地址就是2的32次方即4GB,开发板是512MB的DDR3,即物理内存,经过MMU就可以将512的物理内存映射到4GB虚拟空间。
图片来自正点原子手册
为什么要用到地址映射?
因为我们的开发都是面向虚拟地址的,真实物理地址空间是很小的,当运行的时候就需要将虚拟地址映射到真实物理地址,否则cpu是找不到的。
imx6ull裸机开发和STM32基本相同,都可以直接操作寄存器物理地址,但是linux是不能直接操作物理地址的,linux内核一启动便会启动MMU(memory manage uint),即内存管理单元。MMU是硬件,他主要完成下面两项功能:
①它用来将虚拟内存地址翻译到实际的物理内存地址。
②内存保护,可以设置特定的内存块的读写属性,只读、可读可写。
暂时知道这些概念即可,接下来学习操作寄存器进行led驱动开发。
led驱动开发
如果没有MMU,那么裸机开发是直接操作物理寄存器,,一旦linux的MMU设置好内存映射,就不能在对你当时记得那个物理寄存器地址操作了,cpu寻址是从虚拟地址开始的,因为这个地址名已经不再是物理地址的编号了,而是映射出了一个新编号来和它对应。我们操作的就是这个虚拟地址。如何将物理地址与内存地址之间转换,很简单,官方给了两个API函数:
1) ioremap
获取指定物理地址空间对应的虚拟地址空间。
在arch/arm/include/asm/io.h定义如下:
#define ioremap(cookie,size) __arm_ioremap((cookie), (size), MT_DEVICE)
extern void __iomem *__arm_ioremap(phys_addr_t, size_t, unsigned int);
//cookie 为物理起始地址
//size 要映射的空间大小(单位字节) 比如在imx6ull的寄存器为4字节。
//返回值为void __iomem 指针
举例说明
//寄存器物理地址
#define CCM_CCGR1_BASE 0X020C406C
//映射虚拟地址指针
static void __iomem *IMX6U_CCM_CCGR1;
//地址映射
IMX6U_CCM_CCGR1 = ioremap(CCM_CCGR1_BASE,4);
2) iounmap
与ioremap相反,释放ioremap映射的虚拟空间地址。
在arch/arm/include/asm/io.h定义如下:
#define iounmap __arm_iounmap
extern void __arm_iounmap(volatile void __iomem *addr);
//参数就是前面定义的地址映射指针
/*比如释放掉前面映射的虚拟地址*/
iounmap(IMX6U_CCM_CCGR1 );
地址映射完成后就可以对获得的虚拟地址进行读写操作了(和裸机开发一样),API如下
1)读寄存器函数 *readx,根据寄存器的位数,分为如下三种
#define readb(c) ({ u8 __v = readb_relaxed(c); __iormb(); __v; })//8位
#define readw(c) ({ u16 __v = readw_relaxed(c); __iormb(); __v; })//16位
#define readl(c) ({ u32 __v = readl_relaxed(c); __iormb(); __v; })//32位
//返回值u8/u16/u32
//举例
//u32 val = readl(地址映射指针);
2)写寄存器函数 writex
#define writeb(v,c) ({ __iowmb(); writeb_relaxed(v,c); })//8位寄存器
#define writew(v,c) ({ __iowmb(); writew_relaxed(v,c); })//16位寄存器
#define writel(v,c) ({ __iowmb(); writel_relaxed(v,c); })//32位寄存器
//参数v:要写入寄存器的值
//c:虚拟地址指针
关键的就说完了,剩余的流程就参看
字符设备驱动开发框架笔记