I/O资源如何映射到内核虚拟空间

转载 2012年03月30日 10:25:16

转自:http://www.cnitblog.com/vsolo/archive/2008/04/26/42786.html

(1)  系统启动初始化时iotable_init()
-----------------------------
MACHINE_START(AT91SAM9261EK, "ATMEL AT91SAM9261")
············································
        .map_io         = at91sam9261_map_io,
············································
MACHINE_END
--------------------------------------
void __init at91sam9261_map_io(void)
{
 iotable_init(at91sam9261_io_desc, ARRAY_SIZE(at91sam9261_io_desc));
}
--------------------------------------
/*
 * System peripheral registers mapped at virtual address.
 */

static struct map_desc at91sam9261_io_desc[] __initdata = {
 {
  .virtual = AT91C_VA_BASE_SYS,
  .pfn = __phys_to_pfn(AT91C_BASE_AIC),
  .length = SZ_4K,
  .type = MT_DEVICE
 },
 {
  .virtual = AT91C_VA_BASE_EBI,
  .pfn = __phys_to_pfn(AT91C_BASE_EBI),
  .length = SZ_4K,
  .type = MT_DEVICE
 },
     ··············································
};

<./linux/include/asm-arm/map.h>-----------------------
struct map_desc {
 unsigned long virtual;
 unsigned long pfn;
 unsigned long length;
 unsigned int type;   //标志位:domain、read、write、cache、buffer
};

#define __phys_to_pfn(paddr)             ((paddr) >> PAGE_SHIFT)
#define __pfn_to_phys(pfn)                 ((pfn) << PAGE_SHIFT)
--------------------------------------
    iotable_init()函数<./arch/arm/mm/mm-armv.c>循环调用create_mapping()函数完成IO的虚拟地址到物理地址的映射。


(2)  系统启动后,在驱动中ioremap()
--------------------------------------
static struct platform_device *smdk2410_devices[] __initdata = {
&s3c_device_usb,    //片上的各个设备
&s3c_device_lcd,    //下面以s3c_device_lcd为例
&s3c_device_wdt,
&s3c_device_i2c,
&s3c_device_iis,
};
--------------------------------------
struct platform_device s3c_device_lcd = {
.name = "s3c2410-lcd",  //此处设备的命名应和相应驱动程序命名一致以实现driver bind
.id = -1,                        //-1表示不支持同类多个设备
.num_resources = ARRAY_SIZE(s3c_lcd_resource),
.resource = s3c_lcd_resource,
.dev = {
.dma_mask = &s3c_device_lcd_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};

-------------------------------------
/* LCD Controller */
static struct resource s3c_lcd_resource[] = {   //LCD的两个资源
[0] = {
.start = S3C2410_PA_LCD,
.end = S3C2410_PA_LCD + S3C2410_SZ_LCD,
.flags = IORESOURCE_MEM,
},
[1] = {
.start = IRQ_LCD,
.end = IRQ_LCD,
.flags = IORESOURCE_IRQ,
}

};
------------------------------------
/* -------Resource type -------- */
#define IORESOURCE_IO                 0x00000100       
#define IORESOURCE_MEM                 0x00000200
#define IORESOURCE_IRQ                 0x00000400
#define IORESOURCE_DMA                 0x00000800
------------------------------------

-----s3c_device_lcd的resource中硬件地址---------------

#define S3C2410_LCDREG(x) (x)

/* LCD control registers */
#define S3C2410_LCDCON1     S3C2410_LCDREG(0x00)
#define S3C2410_LCDCON2     S3C2410_LCDREG(0x04)
#define S3C2410_LCDCON3     S3C2410_LCDREG(0x08)
#define S3C2410_LCDCON4     S3C2410_LCDREG(0x0C)
#define S3C2410_LCDCON5     S3C2410_LCDREG(0x10)

/* LCD controller */
#define S3C2410_PA_LCD    (0x4D000000)
#define S3C24XX_SZ_LCD    SZ_1M
-----------------------------------
/**
* platform_device_register - add a platform-level device
* @pdev: platform device we're adding
*
*/
int platform_device_register(struct platform_device * pdev)
{
device_initialize(&pdev->dev);        //初始化设备结构
return platform_device_add(pdev); //添加一个片上的设备到设备层
}
------------------------------------------
/**
 * platform_device_add - add a platform device to device hierarchy
 * @pdev: platform device we're adding
 *
 * This is part 2 of platform_device_register(), though may be called
 * separately _iff_ pdev was allocated by platform_device_alloc().
 */
int platform_device_add(struct platform_device *pdev)
{
 int i, ret = 0;
 if (!pdev)
  return -EINVAL;

 if (!pdev->dev.parent)
  pdev->dev.parent = &platform_bus;
 pdev->dev.bus = &platform_bus_type;

 if (pdev->id != -1)
  snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id);
                 /* 若支持同类多个设备,则用pdev->name和pdev->id在总线上标识该设备  */
 else
  strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
                /*               否则,用pdev->name(即"s3c2410-lcd")在总线上标识该设备               */

 for (i = 0; i < pdev->num_resources; i++) {
                /*           遍历资源数,并为各自在总线地址空间请求分配            */
  struct resource *p, *r = &pdev->resource[i];

  if (r->name == NULL)
   r->name = pdev->dev.bus_id;

  p = r->parent;
  if (!p) {
   if (r->flags & IORESOURCE_MEM)
      p = &iomem_resource; 
                                          /*    LCD寄存器地址作为IO内存资源分配   */
                                                      ----------------
                                                      struct resource iomem_resource = {
                                                              .name   = "PCI mem",
                                                              .start  = 0UL,
                                                              .end    = ~0UL,
                                                              .flags  = IORESOURCE_MEM,
                                                               };
                                                      ----------------
   else if (r->flags & IORESOURCE_IO)
      p = &ioport_resource;
  }
                      
  if (p && insert_resource(p, r)) {
                              /*   将LCD寄存器地址插入到IO内存空间  */
   printk(KERN_ERR
          "%s: failed to claim resource %d\n",
          pdev->dev.bus_id, i);
   ret = -EBUSY;
   goto failed;
  }
 }

 pr_debug("Registering platform device '%s'. Parent at %s\n",
   pdev->dev.bus_id, pdev->dev.parent->bus_id);

 ret = device_add(&pdev->dev);
 if (ret == 0)
  return ret;

 failed:
 while (--i >= 0)
  if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))
   release_resource(&pdev->resource[i]);
 return ret;
}
-----------------------------------------


static struct platform_driver s3c2410fb_driver = {
 .probe  = s3c2410fb_probe,
 .remove  = s3c2410fb_remove,
 .suspend = s3c2410fb_suspend,
 .resume  = s3c2410fb_resume,
 .driver  = {
  .name = "s3c2410-lcd",
  .owner = THIS_MODULE,
 },
};

platform_driver_register(&s3c2410fb_driver)----->
driver_register(&drv->driver)----->
bus_add_driver(drv)----->
driver_attach(drv)----->
bus_for_each_dev(drv->bus, NULL, drv, __driver_attach)----->
__driver_attach(struct device * dev, void * data)----->
driver_probe_device(drv, dev)----->
really_probe(dev, drv)----->

在really_probe()中:
            为设备指派管理该设备的驱动:dev->driver = drv
            调用s3c2410fb_probe()初始化设备:drv->probe(dev)

---------------------------------
static int __init s3c2410fb_probe(struct platform_device *pdev)
{
 ·····························
                res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
                /*          取得LCD控制寄存器的物理地址           */

 size = (res->end - res->start)+1;
 info->mem = request_mem_region(res->start, size, pdev->name);
               /* 个人理解:设备注册时已经分配区域,驱动这里应该不是必须的*/

 info->io = ioremap(res->start, size);
               /*      此时驱动便可以用指针info->io  读写LCD控制寄存器了    */
               /*              eg: readl(info->io + S3C2410_LCDCON1)              */
 ····························
}

--------------------------------
以下是AT91SAM9261EK的IOMEM:

root@ebd9261:~# cat /proc/iomem
00500000-005fffff : usb-ohci.0
  00500000-005fffff : ohci_hcd
00600000-00600fff : sidsa-lcdc.0     //支持同类多个设备,在驱动中未分配I/O内存区域
20000000-23ffffff : System RAM
  20022000-20225e47 : Kernel text
  20226000-2028da23 : Kernel data
30000000-30000003 : dm9000.0   
  30000000-30000003 : dm9000   
30000044-300000ff : dm9000.0
  30000044-300000ff : dm9000
fffa4000-fffa7fff : at91_udc            //不支持同类多个设备,在驱动中也分配I/O内存区域
  fffa4000-fffa7fff : at91_udc
fffb0000-fffb3fff : usart.1
fffb4000-fffb7fff : usart.2
fffc8000-fffcbfff : spi.0
fffff200-fffff3ff : usart.0

相关文章推荐

I/O资源如何映射到内核虚拟空间

(1) 系统启动初始化时iotable_init()-----------------------------MACHINE_START(AT91SAM9261EK, "ATMEL AT91SAM92...

Linux内核访问外设I/O资源的方式-静态映射(map_desc)方式

Linux内核访问外设I/O资源的方式-静态映射(map_desc)方式

内核源码学习:进程的虚拟空间

如前所述,每个进程拥有3G字节的用户虚存空间。但是,这并不意味着用户进程在这3G的范围内可以任意使用,因为虚存空间最终得映射到某个物理存储空间(内存或磁盘空间),才真正可以使用。 那么,内核怎样...

Linux内核访问外设I/O资源的方式-静态映射(map_desc)方式

Linux内核访问外设I/O资源的方式 Author: Dongas Date: 08-08-02 我们知道默认外设I/O资源是不在Linux内核空间中的(如sram或硬件接口寄存器等),若需要...
  • swymomo
  • swymomo
  • 2012年01月09日 10:06
  • 293

电学实验虚拟空间

  • 2017年07月20日 17:20
  • 561KB
  • 下载

虚拟空间如何利用UrlRewrite防止盗链?

比如,别人上传的东西,你直接拿了LINK去,贴到别的论坛或者网站,宣称"有好东西,快来下载",又或者告诉你的朋友"你要这个文件?俺有连接,快下",然后在那里接受别人的滔滔不绝的景仰之情(当然,结果就是...
  • tangbow
  • tangbow
  • 2012年07月25日 14:26
  • 366

Linux进程环境之进程虚拟空间及存储

Linux 进程虚拟空间及存储

虚拟空间犯罪的特点和对策

  • 2011年07月17日 17:18
  • 26KB
  • 下载

虚拟空间日记本

  • 2004年08月30日 00:15
  • 45KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:I/O资源如何映射到内核虚拟空间
举报原因:
原因补充:

(最多只允许输入30个字)