aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/base
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@arm.linux.org.uk>2014-01-10 18:23:37 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-01-10 19:27:36 -0500
commit2a41e6070dd7ef539d0f3b1652b4839d04378e11 (patch)
tree9cb329a1231a1f971e6c18ff472090872adb8da2 /drivers/base
parentd1ba277e79889085a2faec3b68b91ce89c63f888 (diff)
drivers/base: provide an infrastructure for componentised subsystems
Subsystems such as ALSA, DRM and others require a single card-level device structure to represent a subsystem. However, firmware tends to describe the individual devices and the connections between them. Therefore, we need a way to gather up the individual component devices together, and indicate when we have all the component devices. We do this in DT by providing a "superdevice" node which specifies the components, eg: imx-drm { compatible = "fsl,drm"; crtcs = <&ipu1>; connectors = <&hdmi>; }; The superdevice is declared into the component support, along with the subcomponents. The superdevice receives callbacks to locate the subcomponents, and identify when all components are present. At this point, we bind the superdevice, which causes the appropriate subsystem to be initialised in the conventional way. When any of the components or superdevice are removed from the system, we unbind the superdevice, thereby taking the subsystem down. Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/base')
-rw-r--r--drivers/base/Makefile2
-rw-r--r--drivers/base/component.c382
2 files changed, 383 insertions, 1 deletions
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 94e8a80e87f8..870ecfd503af 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -1,6 +1,6 @@
1# Makefile for the Linux device tree 1# Makefile for the Linux device tree
2 2
3obj-y := core.o bus.o dd.o syscore.o \ 3obj-y := component.o core.o bus.o dd.o syscore.o \
4 driver.o class.o platform.o \ 4 driver.o class.o platform.o \
5 cpu.o firmware.o init.o map.o devres.o \ 5 cpu.o firmware.o init.o map.o devres.o \
6 attribute_container.o transport_class.o \ 6 attribute_container.o transport_class.o \
diff --git a/drivers/base/component.c b/drivers/base/component.c
new file mode 100644
index 000000000000..c53efe6c6d8e
--- /dev/null
+++ b/drivers/base/component.c
@@ -0,0 +1,382 @@
1/*
2 * Componentized device handling.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This is work in progress. We gather up the component devices into a list,
9 * and bind them when instructed. At the moment, we're specific to the DRM
10 * subsystem, and only handles one master device, but this doesn't have to be
11 * the case.
12 */
13#include <linux/component.h>
14#include <linux/device.h>
15#include <linux/kref.h>
16#include <linux/list.h>
17#include <linux/module.h>
18#include <linux/mutex.h>
19#include <linux/slab.h>
20
21struct master {
22 struct list_head node;
23 struct list_head components;
24 bool bound;
25
26 const struct component_master_ops *ops;
27 struct device *dev;
28};
29
30struct component {
31 struct list_head node;
32 struct list_head master_node;
33 struct master *master;
34 bool bound;
35
36 const struct component_ops *ops;
37 struct device *dev;
38};
39
40static DEFINE_MUTEX(component_mutex);
41static LIST_HEAD(component_list);
42static LIST_HEAD(masters);
43
44static struct master *__master_find(struct device *dev,
45 const struct component_master_ops *ops)
46{
47 struct master *m;
48
49 list_for_each_entry(m, &masters, node)
50 if (m->dev == dev && (!ops || m->ops == ops))
51 return m;
52
53 return NULL;
54}
55
56/* Attach an unattached component to a master. */
57static void component_attach_master(struct master *master, struct component *c)
58{
59 c->master = master;
60
61 list_add_tail(&c->master_node, &master->components);
62}
63
64/* Detach a component from a master. */
65static void component_detach_master(struct master *master, struct component *c)
66{
67 list_del(&c->master_node);
68
69 c->master = NULL;
70}
71
72int component_master_add_child(struct master *master,
73 int (*compare)(struct device *, void *), void *compare_data)
74{
75 struct component *c;
76 int ret = -ENXIO;
77
78 list_for_each_entry(c, &component_list, node) {
79 if (c->master)
80 continue;
81
82 if (compare(c->dev, compare_data)) {
83 component_attach_master(master, c);
84 ret = 0;
85 break;
86 }
87 }
88
89 return ret;
90}
91EXPORT_SYMBOL_GPL(component_master_add_child);
92
93/* Detach all attached components from this master */
94static void master_remove_components(struct master *master)
95{
96 while (!list_empty(&master->components)) {
97 struct component *c = list_first_entry(&master->components,
98 struct component, master_node);
99
100 WARN_ON(c->master != master);
101
102 component_detach_master(master, c);
103 }
104}
105
106/*
107 * Try to bring up a master. If component is NULL, we're interested in
108 * this master, otherwise it's a component which must be present to try
109 * and bring up the master.
110 *
111 * Returns 1 for successful bringup, 0 if not ready, or -ve errno.
112 */
113static int try_to_bring_up_master(struct master *master,
114 struct component *component)
115{
116 int ret = 0;
117
118 if (!master->bound) {
119 /*
120 * Search the list of components, looking for components that
121 * belong to this master, and attach them to the master.
122 */
123 if (master->ops->add_components(master->dev, master)) {
124 /* Failed to find all components */
125 master_remove_components(master);
126 ret = 0;
127 goto out;
128 }
129
130 if (component && component->master != master) {
131 master_remove_components(master);
132 ret = 0;
133 goto out;
134 }
135
136 /* Found all components */
137 ret = master->ops->bind(master->dev);
138 if (ret < 0) {
139 master_remove_components(master);
140 goto out;
141 }
142
143 master->bound = true;
144 ret = 1;
145 }
146out:
147
148 return ret;
149}
150
151static int try_to_bring_up_masters(struct component *component)
152{
153 struct master *m;
154 int ret = 0;
155
156 list_for_each_entry(m, &masters, node) {
157 ret = try_to_bring_up_master(m, component);
158 if (ret != 0)
159 break;
160 }
161
162 return ret;
163}
164
165static void take_down_master(struct master *master)
166{
167 if (master->bound) {
168 master->ops->unbind(master->dev);
169 master->bound = false;
170 }
171
172 master_remove_components(master);
173}
174
175int component_master_add(struct device *dev,
176 const struct component_master_ops *ops)
177{
178 struct master *master;
179 int ret;
180
181 master = kzalloc(sizeof(*master), GFP_KERNEL);
182 if (!master)
183 return -ENOMEM;
184
185 master->dev = dev;
186 master->ops = ops;
187 INIT_LIST_HEAD(&master->components);
188
189 /* Add to the list of available masters. */
190 mutex_lock(&component_mutex);
191 list_add(&master->node, &masters);
192
193 ret = try_to_bring_up_master(master, NULL);
194
195 if (ret < 0) {
196 /* Delete off the list if we weren't successful */
197 list_del(&master->node);
198 kfree(master);
199 }
200 mutex_unlock(&component_mutex);
201
202 return ret < 0 ? ret : 0;
203}
204EXPORT_SYMBOL_GPL(component_master_add);
205
206void component_master_del(struct device *dev,
207 const struct component_master_ops *ops)
208{
209 struct master *master;
210
211 mutex_lock(&component_mutex);
212 master = __master_find(dev, ops);
213 if (master) {
214 take_down_master(master);
215
216 list_del(&master->node);
217 kfree(master);
218 }
219 mutex_unlock(&component_mutex);
220}
221EXPORT_SYMBOL_GPL(component_master_del);
222
223static void component_unbind(struct component *component,
224 struct master *master, void *data)
225{
226 WARN_ON(!component->bound);
227
228 component->ops->unbind(component->dev, master->dev, data);
229 component->bound = false;
230
231 /* Release all resources claimed in the binding of this component */
232 devres_release_group(component->dev, component);
233}
234
235void component_unbind_all(struct device *master_dev, void *data)
236{
237 struct master *master;
238 struct component *c;
239
240 WARN_ON(!mutex_is_locked(&component_mutex));
241
242 master = __master_find(master_dev, NULL);
243 if (!master)
244 return;
245
246 list_for_each_entry_reverse(c, &master->components, master_node)
247 component_unbind(c, master, data);
248}
249EXPORT_SYMBOL_GPL(component_unbind_all);
250
251static int component_bind(struct component *component, struct master *master,
252 void *data)
253{
254 int ret;
255
256 /*
257 * Each component initialises inside its own devres group.
258 * This allows us to roll-back a failed component without
259 * affecting anything else.
260 */
261 if (!devres_open_group(master->dev, NULL, GFP_KERNEL))
262 return -ENOMEM;
263
264 /*
265 * Also open a group for the device itself: this allows us
266 * to release the resources claimed against the sub-device
267 * at the appropriate moment.
268 */
269 if (!devres_open_group(component->dev, component, GFP_KERNEL)) {
270 devres_release_group(master->dev, NULL);
271 return -ENOMEM;
272 }
273
274 dev_dbg(master->dev, "binding %s (ops %ps)\n",
275 dev_name(component->dev), component->ops);
276
277 ret = component->ops->bind(component->dev, master->dev, data);
278 if (!ret) {
279 component->bound = true;
280
281 /*
282 * Close the component device's group so that resources
283 * allocated in the binding are encapsulated for removal
284 * at unbind. Remove the group on the DRM device as we
285 * can clean those resources up independently.
286 */
287 devres_close_group(component->dev, NULL);
288 devres_remove_group(master->dev, NULL);
289
290 dev_info(master->dev, "bound %s (ops %ps)\n",
291 dev_name(component->dev), component->ops);
292 } else {
293 devres_release_group(component->dev, NULL);
294 devres_release_group(master->dev, NULL);
295
296 dev_err(master->dev, "failed to bind %s (ops %ps): %d\n",
297 dev_name(component->dev), component->ops, ret);
298 }
299
300 return ret;
301}
302
303int component_bind_all(struct device *master_dev, void *data)
304{
305 struct master *master;
306 struct component *c;
307 int ret = 0;
308
309 WARN_ON(!mutex_is_locked(&component_mutex));
310
311 master = __master_find(master_dev, NULL);
312 if (!master)
313 return -EINVAL;
314
315 list_for_each_entry(c, &master->components, master_node) {
316 ret = component_bind(c, master, data);
317 if (ret)
318 break;
319 }
320
321 if (ret != 0) {
322 list_for_each_entry_continue_reverse(c, &master->components,
323 master_node)
324 component_unbind(c, master, data);
325 }
326
327 return ret;
328}
329EXPORT_SYMBOL_GPL(component_bind_all);
330
331int component_add(struct device *dev, const struct component_ops *ops)
332{
333 struct component *component;
334 int ret;
335
336 component = kzalloc(sizeof(*component), GFP_KERNEL);
337 if (!component)
338 return -ENOMEM;
339
340 component->ops = ops;
341 component->dev = dev;
342
343 dev_dbg(dev, "adding component (ops %ps)\n", ops);
344
345 mutex_lock(&component_mutex);
346 list_add_tail(&component->node, &component_list);
347
348 ret = try_to_bring_up_masters(component);
349 if (ret < 0) {
350 list_del(&component->node);
351
352 kfree(component);
353 }
354 mutex_unlock(&component_mutex);
355
356 return ret < 0 ? ret : 0;
357}
358EXPORT_SYMBOL_GPL(component_add);
359
360void component_del(struct device *dev, const struct component_ops *ops)
361{
362 struct component *c, *component = NULL;
363
364 mutex_lock(&component_mutex);
365 list_for_each_entry(c, &component_list, node)
366 if (c->dev == dev && c->ops == ops) {
367 list_del(&c->node);
368 component = c;
369 break;
370 }
371
372 if (component && component->master)
373 take_down_master(component->master);
374
375 mutex_unlock(&component_mutex);
376
377 WARN_ON(!component);
378 kfree(component);
379}
380EXPORT_SYMBOL_GPL(component_del);
381
382MODULE_LICENSE("GPL v2");