From 689ae231afbac8979f96100b372a5a73458baaa9 Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Fri, 27 Jul 2012 22:14:59 +0200 Subject: platform: Add support for automatic device IDs Right now we have support for explicit platform device IDs, as well as ID-less platform devices when a given device type can only have one instance. However there are cases where multiple instances of a device type can exist, and their IDs aren't (and can't be) known in advance and do not matter. In that case we need automatic device IDs to avoid device name collisions. I am using magic ID value -2 (PLATFORM_DEVID_AUTO) for this, similar to -1 for ID-less devices. The automatically allocated device IDs are global (to avoid an additional per-driver cost.) We keep note that the ID was automatically allocated so that it can be freed later. Note that we also restore the ID to PLATFORM_DEVID_AUTO on error and device deletion, to avoid avoid unexpected behavior on retry. I don't really expect retries on platform device addition, but better safe than sorry. Signed-off-by: Jean Delvare Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 38 +++++++++++++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 3 deletions(-) (limited to 'drivers/base/platform.c') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index a1a722502587..3f8077ce585c 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -20,9 +20,13 @@ #include #include #include +#include #include "base.h" +/* For automatically allocated device IDs */ +static DEFINE_IDA(platform_devid_ida); + #define to_platform_driver(drv) (container_of((drv), struct platform_driver, \ driver)) @@ -263,7 +267,7 @@ EXPORT_SYMBOL_GPL(platform_device_add_data); */ int platform_device_add(struct platform_device *pdev) { - int i, ret = 0; + int i, ret; if (!pdev) return -EINVAL; @@ -273,10 +277,27 @@ int platform_device_add(struct platform_device *pdev) pdev->dev.bus = &platform_bus_type; - if (pdev->id != -1) + switch (pdev->id) { + default: dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); - else + break; + case PLATFORM_DEVID_NONE: dev_set_name(&pdev->dev, "%s", pdev->name); + break; + case PLATFORM_DEVID_AUTO: + /* + * Automatically allocated device ID. We mark it as such so + * that we remember it must be freed, and we append a suffix + * to avoid namespace collision with explicit IDs. + */ + ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL); + if (ret < 0) + goto err_out; + pdev->id = ret; + pdev->id_auto = true; + dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id); + break; + } for (i = 0; i < pdev->num_resources; i++) { struct resource *p, *r = &pdev->resource[i]; @@ -309,6 +330,11 @@ int platform_device_add(struct platform_device *pdev) return ret; failed: + if (pdev->id_auto) { + ida_simple_remove(&platform_devid_ida, pdev->id); + pdev->id = PLATFORM_DEVID_AUTO; + } + while (--i >= 0) { struct resource *r = &pdev->resource[i]; unsigned long type = resource_type(r); @@ -317,6 +343,7 @@ int platform_device_add(struct platform_device *pdev) release_resource(r); } + err_out: return ret; } EXPORT_SYMBOL_GPL(platform_device_add); @@ -336,6 +363,11 @@ void platform_device_del(struct platform_device *pdev) if (pdev) { device_del(&pdev->dev); + if (pdev->id_auto) { + ida_simple_remove(&platform_devid_ida, pdev->id); + pdev->id = PLATFORM_DEVID_AUTO; + } + for (i = 0; i < pdev->num_resources; i++) { struct resource *r = &pdev->resource[i]; unsigned long type = resource_type(r); -- cgit v1.2.3 From bed2b42d9f0b411f384c5619870ab0fea5dd116b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 6 Aug 2012 01:45:11 +0200 Subject: PM / Runtime: Allow helpers to be called by early platform drivers Runtime PM helper functions, like pm_runtime_get_sync(), cannot be called by early platform device drivers, because the devices' power management locks are not initialized at that time. This is quite inconvenient, so modify early_platform_add_devices() to initialize the devices power management locks as appropriate and make sure that they won't be initialized more than once if an early platform device is going to be used as a regular one later. Signed-off-by: Rafael J. Wysocki --- drivers/base/platform.c | 2 ++ drivers/base/power/power.h | 18 ++++++++++++++++-- include/linux/pm.h | 1 + 3 files changed, 19 insertions(+), 2 deletions(-) (limited to 'drivers/base/platform.c') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index a1a722502587..d51514b79efe 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -22,6 +22,7 @@ #include #include "base.h" +#include "power/power.h" #define to_platform_driver(drv) (container_of((drv), struct platform_driver, \ driver)) @@ -948,6 +949,7 @@ void __init early_platform_add_devices(struct platform_device **devs, int num) dev = &devs[i]->dev; if (!dev->devres_head.next) { + pm_runtime_early_init(dev); INIT_LIST_HEAD(&dev->devres_head); list_add_tail(&dev->devres_head, &early_platform_device_list); diff --git a/drivers/base/power/power.h b/drivers/base/power/power.h index 8a0dcc7f98f9..0dbfdf4419af 100644 --- a/drivers/base/power/power.h +++ b/drivers/base/power/power.h @@ -2,17 +2,31 @@ static inline void device_pm_init_common(struct device *dev) { - spin_lock_init(&dev->power.lock); - dev->power.power_state = PMSG_INVALID; + if (!dev->power.early_init) { + spin_lock_init(&dev->power.lock); + dev->power.power_state = PMSG_INVALID; + dev->power.early_init = true; + } } #ifdef CONFIG_PM_RUNTIME +static inline void pm_runtime_early_init(struct device *dev) +{ + dev->power.disable_depth = 1; + device_pm_init_common(dev); +} + extern void pm_runtime_init(struct device *dev); extern void pm_runtime_remove(struct device *dev); #else /* !CONFIG_PM_RUNTIME */ +static inline void pm_runtime_early_init(struct device *dev) +{ + device_pm_init_common(dev); +} + static inline void pm_runtime_init(struct device *dev) {} static inline void pm_runtime_remove(struct device *dev) {} diff --git a/include/linux/pm.h b/include/linux/pm.h index f067e60a3832..716517af1543 100644 --- a/include/linux/pm.h +++ b/include/linux/pm.h @@ -510,6 +510,7 @@ struct dev_pm_info { bool is_prepared:1; /* Owned by the PM core */ bool is_suspended:1; /* Ditto */ bool ignore_children:1; + bool early_init:1; /* Owned by the PM core */ spinlock_t lock; #ifdef CONFIG_PM_SLEEP struct list_head entry; -- cgit v1.2.3 From 1b8cb9290671a4c025c16a51d316031f9ec04976 Mon Sep 17 00:00:00 2001 From: Peter Ujfalusi Date: Thu, 23 Aug 2012 17:10:00 +0300 Subject: driver core: Check if r->name is valid in platform_get_resource_byname() Safety check for the validity of the resource name before calling strcmp(). If the resource name is NULL do not compare it, just skip it. Signed-off-by: Peter Ujfalusi Signed-off-by: Greg Kroah-Hartman --- drivers/base/platform.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/base/platform.c') diff --git a/drivers/base/platform.c b/drivers/base/platform.c index 3f8077ce585c..ddeca142293c 100644 --- a/drivers/base/platform.c +++ b/drivers/base/platform.c @@ -103,6 +103,9 @@ struct resource *platform_get_resource_byname(struct platform_device *dev, for (i = 0; i < dev->num_resources; i++) { struct resource *r = &dev->resource[i]; + if (unlikely(!r->name)) + continue; + if (type == resource_type(r) && !strcmp(r->name, name)) return r; } -- cgit v1.2.3