aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd/mfd-core.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mfd/mfd-core.c')
-rw-r--r--drivers/mfd/mfd-core.c135
1 files changed, 124 insertions, 11 deletions
diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c
index d83ad0f141a..79eda0264fb 100644
--- a/drivers/mfd/mfd-core.c
+++ b/drivers/mfd/mfd-core.c
@@ -18,6 +18,43 @@
18#include <linux/pm_runtime.h> 18#include <linux/pm_runtime.h>
19#include <linux/slab.h> 19#include <linux/slab.h>
20 20
21int mfd_cell_enable(struct platform_device *pdev)
22{
23 const struct mfd_cell *cell = mfd_get_cell(pdev);
24 int err = 0;
25
26 /* only call enable hook if the cell wasn't previously enabled */
27 if (atomic_inc_return(cell->usage_count) == 1)
28 err = cell->enable(pdev);
29
30 /* if the enable hook failed, decrement counter to allow retries */
31 if (err)
32 atomic_dec(cell->usage_count);
33
34 return err;
35}
36EXPORT_SYMBOL(mfd_cell_enable);
37
38int mfd_cell_disable(struct platform_device *pdev)
39{
40 const struct mfd_cell *cell = mfd_get_cell(pdev);
41 int err = 0;
42
43 /* only disable if no other clients are using it */
44 if (atomic_dec_return(cell->usage_count) == 0)
45 err = cell->disable(pdev);
46
47 /* if the disable hook failed, increment to allow retries */
48 if (err)
49 atomic_inc(cell->usage_count);
50
51 /* sanity check; did someone call disable too many times? */
52 WARN_ON(atomic_read(cell->usage_count) < 0);
53
54 return err;
55}
56EXPORT_SYMBOL(mfd_cell_disable);
57
21static int mfd_add_device(struct device *parent, int id, 58static int mfd_add_device(struct device *parent, int id,
22 const struct mfd_cell *cell, 59 const struct mfd_cell *cell,
23 struct resource *mem_base, 60 struct resource *mem_base,
@@ -37,14 +74,10 @@ static int mfd_add_device(struct device *parent, int id,
37 goto fail_device; 74 goto fail_device;
38 75
39 pdev->dev.parent = parent; 76 pdev->dev.parent = parent;
40 platform_set_drvdata(pdev, cell->driver_data);
41 77
42 if (cell->data_size) { 78 ret = platform_device_add_data(pdev, cell, sizeof(*cell));
43 ret = platform_device_add_data(pdev, 79 if (ret)
44 cell->platform_data, cell->data_size); 80 goto fail_res;
45 if (ret)
46 goto fail_res;
47 }
48 81
49 for (r = 0; r < cell->num_resources; r++) { 82 for (r = 0; r < cell->num_resources; r++) {
50 res[r].name = cell->resources[r].name; 83 res[r].name = cell->resources[r].name;
@@ -100,14 +133,22 @@ fail_alloc:
100} 133}
101 134
102int mfd_add_devices(struct device *parent, int id, 135int mfd_add_devices(struct device *parent, int id,
103 const struct mfd_cell *cells, int n_devs, 136 struct mfd_cell *cells, int n_devs,
104 struct resource *mem_base, 137 struct resource *mem_base,
105 int irq_base) 138 int irq_base)
106{ 139{
107 int i; 140 int i;
108 int ret = 0; 141 int ret = 0;
142 atomic_t *cnts;
143
144 /* initialize reference counting for all cells */
145 cnts = kcalloc(sizeof(*cnts), n_devs, GFP_KERNEL);
146 if (!cnts)
147 return -ENOMEM;
109 148
110 for (i = 0; i < n_devs; i++) { 149 for (i = 0; i < n_devs; i++) {
150 atomic_set(&cnts[i], 0);
151 cells[i].usage_count = &cnts[i];
111 ret = mfd_add_device(parent, id, cells + i, mem_base, irq_base); 152 ret = mfd_add_device(parent, id, cells + i, mem_base, irq_base);
112 if (ret) 153 if (ret)
113 break; 154 break;
@@ -120,17 +161,89 @@ int mfd_add_devices(struct device *parent, int id,
120} 161}
121EXPORT_SYMBOL(mfd_add_devices); 162EXPORT_SYMBOL(mfd_add_devices);
122 163
123static int mfd_remove_devices_fn(struct device *dev, void *unused) 164static int mfd_remove_devices_fn(struct device *dev, void *c)
124{ 165{
125 platform_device_unregister(to_platform_device(dev)); 166 struct platform_device *pdev = to_platform_device(dev);
167 const struct mfd_cell *cell = mfd_get_cell(pdev);
168 atomic_t **usage_count = c;
169
170 /* find the base address of usage_count pointers (for freeing) */
171 if (!*usage_count || (cell->usage_count < *usage_count))
172 *usage_count = cell->usage_count;
173
174 platform_device_unregister(pdev);
126 return 0; 175 return 0;
127} 176}
128 177
129void mfd_remove_devices(struct device *parent) 178void mfd_remove_devices(struct device *parent)
130{ 179{
131 device_for_each_child(parent, NULL, mfd_remove_devices_fn); 180 atomic_t *cnts = NULL;
181
182 device_for_each_child(parent, &cnts, mfd_remove_devices_fn);
183 kfree(cnts);
132} 184}
133EXPORT_SYMBOL(mfd_remove_devices); 185EXPORT_SYMBOL(mfd_remove_devices);
134 186
187static int add_shared_platform_device(const char *cell, const char *name)
188{
189 struct mfd_cell cell_entry;
190 struct device *dev;
191 struct platform_device *pdev;
192 int err;
193
194 /* check if we've already registered a device (don't fail if we have) */
195 if (bus_find_device_by_name(&platform_bus_type, NULL, name))
196 return 0;
197
198 /* fetch the parent cell's device (should already be registered!) */
199 dev = bus_find_device_by_name(&platform_bus_type, NULL, cell);
200 if (!dev) {
201 printk(KERN_ERR "failed to find device for cell %s\n", cell);
202 return -ENODEV;
203 }
204 pdev = to_platform_device(dev);
205 memcpy(&cell_entry, mfd_get_cell(pdev), sizeof(cell_entry));
206
207 WARN_ON(!cell_entry.enable);
208
209 cell_entry.name = name;
210 err = mfd_add_device(pdev->dev.parent, -1, &cell_entry, NULL, 0);
211 if (err)
212 dev_err(dev, "MFD add devices failed: %d\n", err);
213 return err;
214}
215
216int mfd_shared_platform_driver_register(struct platform_driver *drv,
217 const char *cellname)
218{
219 int err;
220
221 err = add_shared_platform_device(cellname, drv->driver.name);
222 if (err)
223 printk(KERN_ERR "failed to add platform device %s\n",
224 drv->driver.name);
225
226 err = platform_driver_register(drv);
227 if (err)
228 printk(KERN_ERR "failed to add platform driver %s\n",
229 drv->driver.name);
230
231 return err;
232}
233EXPORT_SYMBOL(mfd_shared_platform_driver_register);
234
235void mfd_shared_platform_driver_unregister(struct platform_driver *drv)
236{
237 struct device *dev;
238
239 dev = bus_find_device_by_name(&platform_bus_type, NULL,
240 drv->driver.name);
241 if (dev)
242 platform_device_unregister(to_platform_device(dev));
243
244 platform_driver_unregister(drv);
245}
246EXPORT_SYMBOL(mfd_shared_platform_driver_unregister);
247
135MODULE_LICENSE("GPL"); 248MODULE_LICENSE("GPL");
136MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov"); 249MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov");