mfd: add platform_device sharing support for mfd
authorAndres Salomon <dilinger@queued.net>
Fri, 18 Feb 2011 03:07:35 +0000 (19:07 -0800)
committerSamuel Ortiz <sameo@linux.intel.com>
Wed, 23 Mar 2011 09:41:58 +0000 (10:41 +0100)
This adds functions to enable platform_device sharing for mfd clients.

Each platform driver (mfd client) that wants to share an mfd_cell's
platform_device uses the mfd_shared_platform_driver_{un,}register()
functions instead of platform_driver_{un,}register().  Along with
registering the platform driver, these also register a new platform
device with the same characteristics as the original cell, but a different
name.  Given an mfd_cell with the name "foo", drivers that want to
share access to its resources can call mfd_shared_platform_driver_register
with platform drivers named (for example) "bar" and "baz".  This
will register two platform devices and drivers named "bar" and "baz"
that share the same cell as the platform device "foo".  The drivers
can then call "foo" cell's enable hooks (or mfd_shared_cell_enable)
to enable resources, and obtain platform resources as they normally
would.

This deals with platform handling only; mfd driver-specific details,
hardware handling, refcounting, etc are all dealt with separately.

Signed-off-by: Andres Salomon <dilinger@queued.net>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
drivers/mfd/mfd-core.c
include/linux/mfd/core.h

index ca789f882305fb8a712a258c783a3e38ec89f0cf..bb2264cc410bd7d4cbfa7f0ce6ba7d13b3ee8aee 100644 (file)
@@ -184,5 +184,66 @@ void mfd_remove_devices(struct device *parent)
 }
 EXPORT_SYMBOL(mfd_remove_devices);
 
+static int add_shared_platform_device(const char *cell, const char *name)
+{
+       struct mfd_cell cell_entry;
+       struct device *dev;
+       struct platform_device *pdev;
+       int err;
+
+       /* check if we've already registered a device (don't fail if we have) */
+       if (bus_find_device_by_name(&platform_bus_type, NULL, name))
+               return 0;
+
+       /* fetch the parent cell's device (should already be registered!) */
+       dev = bus_find_device_by_name(&platform_bus_type, NULL, cell);
+       if (!dev) {
+               printk(KERN_ERR "failed to find device for cell %s\n", cell);
+               return -ENODEV;
+       }
+       pdev = to_platform_device(dev);
+       memcpy(&cell_entry, mfd_get_cell(pdev), sizeof(cell_entry));
+
+       WARN_ON(!cell_entry.enable);
+
+       cell_entry.name = name;
+       err = mfd_add_device(pdev->dev.parent, -1, &cell_entry, NULL, 0);
+       if (err)
+               dev_err(dev, "MFD add devices failed: %d\n", err);
+       return err;
+}
+
+int mfd_shared_platform_driver_register(struct platform_driver *drv,
+               const char *cellname)
+{
+       int err;
+
+       err = add_shared_platform_device(cellname, drv->driver.name);
+       if (err)
+               printk(KERN_ERR "failed to add platform device %s\n",
+                               drv->driver.name);
+
+       err = platform_driver_register(drv);
+       if (err)
+               printk(KERN_ERR "failed to add platform driver %s\n",
+                               drv->driver.name);
+
+       return err;
+}
+EXPORT_SYMBOL(mfd_shared_platform_driver_register);
+
+void mfd_shared_platform_driver_unregister(struct platform_driver *drv)
+{
+       struct device *dev;
+
+       dev = bus_find_device_by_name(&platform_bus_type, NULL,
+                       drv->driver.name);
+       if (dev)
+               platform_device_unregister(to_platform_device(dev));
+
+       platform_driver_unregister(drv);
+}
+EXPORT_SYMBOL(mfd_shared_platform_driver_unregister);
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov");
index 22a2f5ebd9db398c9cda4e368f2632677185ecf6..ed9970412cc2a77f6436987a62ef7cd60bc35976 100644 (file)
@@ -87,4 +87,13 @@ extern int mfd_add_devices(struct device *parent, int id,
 
 extern void mfd_remove_devices(struct device *parent);
 
+/*
+ * For MFD drivers with clients sharing access to resources, these create
+ * multiple platform devices per cell.  Contention handling must still be
+ * handled via drivers (ie, with enable/disable hooks).
+ */
+extern int mfd_shared_platform_driver_register(struct platform_driver *drv,
+               const char *cellname);
+extern void mfd_shared_platform_driver_unregister(struct platform_driver *drv);
+
 #endif