Blame view

drivers/mfd/mfd-core.c 5.51 KB
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
  /*
   * drivers/mfd/mfd-core.c
   *
   * core MFD support
   * Copyright (c) 2006 Ian Molton
   * Copyright (c) 2007,2008 Dmitry Baryshkov
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   */
  
  #include <linux/kernel.h>
  #include <linux/platform_device.h>
91fedede0   Samuel Ortiz   mfd: Check for AC...
16
  #include <linux/acpi.h>
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
17
  #include <linux/mfd/core.h>
4c90aa94f   Mark Brown   mfd: Provide pm_r...
18
  #include <linux/pm_runtime.h>
5a0e3ad6a   Tejun Heo   include cleanup: ...
19
  #include <linux/slab.h>
4e36dd331   Paul Gortmaker   mfd: Add module.h...
20
  #include <linux/module.h>
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
21

f77289ac2   Andres Salomon   mfd: Rename mfd_s...
22
  int mfd_cell_enable(struct platform_device *pdev)
1e29af62f   Andres Salomon   mfd: Add refcount...
23
24
25
26
27
28
29
30
31
32
33
34
35
36
  {
  	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;
  }
f77289ac2   Andres Salomon   mfd: Rename mfd_s...
37
  EXPORT_SYMBOL(mfd_cell_enable);
1e29af62f   Andres Salomon   mfd: Add refcount...
38

f77289ac2   Andres Salomon   mfd: Rename mfd_s...
39
  int mfd_cell_disable(struct platform_device *pdev)
1e29af62f   Andres Salomon   mfd: Add refcount...
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
  {
  	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;
  }
f77289ac2   Andres Salomon   mfd: Rename mfd_s...
57
  EXPORT_SYMBOL(mfd_cell_disable);
1e29af62f   Andres Salomon   mfd: Add refcount...
58

e710d7d5a   Samuel Ortiz   mfd: Fetch cell p...
59
60
61
62
63
64
65
66
67
68
69
70
  static int mfd_platform_add_cell(struct platform_device *pdev,
  				 const struct mfd_cell *cell)
  {
  	if (!cell)
  		return 0;
  
  	pdev->mfd_cell = kmemdup(cell, sizeof(*cell), GFP_KERNEL);
  	if (!pdev->mfd_cell)
  		return -ENOMEM;
  
  	return 0;
  }
424f525a1   Dmitry Eremin-Solenikov   mfd: accept pure ...
71
  static int mfd_add_device(struct device *parent, int id,
7f71ac937   Ben Dooks   mfd: Coding style...
72
73
74
  			  const struct mfd_cell *cell,
  			  struct resource *mem_base,
  			  int irq_base)
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
75
  {
a87903f3b   Ian Molton   mfd: reduce stack...
76
  	struct resource *res;
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
77
78
79
  	struct platform_device *pdev;
  	int ret = -ENOMEM;
  	int r;
3bed6e415   Mark Brown   mfd: Allow multip...
80
  	pdev = platform_device_alloc(cell->name, id + cell->id);
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
81
82
  	if (!pdev)
  		goto fail_alloc;
a87903f3b   Ian Molton   mfd: reduce stack...
83
84
85
  	res = kzalloc(sizeof(*res) * cell->num_resources, GFP_KERNEL);
  	if (!res)
  		goto fail_device;
424f525a1   Dmitry Eremin-Solenikov   mfd: accept pure ...
86
  	pdev->dev.parent = parent;
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
87

eb8956074   Samuel Ortiz   mfd: Add platform...
88
89
90
91
92
93
  	if (cell->pdata_size) {
  		ret = platform_device_add_data(pdev,
  					cell->platform_data, cell->pdata_size);
  		if (ret)
  			goto fail_res;
  	}
e710d7d5a   Samuel Ortiz   mfd: Fetch cell p...
94
  	ret = mfd_platform_add_cell(pdev, cell);
fe891a008   Andres Salomon   mfd-core: Uncondi...
95
96
  	if (ret)
  		goto fail_res;
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
97

aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
98
99
100
101
102
  	for (r = 0; r < cell->num_resources; r++) {
  		res[r].name = cell->resources[r].name;
  		res[r].flags = cell->resources[r].flags;
  
  		/* Find out base to use */
f03cfcbc8   Samuel Ortiz   mfd: Check for me...
103
  		if ((cell->resources[r].flags & IORESOURCE_MEM) && mem_base) {
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
  			res[r].parent = mem_base;
  			res[r].start = mem_base->start +
  				cell->resources[r].start;
  			res[r].end = mem_base->start +
  				cell->resources[r].end;
  		} else if (cell->resources[r].flags & IORESOURCE_IRQ) {
  			res[r].start = irq_base +
  				cell->resources[r].start;
  			res[r].end   = irq_base +
  				cell->resources[r].end;
  		} else {
  			res[r].parent = cell->resources[r].parent;
  			res[r].start = cell->resources[r].start;
  			res[r].end   = cell->resources[r].end;
  		}
91fedede0   Samuel Ortiz   mfd: Check for AC...
119

5f2545fa1   Daniel Drake   mfd: Allow for by...
120
121
122
123
124
  		if (!cell->ignore_resource_conflicts) {
  			ret = acpi_check_resource_conflict(res);
  			if (ret)
  				goto fail_res;
  		}
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
125
  	}
8af5fe3bc   Axel Lin   mfd: properly han...
126
127
128
  	ret = platform_device_add_resources(pdev, res, cell->num_resources);
  	if (ret)
  		goto fail_res;
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
129
130
131
  
  	ret = platform_device_add(pdev);
  	if (ret)
a87903f3b   Ian Molton   mfd: reduce stack...
132
  		goto fail_res;
4c90aa94f   Mark Brown   mfd: Provide pm_r...
133
134
  	if (cell->pm_runtime_no_callbacks)
  		pm_runtime_no_callbacks(&pdev->dev);
a87903f3b   Ian Molton   mfd: reduce stack...
135
  	kfree(res);
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
136
137
  
  	return 0;
a87903f3b   Ian Molton   mfd: reduce stack...
138
139
  fail_res:
  	kfree(res);
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
140
141
142
143
144
  fail_device:
  	platform_device_put(pdev);
  fail_alloc:
  	return ret;
  }
424f525a1   Dmitry Eremin-Solenikov   mfd: accept pure ...
145
  int mfd_add_devices(struct device *parent, int id,
1e29af62f   Andres Salomon   mfd: Add refcount...
146
  		    struct mfd_cell *cells, int n_devs,
7f71ac937   Ben Dooks   mfd: Coding style...
147
148
  		    struct resource *mem_base,
  		    int irq_base)
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
149
150
151
  {
  	int i;
  	int ret = 0;
1e29af62f   Andres Salomon   mfd: Add refcount...
152
153
154
155
156
157
  	atomic_t *cnts;
  
  	/* initialize reference counting for all cells */
  	cnts = kcalloc(sizeof(*cnts), n_devs, GFP_KERNEL);
  	if (!cnts)
  		return -ENOMEM;
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
158
159
  
  	for (i = 0; i < n_devs; i++) {
1e29af62f   Andres Salomon   mfd: Add refcount...
160
161
  		atomic_set(&cnts[i], 0);
  		cells[i].usage_count = &cnts[i];
424f525a1   Dmitry Eremin-Solenikov   mfd: accept pure ...
162
  		ret = mfd_add_device(parent, id, cells + i, mem_base, irq_base);
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
163
164
165
166
167
168
169
170
171
172
  		if (ret)
  			break;
  	}
  
  	if (ret)
  		mfd_remove_devices(parent);
  
  	return ret;
  }
  EXPORT_SYMBOL(mfd_add_devices);
1e29af62f   Andres Salomon   mfd: Add refcount...
173
  static int mfd_remove_devices_fn(struct device *dev, void *c)
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
174
  {
1e29af62f   Andres Salomon   mfd: Add refcount...
175
176
177
178
179
180
181
182
183
  	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);
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
184
185
  	return 0;
  }
424f525a1   Dmitry Eremin-Solenikov   mfd: accept pure ...
186
  void mfd_remove_devices(struct device *parent)
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
187
  {
1e29af62f   Andres Salomon   mfd: Add refcount...
188
189
190
191
  	atomic_t *cnts = NULL;
  
  	device_for_each_child(parent, &cnts, mfd_remove_devices_fn);
  	kfree(cnts);
aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
192
193
  }
  EXPORT_SYMBOL(mfd_remove_devices);
fa1df6916   Andres Salomon   mfd: Add mfd_clon...
194
  int mfd_clone_cell(const char *cell, const char **clones, size_t n_clones)
a9bbba996   Andres Salomon   mfd: add platform...
195
196
197
198
  {
  	struct mfd_cell cell_entry;
  	struct device *dev;
  	struct platform_device *pdev;
fa1df6916   Andres Salomon   mfd: Add mfd_clon...
199
  	int i;
a9bbba996   Andres Salomon   mfd: add platform...
200
201
202
203
204
205
206
207
208
209
210
211
  
  	/* 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
  ", cell);
  		return -ENODEV;
  	}
  	pdev = to_platform_device(dev);
  	memcpy(&cell_entry, mfd_get_cell(pdev), sizeof(cell_entry));
  
  	WARN_ON(!cell_entry.enable);
fa1df6916   Andres Salomon   mfd: Add mfd_clon...
212
213
214
215
216
217
218
219
  	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'
  ",
  					clones[i]);
  	}
a9bbba996   Andres Salomon   mfd: add platform...
220

fa1df6916   Andres Salomon   mfd: Add mfd_clon...
221
  	return 0;
a9bbba996   Andres Salomon   mfd: add platform...
222
  }
fa1df6916   Andres Salomon   mfd: Add mfd_clon...
223
  EXPORT_SYMBOL(mfd_clone_cell);
a9bbba996   Andres Salomon   mfd: add platform...
224

aa613de67   Dmitry Eremin-Solenikov   [ARM] 5127/1: Cor...
225
226
  MODULE_LICENSE("GPL");
  MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov");