aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base/platform.c
diff options
context:
space:
mode:
authorJean Delvare <khali@linux-fr.org>2012-07-27 16:14:59 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-08-16 13:32:07 -0400
commit689ae231afbac8979f96100b372a5a73458baaa9 (patch)
tree12b0478fa0fdef746f4d571c6e374aa30ab0c545 /drivers/base/platform.c
parenta525a3ddeaca69f405d98442ab3c0746e53168dc (diff)
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 <khali@linux-fr.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/base/platform.c')
-rw-r--r--drivers/base/platform.c38
1 files changed, 35 insertions, 3 deletions
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 @@
20#include <linux/err.h> 20#include <linux/err.h>
21#include <linux/slab.h> 21#include <linux/slab.h>
22#include <linux/pm_runtime.h> 22#include <linux/pm_runtime.h>
23#include <linux/idr.h>
23 24
24#include "base.h" 25#include "base.h"
25 26
27/* For automatically allocated device IDs */
28static DEFINE_IDA(platform_devid_ida);
29
26#define to_platform_driver(drv) (container_of((drv), struct platform_driver, \ 30#define to_platform_driver(drv) (container_of((drv), struct platform_driver, \
27 driver)) 31 driver))
28 32
@@ -263,7 +267,7 @@ EXPORT_SYMBOL_GPL(platform_device_add_data);
263 */ 267 */
264int platform_device_add(struct platform_device *pdev) 268int platform_device_add(struct platform_device *pdev)
265{ 269{
266 int i, ret = 0; 270 int i, ret;
267 271
268 if (!pdev) 272 if (!pdev)
269 return -EINVAL; 273 return -EINVAL;
@@ -273,10 +277,27 @@ int platform_device_add(struct platform_device *pdev)
273 277
274 pdev->dev.bus = &platform_bus_type; 278 pdev->dev.bus = &platform_bus_type;
275 279
276 if (pdev->id != -1) 280 switch (pdev->id) {
281 default:
277 dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id); 282 dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
278 else 283 break;
284 case PLATFORM_DEVID_NONE:
279 dev_set_name(&pdev->dev, "%s", pdev->name); 285 dev_set_name(&pdev->dev, "%s", pdev->name);
286 break;
287 case PLATFORM_DEVID_AUTO:
288 /*
289 * Automatically allocated device ID. We mark it as such so
290 * that we remember it must be freed, and we append a suffix
291 * to avoid namespace collision with explicit IDs.
292 */
293 ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);
294 if (ret < 0)
295 goto err_out;
296 pdev->id = ret;
297 pdev->id_auto = true;
298 dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);
299 break;
300 }
280 301
281 for (i = 0; i < pdev->num_resources; i++) { 302 for (i = 0; i < pdev->num_resources; i++) {
282 struct resource *p, *r = &pdev->resource[i]; 303 struct resource *p, *r = &pdev->resource[i];
@@ -309,6 +330,11 @@ int platform_device_add(struct platform_device *pdev)
309 return ret; 330 return ret;
310 331
311 failed: 332 failed:
333 if (pdev->id_auto) {
334 ida_simple_remove(&platform_devid_ida, pdev->id);
335 pdev->id = PLATFORM_DEVID_AUTO;
336 }
337
312 while (--i >= 0) { 338 while (--i >= 0) {
313 struct resource *r = &pdev->resource[i]; 339 struct resource *r = &pdev->resource[i];
314 unsigned long type = resource_type(r); 340 unsigned long type = resource_type(r);
@@ -317,6 +343,7 @@ int platform_device_add(struct platform_device *pdev)
317 release_resource(r); 343 release_resource(r);
318 } 344 }
319 345
346 err_out:
320 return ret; 347 return ret;
321} 348}
322EXPORT_SYMBOL_GPL(platform_device_add); 349EXPORT_SYMBOL_GPL(platform_device_add);
@@ -336,6 +363,11 @@ void platform_device_del(struct platform_device *pdev)
336 if (pdev) { 363 if (pdev) {
337 device_del(&pdev->dev); 364 device_del(&pdev->dev);
338 365
366 if (pdev->id_auto) {
367 ida_simple_remove(&platform_devid_ida, pdev->id);
368 pdev->id = PLATFORM_DEVID_AUTO;
369 }
370
339 for (i = 0; i < pdev->num_resources; i++) { 371 for (i = 0; i < pdev->num_resources; i++) {
340 struct resource *r = &pdev->resource[i]; 372 struct resource *r = &pdev->resource[i];
341 unsigned long type = resource_type(r); 373 unsigned long type = resource_type(r);