1、概述:

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

从Linux2.6起,引入了一套新的驱动管理和注册机制:Platform_device和Platform_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

Platform 概述相关推荐

  1. Linux ALSA驱动之Platform源码分析(wm8350.c)

    1.Platform概述 ASoC被分为Machine,Platform和Codec三大部件,Platform驱动的主要作用是完成音频数据的管理,最终通过CPU的数字音频接口(DA〉把音频数据传送给C ...

  2. Linux ALSA 音频系统:物理链路篇

    原址 1. Overview 硬件平台及软件版本: Kernel - 3.4.5 SoC - Samsung exynos CODEC - WM8994 Machine - goni_wm8994 U ...

  3. ALSA-ASOC音频驱动框架简述

    ##ALSA-ASOC音频驱动框架简述 注意:本文只限于讲解ALSA-ASOC音频驱动框架,不深入到寄存器.时序等配置,文章有不足之处,日后逐渐完善补充 另外多谢两位前辈的博客,学到了很多,多谢. h ...

  4. knime 大数据_如何使用Knime进行数据科学

    knime 大数据 Knime(K是无声的,因此发音为nīm )是一个高度评价的数据分析平台,具有广泛的适用性,并且与其他产品(例如与数据库,语言,机器学习框架和深度学习框架)进行了许多集成. Kni ...

  5. knime 大数据_如何将KNIME用于数据科学

    knime 大数据 . KNIME(K是无声的,因此发音为nīm )是一个高度评价的数据分析平台,具有广泛的适用性,并且与其他产品(例如与数据库,语言,机器学习框架和深度学习框架)进行了许多集成. K ...

  6. 微软power bi_Microsoft Power Platform快速概述

    微软power bi In this article, I am going to explain the various products offered by the Microsoft Powe ...

  7. BEA WebLogic Platform 8.1 Single Sign-On Enablement:概述

    BEA WebLogic Platform 8.1 Single Sign-On Enablement:概述 时间:2004-03-10 作者: 浏览次数: 4162 本文关键字:SSO,  单点登录 ...

  8. 【Azure Services Platform Step by Step-第5篇】.NET Services 概述

    在云端运行应用程序.存储和处理数据只是云计算的一部分.我们还想搭建云端服务(cloud-based services).云端服务当然和普通的服务不同了,需要更多的管理和约束..NET Services ...

  9. MIT Graph实践概述

    MIT Graph实践概述 Features功能 • iCloud Support • Multi Local & Cloud Graphs • Thread Safe • Store Any ...

最新文章

  1. CNN如何用于NLP任务?一文简述文本分类任务的7个模型(附代码)
  2. HIve分组查询返回每组的一条记录
  3. 13.执行外部命令subprocess
  4. python中数组的维度_Python数组维度问题
  5. es6 --- Promise封装读取文件操作
  6. 开着开着,Model S天窗飞了!特斯拉回应...
  7. 弱引用WeakReference
  8. 当下大数据体系的4个热点,4个趋势和3个问题
  9. 学计算机辐射,离散数学对计算机专业系统知识辐射作用.doc
  10. asp.net core3.0 mvc 用 autofac
  11. Android Google Map –两点之间的绘图路线
  12. Python库的下载及导入使用教程
  13. flash 10 android,adobe flash 10.0.0
  14. python 查询ip工具
  15. 32 Pin和 8 Pin(引脚 )flash烧录操作指导
  16. 使用python对微信好友进行数据分析
  17. yolov5s.pt下载
  18. Doc2Vec 模型参数
  19. 文件怎么复制到虚拟机中的linux系统吗,Windows下的文件如何复制到虚拟机的Linux中...
  20. 计算机辅助翻译政府工作报告,Trados辅助翻译软件在科技英语翻译中的应用

热门文章

  1. DDR中bank,die,rank,channel的概念
  2. C++的STL中accumulate的用法
  3. Android开发应该用什么语言
  4. oracle导出一半报1046,Oracle 数据库1046事件
  5. 常见内网穿透-花生壳、神卓互联、FRP、ngork分析
  6. vue directives自定义指令的使用
  7. 心蓝12306订票助手
  8. C语言sort和qsort函数的用法
  9. Android FTP功能开发基于swiftp
  10. .gitignore文件作用