Linux 字符设备驱动开发基础(五)—— ioremap() 函数解析

一、 ioremap() 函数基础概念

       几乎每一种外设都是通过读写设备上的寄存器来进行的,通常包括控制寄存器、状态寄存器和数据寄存器三大类,外设的寄存器通常被连续地编址。根据CPU体系结构的不同,CPU对IO端口的编址方式有两种:

a -- I/O 映射方式(I/O-mapped)

       典型地,如X86处理器为外设专门实现了一个单独的地址空间,称为"I/O地址空间"或者"I/O端口空间",CPU通过专门的I/O指令(如X86的IN和OUT指令)来访问这一空间中的地址单元。

b -- 内存映射方式(Memory-mapped)

  RISC指令系统的CPU(如ARM、PowerPC等)通常只实现一个物理地址空间,外设I/O端口成为内存的一部分。此时,CPU可以象访问一个内存单元那样访问外设I/O端口,而不需要设立专门的外设I/O指令。

     但是,这两者在硬件实现上的差异对于软件来说是完全透明的,驱动程序开发人员可以将内存映射方式的I/O端口和外设内存统一看作是"I/O内存"资源。

    一般来说,在系统运行时,外设的I/O内存资源的物理地址是已知的,由硬件的设计决定。但是CPU通常并没有为这些已知的外设I/O内存资源的物理地址预定义虚拟地址范围,驱动程序并不能直接通过物理地址访问I/O内存资源,而必须将它们映射到核心虚地址空间内(通过页表),然后才能根据映射所得到的核心虚地址范围,通过访内指令访问这些I/O内存资源。


      Linux在io.h头文件中声明了函数ioremap(),用来将I/O内存资源的物理地址映射到核心虚地址空间(3GB-4GB)中(这里是内核空间),原型如下:

1、ioremap函数

     ioremap宏定义在asm/io.h内:

#define ioremap(cookie,size)           __ioremap(cookie,size,0)

__ioremap函数原型为(arm/mm/ioremap.c):

void __iomem * __ioremap(unsigned long phys_addr, size_t size, unsigned long flags);

参数:

phys_addr:要映射的起始的IO地址

size:要映射的空间的大小

flags:要映射的IO空间和权限有关的标志

该函数返回映射后的内核虚拟地址(3G-4G). 接着便可以通过读写该返回的内核虚拟地址去访问之这段I/O内存资源。


2、iounmap函数

    iounmap函数用于取消ioremap()所做的映射,原型如下:

     void iounmap(void * addr);


二、 ioremap() 相关函数解析

        在将I/O内存资源的物理地址映射成核心虚地址后,理论上讲我们就可以象读写RAM那样直接读写I/O内存资源了。为了保证驱动程序的跨平台的可移植性,我们应该使用Linux中特定的函数来访问I/O内存资源,而不应该通过指向核心虚地址的指针来访问

读写I/O的函数如下所示:

a -- writel()

       writel()往内存映射的 I/O 空间上写数据,wirtel()  I/O 上写入 32 位数据 (4字节)。

 原型:void writel (unsigned char data , unsigned short addr )

b -- readl()

      readl() 从内存映射的 I/O 空间上读数据,readl 从 I/O 读取 32 位数据 ( 4 字节 )。
 
原型:unsigned char readl (unsigned int addr )

变量    addr  是 I/O 地址。

返回值 : 从 I/O 空间读取的数值。

具体定义如下:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #define readb __raw_readb  
  2. #define readw(addr) __le16_to_cpu(__raw_readw(addr))  
  3. #define readl(addr) __le32_to_cpu(__raw_readl(addr))  
  4. #ifndef __raw_readb  
  5. static inline u8 __raw_readb(const volatile void __iomem *addr)  
  6. {  
  7.     return *(const volatile u8 __force *) addr;  
  8. }  
  9. #endif  
  10.    
  11. #ifndef __raw_readw  
  12. static inline u16 __raw_readw(const volatile void __iomem *addr)  
  13. {  
  14.     return *(const volatile u16 __force *) addr;  
  15. }  
  16. #endif  
  17.    
  18. #ifndef __raw_readl  
  19. static inline u32 __raw_readl(const volatile void __iomem *addr)  
  20. {  
  21.     return *(const volatile u32 __force *) addr;  
  22. }  
  23. #endif  
  24.    
  25. #define writeb __raw_writeb  
  26. #define writew(b,addr) __raw_writew(__cpu_to_le16(b),addr)  
  27. #define writel(b,addr) __raw_writel(__cpu_to_le32(b),addr)  


三、使用实例

        还是拿我们写PWM驱动的实例来解析

1、这里我们先定义了一些寄存器,这里使用的地址均是物理地址:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. #define GPD0CON       0x114000a0    
  2. #define TIMER_BASE    0x139D0000               
  3. #define TCFG0         0x0000                   
  4. #define TCFG1         0x0004                                
  5. #define TCON          0x0008                 
  6. #define TCNTB0        0x000C              
  7. #define TCMPB0        0x0010   


2、为了使用内存映射,我们需先定义指针用来保存内存映射后的地址:

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. static unsigned int *gpd0con;    
  2. static void *timer_base;    
注意:这里timer_base 指针指向的类型设为 void, 主要因为上面使用了地址偏移,使用void 更有利于我们使用;


3、使用ioremap() 函数进行内存映射,并将映射的地址赋给我们刚才定义的指针

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. gpd0con = ioremap(GPD0CON,4);    
  2. timer_base = ioremap(TIMER_BASE,0x14);   

4、得到地址后,可以调用 writel() 、readl() 函数进行相应的操作

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  1. writel ((readl(gpd0con)&~(0xf<<0)) | (0x2<<0),gpd0con);    
  2. writel ((readl(timer_base +TCFG0  )&~(0xff<<0)) | (0xff <<0),timer_base +TCFG0);     
  3. writel ((readl(timer_base +TCFG1 )&~(0xf<<0)) | (0x2 <<0),timer_base +TCFG1 );     
  4.     
  5. writel (500, timer_base +TCNTB0  );    
  6. writel (250, timer_base +TCMPB0 );    
  7. writel ((readl(timer_base +TCON )&~(0xf<<0)) | (0x2 <<0),timer_base +TCON );     

可以看到,这里先从相应的地址中读取数据,修改完毕后,再利用writel函数进行数据写入。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402145.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Linux 字符设备驱动开发基础(三)—— read()、write() 相关函数解析

我们在前面讲到了file_operations&#xff0c;其是一个函数指针的集合&#xff0c;用于存放我们定义的用于操作设备的函数的指针&#xff0c;如果我们不定义&#xff0c;它默认保留为NULL。其中有最重要的几个函数&#xff0c;分别是open()、read()、write()、ioctl()&#xff…

机电传动控制第一周学习笔记

机电传动控制第一周学习笔记&#xff1a; 1 这一周主要讲述了概论和机电传动控制系统动力学基础两个章节内容。 2 绪论中说明了《机电传动控制》课程主要内容为下图所示&#xff1a; 3机电传动控制系统动力学基础章节主要内容分为&#xff1a; &#xff08;1&#xff09;a&…

opengl 配置

OpenGL(Open Graphics Library)是一个跨编程语言、跨平台的专业图形程序接口。 OpenGL是SGI公司开发的一套计算机图形处理系统&#xff0c;是图形硬件的软件接口&#xff0c;任何一个OpenGL应用程序无须考虑其运行环境所在平台与操作系统&#xff0c;在任何一个遵循OpenG…

Linux 字符设备驱动开发基础(二)—— 编写简单 PWM 设备驱动

编写驱动的第一步仍是看原理图&#xff1a; 可以看到&#xff0c;该蜂鸣器由 GPD0_0 来控制 &#xff0c;查手册可知该I/O口由Time0 来控制&#xff0c;找到相应的寄存器&#xff1a; a -- I/O口寄存器及地址 GPD0CON 0x114000a0 b -- Time0 寄存器及地址 基地址为&#xff1a…

专访:混合云的发展趋势

近日&#xff0c;业界享有盛誉的vForum2013大会在京召开&#xff0c;此次大会云集了近百家国内外知名的云计算、数据存储、大数据及信息安全厂商&#xff0c;共同讨论了虚拟化、云计算及未来IT模式的发展趋势。笔者也有幸在大会期间采访到了VMware 大中华区技术总监张振伦先生&…

Tomcat7性能优化

用了很久的Tomcat&#xff0c;没怎么看过它的优化&#xff0c;今天抽出时间研究了下&#xff0c;将内容记录下。 首先&#xff0c;是客户端访问tomcat的一个过程&#xff0c;如图所示&#xff1a; 图中间虚线框部分是 Apache基金下的服务器来做静态资源处理的&#xff0c;而这部…

Fast Image Cache – iOS 应用程序高性能图片缓存

Fast Image Cache 是一种在 iOS 应用程序中高效、持续、超快速的存储和检索图像的解决方案。任何良好的 iOS 应用程序的用户体验都应该是快速&#xff0c;平滑滚动的&#xff0c;Fast Image Cache 提供图像高速缓存有助于使这更容易实现。 对于图片丰富的应用程序&#xff0c;图…

Linux 字符设备驱动开发基础(四)—— ioctl() 函数解析

解析完 open、close、read、write 四个函数后&#xff0c;终于到我们的 ioctl() 函数了 一、 什么是ioctl ioctl是设备驱动程序中对设备的I/O通道进行管理的函数。所谓对I/O通道进行管理&#xff0c;就是对设备的一些特性进行控制&#xff0c;例如串口的传输波特率、马达的转速…

android自动化框架简要剖析(一):运行原理+基本框架

android自动化测试原理&#xff1a; 1、将测试apk和被测试apk&#xff0c;运行在一个进程中&#xff1b;通过instrumentation进行线程间的通信 2、通过android.test.AndroidTestCase及其子类&#xff0c;控制android系统对象 3、通过android.test.InstrumentationTestCase 及其…

Linux 字符设备驱动开发基础(一)—— 编写简单 LED 设备驱动

现在&#xff0c;我们来编写自己第一个字符设备驱动 —— 点亮LED。&#xff08;不完善&#xff0c;后面再完善&#xff09; 硬件平台&#xff1a;Exynos4412&#xff08;FS4412&#xff09; 编写驱动分下面几步&#xff1a; a -- 查看原理图、数据手册&#xff0c;了解设备的操…

Linux 字符设备驱动结构(四)—— file_operations 结构体知识解析

前面在 Linux 字符设备驱动开发基础 &#xff08;三&#xff09;—— 字符设备驱动结构&#xff08;中&#xff09; &#xff0c;我们已经介绍了两种重要的数据结构 struct inode{...}与 struct file{...} &#xff0c;下面来介绍另一个比较重要数据结构 struct _file_operatio…

Android开发群

为什么80%的码农都做不了架构师&#xff1f;>>> 我的自建Android应用开发群&#xff0c;欢迎大家来聊聊呀&#xff01;201427584 转载于:https://my.oschina.net/catia/blog/176384

Linux 字符设备驱动结构(三)—— file、inode结构体及chardevs数组等相关知识解析

前面我们学习了字符设备结构体cdev Linux 字符设备驱动开发 &#xff08;一&#xff09;—— 字符设备驱动结构&#xff08;上&#xff09; 下面继续学习字符设备另外几个重要的数据结构。 先看下面这张图&#xff0c;这是Linux 中虚拟文件系统、一般的设备文件与设备驱动程序…

技术人生:三亚之行

人生收获 此次是公司组团去的三亚&#xff0c;地接的导游非常热情&#xff0c;如同大多数人一样&#xff0c;导游也会在这短短的几天内&#xff0c;尽可能的表现自己&#xff0c;此文聊聊导游说的三句话。 旅游三不“较”&#xff1a; 不比较不计较不睡觉人生何尝不是如此&…

Linux 字符设备驱动结构(二)—— 自动创建设备节点

上一篇我们介绍到创建设备文件的方法&#xff0c;利用cat /proc/devices查看申请到的设备名&#xff0c;设备号。 第一种是使用mknod手工创建&#xff1a;mknod filename type major minor 第二种是自动创建设备节点&#xff1a;利用udev&#xff08;mdev&#xff09;来实现设备…

Linux 字符设备驱动结构(一)—— cdev 结构体、设备号相关知识解析

一、字符设备基础知识 1、设备驱动分类 linux系统将设备分为3类&#xff1a;字符设备、块设备、网络设备。使用驱动程序&#xff1a; 字符设备&#xff1a;是指只能一个字节一个字节读写的设备&#xff0c;不能随机读取设备内存中的某一数据&#xff0c;读取数据需要按照先后数…

Linux 驱动开发之内核模块开发(四)—— 符号表的导出

Linux内核头文件提供了一个方便的方法用来管理符号的对模块外部的可见性,因此减少了命名空间的污染(命名空间的名称可能会与内核其他地方定义的名称冲突),并且适当信息隐藏。 如果你的模块需要输出符号给其他模块使用,应当使用下面的宏定义: EXPORT_SYMBOL(name); EXPORT_SYMBO…

Linux 驱动开发之内核模块开发 (三)—— 模块传参

一、module_param() 定义 通常在用户态下编程&#xff0c;即应用程序&#xff0c;可以通过main()的来传递命令行参数&#xff0c;而编写一个内核模块&#xff0c;则通过module_param() 来传参。 module_param()宏是Linux 2.6内核中新增的&#xff0c;该宏被定义在include/linux…

Exynos4412 文件系统制作(二)—— 文件系统简介

一、Linux磁盘分区和目录 Linux发行版本之间的差别很少&#xff0c;差别主要表现在系统管理的特色工具以及软件包管理方式的不同。目录结构基本上都是一样的。 Windows的文件结构是多个并列的树状结构&#xff0c;最顶部的是不同的磁盘&#xff08;分区&#xff09;&#xff0c…