summaryrefslogtreecommitdiffstats
path: root/drivers/base/platform.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/base/platform.c')
-rw-r--r--drivers/base/platform.c251
1 files changed, 209 insertions, 42 deletions
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 361e204209e..8827dafba94 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -10,12 +10,17 @@
* information.
*/
-#include <linux/device.h>
+#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/dma-mapping.h>
#include <linux/bootmem.h>
#include <linux/err.h>
+#include <linux/slab.h>
+
+#include "base.h"
+
+#define to_platform_driver(drv) (container_of((drv), struct platform_driver, driver))
struct device platform_bus = {
.bus_id = "platform",
@@ -113,12 +118,115 @@ int platform_add_devices(struct platform_device **devs, int num)
return ret;
}
+struct platform_object {
+ struct platform_device pdev;
+ char name[1];
+};
+
/**
- * platform_device_register - add a platform-level device
+ * platform_device_put
+ * @pdev: platform device to free
+ *
+ * Free all memory associated with a platform device. This function
+ * must _only_ be externally called in error cases. All other usage
+ * is a bug.
+ */
+void platform_device_put(struct platform_device *pdev)
+{
+ if (pdev)
+ put_device(&pdev->dev);
+}
+EXPORT_SYMBOL_GPL(platform_device_put);
+
+static void platform_device_release(struct device *dev)
+{
+ struct platform_object *pa = container_of(dev, struct platform_object, pdev.dev);
+
+ kfree(pa->pdev.dev.platform_data);
+ kfree(pa->pdev.resource);
+ kfree(pa);
+}
+
+/**
+ * platform_device_alloc
+ * @name: base name of the device we're adding
+ * @id: instance id
+ *
+ * Create a platform device object which can have other objects attached
+ * to it, and which will have attached objects freed when it is released.
+ */
+struct platform_device *platform_device_alloc(const char *name, unsigned int id)
+{
+ struct platform_object *pa;
+
+ pa = kzalloc(sizeof(struct platform_object) + strlen(name), GFP_KERNEL);
+ if (pa) {
+ strcpy(pa->name, name);
+ pa->pdev.name = pa->name;
+ pa->pdev.id = id;
+ device_initialize(&pa->pdev.dev);
+ pa->pdev.dev.release = platform_device_release;
+ }
+
+ return pa ? &pa->pdev : NULL;
+}
+EXPORT_SYMBOL_GPL(platform_device_alloc);
+
+/**
+ * platform_device_add_resources
+ * @pdev: platform device allocated by platform_device_alloc to add resources to
+ * @res: set of resources that needs to be allocated for the device
+ * @num: number of resources
+ *
+ * Add a copy of the resources to the platform device. The memory
+ * associated with the resources will be freed when the platform
+ * device is released.
+ */
+int platform_device_add_resources(struct platform_device *pdev, struct resource *res, unsigned int num)
+{
+ struct resource *r;
+
+ r = kmalloc(sizeof(struct resource) * num, GFP_KERNEL);
+ if (r) {
+ memcpy(r, res, sizeof(struct resource) * num);
+ pdev->resource = r;
+ pdev->num_resources = num;
+ }
+ return r ? 0 : -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(platform_device_add_resources);
+
+/**
+ * platform_device_add_data
+ * @pdev: platform device allocated by platform_device_alloc to add resources to
+ * @data: platform specific data for this platform device
+ * @size: size of platform specific data
+ *
+ * Add a copy of platform specific data to the platform device's platform_data
+ * pointer. The memory associated with the platform data will be freed
+ * when the platform device is released.
+ */
+int platform_device_add_data(struct platform_device *pdev, void *data, size_t size)
+{
+ void *d;
+
+ d = kmalloc(size, GFP_KERNEL);
+ if (d) {
+ memcpy(d, data, size);
+ pdev->dev.platform_data = d;
+ }
+ return d ? 0 : -ENOMEM;
+}
+EXPORT_SYMBOL_GPL(platform_device_add_data);
+
+/**
+ * platform_device_add - add a platform device to device hierarchy
* @pdev: platform device we're adding
*
+ * This is part 2 of platform_device_register(), though may be called
+ * separately _iff_ pdev was allocated by platform_device_alloc().
*/
-int platform_device_register(struct platform_device * pdev)
+int platform_device_add(struct platform_device *pdev)
{
int i, ret = 0;
@@ -171,6 +279,18 @@ int platform_device_register(struct platform_device * pdev)
release_resource(&pdev->resource[i]);
return ret;
}
+EXPORT_SYMBOL_GPL(platform_device_add);
+
+/**
+ * platform_device_register - add a platform-level device
+ * @pdev: platform device we're adding
+ *
+ */
+int platform_device_register(struct platform_device * pdev)
+{
+ device_initialize(&pdev->dev);
+ return platform_device_add(pdev);
+}
/**
* platform_device_unregister - remove a platform-level device
@@ -194,18 +314,6 @@ void platform_device_unregister(struct platform_device * pdev)
}
}
-struct platform_object {
- struct platform_device pdev;
- struct resource resources[0];
-};
-
-static void platform_device_release_simple(struct device *dev)
-{
- struct platform_device *pdev = to_platform_device(dev);
-
- kfree(container_of(pdev, struct platform_object, pdev));
-}
-
/**
* platform_device_register_simple
* @name: base name of the device we're adding
@@ -222,36 +330,103 @@ static void platform_device_release_simple(struct device *dev)
struct platform_device *platform_device_register_simple(char *name, unsigned int id,
struct resource *res, unsigned int num)
{
- struct platform_object *pobj;
+ struct platform_device *pdev;
int retval;
- pobj = kzalloc(sizeof(*pobj) + sizeof(struct resource) * num, GFP_KERNEL);
- if (!pobj) {
+ pdev = platform_device_alloc(name, id);
+ if (!pdev) {
retval = -ENOMEM;
goto error;
}
- pobj->pdev.name = name;
- pobj->pdev.id = id;
- pobj->pdev.dev.release = platform_device_release_simple;
-
if (num) {
- memcpy(pobj->resources, res, sizeof(struct resource) * num);
- pobj->pdev.resource = pobj->resources;
- pobj->pdev.num_resources = num;
+ retval = platform_device_add_resources(pdev, res, num);
+ if (retval)
+ goto error;
}
- retval = platform_device_register(&pobj->pdev);
+ retval = platform_device_add(pdev);
if (retval)
goto error;
- return &pobj->pdev;
+ return pdev;
error:
- kfree(pobj);
+ platform_device_put(pdev);
return ERR_PTR(retval);
}
+static int platform_drv_probe(struct device *_dev)
+{
+ struct platform_driver *drv = to_platform_driver(_dev->driver);
+ struct platform_device *dev = to_platform_device(_dev);
+
+ return drv->probe(dev);
+}
+
+static int platform_drv_remove(struct device *_dev)
+{
+ struct platform_driver *drv = to_platform_driver(_dev->driver);
+ struct platform_device *dev = to_platform_device(_dev);
+
+ return drv->remove(dev);
+}
+
+static void platform_drv_shutdown(struct device *_dev)
+{
+ struct platform_driver *drv = to_platform_driver(_dev->driver);
+ struct platform_device *dev = to_platform_device(_dev);
+
+ drv->shutdown(dev);
+}
+
+static int platform_drv_suspend(struct device *_dev, pm_message_t state)
+{
+ struct platform_driver *drv = to_platform_driver(_dev->driver);
+ struct platform_device *dev = to_platform_device(_dev);
+
+ return drv->suspend(dev, state);
+}
+
+static int platform_drv_resume(struct device *_dev)
+{
+ struct platform_driver *drv = to_platform_driver(_dev->driver);
+ struct platform_device *dev = to_platform_device(_dev);
+
+ return drv->resume(dev);
+}
+
+/**
+ * platform_driver_register
+ * @drv: platform driver structure
+ */
+int platform_driver_register(struct platform_driver *drv)
+{
+ drv->driver.bus = &platform_bus_type;
+ if (drv->probe)
+ drv->driver.probe = platform_drv_probe;
+ if (drv->remove)
+ drv->driver.remove = platform_drv_remove;
+ if (drv->shutdown)
+ drv->driver.shutdown = platform_drv_shutdown;
+ if (drv->suspend)
+ drv->driver.suspend = platform_drv_suspend;
+ if (drv->resume)
+ drv->driver.resume = platform_drv_resume;
+ return driver_register(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(platform_driver_register);
+
+/**
+ * platform_driver_unregister
+ * @drv: platform driver structure
+ */
+void platform_driver_unregister(struct platform_driver *drv)
+{
+ driver_unregister(&drv->driver);
+}
+EXPORT_SYMBOL_GPL(platform_driver_unregister);
+
/**
* platform_match - bind platform device to platform driver.
@@ -279,13 +454,9 @@ static int platform_suspend(struct device * dev, pm_message_t state)
{
int ret = 0;
- if (dev->driver && dev->driver->suspend) {
- ret = dev->driver->suspend(dev, state, SUSPEND_DISABLE);
- if (ret == 0)
- ret = dev->driver->suspend(dev, state, SUSPEND_SAVE_STATE);
- if (ret == 0)
- ret = dev->driver->suspend(dev, state, SUSPEND_POWER_DOWN);
- }
+ if (dev->driver && dev->driver->suspend)
+ ret = dev->driver->suspend(dev, state);
+
return ret;
}
@@ -293,13 +464,9 @@ static int platform_resume(struct device * dev)
{
int ret = 0;
- if (dev->driver && dev->driver->resume) {
- ret = dev->driver->resume(dev, RESUME_POWER_ON);
- if (ret == 0)
- ret = dev->driver->resume(dev, RESUME_RESTORE_STATE);
- if (ret == 0)
- ret = dev->driver->resume(dev, RESUME_ENABLE);
- }
+ if (dev->driver && dev->driver->resume)
+ ret = dev->driver->resume(dev);
+
return ret;
}