8.驱动与硬件通信

          驱动与硬件通信

  

  驱动程序控制设备,主要是通过访问设备内的寄存器来达到控制目的,因此我们讨论如何访问硬件,就成了如何访问这些寄存器了.

   

 一、地址映射

  在Linux系统中,无论是内核程序还是应用程序,都只能使用虚拟地址,而芯片手册中给出的硬件寄存器地址或者RAM地址则是物理地址,无法直接使用,因此,我们读写寄存器的第1步就是将将它的物理地址映射为虚拟地址。

 1.1动态映射

  所谓动态映射,是指在驱动程序中采用ioremap函数将物理地址映射为虚拟地址。

原型:void * ioremap(physaddr, size)
参数:
  Physaddr:待映射的物理地址
  Size: 映射的区域长度
  返回值:映射后的虚拟地址

1.2静态映射 

  所谓静态映射,是指Linux系统根据用户事先指定的映射关系,在内核启动时,自动地将物理地址映射为虚拟地址。

1.2.1映射关系 

  在静态映射中,用户是通过map_desc结构来指明物理地址与虚拟地址的映射关系。

struct map_desc{
  unsigned long virtual; /* 映射后的虚拟地址 */
  unsigned long pfn; /* 物理地址所在的页帧号 */
  unsigned long length; /* 映射长度 */
  unsigned int type; /* 映射的设备类型 */
};
  pfn: 利用__phys_to_pfn(物理地址)可以计算出物理地址所在的物理页帧号

   该结构有四个成员:第一个参数是我们的虚拟地址,第二个参数才是我们实际的物理地址,只不过我们现在使用页桢号来表示。假如物理地址是50008000,一页的大小为4K,用物理地址除以4K,就得到页桢号了。

 1.2.2如何映射

  在source insight里面找到自己平台对应的Cpu.c文件进入,可以找到如下结构:

  

  可以看出这是个数组,这个数组的元素是结构struct map_desc。一个这样的结构对应的是内存的一片映射区域。  

  看一下GPIO的这一片区域映射关系:

  

其中主要参数的来源:

  

查看S3c6410芯片手册memory map章节:

  

0x7f008000刚好是GPIO这片区域的起始物理地址。然后这片区域会被映射到下面的虚拟地址:

S3C64XX_VA_GPIO:

  

通过查找找到S3C_ADRR_CPU:

  

可以看到同一个文件里面的上面个有对S3C_ADDR的定义:

  

同时也有对S3C_ADDR_BASE的定义

找到的是上面的S3C_ADDR_BASE   0XF4000000加上偏移量x,就是S3C_ADDR的地址。有了这样的地址,当我们的内核要去使用这表的的时候:

  

  当我们的6410启动的时候,会去找这样的一张表来完成映射。这就是内核启动的时候完成的静态映射。

12.3静态映射应用

在工程里面搜索S3C64XX_GPNCON:

  

  这就是我们拿到的IO的虚拟地址,下面我们分析一下到底是如何来的:

1.搜索:S3C64XX_GPN_BASE

  

我们可以同时看到S3C64XX_GPN_BASE以及S3C64XX_GPIOREG的来源

2.我们接着搜索:S3C64XX_VA_GPIO

   

可以看到,我们又回到了前面静态映射的物理地址所以S3C64XX_GPNCON的地址就是我们的虚拟地址,我们可以直接使用。

  理论上呢拿到S3C64XX_GPNCON这个值就可以使用了但是我在led程序中使用的时候居然卡死在程序里面出不来了,而我学习内存静态转换的过程也就卡到这儿了希望日后可以解决吧

 1 #include <mach/map.h>
 2 #include <mach/regs-gpio.h>
 3 #include <mach/regs-clock.h>
 4 #include <mach/gpio-bank-n.h>
 5 #include <plat/gpio-cfg.h>
 6 
 7 
 8 
 9 int led_open (struct inode *node, struct file *filp)
10 {
11     writel(0x1111,S3C64XX_GPNCON); //为虚拟地址写入值
12     return 0;
13 }
14 
15 //响应系统调用函数的驱动函数
16 long led_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
17 {
18     switch (cmd)   //通过不同命令执行控制命令
19     {
20         case LED_ON:
21             writel(0x00,S3C64XX_GPNDAT);
22             return 0;
23         
24         case LED_OFF:
25             writel(0xff,S3C64XX_GPNDAT);
26             return 0;
27         
28         default:
29             return -EINVAL;
30     }
31 }

 二、寄存器读写

  在完成地址映射后,就可以读写寄存器了,Linux内核提供了一系列函数,来读写寄存器。

 1   unsigned ioread8(void *addr)
 2   unsigned ioread16(void *addr)
 3   unsigned ioread32(void *addr)
 4   unsigned readb(address)
 5   unsigned readw(address)
 6   unsigned readl(address)
 7 
 8 
 9   void iowrite8(u8 value, void *addr)
10   void iowrite16(u16 value, void *addr)
11   void iowrite32(u32 value, void *addr)
12   void writeb(unsigned value, address)
13   void writew(unsigned value, address)
14   void writel(unsigned value, address)
原文地址:https://www.cnblogs.com/wmx-learn/p/5359450.html