From 2798e226ad7db82725ba03da933638e981b472f7 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Thu, 17 Feb 2011 19:07:08 -0800 Subject: mfd-core: Fix up typos/vagueness in comment Signed-off-by: Andres Salomon Signed-off-by: Samuel Ortiz --- include/linux/mfd/core.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux/mfd/core.h') diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index 835996e167e1..1fd7c4467e5a 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -39,8 +39,8 @@ struct mfd_cell { size_t data_size; /* - * This resources can be specified relatively to the parent device. - * For accessing device you should use resources from device + * These resources can be specified relative to the parent device. + * For accessing hardware you should use resources from the platform dev */ int num_resources; const struct resource *resources; -- cgit v1.2.3 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 +++------ include/linux/mfd/core.h | 23 +++++++++++++++++++++-- 2 files changed, 24 insertions(+), 8 deletions(-) (limited to 'include/linux/mfd/core.h') 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; diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index 1fd7c4467e5a..aefc378f8dc9 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -33,9 +33,10 @@ struct mfd_cell { /* driver-specific data for MFD-aware "cell" drivers */ void *driver_data; - /* platform_data can be used to either pass data to "generic" - driver or as a hook to mfd_cell for the "cell" drivers */ + /* platform_data can be used to pass data to "generic" drivers */ void *platform_data; + + /* unused */ size_t data_size; /* @@ -55,6 +56,24 @@ struct mfd_cell { bool pm_runtime_no_callbacks; }; +/* + * Given a platform device that's been created by mfd_add_devices(), fetch + * the mfd_cell that created it. + */ +static inline const struct mfd_cell *mfd_get_cell(struct platform_device *pdev) +{ + return pdev->dev.platform_data; +} + +/* + * Given a platform device that's been created by mfd_add_devices(), fetch + * the .platform_data entry from the mfd_cell that created it. + */ +static inline void *mfd_get_data(struct platform_device *pdev) +{ + return mfd_get_cell(pdev)->platform_data; +} + extern int mfd_add_devices(struct device *parent, int id, const struct mfd_cell *cells, int n_devs, struct resource *mem_base, -- cgit v1.2.3 From 40e03f571b2e63827f2afb90ea9aa459612c29e3 Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Thu, 17 Feb 2011 19:07:24 -0800 Subject: mfd: Drop data_size from mfd_cell struct Now that there are no more users of this, drop it. Signed-off-by: Andres Salomon Signed-off-by: Samuel Ortiz --- include/linux/mfd/core.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'include/linux/mfd/core.h') diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index aefc378f8dc9..923ec2591eb7 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -36,9 +36,6 @@ struct mfd_cell { /* platform_data can be used to pass data to "generic" drivers */ void *platform_data; - /* unused */ - size_t data_size; - /* * These resources can be specified relative to the parent device. * For accessing hardware you should use resources from the platform dev -- cgit v1.2.3 From 65e523595a31813c0f20ffd249792c60e253438e Mon Sep 17 00:00:00 2001 From: Andres Salomon Date: Thu, 17 Feb 2011 19:07:25 -0800 Subject: mfd: Rename platform_data field of mfd_cell to mfd_data Rename the platform_data variable to imply a distinction between common platform_data driver usage (typically accessed via pdev->dev.platform_data) and the way MFD passes data down to clients (using a wrapper named mfd_get_data). All clients have already been changed to use the wrapper function, so this can be a quick single-commit change that only touches things in drivers/mfd. Signed-off-by: Andres Salomon Acked-by: Linus Walleij Signed-off-by: Samuel Ortiz --- drivers/mfd/ab3100-core.c | 2 +- drivers/mfd/ab3550-core.c | 2 +- drivers/mfd/janz-cmodio.c | 2 +- drivers/mfd/mc13xxx-core.c | 2 +- drivers/mfd/timberdale.c | 54 ++++++++++++++++++++++----------------------- drivers/mfd/twl4030-codec.c | 4 ++-- drivers/mfd/wl1273-core.c | 4 ++-- include/linux/mfd/core.h | 8 +++---- 8 files changed, 39 insertions(+), 39 deletions(-) (limited to 'include/linux/mfd/core.h') diff --git a/drivers/mfd/ab3100-core.c b/drivers/mfd/ab3100-core.c index 2dcab8643e71..a751927047ac 100644 --- a/drivers/mfd/ab3100-core.c +++ b/drivers/mfd/ab3100-core.c @@ -950,7 +950,7 @@ static int __devinit ab3100_probe(struct i2c_client *client, /* Set up and register the platform devices. */ for (i = 0; i < ARRAY_SIZE(ab3100_devs); i++) - ab3100_devs[i].platform_data = ab3100_plf_data; + ab3100_devs[i].mfd_data = ab3100_plf_data; err = mfd_add_devices(&client->dev, 0, ab3100_devs, ARRAY_SIZE(ab3100_devs), NULL, 0); diff --git a/drivers/mfd/ab3550-core.c b/drivers/mfd/ab3550-core.c index c8e555a9ee6c..c12d04285226 100644 --- a/drivers/mfd/ab3550-core.c +++ b/drivers/mfd/ab3550-core.c @@ -1321,7 +1321,7 @@ static int __init ab3550_probe(struct i2c_client *client, /* Set up and register the platform devices. */ for (i = 0; i < AB3550_NUM_DEVICES; i++) - ab3550_devs[i].platform_data = ab3550_plf_data->dev_data[i]; + ab3550_devs[i].mfd_data = ab3550_plf_data->dev_data[i]; err = mfd_add_devices(&client->dev, 0, ab3550_devs, ARRAY_SIZE(ab3550_devs), NULL, diff --git a/drivers/mfd/janz-cmodio.c b/drivers/mfd/janz-cmodio.c index 58de1e28788e..fc4191137e90 100644 --- a/drivers/mfd/janz-cmodio.c +++ b/drivers/mfd/janz-cmodio.c @@ -86,7 +86,7 @@ static int __devinit cmodio_setup_subdevice(struct cmodio_device *priv, /* Add platform data */ pdata->modno = modno; - cell->platform_data = pdata; + cell->mfd_data = pdata; /* MODULbus registers -- PCI BAR3 is big-endian MODULbus access */ res->flags = IORESOURCE_MEM; diff --git a/drivers/mfd/mc13xxx-core.c b/drivers/mfd/mc13xxx-core.c index 30807d3a6539..97a3b400ed4f 100644 --- a/drivers/mfd/mc13xxx-core.c +++ b/drivers/mfd/mc13xxx-core.c @@ -689,7 +689,7 @@ static int mc13xxx_add_subdevice_pdata(struct mc13xxx *mc13xxx, const char *name = mc13xxx_get_chipname(mc13xxx); struct mfd_cell cell = { - .platform_data = pdata, + .mfd_data = pdata, }; /* there is no asnprintf in the kernel :-( */ diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c index 6353921c1729..94c6c8afad12 100644 --- a/drivers/mfd/timberdale.c +++ b/drivers/mfd/timberdale.c @@ -384,7 +384,7 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, - .platform_data = &timb_dma_platform_data, + .mfd_data = &timb_dma_platform_data, }, { .name = "timb-uart", @@ -395,37 +395,37 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg0[] = { .name = "xiic-i2c", .num_resources = ARRAY_SIZE(timberdale_xiic_resources), .resources = timberdale_xiic_resources, - .platform_data = &timberdale_xiic_platform_data, + .mfd_data = &timberdale_xiic_platform_data, }, { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, + .mfd_data = &timberdale_gpio_platform_data, }, { .name = "timb-video", .num_resources = ARRAY_SIZE(timberdale_video_resources), .resources = timberdale_video_resources, - .platform_data = &timberdale_video_platform_data, + .mfd_data = &timberdale_video_platform_data, }, { .name = "timb-radio", .num_resources = ARRAY_SIZE(timberdale_radio_resources), .resources = timberdale_radio_resources, - .platform_data = &timberdale_radio_platform_data, + .mfd_data = &timberdale_radio_platform_data, }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), .resources = timberdale_spi_resources, - .platform_data = &timberdale_xspi_platform_data, + .mfd_data = &timberdale_xspi_platform_data, }, { .name = "ks8842", .num_resources = ARRAY_SIZE(timberdale_eth_resources), .resources = timberdale_eth_resources, - .platform_data = &timberdale_ks8842_platform_data, + .mfd_data = &timberdale_ks8842_platform_data, }, }; @@ -434,7 +434,7 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, - .platform_data = &timb_dma_platform_data, + .mfd_data = &timb_dma_platform_data, }, { .name = "timb-uart", @@ -450,13 +450,13 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { .name = "xiic-i2c", .num_resources = ARRAY_SIZE(timberdale_xiic_resources), .resources = timberdale_xiic_resources, - .platform_data = &timberdale_xiic_platform_data, + .mfd_data = &timberdale_xiic_platform_data, }, { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, + .mfd_data = &timberdale_gpio_platform_data, }, { .name = "timb-mlogicore", @@ -467,25 +467,25 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg1[] = { .name = "timb-video", .num_resources = ARRAY_SIZE(timberdale_video_resources), .resources = timberdale_video_resources, - .platform_data = &timberdale_video_platform_data, + .mfd_data = &timberdale_video_platform_data, }, { .name = "timb-radio", .num_resources = ARRAY_SIZE(timberdale_radio_resources), .resources = timberdale_radio_resources, - .platform_data = &timberdale_radio_platform_data, + .mfd_data = &timberdale_radio_platform_data, }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), .resources = timberdale_spi_resources, - .platform_data = &timberdale_xspi_platform_data, + .mfd_data = &timberdale_xspi_platform_data, }, { .name = "ks8842", .num_resources = ARRAY_SIZE(timberdale_eth_resources), .resources = timberdale_eth_resources, - .platform_data = &timberdale_ks8842_platform_data, + .mfd_data = &timberdale_ks8842_platform_data, }, }; @@ -494,7 +494,7 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, - .platform_data = &timb_dma_platform_data, + .mfd_data = &timb_dma_platform_data, }, { .name = "timb-uart", @@ -505,31 +505,31 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg2[] = { .name = "xiic-i2c", .num_resources = ARRAY_SIZE(timberdale_xiic_resources), .resources = timberdale_xiic_resources, - .platform_data = &timberdale_xiic_platform_data, + .mfd_data = &timberdale_xiic_platform_data, }, { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, + .mfd_data = &timberdale_gpio_platform_data, }, { .name = "timb-video", .num_resources = ARRAY_SIZE(timberdale_video_resources), .resources = timberdale_video_resources, - .platform_data = &timberdale_video_platform_data, + .mfd_data = &timberdale_video_platform_data, }, { .name = "timb-radio", .num_resources = ARRAY_SIZE(timberdale_radio_resources), .resources = timberdale_radio_resources, - .platform_data = &timberdale_radio_platform_data, + .mfd_data = &timberdale_radio_platform_data, }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), .resources = timberdale_spi_resources, - .platform_data = &timberdale_xspi_platform_data, + .mfd_data = &timberdale_xspi_platform_data, }, }; @@ -538,7 +538,7 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = { .name = "timb-dma", .num_resources = ARRAY_SIZE(timberdale_dma_resources), .resources = timberdale_dma_resources, - .platform_data = &timb_dma_platform_data, + .mfd_data = &timb_dma_platform_data, }, { .name = "timb-uart", @@ -549,37 +549,37 @@ static __devinitdata struct mfd_cell timberdale_cells_bar0_cfg3[] = { .name = "ocores-i2c", .num_resources = ARRAY_SIZE(timberdale_ocores_resources), .resources = timberdale_ocores_resources, - .platform_data = &timberdale_ocores_platform_data, + .mfd_data = &timberdale_ocores_platform_data, }, { .name = "timb-gpio", .num_resources = ARRAY_SIZE(timberdale_gpio_resources), .resources = timberdale_gpio_resources, - .platform_data = &timberdale_gpio_platform_data, + .mfd_data = &timberdale_gpio_platform_data, }, { .name = "timb-video", .num_resources = ARRAY_SIZE(timberdale_video_resources), .resources = timberdale_video_resources, - .platform_data = &timberdale_video_platform_data, + .mfd_data = &timberdale_video_platform_data, }, { .name = "timb-radio", .num_resources = ARRAY_SIZE(timberdale_radio_resources), .resources = timberdale_radio_resources, - .platform_data = &timberdale_radio_platform_data, + .mfd_data = &timberdale_radio_platform_data, }, { .name = "xilinx_spi", .num_resources = ARRAY_SIZE(timberdale_spi_resources), .resources = timberdale_spi_resources, - .platform_data = &timberdale_xspi_platform_data, + .mfd_data = &timberdale_xspi_platform_data, }, { .name = "ks8842", .num_resources = ARRAY_SIZE(timberdale_eth_resources), .resources = timberdale_eth_resources, - .platform_data = &timberdale_ks8842_platform_data, + .mfd_data = &timberdale_ks8842_platform_data, }, }; diff --git a/drivers/mfd/twl4030-codec.c b/drivers/mfd/twl4030-codec.c index 0f5742655b46..c02fded316c9 100644 --- a/drivers/mfd/twl4030-codec.c +++ b/drivers/mfd/twl4030-codec.c @@ -208,13 +208,13 @@ static int __devinit twl4030_codec_probe(struct platform_device *pdev) if (pdata->audio) { cell = &codec->cells[childs]; cell->name = "twl4030-codec"; - cell->platform_data = pdata->audio; + cell->mfd_data = pdata->audio; childs++; } if (pdata->vibra) { cell = &codec->cells[childs]; cell->name = "twl4030-vibra"; - cell->platform_data = pdata->vibra; + cell->mfd_data = pdata->vibra; childs++; } diff --git a/drivers/mfd/wl1273-core.c b/drivers/mfd/wl1273-core.c index b4823bf9523b..529d65ba5353 100644 --- a/drivers/mfd/wl1273-core.c +++ b/drivers/mfd/wl1273-core.c @@ -78,7 +78,7 @@ static int __devinit wl1273_core_probe(struct i2c_client *client, cell = &core->cells[children]; cell->name = "wl1273_fm_radio"; - cell->platform_data = &core; + cell->mfd_data = &core; children++; if (pdata->children & WL1273_CODEC_CHILD) { @@ -86,7 +86,7 @@ static int __devinit wl1273_core_probe(struct i2c_client *client, dev_dbg(&client->dev, "%s: Have codec.\n", __func__); cell->name = "wl1273-codec"; - cell->platform_data = &core; + cell->mfd_data = &core; children++; } diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index 923ec2591eb7..f317fe4f8366 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -33,8 +33,8 @@ struct mfd_cell { /* driver-specific data for MFD-aware "cell" drivers */ void *driver_data; - /* platform_data can be used to pass data to "generic" drivers */ - void *platform_data; + /* mfd_data can be used to pass data to client drivers */ + void *mfd_data; /* * These resources can be specified relative to the parent device. @@ -64,11 +64,11 @@ static inline const struct mfd_cell *mfd_get_cell(struct platform_device *pdev) /* * Given a platform device that's been created by mfd_add_devices(), fetch - * the .platform_data entry from the mfd_cell that created it. + * the .mfd_data entry from the mfd_cell that created it. */ static inline void *mfd_get_data(struct platform_device *pdev) { - return mfd_get_cell(pdev)->platform_data; + return mfd_get_cell(pdev)->mfd_data; } extern int mfd_add_devices(struct device *parent, int id, -- 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 - include/linux/mfd/core.h | 3 --- 2 files changed, 4 deletions(-) (limited to 'include/linux/mfd/core.h') 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) diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index f317fe4f8366..71cd1f983cce 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -30,9 +30,6 @@ struct mfd_cell { int (*suspend)(struct platform_device *dev); int (*resume)(struct platform_device *dev); - /* driver-specific data for MFD-aware "cell" drivers */ - void *driver_data; - /* mfd_data can be used to pass data to client drivers */ void *mfd_data; -- 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 +++++++++++++++++++++++++++++++++++++++++++++--- include/linux/mfd/core.h | 14 ++++++++++- 2 files changed, 73 insertions(+), 5 deletions(-) (limited to 'include/linux/mfd/core.h') 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); diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index 71cd1f983cce..22a2f5ebd9db 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -25,8 +25,11 @@ struct mfd_cell { const char *name; int id; + /* refcounting for multiple drivers to use a single cell */ + atomic_t *usage_count; int (*enable)(struct platform_device *dev); int (*disable)(struct platform_device *dev); + int (*suspend)(struct platform_device *dev); int (*resume)(struct platform_device *dev); @@ -50,6 +53,15 @@ struct mfd_cell { bool pm_runtime_no_callbacks; }; +/* + * Convenience functions for clients using shared cells. Refcounting + * happens automatically, with the cell's enable/disable callbacks + * being called only when a device is first being enabled or no other + * clients are making use of it. + */ +extern int mfd_shared_cell_enable(struct platform_device *pdev); +extern int mfd_shared_cell_disable(struct platform_device *pdev); + /* * Given a platform device that's been created by mfd_add_devices(), fetch * the mfd_cell that created it. @@ -69,7 +81,7 @@ static inline void *mfd_get_data(struct platform_device *pdev) } extern 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); -- 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 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/core.h | 9 +++++++ 2 files changed, 70 insertions(+) (limited to 'include/linux/mfd/core.h') 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"); diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index 22a2f5ebd9db..ed9970412cc2 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -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 -- 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 --- arch/x86/platform/olpc/olpc-xo1.c | 4 ++-- drivers/mfd/mfd-core.c | 8 ++++---- include/linux/mfd/core.h | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include/linux/mfd/core.h') diff --git a/arch/x86/platform/olpc/olpc-xo1.c b/arch/x86/platform/olpc/olpc-xo1.c index f4155354a7b0..99513642a0e6 100644 --- a/arch/x86/platform/olpc/olpc-xo1.c +++ b/arch/x86/platform/olpc/olpc-xo1.c @@ -63,7 +63,7 @@ static int __devinit olpc_xo1_probe(struct platform_device *pdev) if (!machine_is_olpc()) return -ENODEV; - err = mfd_shared_cell_enable(pdev); + err = mfd_cell_enable(pdev); if (err) return err; @@ -88,7 +88,7 @@ static int __devinit olpc_xo1_probe(struct platform_device *pdev) static int __devexit olpc_xo1_remove(struct platform_device *pdev) { - mfd_shared_cell_disable(pdev); + mfd_cell_disable(pdev); if (strcmp(pdev->name, "olpc-xo1-pms") == 0) pms_base = 0; 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, diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index ed9970412cc2..1408bf8eed5f 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -59,8 +59,8 @@ struct mfd_cell { * being called only when a device is first being enabled or no other * clients are making use of it. */ -extern int mfd_shared_cell_enable(struct platform_device *pdev); -extern int mfd_shared_cell_disable(struct platform_device *pdev); +extern int mfd_cell_enable(struct platform_device *pdev); +extern int mfd_cell_disable(struct platform_device *pdev); /* * Given a platform device that's been created by mfd_add_devices(), fetch -- 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 --- arch/x86/platform/olpc/olpc-xo1.c | 11 ++++---- drivers/mfd/cs5535-mfd.c | 18 +++++++++++++ drivers/mfd/mfd-core.c | 53 ++++++++------------------------------- include/linux/mfd/core.h | 27 +++++++++++++------- 4 files changed, 52 insertions(+), 57 deletions(-) (limited to 'include/linux/mfd/core.h') diff --git a/arch/x86/platform/olpc/olpc-xo1.c b/arch/x86/platform/olpc/olpc-xo1.c index 99513642a0e6..386e3a159cca 100644 --- a/arch/x86/platform/olpc/olpc-xo1.c +++ b/arch/x86/platform/olpc/olpc-xo1.c @@ -121,22 +121,21 @@ static int __init olpc_xo1_init(void) { int r; - r = mfd_shared_platform_driver_register(&cs5535_pms_drv, "cs5535-pms"); + r = platform_driver_register(&cs5535_pms_drv); if (r) return r; - r = mfd_shared_platform_driver_register(&cs5535_acpi_drv, - "cs5535-acpi"); + r = platform_driver_register(&cs5535_acpi_drv); if (r) - mfd_shared_platform_driver_unregister(&cs5535_pms_drv); + platform_driver_unregister(&cs5535_pms_drv); return r; } static void __exit olpc_xo1_exit(void) { - mfd_shared_platform_driver_unregister(&cs5535_acpi_drv); - mfd_shared_platform_driver_unregister(&cs5535_pms_drv); + platform_driver_unregister(&cs5535_acpi_drv); + platform_driver_unregister(&cs5535_pms_drv); } MODULE_AUTHOR("Daniel Drake "); diff --git a/drivers/mfd/cs5535-mfd.c b/drivers/mfd/cs5535-mfd.c index 886a06871065..24959ddd9324 100644 --- a/drivers/mfd/cs5535-mfd.c +++ b/drivers/mfd/cs5535-mfd.c @@ -27,6 +27,7 @@ #include #include #include +#include #define DRV_NAME "cs5535-mfd" @@ -111,6 +112,22 @@ static __devinitdata struct mfd_cell cs5535_mfd_cells[] = { }, }; +#ifdef CONFIG_OLPC +static void __devinit cs5535_clone_olpc_cells(void) +{ + const char *acpi_clones[] = { "olpc-xo1-acpi" }; + const char *pms_clones[] = { "olpc-xo1-pms" }; + + if (!machine_is_olpc()) + return; + + mfd_clone_cell("cs5535-acpi", acpi_clones, ARRAY_SIZE(acpi_clones)); + mfd_clone_cell("cs5535-pms", pms_clones, ARRAY_SIZE(pms_clones)); +} +#else +static void cs5535_clone_olpc_cells(void) { } +#endif + static int __devinit cs5535_mfd_probe(struct pci_dev *pdev, const struct pci_device_id *id) { @@ -139,6 +156,7 @@ static int __devinit cs5535_mfd_probe(struct pci_dev *pdev, dev_err(&pdev->dev, "MFD add devices failed: %d\n", err); goto err_disable; } + cs5535_clone_olpc_cells(); dev_info(&pdev->dev, "%zu devices registered.\n", ARRAY_SIZE(cs5535_mfd_cells)); 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"); diff --git a/include/linux/mfd/core.h b/include/linux/mfd/core.h index 1408bf8eed5f..ad1b19aa6508 100644 --- a/include/linux/mfd/core.h +++ b/include/linux/mfd/core.h @@ -62,6 +62,24 @@ struct mfd_cell { extern int mfd_cell_enable(struct platform_device *pdev); extern int mfd_cell_disable(struct platform_device *pdev); +/* + * "Clone" multiple platform devices for a single cell. This is to be used + * for devices that have multiple users of a cell. For example, if an mfd + * driver wants the cell "foo" to be used by a GPIO driver, an MTD driver, + * and a platform driver, the following bit of code would be use after first + * calling mfd_add_devices(): + * + * const char *fclones[] = { "foo-gpio", "foo-mtd" }; + * err = mfd_clone_cells("foo", fclones, ARRAY_SIZE(fclones)); + * + * Each driver (MTD, GPIO, and platform driver) would then register + * platform_drivers for "foo-mtd", "foo-gpio", and "foo", respectively. + * The cell's .enable/.disable hooks should be used to deal with hardware + * resource contention. + */ +extern int mfd_clone_cell(const char *cell, const char **clones, + size_t n_clones); + /* * Given a platform device that's been created by mfd_add_devices(), fetch * the mfd_cell that created it. @@ -87,13 +105,4 @@ 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 -- cgit v1.2.3