转自http://blog.csdn.net/xiaochongtou123/article/details/7752328

1、概述:

通常在Linux中,把SoC系统中集成的独立外设单元(如:I2CIISRTC、看门狗等)都被当作平台设备来处理。

从Linux2.6起,引入了一套新的驱动管理和注册机制:Platform_devicePlatform_driver,来管理相应设备。

Linux中大部分的设备驱动,都可以使用这套机制,设备用platform_device表示,驱动用platform_driver进行注册。

Linux platform driver机制和传统的device_driver机制相比,一个十分明显的优势在于platform机制将本身的资源注册进内核,由内核统一管理,在驱动程序中使用这些资源时通过platform_device提供的标准接口进行申请并使用。

这样提高了驱动和资源管理的独立性,并且拥有较好的可移植性和安全性。

platform是一个虚拟的地址总线,相比pci,usb,它主要用于描述SOC上的片上资源,比如s3c2410上集成的控制器(lcd,watchdog,rtc等),platform所描述的资源有一个共同点,就是可以在cpu的总线上直接取址。

2、platform机制分为三个步骤

1)总线注册阶段:

内核启动初始化时的main.c文件中的

kernel_init() ->do_basic_setup() -> driver_init() ->platform_bus_init() ->bus_register(&platform_bus_type),注册了一条platform总线(虚拟总线)

platform总线用于连接各类采用platform机制的设备,此阶段无需我们修改,由linux内核维护。

2)添加设备阶段

设备注册的时候

Platform_device_register() -> platform_device_add() -> pdev->dev.bus = &platform_bus_type -> device_add(),就这样把设备给挂到虚拟的总线上。

本阶段是增加设备到plartform总线上,我们增加硬件设备,需要修改此处信息。

此部分操作一般arm/arm/mach-s3c2440/mach-smdk2440.c类似的文件中,需要我们根据硬件的实际需要修改相应代码

3)驱动注册阶段:

Platform_driver_register() ->driver_register() -> bus_add_driver() -> driver_attach() ->bus_for_each_dev(),
对在每个挂在虚拟的platform bus的设备作__driver_attach() ->driver_probe_device()

判断drv->bus->match()是否执行成功,此时通过指针执行platform_match-> strncmp(pdev->name , drv->name , BUS_ID_SIZE),如果相符就调用really_probe(实际就是执行相应设备的platform_driver->probe(platform_device)。)
开始真正的探测,如果probe成功,则绑定设备到该驱动。

本阶段是在编写具体的驱动程序时完成,在注册驱动时获取步骤2中已经申请的资料,一般由程序开发者完成。

3、platform设备开发过程

platform机制开发的并不复杂,由两部分组成:platform_device和platfrom_driver

通过Platform机制开发发底层驱动的大致流程为:
定义 platform_device

注册 platform_device

定义 platform_driver

注册 platform_driver

以linux2.6.34平台下S3C2440为例:前两项工作主要在arch/arm/mach-s3c2440/match-smdk2440.c与arch/arm/platform-s3c24xx/devs.c中完成,后两项工作主要在编写具体的驱动程序时完成

在2.6内核中platform设备用结构体platform_device来描述,该结构体定义在kernel\include\linux\platform_device.h中,
struct platform_device
{
  const char * name;
  u32 id;
  struct device dev;
  u32 num_resources;
  struct resource * resource;
};
每个具体的驱动都对应一个这样的结构体。

Platform_device结构体描述了一个platform结构的设备,在其中包含了

一般设备的结构体:struct device dev;

设备的资源结构体:struct resource * resource;

还有设备的名字:const char * name。
(注意,这个名字一定要和后面platform_driver.driver->name相同,因为在注册具体的设备驱动时会遍历这个结构体查找相应的数据结构,后面会详细讲解)

该结构一个重要的元素是resource,该元素存入了最为重要的设备资源信息,定义在kernel\include\linux\ioport.h中,
struct resource
{
  resource_size_t start; //定义资源的起始地址
  resource_size_t end; //定义资源的结束地址
  const char *name; //定义资源的名称
  unsigned long flags; //定义资源的类型,比如MEM,IO,IRQ,DMA类型
  struct resource *parent, *sibling, *child; //资源链表指针
};
主要用于定义具体设备占用的硬件资源(如:地址空间、中断号等;

其中 flags位表示该资源的类型

start和end分别表示该资源的起始地址和结束地址;

要注意的是,这里的platform_device设备的注册过程必须在相应设备驱动加载之前被调用;

即执行platform_driver_register()之前,原因是驱动注册时需要匹配内核中所有已注册的设备名。

我们以内核中对SMDK2440的支持为例观察一下整个过程:

内核启动过程中会调用用 arch/arm/mach-s3c2440/smdk2440_machine_init()函数进行板级硬件初始化
static void __init smdk2440_machine_init(void)
{
  s3c24xx_fb_set_platdata(&smdk2440_fb_info);
  s3c_i2c0_set_platdata(NULL);
  platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
  smdk_machine_init();
}
此函数中调用了platform_add_devices() -> platform_device_register()注册platform设备
注册顺序根据同文件夹下的

static struct platform_device *smdk2440_devices[] __initdata =
{
  &s3c_device_ohci,
  &s3c_device_lcd,
  &s3c_device_wdt,
  &s3c_device_i2c0,
  &s3c_device_iis,
  &s3c_device_dm9k,
  &s3c24xx_uda134x,
  &s3c_device_sdi,
};
结构体进行
这些设备的初始化一般都在arch/arm/plat-s3c24xx/devs.c下
我们以s3c_device_wdt为例进行观察:


/* Watchdog */
//
看门狗资源结构体
static struct resource s3c_wdt_resource[] = {
[0] = {
  .start = S3C24XX_PA_WATCHDOG,
  .end = S3C24XX_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG - 1,
  .flags = IORESOURCE_MEM, //
看门狗所使用的IO口范围
},
[1] = {
  .start = IRQ_WDT,
  .end = IRQ_WDT,
  .flags = IORESOURCE_IRQ, //看门狗所使用的中断资源
  }
};


//
定义了一个看门狗结构体
struct platform_device s3c_device_wdt = {
  .name  = "s3c2410-wdt", //
驱动名称
  .id  = -1, //id号,-1代表自动分配
  .num_resources = ARRAY_SIZE(s3c_wdt_resource),
    //指定资源数量
  .resource  = s3c_wdt_resource, //指定资源结构体
};


platform_driver
在具体的硬件设备驱动编写中完成:
同plartform_device相似,需要定义并实现以下结构体

struct platform_driver (/include/linux/Platform_device.h)
{
  int (*probe)(struct platform_device *);
  int (*remove)(struct platform_device *);
  void (*shutdown)(struct platform_device *);
  int (*suspend)(struct platform_device *, pm_message_t state);
  int (*suspend_late)(struct platform_device *, pm_message_t state);
  int (*resume_early)(struct platform_device *);
  int (*resume)(struct platform_device *);
  struct device_driver driver;
};

Platform_driver结构体描述了一个platform结构的驱动。
其中除了一些函数指针外,还有一个一般驱动的device_driver结构。

/*Watchdog平台驱动结构体,平台驱动结构体定义在platform_device.h中,该结构体内的接口函数需要单独实现*/
static struct platform_driver watchdog_driver =
{
  .probe = watchdog_probe, /*Watchdog探测函数*/
  .remove = __devexit_p(watchdog_remove),/*Watchdog移除函数*/
  .shutdown = watchdog_shutdown, /*Watchdog关闭函数*/
  .suspend = watchdog_suspend, /*Watchdog挂起函数*/
  .resume = watchdog_resume, /*Watchdog恢复函数*/
  .driver =
      {
        /*注意这里的名称一定要和系统中定义平台设备的地方一致,这样才能把平台设备与该平台设备的驱动关联起来*/
        .name = "s3c2410-wdt",
        .owner = THIS_MODULE,
      },
};

static int __init watchdog_init(void)
{
  /*将Watchdog注册成平台设备驱动*/
  return platform_driver_register(&watchdog_driver);
}

static void __exit watchdog_exit(void)
{
  /*注销Watchdog平台设备驱动*/
  platform_driver_unregister(&watchdog_driver);
}

module_init(watchdog_init);
module_exit(watchdog_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("linux");
MODULE_DESCRIPTION("S3C2440 Watchdog Driver");

static int __devinit watchdog_probe(struct platform_device *pdev)
{
  int ret;
  int started = 0;
  struct resource *res;/*定义一个资源,用来保存获取的watchdog的IO资源*/

/*在系统定义的watchdog平台设备中获取watchdog中断号platform_get_irq定义在platform_device.h中*/
  wdt_irqno = platform_get_irq(pdev, 0);

/*申请Watchdog中断服务,这里使用的是快速中断:IRQF_DISABLED。中断服务程序为:wdt_irq,将Watchdog平台设备pdev做参数传递过去了*/
  ret = request_irq(wdt_irqno, wdt_irq, IRQF_DISABLED, pdev->name, pdev);

/*获取watchdog平台设备所使用的IO端口资源,注意这个IORESOURCE_MEM标志和watchdog平台设备定义中的一致*/
  res = platform_get_resource(pdev, IORESOURCE_MEM, 0);

/*申请watchdog的IO端口资源所占用的IO空间(要注意理解IO空间和内存空间的区别),request_mem_region定义在ioport.h中*/
  wdt_mem = request_mem_region(res->start, res->end - res->start + 1, pdev->name);

/*将watchdog的IO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定义在io.h中。
注意:IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作,*/
  wdt_base = ioremap(res->start, res->end - res->start + 1);

  return ret;
}

/*Watchdog平台驱动的设备移除接口函数的实现*/
static int __devexit wdt_remove(struct platform_device *dev)
{
  /*释放获取的Watchdog平台设备的IO资源*/
  release_resource(wdt_mem);
  kfree(wdt_mem);

  wdt_mem = NULL;

  /*同watchdog_probe中中断的申请相对应,在那里申请中断,这里就释放中断*/
  free_irq(wdt_irqno, dev);
  wdt_irq = NULL;

  /*释放获取的Watchdog平台设备的时钟*/
  clk_disable(wdt_clock);
  clk_put(wdt_clock);

  wdt_clock = NULL;

  /*释放Watchdog设备虚拟地址映射空间*/
  iounmap(wdt_base);
  return 0;
}

#ifdef CONFIG_PM


/*定义两个变量来分别保存挂起时的WTCON和WTDAT值,到恢复的时候使用*/
static unsigned long wtcon_save;
static unsigned long wtdat_save;

/*Watchdog平台驱动的设备挂起接口函数的实现*/
static int wdt_suspend(struct platform_device *dev, pm_message_t state)
{
    /*保存挂起时的WTCON和WTDAT值*/
  wtcon_save = readl(wdt_base + S3C2410_WTCON);
  wtdat_save = readl(wdt_base + S3C2410_WTDAT);

  /*停止看门狗定时器*/
  wdt_start_or_stop(0);
  return 0;
}

/*Watchdog平台驱动的设备恢复接口函数的实现*/
static int wdt_resume(struct platform_device *dev)
{
  /*恢复挂起时的WTCON和WTDAT值,注意这个顺序*/
  writel(wtdat_save, wdt_base + S3C2410_WTDAT);
  writel(wtdat_save, wdt_base + S3C2410_WTCNT);
  writel(wtcon_save, wdt_base + S3C2410_WTCON);
  return 0;
}

#else /*配置内核时没选上电源管理,Watchdog平台驱动的设备挂起和恢复功能均无效,这两个函数也就无需实现了*/
#define wdt_suspend NULL
#define wdt_resume NULL
#endif

2016-05-02 11:42:38

Smart210学习记录------paltform总线的更多相关文章

  1. Smart210学习记录-----SD/MMC/SDIO驱动

    转自:http://jingpin.jikexueyuan.com/article/23369.html http://blog.csdn.net/evilcode/article/details/7 ...

  2. Smart210学习记录-----Linux i2c驱动

    一:Linux i2c子系统简介: 1.Linux 的 I2C 体系结构分为 3 个组成部分: (1) I2C 核心. I2C 核心提供了 I2C 总线驱动和设备驱动的注册.注销方法,I2C 通信方法 ...

  3. Smart210学习记录------块设备

    转自:http://bbs.chinaunix.net/thread-2017377-1-1.html 本章的目的用尽可能最简单的方法写出一个能用的块设备驱动.所谓的能用,是指我们可以对这个驱动生成的 ...

  4. Smart210学习记录-------内存初始化

    买了Smart210的板子,开始学习中,,,,, 今天看了重定位DRAM ,然而内存需要初始化,早上信心满满的我到现在崩溃的我....也不知遭受了什么样的蹂躏 ,,还是记下一点学到的知识吧.. 数据手 ...

  5. Smart210学习记录-------linux内核模块

    Linux 驱动工程师需要牢固地掌握 Linux 内核的编译方法以为嵌入式系统构建可运行的Linux 操作系统映像.在编译 LDD6410 的内核时,需要配置内核,可以使用下面命令中的 一个: #ma ...

  6. Smart210学习记录------linux串口驱动

    转自:http://blog.chinaunix.net/xmlrpc.php?r=blog/article&uid=27025492&id=327609 一.核心数据结构 串口驱动有 ...

  7. Smart210学习记录------nor flash驱动

    nor flash驱动与nand flash驱动的差别不大,只是设置不同的结构体而已,, nor flash驱动代码: #include <linux/module.h> #include ...

  8. Smart210学习记录----nand flash驱动

    [详解]如何编写Linux下Nand Flash驱动  :http://www.cnblogs.com/linux-rookie/articles/3016990.html 当读写文件请求到来的时候, ...

  9. Smart210学习记录-----linux定时器

    1.内核定时器: Linux 内核所提供的用于操作定时器的数据结构和函数如下: (1) timer_list 在 Linux 内核中,timer_list 结构体的一个实例对应一个定时器 1 stru ...

随机推荐

  1. Excel中连接函数CONCATENATE()

    直接API: CONCATENATE 函数语法具有下列参数 (参数:为操作.事件.方法.属性.函数或过程提供信息的值.): Text1 必需. 要连接的第一个文本项. Text2, ... 可选. 其 ...

  2. maven的安装与使用

    一.Maven是什么 Maven是一个采用纯Java编写的开 源项目管理工具.Maven采用了一种被称之为project object model (POM)概念来管理项目,所有的项目配置信息都被定义 ...

  3. cisco LAN

    由于实验需要,需要搞个内部局域网(以前我们实验室是直接从学校拖了根线过来,然后直接用switch连).因此得先配个router,做个小局域网.由于从没接触过路由器,所以先去网上找了些资料,接着就打开c ...

  4. php solr 查询

    $options = array( 'hostname' => 'localhost', 'port' => 8080, 'path' => 'solr/test'); $clien ...

  5. 使用ContentProvider管理多媒体-----向多媒体数据中添加数据

    //为add按钮的单击事件绑定监听器 add.setOnClickListener(new OnClickListener() { @Override public void onClick(View ...

  6. visual studio 2013连接Oracle 11g并获取数据:(一:环境搭建)

    C# WinForm案例: 目标: visual studio 中点击按钮,就可获取到Oracle中数据表的内容 1.安装Visual Studio 2013 ,推荐如下网址,下载ISO镜像,一路ne ...

  7. C++-sizeof和strlen的区别

    一.sizeof    sizeof(...)是运算符,在头文件中typedef为unsigned int,其值在编译时即计算好了,参数可以是数组.指针.类型.对象.函数等.    它的功能是:获得保 ...

  8. VMWare Workstation 10.0 Preview CN

    What's New in the VMware Workstation Technology Preview July 2013 The VMware Workstation team is exc ...

  9. C# WebRequestExtensions

    https://gist.github.com/abombss/2720757 public static class WebRequestExtensions { public static Htt ...

  10. jQuery中 判断事件

    $('button.top').on('mousedown', function() { var $this = $(this); if ($this.hasClass('settop')) { $t ...