platform机制由两部分组成,platform_device和platform_driver。
Platform驱动与传统的设备驱动模型(即通过driver_register函数进行注册)相比,优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接口(即通过platform
device提供的标准接口),这样提高了程序可移植性。
platform 是一个虚拟的地址总线,相比 PCI、USB、I2C,它主要用于描述 SOC 上的片上资源。比如 S3C2410 上集成的控制器( LCD、Watchdog、RTC等),platform 所描述的资源有一个共同点:在 CPU 的总线上直接取址。
用platform机制开发底层设备驱动流程图。
1.平台设备
(1)描述
- struct platform_device {
-
const char * name;//设备名字
-
int id; //设备编号
-
struct device dev;
-
u32 num_resources;
-
struct resource * resource;//设备资源
-
-
struct platform_device_id *id_entry;
-
-
/* arch specific additions */
-
struct pdev_archdata archdata;
-
};
(2)分配
- struct platform_device *platform_device_alloc(const char *name, unsigned int id)
- name:设备名
- id:一般为-1
(3)注册
- int platform_device_add(struct platform_device *pdev)
- {
- //增加的platform设备,都以platform_bus(platform设备)为父节点
- if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus;
- //platform类型设备都挂接在platform总线上 /sys/bus/platform/
- pdev->dev.bus = &platform_bus_type;
- .
- .
- .
- }
(4)资源描述
- /*
-
* Resources are tree-like, allowing
-
* nesting etc..
-
*/
-
struct resource {
-
resource_size_t start;//资源的起始物理地址
-
resource_size_t end; //资源的结束物理地址
-
const char *name; //资源名称
-
unsigned long flags; //资源类型,MEM,IO,IRQ
-
struct resource *parent, *sibling, *child; //资源链表指针
-
};
比如linux-2.6.32内核自带的s3c2410-wdt设备加载过程如下
- /* Watchdog */
- #define S3C24XX_VA_WATCHDOG S3C_VA_WATCHDOG
- #define S3C2410_PA_WATCHDOG (0x53000000)
- #define S3C24XX_SZ_WATCHDOG SZ_1M
- #define S3C24XX_PA_WATCHDOG S3C2410_PA_WATCHDOG
-
static struct resource s3c_wdt_resource[] = {
-
[0] = {//硬件寄存器资源
-
.start = S3C24XX_PA_WATCHDOG, //0x53000000
-
.end = S3C24XX_PA_WATCHDOG + S3C24XX_SZ_WATCHDOG - 1,
-
.flags = IORESOURCE_MEM,
-
},
-
[1] = {//中断资源
-
.start = IRQ_WDT,
-
.end = IRQ_WDT,
-
.flags = IORESOURCE_IRQ,
-
}
-
-
};
-
-
struct platform_device s3c_device_wdt = {
-
.name = "s3c2410-wdt",//name要与platform_driver中的name一致
-
.id = -1,
-
.num_resources = ARRAY_SIZE(s3c_wdt_resource),
-
.resource = s3c_wdt_resource,
-
};
- static struct platform_device *smdk2440_devices[] __initdata = {
- &s3c_device_usb,
- &s3c_device_lcd,
- &s3c_device_wdt,//platform_device指针
- &s3c_device_i2c0,
- &s3c_device_iis,
- };
- static void __init smdk2440_machine_init(void)
- {
- s3c24xx_fb_set_platdata(&smdk2440_fb_info);
- s3c_i2c0_set_platdata(NULL);
//添加所有platform设备
- platform_add_devices(smdk2440_devices, ARRAY_SIZE(smdk2440_devices));
- smdk_machine_init();
- }
- MACHINE_START(S3C2440, "SMDK2440")
- /* Maintainer: Ben Dooks */
- .phys_io = S3C2410_PA_UART,
- .io_pg_offst = (((u32)S3C24XX_VA_UART) >> 18) & 0xfffc,
- .boot_params = S3C2410_SDRAM_PA + 0x100,
- .init_irq = s3c24xx_init_irq,
- .map_io = smdk2440_map_io,
- .init_machine = smdk2440_machine_init,
- .timer = &s3c24xx_timer,
- MACHINE_END
(5)获取资源方法
- /**
-
* platform_get_resource - get a resource for a device
-
* @dev: platform device
-
* @type: resource type
-
* @num: resource index
-
*/
-
struct resource *platform_get_resource(struct platform_device *dev,
-
unsigned int type, unsigned int num)
- dev:资源所属设备
- type:获取资源类型
- num:获取的资源index
- 比如:
- platform_get_resource(pdev,IORESOURCE_IRQ,0)//获取中断号
- platform_get_resource(pdev,IORESOURCE_MEM,0)//获取第0个资源
2.平台驱动
(1)描述
- 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 (*resume)(struct platform_device *);
-
struct device_driver driver;
-
struct platform_device_id *id_table;
-
};
(2)注册
- /**
-
* platform_driver_register
-
* @drv: platform driver structure
-
*/
- int platform_driver_register(struct platform_driver *drv)
在2.6.32内核s3c2410_wdt.c中实例注册过程
- static struct platform_driver s3c2410wdt_driver = {
- .probe = s3c2410wdt_probe,
- .remove = __devexit_p(s3c2410wdt_remove),
- .shutdown = s3c2410wdt_shutdown,
- .suspend = s3c2410wdt_suspend,
- .resume = s3c2410wdt_resume,
- .driver = {
- .owner = THIS_MODULE,
- .name = "s3c2410-wdt",//名字与platform_device中的name一致
- },
- };
- static int __init watchdog_init(void)
- {
- printk(banner);
- return platform_driver_register(&s3c2410wdt_driver);
- }
- static void __exit watchdog_exit(void)
- {
- platform_driver_unregister(&s3c2410wdt_driver);
- }
- module_init(watchdog_init);
- module_exit(watchdog_exit);
(3)probe函数
执行时机:当所加载驱动与相应设备匹配成功
功能:获得设备资源,完成一个设备驱动所有的操作接口
(4)remove函数
执行:设备或驱动移除时
功能:释放probe申请的一切资源
比如s3c2410_wdt.c
- /* device interface */
-
-
static int __devinit s3c2410wdt_probe(struct platform_device *pdev)
-
{
-
struct resource *res;
-
struct device *dev;
-
unsigned int wtcon;
-
int started = 0;
-
int ret;
-
int size;
-
-
DBG("%s: probe=%p\n", __func__, pdev);
-
-
dev = &pdev->dev;
-
wdt_dev = &pdev->dev;
-
-
/* get the memory region for the watchdog timer */
-
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);//获得资源,物理地址
-
if (res == NULL) {
-
dev_err(dev, "no memory resource specified\n");
-
return -ENOENT;
-
}
-
-
size = (res->end - res->start) + 1;
-
wdt_mem = request_mem_region(res->start, size, pdev->name);
-
if (wdt_mem == NULL) {
-
dev_err(dev, "failed to get memory region\n");
-
ret = -ENOENT;
-
goto err_req;
-
}
-
-
wdt_base = ioremap(res->start, size);
-
if (wdt_base == NULL) {
-
dev_err(dev, "failed to ioremap() region\n");
-
ret = -EINVAL;
-
goto err_req;
-
}
-
-
DBG("probe: mapped wdt_base=%p\n", wdt_base);
-
-
wdt_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);//获得中断号
-
if (wdt_irq == NULL) {
-
dev_err(dev, "no irq resource specified\n");
-
ret = -ENOENT;
-
goto err_map;
-
}
-
-
ret = request_irq(wdt_irq->start, s3c2410wdt_irq, 0, pdev->name, pdev);//注册中断
-
if (ret != 0) {
-
dev_err(dev, "failed to install irq (%d)\n", ret);
-
goto err_map;
-
}
-
-
wdt_clock = clk_get(&pdev->dev, "watchdog");
-
if (IS_ERR(wdt_clock)) {
-
dev_err(dev, "failed to find watchdog clock source\n");
-
ret = PTR_ERR(wdt_clock);
-
goto err_irq;
-
}
-
-
clk_enable(wdt_clock);
-
-
/* see if we can actually set the requested timer margin, and if
-
* not, try the default value */
-
-
if (s3c2410wdt_set_heartbeat(tmr_margin)) {
-
started = s3c2410wdt_set_heartbeat(
-
CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
-
-
if (started == 0)
-
dev_info(dev,
-
"tmr_margin value out of range, default %d used\n",
-
CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME);
-
else
-
dev_info(dev, "default timer value is out of range, "
-
"cannot start\n");
-
}
-
-
ret = misc_register(&s3c2410wdt_miscdev);
-
if (ret) {
-
dev_err(dev, "cannot register miscdev on minor=%d (%d)\n",
-
WATCHDOG_MINOR, ret);
-
goto err_clk;
-
}
-
-
if (tmr_atboot && started == 0) {
-
dev_info(dev, "starting watchdog timer\n");
-
s3c2410wdt_start();
-
} else if (!tmr_atboot) {
-
/* if we're not enabling the watchdog, then ensure it is
-
* disabled if it has been left running from the bootloader
-
* or other source */
-
-
s3c2410wdt_stop();
-
}
-
-
/* print out a statement of readiness */
-
-
wtcon = readl(wdt_base + S3C2410_WTCON);
-
-
dev_info(dev, "watchdog %sactive, reset %sabled, irq %sabled\n",
-
(wtcon & S3C2410_WTCON_ENABLE) ? "" : "in",
-
(wtcon & S3C2410_WTCON_RSTEN) ? "" : "dis",
-
(wtcon & S3C2410_WTCON_INTEN) ? "" : "en");
-
-
return 0;
-
-
err_clk:
-
clk_disable(wdt_clock);
-
clk_put(wdt_clock);
-
-
err_irq:
-
free_irq(wdt_irq->start, pdev);
-
-
err_map:
-
iounmap(wdt_base);
-
-
err_req:
-
release_resource(wdt_mem);
-
kfree(wdt_mem);
-
-
return ret;
-
}
3.平台总线
(1)描述
- struct bus_type platform_bus_type = {
-
.name = "platform",
-
.dev_attrs = platform_dev_attrs,
-
.match = platform_match,
-
.uevent = platform_uevent,
-
.pm = &platform_dev_pm_ops,
-
};
(2)match方法:当platform总线上有设备或驱动变化时执行一次或多次
- static int platform_match(struct device *dev, struct device_driver *drv)
-
{
-
struct platform_device *pdev = to_platform_device(dev);
-
struct platform_driver *pdrv = to_platform_driver(drv);
-
-
/* match against the id table first */
-
if (pdrv->id_table)
-
return platform_match_id(pdrv->id_table, pdev) != NULL;
-
-
/* fall-back to driver name match */
-
return (strcmp(pdev->name, drv->name) == 0); //比较name成员
-
}
4.小结:
(1)当platform总线上有设备或驱动加入时,platform总线的match方法被调用一次或多次,遍历所有设备,一旦name成员与driver匹配成功,调用driver的probe方法,移除时调用remove方法。
(2)platform总线只是提供了一个管理硬件资源与设备的机制,驱动接口真正的实现在probe()函数内完成。
一个简单的platform机制测试代码: platform.rar
实验结果:
阅读(344) | 评论(0) | 转发(0) |