]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/base/platform.c
Merge tag 'pm-for-3.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael...
[karo-tx-linux.git] / drivers / base / platform.c
index d51514b79efedc4ad446e5ce6dd87a8ee9d2fb6d..8727e9c5eea47dd78170e091635f31d29fc7cb52 100644 (file)
 #include <linux/err.h>
 #include <linux/slab.h>
 #include <linux/pm_runtime.h>
+#include <linux/idr.h>
 
 #include "base.h"
 #include "power/power.h"
 
+/* For automatically allocated device IDs */
+static DEFINE_IDA(platform_devid_ida);
+
 #define to_platform_driver(drv)        (container_of((drv), struct platform_driver, \
                                 driver))
 
@@ -100,6 +104,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;
        }
@@ -264,7 +271,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;
@@ -274,10 +281,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];
@@ -310,6 +334,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);
@@ -318,6 +347,7 @@ int platform_device_add(struct platform_device *pdev)
                        release_resource(r);
        }
 
+ err_out:
        return ret;
 }
 EXPORT_SYMBOL_GPL(platform_device_add);
@@ -337,6 +367,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);