<?xml version="1.0" encoding="gbk"?> <rss version="2.0"><channel> <title>定阅帖子更新</title> <link>http://www.broadkey.com.cn/XML.ASP</link><description>TEAM Board - 科伟奇电子</description> <copyright>TEAM 2.0.5 Release</copyright><generator>TEAM Board by TEAM5.Cn Studio</generator> <ttl>30</ttl><item><link>http://www.broadkey.com.cn/Thread.asp?tid=87 </link><title>对IO寄存器访问问题</title><author>frodo</author><pubDate>2009/11/26 22:22:23</pubDate><description><![CDATA[<div style="margin: 15px" id="art">
<div><font size="3"><br />
遇到问题：PowerPC8349开发板，带有两个网卡，VSC8201和VSC7385，现在想通过改变7385的内部寄存器数值来开启一些功能，但不知道如何去读写7385的寄存器。在mpc8349e-mitx board configuration file&mdash;&mdash;MPC8349ITX.h里有定义：</font></div>
<div><font size="3">#define CFG_VSC7385_BASE<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0xF8000000&nbsp;&nbsp; /* start of VSC7385&nbsp;&nbsp; */</span></font></div>
<div><font size="3">下图是8349的内存分布图，其中可以看到，7385是挂在Local Bus上的，地址空间为0xF8000000&mdash;0xF801FFFF，和程序中定义的起始地址一样。</font></div>
<div><font size="3"><span>&nbsp;</span></font>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div>&nbsp;</div>
<div><font size="3">下面是一个访问8349的IMMRBAR寄存器的程序，以模块形式加载</font></div>
<div><font size="3">#include &lt;linux/init.h&gt;</font></div>
<div><font size="3">#include &lt;linux/module.h&gt;</font></div>
<div><font size="3">#include &lt;asm/mpc83xx.h&gt;</font></div>
<div><font size="3">MODULE_LICENSE(&quot;Dual BSD/GPL&quot;);</font></div>
<div><font size="3">static int hello_init(void)</font></div>
<div><font size="3">{</font></div>
<div><font size="3"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(KERN_ALERT &quot;Hello, world\n&quot;);</span></font></div>
<div><font size="3"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(KERN_ALERT &quot;IMMR virtual address: %X\n&quot;, VIRT_IMMRBAR);</span></font></div>
<div><font size="3"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(KERN_ALERT &quot;IMMRBAR: %08lX\n&quot;, *(unsigned long*)(VIRT_IMMRBAR + 0x05008));</span></font></div>
<div><font size="3"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;</span></font></div>
<div><font size="3">}</font></div>
<div><font size="3">static void hello_exit(void)</font></div>
<div><font size="3">{</font></div>
<div><font size="3"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(KERN_ALERT &quot;Goodbye, cruel world\n&quot;);</span></font></div>
<div><font size="3">}</font></div>
<div><font size="3">module_init(hello_init);</font></div>
<div><font size="3">module_exit(hello_exit);</font></div>
<div style="text-indent: 21.75pt"><font size="3">这个程序加载之后，可以成功的读出IMMRBAR寄存器中的数值，也可以直接从绝对地址中读取，但把地址改成7385的0xF8000000之后，再次进行读取就会出现段错误，而且从其它大部分地址中读取都会出现段错误。</font></div>
<div style="text-indent: 21.75pt"><font size="3">在网上参阅了一些资料后发现，驱动程序访问的是IO空间，与一般我们说的内存空间是不同的。外设的IO空间地址是系统启动后才知道，CPU通常并没有为这些已知的外设I/O内存资源的物理地址预定义虚拟地址范围，所以驱动程序并不能直接通过物理地址访问I/O内存资源，而必须将它们映射到核心虚地址空间内然后才能根据映射所得到的核心虚地址范围，通过访内指令访问这些I/O内存资源。</font></div>
<div style="text-indent: 21.75pt">&nbsp;</div>
<div style="text-indent: 21.75pt"><font size="3">下面这一段讲了一下Linux内存空间的划分，不是很懂，还需要再仔细理解一下。（转自 <a href="http://tech.sina.com.cn/s/2006-10-27/15181207636.shtml">http://tech.sina.com.cn/s/2006-10-27/15181207636.shtml</a>）</font></div>
<div style="text-indent: 21.75pt"><font size="3">对于提供了MMU（存储管理器，辅助操作系统进行内存管理，提供虚实地址转换等硬件支持）的处理器而言，Linux提供了复杂的存储管理系统，使得进程所能访问的内存达到4GB。</font></div>
<div style="text-align: left; text-indent: 21.75pt; margin: 0cm 0cm 12pt" align="left"><font size="3">进程的4GB内存空间被人为的分为两个部分--用户空间与内核空间。用户空间地址分布从0到3GB(PAGE_OFFSET，在0x86中它等于0xC0000000)，3GB到4GB为内核空间，如下图：</font></div>
<font size="3"><span>
<div align="center"><img border="0" onload="javascript:if(this.width&gt;500)this.width=500;" alt="" src="http://blogimg.chinaunix.net/blog/upfile2/080926144931.jpg" /></div>
</span></font>
<div style="text-align: left; text-indent: 21.75pt; margin: 0cm 0cm 12pt" align="left"><br />
<font size="3">　　内核空间中，从3G到vmalloc_start这段地址是物理内存映射区域（该区域中包含了内核镜像、物理页框表mem_map等等），比如我们使用的VMware虚拟系统内存是160M，那么3G～3G+160M这片内存就应该映射物理内存。在物理内存映射区之后，就是vmalloc区域。对于160M的系统而言，vmalloc_start位置应在3G+160M附近（在物理内存映射区与vmalloc_start期间还存在一个8M的gap来防止跃界），vmalloc_end的位置接近4G(最后位置系统会保留一片128k大小的区域用于专用页面映射)，如下图：</font>
<div style="text-align: left; margin: 0cm 0cm 12pt" align="left"><span style="font-size: 12pt">　一般来说，在系统运行时，外设的I/O内存资源的物理地址是已知的，由硬件的设计决定。但是CPU通常并没有为这些已知的外设I/O内存资源的物理地址预定义虚拟地址范围，驱动程序并不能直接通过物理地址访问I/O内存资源，而必须将它们映射到核心虚地址空间内（通过页表），然后才能根据映射所得到的核心虚地址范围，通过访内指令访问这些I/O内存资源。Linux在io.h头文件中声明了函数ioremap（），用来将I/O内存资源的物理地址映射到核心虚地址空间（3GB－4GB）中，原型如下：</span></div>
<div style="text-align: left; margin: 0cm 0cm 12pt" align="left"><span style="font-size: 12pt">void * ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags);<br />
　　iounmap函数用于取消ioremap（）所做的映射，原型如下：</span></div>
<div style="text-align: left; margin: 0cm 0cm 12pt" align="left"><span style="font-size: 12pt">void iounmap(void * addr);</span></div>
<div style="text-align: left; margin: 0cm 0cm 12pt" align="left"><span style="font-size: 12pt">这两个函数都是实现在mm/ioremap.c文件中。</span></div>
<div style="text-align: left; margin: 0cm 0cm 12pt" align="left"><span style="font-size: 12pt">在将I/O内存资源的物理地址映射成核心虚地址后，理论上讲我们就可以象读写RAM那样直接读写I/O内存资源了。为了保证驱动程序的跨平台的可移植性，我们应该使用Linux中特定的函数来访问I/O内存资源，而不应该通过指向核心虚地址的指针来访问。如在x86平台上，读写I/O的函数如下所示：</span></div>
<div style="text-align: left; margin: 0cm 0cm 12pt" align="left"><span style="font-size: 12pt">#define readb(addr) (*(volatile unsigned char *) __io_virt(addr))</span></div>
<div style="text-align: left; margin: 0cm 0cm 12pt" align="left"><span style="font-size: 12pt">#define readw(addr) (*(volatile unsigned short *) __io_virt(addr))</span></div>
<div style="text-align: left; margin: 0cm 0cm 12pt" align="left"><span style="font-size: 12pt">#define readl(addr) (*(volatile unsigned int *) __io_virt(addr))</span></div>
<div style="text-align: left; margin: 0cm 0cm 12pt" align="left"><span style="font-size: 12pt">#define writeb(b,addr) (*(volatile unsigned char *) __io_virt(addr) = (b))</span></div>
<div style="text-align: left; margin: 0cm 0cm 12pt" align="left"><span style="font-size: 12pt">#define writew(b,addr) (*(volatile unsigned short *) __io_virt(addr) = (b))</span></div>
<div style="text-align: left; margin: 0cm 0cm 12pt" align="left"><span style="font-size: 12pt">#define writel(b,addr) (*(volatile unsigned int *) __io_virt(addr) = (b))</span></div>
<div style="text-align: left; margin: 0cm 0cm 12pt" align="left"><span style="font-size: 12pt">#define memset_io(a,b,c) memset(__io_virt(a),(b),(c))</span></div>
<div style="text-align: left; margin: 0cm 0cm 12pt" align="left"><span style="font-size: 12pt">#define memcpy_fromio(a,b,c) memcpy((a),__io_virt(b),(c))</span></div>
<div style="text-align: left; margin: 0cm 0cm 12pt" align="left"><span style="font-size: 12pt">#define memcpy_toio(a,b,c) memcpy(__io_virt(a),(b),(c))</span></div>
<div style="text-align: left; margin: 0cm 0cm 12pt" align="left">&nbsp;</div>
<div style="text-align: left; margin: 0cm 0cm 12pt" align="left"><span style="font-size: 12pt">最后，我们要特别强调驱动程序中mmap函数的实现方法。用mmap映射一个设备，意味着使用户空间的一段地址关联到设备内存上，这使得只要程序在分配的地址范围内进行读取或者写入，实际上就是对设备的访问。</span></div>
<div style="text-align: left; margin: 0cm 0cm 12pt" align="left"><span style="font-size: 12pt">最后解决办法：用ioremap函数对物理地址进行转换，对转换后得到的地址进行读写操作都是可以的，代码如下：</span></div>
<div style="text-align: left; margin: 0cm 0cm 12pt" align="left"><font size="3">#include &lt;linux/module.h&gt;</font></div>
<div style="text-indent: 21pt"><font size="3">MODULE_LICENSE(&quot;Dual BSD/GPL&quot;);</font></div>
<div style="text-indent: 21pt"><font size="3">#define CFG_VSC7385_BASE 0xF8000000</font></div>
<div style="text-indent: 21pt"><font size="3">static int hello_init(void)</font></div>
<div style="text-indent: 21pt"><font size="3">{</font></div>
<div style="text-indent: 21pt"><font size="3">&nbsp;<span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void * mypointer = ioremap(0xF8000000,128*1024);&nbsp;//</span>地址转换</font></div>
<div style="text-indent: 21pt"><font size="3"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(KERN_ALERT &quot;Hello, world\n&quot;);</span></font></div>
<div style="text-indent: -42pt; margin: 0cm 0cm 0pt 63pt"><font size="3"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(KERN_ALERT&quot; CHIPID:%08lX\n&quot;,*(unsignedlong*)(mypointer+0X1C060));</span></font></div>
<div style="text-indent: 21pt"><font size="3"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; iounmap(mypointer);&nbsp;&nbsp;&nbsp; //</span>取消转换</font></div>
<div style="text-indent: 21pt"><font size="3"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return 0;</span></font></div>
<div style="text-indent: 21pt"><font size="3">}</font></div>
<div style="text-indent: 21pt"><font size="3">static void hello_exit(void)</font></div>
<div style="text-indent: 21pt"><font size="3">{</font></div>
<div style="text-indent: 21pt"><font size="3"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; printk(KERN_ALERT &quot;Goodbye, cruel world\n&quot;);</span></font></div>
<div style="text-indent: 21pt"><font size="3">}</font></div>
<div style="text-indent: 21pt"><font size="3">module_init(hello_init);</font></div>
<div style="text-indent: 21pt"><font size="3">module_exit(hello_exit);</font></div>
<div style="text-indent: 24pt"><span style="font-size: 12pt">疑问：是不是只有对3G(即</span><font size="3">0xC0000000<span style="font-size: 12pt">)</span></font><span style="font-size: 12pt">之后的地址才能用这种地址转换的方式来访问呢？<br />
[UPLOAD=jpg]ShowFile.asp?ID=18[/UPLOAD]<br />
附件尺寸：10.9 KB</span></div>
</div>
<div align="center"><img border="0" onload="javascript:if(this.width&gt;500)this.width=500;" alt="" src="http://blogimg.chinaunix.net/blog/upfile2/080926145057.jpg" /></div>
<div align="center">&nbsp;</div>
</div>
<div align="center"><img style="width: 897px; height: 313px" border="0" width="500" onload="javascript:if(this.width&gt;500)this.width=500;" height="350" alt="" src="http://blogimg.chinaunix.net/blog/upfile2/080926144302.png" /></div>
</div>]]></description></item></channel></rss>