platform_device_系列函式及其設備註冊的作用
platform_device_系列函式,實際上是註冊了一個叫platform的虛擬匯流排。使用約定是如果一個不屬於任何匯流排的裝置,例如藍芽,串列埠等裝置,都需要掛在這個虛擬總線上。
river/base/platform.c
//platform裝置宣告 struct device platform_bus = { .bus_id = "platform", }; EXPORT_SYMBOL_GPL(platform_bus); //platform匯流排裝置宣告 struct bus_type platform_bus_type = { .name = "platform", .dev_attrs = platform_dev_attrs, .match = platform_match, .uevent = platform_uevent, .suspend = platform_suspend, .suspend_late = platform_suspend_late, .resume_early = platform_resume_early, .resume = platform_resume, }; EXPORT_SYMBOL_GPL(platform_bus_type); int __init platform_bus_init(void) { int error; error = device_register(&platform_bus);//註冊了"platform"的裝置 if (error) return error; error = bus_register(&platform_bus_type);//註冊了叫"platform"的匯流排 if (error) device_unregister(&platform_bus); return error; } //這裡在platform總線上掛裝置 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;//父裝置設定為platform_bus pdev->dev.bus = &platform_bus_type;//設定掛在platform總線上 if (pdev->id != -1) snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name,pdev->id); else strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE); 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; else if (r->flags & IORESOURCE_IO) p = &ioport_resource; } if (p && insert_resource(p, r)) { 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; } EXPORT_SYMBOL_GPL(platform_device_add); //常用的platform_device_register,內部呼叫了platform_device_add,將裝置掛在了platform總線上 /** * 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); } EXPORT_SYMBOL_GPL(platform_device_register);
要用註冊一個platform驅動的步驟:
1,註冊裝置platform_device_register
2,註冊驅動platform_driver_register
註冊時候的兩個名字必須一樣,才能match上,才能work,例如:
而且device註冊的時候,可以給driver傳引數struct platform_device pxa3xx_device_nand = { .name = "pxa3xx-nand", .id = -1, .dev = { .dma_mask = &pxa3xx_nand_dma_mask, .coherent_dma_mask = DMA_BIT_MASK(32), }, .resource = pxa3xx_resource_nand, .num_resources = ARRAY_SIZE(pxa3xx_resource_nand), }; static struct platform_driver pxa3xx_nand_driver = { .driver = { .name = "pxa3xx-nand", }, .probe = pxa3xx_nand_probe, .remove = pxa3xx_nand_remove, #ifdef CONFIG_PM .suspend = pxa3xx_nand_suspend, .resume = pxa3xx_nand_resume, #endif };
struct device { struct klist klist_children; struct klist_node knode_parent; /* node in sibling list */ struct klist_node knode_driver; struct klist_node knode_bus; struct device *parent; struct kobject kobj; char bus_id[BUS_ID_SIZE]; /* position on parent bus */ struct device_type *type; unsigned is_registered:1; unsigned uevent_suppress:1; struct semaphore sem; /* semaphore to synchronize calls to its driver.*/ struct bus_type *bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */ void *driver_data; /* data private to the driver */ void *platform_data; /* Platform specific data, device core doesn't touch it */ struct dev_pm_info power; #ifdef CONFIG_NUMA int numa_node; /* NUMA node this device is close to */ #endif u64 *dma_mask; /* dma mask (if dma'able device) */ u64 coherent_dma_mask;/* Like dma_mask, but for alloc_coherent mappings as not all hardware supports 64 bit addresses for consistent allocations such descriptors. */ struct device_dma_parameters *dma_parms; struct list_head dma_pools; /* dma pools (if dma'ble) */ struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */ /* arch specific additions */ struct dev_archdata archdata; spinlock_t devres_lock; struct list_head devres_head; /* class_device migration path */ struct list_head node; struct class *class; dev_t devt; /* dev_t, creates the sysfs "dev" */ struct attribute_group **groups; /* optional groups */ void (*release)(struct device *dev); };
傳引數都是通過platform_data傳,所以定義為void *
void *platform_data;
static struct pxa3xx_nand_platform_data XXX_nand_info = {
.parts = android_256m_v75_partitions,
.nr_parts = ARRAY_SIZE(android_256m_v75_partitions),
};
static void __init XXX_init_nand(void)
{
pxa3xx_device_nand.dev.platform_data = &XXX_nand_info;
platform_device_register(&pxa3xx_device_nand);
}
static int __init pxa3xx_nand_probe(struct platform_device *pdev)
{
struct pxa3xx_nand_platform_data *pdata;
struct nand_chip *this;
struct pxa3xx_nand_info *info;
struct resource *res;
struct clk *clk = NULL, *smc_clk = NULL;
int status = -1;
struct mtd_partition *parts;
unsigned int data_buf_len;
#ifdef CONFIG_MTD_NAND_PXA3xx_DMA
unsigned int buf_len;
#endif
int i, ret = 0;
#ifdef CONFIG_MTD_PARTITIONS
int err;
#endif
pdata = pdev->dev.platform_data;
....
....
....
}
下面解釋一下pxa_register_device函式 pxa_set_ohci_info(&XXX_ohci_info);
void __init pxa_set_ohci_info(struct pxaohci_platform_data *info)
{
pxa_register_device(&pxa27x_device_ohci, info);
}
void __init pxa_register_device(struct platform_device *dev, void *data)
{
int ret;
dev->dev.platform_data = data;
ret = platform_device_register(dev);
if (ret)
dev_err(&dev->dev, "unable to register device: %d\n", ret);
}
其實上,也就是給driver傳引數,通過dev.platform_data。到這裡,platform_device系列函式,基本算通了,系列函式還有一堆設定的函式,和device_register同級別的那些功能函式,用法基本差不多,只不過都將裝置掛在了platform總線上。
platform_device_register向系統註冊裝置
platform_driver_register向系統註冊驅動,過程中在系統尋找註冊的裝置(根據.name),找到後執行.probe進行初始化。
首先看device和platform_device區別
由struct platform_device {
const char * name; //NOTE:此處裝置的命名應和相應驅動程式命名一致
u32 id; //以實現driver binding
struct device dev;
u32 num_resources;
struct resource * resource;
};
可知:platform_device由device派生而來
核心中關於Platform devices的註釋
Platform devices
~~~~~~~~~~~~~~~~
Platform devices are devices that typically appear as autonomous
entities in the system. This includes legacy port-based devices and
host bridges to peripheral buses, and most controllers integrated
into system-on-chip platforms. What they usually have in common
is direct addressing from a CPU bus. Rarely, a platform_device will
be connected through a segment of some other kind of bus; but its
registers will still be directly addressable.
Platform devices are given a name, used in driver binding, and a
list of resources such as addresses and IRQs.
個人理解:Platform devices是相對獨立的裝置,擁有各自獨自的資源(addresses and IRQs)
一個Platform
devices例項:
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,
};
/* 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,
}
};
struct platform_device s3c_device_lcd = {//s3c_device_lcd裝置
.name = "s3c2410-lcd",
.id = -1,
.num_resources = ARRAY_SIZE(s3c_lcd_resource),
.resource = s3c_lcd_resource,
.dev = { //device例項
.dma_mask = &s3c_device_lcd_dmamask,
.coherent_dma_mask = 0xffffffffUL
}
};
s3c_device_lcd的resource中硬體地址:#define S3C2410_LCDREG(x) ((x) + S3C2410_VA_LCD)
/* 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)
#define S3C2410_LCDCON1_CLKVAL(x) ((x) << 8)
#define S3C2410_LCDCON1_MMODE (1<<7)
#define S3C2410_LCDCON1_DSCAN4 (0<<5)
#define S3C2410_LCDCON1_STN4 (1<<5)
#define S3C2410_LCDCON1_STN8 (2<<5)
#define S3C2410_LCDCON1_TFT (3<<5)
--------------------------
#define S3C2410_ADDR(x) (0xF0000000 + (x))
/* LCD controller */
#define S3C2410_VA_LCD S3C2410_ADDR(0x00600000)
#define S3C2410_PA_LCD (0x4D000000)
#define S3C2410_SZ_LCD SZ_1M
再分析device_register()和platform_device_register()的實現程式碼:
device_register()------------------------
/**
* device_register - register a device with the system.
* @dev: pointer to the device structure
*
* This happens in two clean steps - initialize the device
* and add it to the system. The two steps can be called
* separately, but this is the easiest and most common.
* I.e. you should only call the two helpers separately if
* have a clearly defined need to use and refcount the device
* before it is added to the hierarchy.
*/
int device_register(struct device *dev)
{
device_initialize(dev); //初始化裝置結構
return device_add(dev); //新增裝置到裝置層
}
platform_device_register()--------------------
/**
* 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); //新增一個片上的裝置到裝置層
}
由以上函式可知:device_register()和platform_device_register()都會首先初始化裝置
區別在於第二步:其實platform_device_add()包括device_add(),只不過要先註冊resources
platform_device_add()----------------------
/**
* 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;
/*++++++++++++++
The platform_device.dev.bus_id is the canonical name for the devices.
It's built from two components:
* platform_device.name ... which is also used to for driver matching.
* platform_device.id ... the device instance number, or else "-1"
to indicate there's only one.
These are concatenated, so name/id "serial"/0 indicates bus_id "serial.0", and
"serial/3" indicates bus_id "serial.3"; both would use the platform_driver
named "serial". While "my_rtc"/-1 would be bus_id "my_rtc" (no instance id)
and use the platform_driver called "my_rtc".
++++++++++++++*/
if (pdev->id != -1)
snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id);
else //"-1" indicate there's only one
strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
for (i = 0; i < pdev->num_resources; i++) { //遍歷裝置資源個數,如LCD的兩個資源:控制器和IRQ
struct resource *p, *r = &pdev->resource[i];
if (r->name == NULL)
r->name = pdev->dev.bus_id;
p = r->parent;
if (!p) { //resources分為兩種IORESOURCE_MEM和IORESOURCE_IO
//CPU對外設IO埠實體地址的編址方式有兩種:I/O對映方式和記憶體對映方式
if (r->flags & IORESOURCE_MEM)
p = &iomem_resource;
else if (r->flags & IORESOURCE_IO)
p = &ioport_resource;
}
if (p && insert_resource(p, r)) {
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;
}
相關參考+++++++++++++++++++++++
device_initialize()------------------
/** </drivers/base/core.c>
* device_initialize - init device structure.
* @dev: device.
*
* This prepares the device for use by other layers,
* including adding it to the device hierarchy.
* It is the first half of device_register(), if called by
* that, though it can also be called separately, so one
* may use @dev's fields (e.g. the refcount).
*/
void device_initialize(struct device *dev)
{
kobj_set_kset_s(dev, devices_subsys);
kobject_init(&dev->kobj);
klist_init(&dev->klist_children, klist_children_get,
klist_children_put);
INIT_LIST_HEAD(&dev->dma_pools);
INIT_LIST_HEAD(&dev->node);
init_MUTEX(&dev->sem);
spin_lock_init(&dev->devres_lock);
INIT_LIST_HEAD(&dev->devres_head);
device_init_wakeup(dev, 0);
set_dev_node(dev, -1);
}
// device_add(struct device *dev)-------------
/**
* device_add - add device to device hierarchy.
* @dev: device.
*
* This is part 2 of device_register(), though may be called
* separately _iff_ device_initialize() has been called separately.
*
* This adds it to the kobject hierarchy via kobject_add(), adds it
* to the global and sibling lists for the device, then
* adds it to the other relevant subsystems of the driver model.
*/
//結構體resource----------------------
/* < /include/linux/ioport.h>
* Resources are tree-like, allowing
* nesting etc..
*/
struct resource {
resource_size_t start;
resource_size_t end;
const char *name;
unsigned long flags;
struct resource *parent, *sibling, *child;
};
//---------------------------
原文地址:http://blog.chinaunix.net/u1/58968/showart_467998.html ,
在8250.c(driver/serial/8250.c)的初始化函式serial8250_init()中,給出了一個很簡單的例子
static struct platform_device *serial8250_isa_devs;
......
//create a platform_device
serial8250_isa_devs = platform_device_alloc("serial8250",PLAT8250_DEV_LEGACY);
platform_device_add(serial8250_isa_devs); //add the platform_device to system
platform_driver_register(&serial8250_isa_driver);//then register the platform_driver
還有另外一個比較類似的比較,就是driver_register和platform_driver_register的比較
platform_driver_register(&xx_driver) 會向系統註冊xx_driver這個驅動程式,這個函式會根據 xx_driver中的.name內容,搜尋系統註冊的device中有沒有這個platform_device,如果有,就會執行 platform_driver(也就是xx_driver的型別)中的.probe函式。
對只需要初始化執行一次的函式都加上__init屬性,__init 巨集告訴編譯器如果這個模組被編譯到核心則把這個函式放到(.init.text)段,module_exit的引數解除安裝時同__init類似,如果驅動被編譯進核心,則__exit巨集會忽略清理函式,因為編譯進核心的模組不需要做清理工作,顯然__init和__exit對動態載入的模組是無效的,只支援完全編譯進核心。