驱动程序中的platform总线详解

heningbo   2011-9-27 10:49 楼主

驱动程序中的platform总线详解

本文摘自:http://www.xcembed.com/bbs/viewthread.php?tid=11&extra=page%3D1

在设备驱动程序中经常会见到和platform相关的字段,分布在驱动程序的多个角落,这也是2.6内核中比较重要的一种机制,把它的原理弄懂了,对以后分析驱动程序很有帮助,下面简单介绍一下:

linux2.6设备模型中,关心总线,设备,驱动这三个实体,总线将设备和驱动绑定,在系统每注册一个设备的时候,会寻找与之匹配的驱动。相反,在系统每注册一个驱动的时候,寻找与之匹配的设备,匹配是由总线来完成的。

一个现实的Linux 设备和驱动通常都需要挂接在一种总线上,对于本身依附于PCIUSBI2CSPI 等的设备而言,这自然不是问题,但是在嵌入式系统里面,SoC 系统中集成的独立的外设控制器、挂接在SoC 内存空间的外设等确不依附于此类总线。基于这一背景,Linux 发明了一种虚拟的总线,称为platform 总线。

SOC系统中集成的独立外设单元(I2CLCDSPIRTC等)都被当作平台设备来处理,而它们本身是字符型设备。

Linux2.6内核起,引入一套新的驱动管理和注册机制:platform_device platform_driver Linux 中大部分的设备驱动,都可以使用这套机制,设备用 platform_device 表示;驱动用platform_driver 进行注册。

platform_device 结构体       include/linux/platform_device.h

struct platform_device

{

const char *name;                   //设备名

u32 id;

struct device dev;

u32 num_resources;                  //设备所使用的各类资源数量

struct resource *resource;          //使用的资源

}

platform_driver 结构体       include/linux/platform_device.h

struct platform_driver

{

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 pm_ext_ops *pm;

struct device_driver driver;

};

系统为platform总线定义一个bus_type的实例platform_bus_type,通过其成员函数match(),确定devicedriver如何匹配。

匹配platform_deviceplatform_driver主要看二者的name字段是否相同。(name必须要相同才能匹配)
platform_device_register()函数注册单个的平台设备。

一般是在平台的BSP文件中定义platform_device,通过platform_add_devices()函数将平台设备注册到系统中

platform_driver 的注册与注销:

platform_driver_register()

platform_driver_unregister()

s3c2440 LCD驱动为例:

BSP文件中:

struct platform_device s3c_device_lcd = {

.name    = "s3c2410-lcd",

.id        = -1,

.num_resources   = ARRAY_SIZE(s3c_lcd_resource),

.resource   = s3c_lcd_resource,

.dev              = {

        .dma_mask  = &s3c_device_lcd_dmamask,

 .coherent_dma_mask = 0xffffffffUL

}

};

为了完成LCD设备的注册,将其放进/arch/arm/mach-s3c2440/mach-smdk2440.c中定义的smdk2440_devices数组中:

static struct platform_device *smdk2440_devices[] __initdata = {

........

........

&s3c_device_lcd,

};

platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));注册在相应的driver文件中:(/drivers/video/s3c2410fb.c

static struct platform_driver s3c2410fb_driver = {

.probe  = s3c2410fb_probe,                 //驱动探测

.remove  = __devexit_p(s3c2410fb_remove),   //驱动移除

.suspend = s3c2410fb_suspend,

.resume  = s3c2410fb_resume,

.driver  = {

      .name = "s3c2410-lcd",    //platform_device中的name相同

.owner = THIS_MODULE,

},

};

static int __devinit s3c2410_fb_init(void)

{

return platform_driver_register(&s3c2410fb_driver);

}

static void __exit s3c2410fb_cleanup(void)

{

platform_driver_unregister(&s3c2410fb_driver);

}

注册成功后会在下面两个目录下看到设备节点:

/sys/bus/platform/devices/

/sys/devices/platform/

平台设备资源和数据:

resource结构体:

struct resource

{

resource_size_t start;

resource_size_t end;

const char *name;

unsigned long flags;

struct resource *parent, *sibling, *child;

};

我们通常关心startend flags 3 个字段,分别标明资源的开始值、结束值和类型,flags可以为IORESOURCE_IOIORESOURCE_MEMIORESOURCE_IRQIORESOURCE_DMA 等。

LCD资源:

static struct resource s3c_lcd_resource[] = {
     [0] = {

          .start = S3C24XX_PA_LCD,         //LCDIO资源起始地始(LCD控制器寄存器地址)

          .end   = S3C24XX_PA_LCD + S3C24XX_SZ_LCD - 1,     //结束地址

          .flags = IORESOURCE_MEM,

     },

[1] = {

          .start = IRQ_LCD,                //LCD中断号

          .end   = IRQ_LCD,

          .flags = IORESOURCE_IRQ,

     }

};

driver中用platform_get_resource()platform_get_irq()等函数获取设备资源

struct resource *platform_get_resource(struct platform_device *, unsigned int, unsigned int);

int platform_get_irq(struct platform_device *dev, unsigned int num);

获取到的内存或IO资源,需要ioremap后才能使用获取到的IRQ资源,需要request_irq

设备除了可以在BSP 中定义资源以外,还可以附加一些数据信息,因为对设备的硬件描述除了中断、内存、DMA 通道以外,可能还会有一些配置信息,而这些配置信息也依赖于板,不适宜直接放置在设备驱动本身,因此,platform 也提供了platform_data 的支持。

platform_data可以自定义,比如DM9000驱动,用platform_data描述它的一些属性:

static struct dm9000_plat_data s3c_dm9000_platdata = {

    .flags   = DM9000_PLATF_16BITONLY,

};

static struct platform_device s3c_device_dm9000 = {

    .name    = "dm9000",

    .id      = 0,

    .num_resources = ARRAY_SIZE(s3c_dm9000_resource),

    .resource = s3c_dm9000_resource,

    .dev     = {

        .platform_data = &s3c_dm9000_platdata,

     }

};

在相应的驱动中使用:

struct dm9000_plat_data *pdata = pdev->dev.platform_data;

可获取platform_data

找一个和平台相关的驱动程序,从BSP文件开始分析它的结构,一直分析到它的最底层的硬件操作,这样很快就能熟悉platform的工作原理

 

回复评论

暂无评论,赶紧抢沙发吧
电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 京公网安备 11010802033920号
    写回复