From fe891a008f3310be47786e87c158edebdb71e265 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Thu, 17 Feb 2011 19:07:09 -0800 Subject: mfd-core: Unconditionally add mfd_cell to every platform_device Previously, one would set the mfd_cell's platform_data/data_size to point to the current mfd_cell in order to pass that information along to drivers. This causes the current mfd_cell to always be available to drivers. It also adds a wrapper function for fetching the mfd cell from a platform device, similar to what originally existed for mfd devices. Drivers who previously used platform_data for other purposes can still use it; the difference is that mfd_get_data() must be used to access it (and the pdata structure is no longer allocated in mfd_add_devices). Note that mfd_get_data is intentionally vague (in name) about where the data is stored; variable name changes can come later without having to touch brazillions of drivers. Signed-off-by: Andres Salomon Signed-off-by: Samuel Ortiz --- drivers/mfd/mfd-core.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'drivers/mfd/mfd-core.c') diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index d83ad0f141af..21a39dc64ea0 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -39,12 +39,9 @@ static int mfd_add_device(struct device *parent, int id, pdev->dev.parent = parent; platform_set_drvdata(pdev, cell->driver_data); - if (cell->data_size) { - ret = platform_device_add_data(pdev, - cell->platform_data, cell->data_size); - if (ret) - goto fail_res; - } + ret = platform_device_add_data(pdev, cell, sizeof(*cell)); + if (ret) + goto fail_res; for (r = 0; r < cell->num_resources; r++) { res[r].name = cell->resources[r].name; -- cgit v1.2.3 From dcb50e83bb86d66d3554ba9c365488669c84d037 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Thu, 17 Feb 2011 19:07:33 -0800 Subject: mfd: Remove driver_data field from mfd_cell All users of this have now been switched over to using mfd_data; it can go away now. Signed-off-by: Andres Salomon Signed-off-by: Samuel Ortiz --- drivers/mfd/mfd-core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/mfd/mfd-core.c') diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 21a39dc64ea0..01115f686dfa 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -37,7 +37,6 @@ static int mfd_add_device(struct device *parent, int id, goto fail_device; pdev->dev.parent = parent; - platform_set_drvdata(pdev, cell->driver_data); ret = platform_device_add_data(pdev, cell, sizeof(*cell)); if (ret) -- cgit v1.2.3 From 1e29af62f2b285bd18685da93c3ce8c33ca2d1db Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Thu, 17 Feb 2011 19:07:34 -0800 Subject: mfd: Add refcounting support to mfd_cells This provides convenience functions for sharing of cells across multiple mfd clients. Mfd drivers can provide enable/disable hooks to actually tweak the hardware, and clients can call mfd_shared_cell_{en,dis}able without having to worry about whether or not another client happens to have enabled or disabled the cell/hardware. Note that this is purely optional; drivers can continue to use the mfd_cell's enable/disable hooks for their own purposes, if desired. Signed-off-by: Andres Salomon Signed-off-by: Samuel Ortiz --- drivers/mfd/mfd-core.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 60 insertions(+), 4 deletions(-) (limited to 'drivers/mfd/mfd-core.c') diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 01115f686dfa..ca789f882305 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -18,6 +18,43 @@ #include #include +int mfd_shared_cell_enable(struct platform_device *pdev) +{ + const struct mfd_cell *cell = mfd_get_cell(pdev); + int err = 0; + + /* only call enable hook if the cell wasn't previously enabled */ + if (atomic_inc_return(cell->usage_count) == 1) + err = cell->enable(pdev); + + /* if the enable hook failed, decrement counter to allow retries */ + if (err) + atomic_dec(cell->usage_count); + + return err; +} +EXPORT_SYMBOL(mfd_shared_cell_enable); + +int mfd_shared_cell_disable(struct platform_device *pdev) +{ + const struct mfd_cell *cell = mfd_get_cell(pdev); + int err = 0; + + /* only disable if no other clients are using it */ + if (atomic_dec_return(cell->usage_count) == 0) + err = cell->disable(pdev); + + /* if the disable hook failed, increment to allow retries */ + if (err) + atomic_inc(cell->usage_count); + + /* sanity check; did someone call disable too many times? */ + WARN_ON(atomic_read(cell->usage_count) < 0); + + return err; +} +EXPORT_SYMBOL(mfd_shared_cell_disable); + static int mfd_add_device(struct device *parent, int id, const struct mfd_cell *cell, struct resource *mem_base, @@ -96,14 +133,22 @@ fail_alloc: } int mfd_add_devices(struct device *parent, int id, - const struct mfd_cell *cells, int n_devs, + struct mfd_cell *cells, int n_devs, struct resource *mem_base, int irq_base) { int i; int ret = 0; + atomic_t *cnts; + + /* initialize reference counting for all cells */ + cnts = kcalloc(sizeof(*cnts), n_devs, GFP_KERNEL); + if (!cnts) + return -ENOMEM; for (i = 0; i < n_devs; i++) { + atomic_set(&cnts[i], 0); + cells[i].usage_count = &cnts[i]; ret = mfd_add_device(parent, id, cells + i, mem_base, irq_base); if (ret) break; @@ -116,15 +161,26 @@ int mfd_add_devices(struct device *parent, int id, } EXPORT_SYMBOL(mfd_add_devices); -static int mfd_remove_devices_fn(struct device *dev, void *unused) +static int mfd_remove_devices_fn(struct device *dev, void *c) { - platform_device_unregister(to_platform_device(dev)); + struct platform_device *pdev = to_platform_device(dev); + const struct mfd_cell *cell = mfd_get_cell(pdev); + atomic_t **usage_count = c; + + /* find the base address of usage_count pointers (for freeing) */ + if (!*usage_count || (cell->usage_count < *usage_count)) + *usage_count = cell->usage_count; + + platform_device_unregister(pdev); return 0; } void mfd_remove_devices(struct device *parent) { - device_for_each_child(parent, NULL, mfd_remove_devices_fn); + atomic_t *cnts = NULL; + + device_for_each_child(parent, &cnts, mfd_remove_devices_fn); + kfree(cnts); } EXPORT_SYMBOL(mfd_remove_devices); -- cgit v1.2.3 From a9bbba996302344b1fac7773cf8198f6fee35ac1 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Thu, 17 Feb 2011 19:07:35 -0800 Subject: mfd: add platform_device sharing support for mfd 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 Signed-off-by: Samuel Ortiz --- drivers/mfd/mfd-core.c | 61 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) (limited to 'drivers/mfd/mfd-core.c') diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index ca789f882305..bb2264cc410b 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -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"); -- cgit v1.2.3 From f77289ac25b0c81acbed6f9c17cb14809a04e18b Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Thu, 3 Mar 2011 09:51:58 -0800 Subject: mfd: Rename mfd_shared_cell_{en,dis}able to drop the "shared" part As requested by Samuel, there's not really any reason to have "shared" in the name. This also modifies the only user of the function, as well. Signed-off-by: Andres Salomon Signed-off-by: Samuel Ortiz --- drivers/mfd/mfd-core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/mfd/mfd-core.c') diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index bb2264cc410b..79eda0264fb2 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -18,7 +18,7 @@ #include #include -int mfd_shared_cell_enable(struct platform_device *pdev) +int mfd_cell_enable(struct platform_device *pdev) { const struct mfd_cell *cell = mfd_get_cell(pdev); int err = 0; @@ -33,9 +33,9 @@ int mfd_shared_cell_enable(struct platform_device *pdev) return err; } -EXPORT_SYMBOL(mfd_shared_cell_enable); +EXPORT_SYMBOL(mfd_cell_enable); -int mfd_shared_cell_disable(struct platform_device *pdev) +int mfd_cell_disable(struct platform_device *pdev) { const struct mfd_cell *cell = mfd_get_cell(pdev); int err = 0; @@ -53,7 +53,7 @@ int mfd_shared_cell_disable(struct platform_device *pdev) return err; } -EXPORT_SYMBOL(mfd_shared_cell_disable); +EXPORT_SYMBOL(mfd_cell_disable); static int mfd_add_device(struct device *parent, int id, const struct mfd_cell *cell, -- cgit v1.2.3 From fa1df691688f34cbcd5bf77bd084bbe47e9d6bfe Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Mon, 21 Mar 2011 19:19:35 -0700 Subject: mfd: Add mfd_clone_cell(), convert cs5535-mfd/olpc-xo1 to it Replace mfd_shared_platform_driver_register with mfd_clone_cell. The former was called by an mfd client, and registered both a platform driver and device. The latter is called by an mfd driver, and registers only a platform device. The downside of this is that mfd drivers need to be modified whenever new clients are added that share a cell; the upside is that it fits Linux's driver model better. It's also simpler. This also converts cs5535-mfd/olpc-xo1 from the old API. cs5535-mfd now creates the olpc-xo1-{acpi,pms} devices, while olpc-xo1 binds to them via platform drivers. Signed-off-by: Andres Salomon Signed-off-by: Samuel Ortiz --- drivers/mfd/mfd-core.c | 53 +++++++++++--------------------------------------- 1 file changed, 11 insertions(+), 42 deletions(-) (limited to 'drivers/mfd/mfd-core.c') diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c index 79eda0264fb2..d01574d98870 100644 --- a/drivers/mfd/mfd-core.c +++ b/drivers/mfd/mfd-core.c @@ -184,16 +184,12 @@ void mfd_remove_devices(struct device *parent) } EXPORT_SYMBOL(mfd_remove_devices); -static int add_shared_platform_device(const char *cell, const char *name) +int mfd_clone_cell(const char *cell, const char **clones, size_t n_clones) { 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; + int i; /* fetch the parent cell's device (should already be registered!) */ dev = bus_find_device_by_name(&platform_bus_type, NULL, cell); @@ -206,44 +202,17 @@ static int add_shared_platform_device(const char *cell, const char *name) 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)); + for (i = 0; i < n_clones; i++) { + cell_entry.name = clones[i]; + /* don't give up if a single call fails; just report error */ + if (mfd_add_device(pdev->dev.parent, -1, &cell_entry, NULL, 0)) + dev_err(dev, "failed to create platform device '%s'\n", + clones[i]); + } - platform_driver_unregister(drv); + return 0; } -EXPORT_SYMBOL(mfd_shared_platform_driver_unregister); +EXPORT_SYMBOL(mfd_clone_cell); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov"); -- cgit v1.2.3