mirror of
https://github.com/torvalds/linux.git
synced 2024-11-16 17:12:06 +00:00
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>
This commit is contained in:
parent
a525a3ddea
commit
689ae231af
@ -20,9 +20,13 @@
|
|||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/pm_runtime.h>
|
#include <linux/pm_runtime.h>
|
||||||
|
#include <linux/idr.h>
|
||||||
|
|
||||||
#include "base.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, \
|
#define to_platform_driver(drv) (container_of((drv), struct platform_driver, \
|
||||||
driver))
|
driver))
|
||||||
|
|
||||||
@ -263,7 +267,7 @@ EXPORT_SYMBOL_GPL(platform_device_add_data);
|
|||||||
*/
|
*/
|
||||||
int platform_device_add(struct platform_device *pdev)
|
int platform_device_add(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int i, ret = 0;
|
int i, ret;
|
||||||
|
|
||||||
if (!pdev)
|
if (!pdev)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@ -273,10 +277,27 @@ int platform_device_add(struct platform_device *pdev)
|
|||||||
|
|
||||||
pdev->dev.bus = &platform_bus_type;
|
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);
|
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);
|
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++) {
|
for (i = 0; i < pdev->num_resources; i++) {
|
||||||
struct resource *p, *r = &pdev->resource[i];
|
struct resource *p, *r = &pdev->resource[i];
|
||||||
@ -309,6 +330,11 @@ int platform_device_add(struct platform_device *pdev)
|
|||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
failed:
|
failed:
|
||||||
|
if (pdev->id_auto) {
|
||||||
|
ida_simple_remove(&platform_devid_ida, pdev->id);
|
||||||
|
pdev->id = PLATFORM_DEVID_AUTO;
|
||||||
|
}
|
||||||
|
|
||||||
while (--i >= 0) {
|
while (--i >= 0) {
|
||||||
struct resource *r = &pdev->resource[i];
|
struct resource *r = &pdev->resource[i];
|
||||||
unsigned long type = resource_type(r);
|
unsigned long type = resource_type(r);
|
||||||
@ -317,6 +343,7 @@ int platform_device_add(struct platform_device *pdev)
|
|||||||
release_resource(r);
|
release_resource(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err_out:
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(platform_device_add);
|
EXPORT_SYMBOL_GPL(platform_device_add);
|
||||||
@ -336,6 +363,11 @@ void platform_device_del(struct platform_device *pdev)
|
|||||||
if (pdev) {
|
if (pdev) {
|
||||||
device_del(&pdev->dev);
|
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++) {
|
for (i = 0; i < pdev->num_resources; i++) {
|
||||||
struct resource *r = &pdev->resource[i];
|
struct resource *r = &pdev->resource[i];
|
||||||
unsigned long type = resource_type(r);
|
unsigned long type = resource_type(r);
|
||||||
|
@ -14,11 +14,15 @@
|
|||||||
#include <linux/device.h>
|
#include <linux/device.h>
|
||||||
#include <linux/mod_devicetable.h>
|
#include <linux/mod_devicetable.h>
|
||||||
|
|
||||||
|
#define PLATFORM_DEVID_NONE (-1)
|
||||||
|
#define PLATFORM_DEVID_AUTO (-2)
|
||||||
|
|
||||||
struct mfd_cell;
|
struct mfd_cell;
|
||||||
|
|
||||||
struct platform_device {
|
struct platform_device {
|
||||||
const char * name;
|
const char * name;
|
||||||
int id;
|
int id;
|
||||||
|
bool id_auto;
|
||||||
struct device dev;
|
struct device dev;
|
||||||
u32 num_resources;
|
u32 num_resources;
|
||||||
struct resource * resource;
|
struct resource * resource;
|
||||||
|
Loading…
Reference in New Issue
Block a user