|
@@ -20,9 +20,13 @@
|
|
|
#include <linux/err.h>
|
|
|
#include <linux/slab.h>
|
|
|
#include <linux/pm_runtime.h>
|
|
|
+#include <linux/idr.h>
|
|
|
|
|
|
#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);
|