]>
Commit | Line | Data |
---|---|---|
2a41e607 RK |
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 | ||
ffc30b74 RK |
21 | struct component; |
22 | ||
ce657b1c RK |
23 | struct component_match_array { |
24 | void *data; | |
25 | int (*compare)(struct device *, void *); | |
26 | void (*release)(struct device *, void *); | |
27 | struct component *component; | |
28 | bool duplicate; | |
29 | }; | |
30 | ||
6955b582 RK |
31 | struct component_match { |
32 | size_t alloc; | |
33 | size_t num; | |
ce657b1c | 34 | struct component_match_array *compare; |
6955b582 RK |
35 | }; |
36 | ||
2a41e607 RK |
37 | struct master { |
38 | struct list_head node; | |
2a41e607 RK |
39 | bool bound; |
40 | ||
41 | const struct component_master_ops *ops; | |
42 | struct device *dev; | |
6955b582 | 43 | struct component_match *match; |
2a41e607 RK |
44 | }; |
45 | ||
46 | struct component { | |
47 | struct list_head node; | |
2a41e607 RK |
48 | struct master *master; |
49 | bool bound; | |
50 | ||
51 | const struct component_ops *ops; | |
52 | struct device *dev; | |
53 | }; | |
54 | ||
55 | static DEFINE_MUTEX(component_mutex); | |
56 | static LIST_HEAD(component_list); | |
57 | static LIST_HEAD(masters); | |
58 | ||
59 | static struct master *__master_find(struct device *dev, | |
60 | const struct component_master_ops *ops) | |
61 | { | |
62 | struct master *m; | |
63 | ||
64 | list_for_each_entry(m, &masters, node) | |
65 | if (m->dev == dev && (!ops || m->ops == ops)) | |
66 | return m; | |
67 | ||
68 | return NULL; | |
69 | } | |
70 | ||
ffc30b74 | 71 | static struct component *find_component(struct master *master, |
2a41e607 RK |
72 | int (*compare)(struct device *, void *), void *compare_data) |
73 | { | |
74 | struct component *c; | |
2a41e607 RK |
75 | |
76 | list_for_each_entry(c, &component_list, node) { | |
fcbcebce | 77 | if (c->master && c->master != master) |
2a41e607 RK |
78 | continue; |
79 | ||
ffc30b74 RK |
80 | if (compare(c->dev, compare_data)) |
81 | return c; | |
2a41e607 RK |
82 | } |
83 | ||
ffc30b74 | 84 | return NULL; |
2a41e607 | 85 | } |
2a41e607 | 86 | |
6955b582 RK |
87 | static int find_components(struct master *master) |
88 | { | |
89 | struct component_match *match = master->match; | |
90 | size_t i; | |
91 | int ret = 0; | |
92 | ||
6955b582 RK |
93 | /* |
94 | * Scan the array of match functions and attach | |
95 | * any components which are found to this master. | |
96 | */ | |
97 | for (i = 0; i < match->num; i++) { | |
ce657b1c | 98 | struct component_match_array *mc = &match->compare[i]; |
ffc30b74 RK |
99 | struct component *c; |
100 | ||
101 | dev_dbg(master->dev, "Looking for component %zu\n", i); | |
102 | ||
103 | if (match->compare[i].component) | |
104 | continue; | |
105 | ||
ce657b1c | 106 | c = find_component(master, mc->compare, mc->data); |
ffc30b74 RK |
107 | if (!c) { |
108 | ret = -ENXIO; | |
6955b582 | 109 | break; |
ffc30b74 RK |
110 | } |
111 | ||
112 | dev_dbg(master->dev, "found component %s, duplicate %u\n", dev_name(c->dev), !!c->master); | |
113 | ||
114 | /* Attach this component to the master */ | |
115 | match->compare[i].duplicate = !!c->master; | |
116 | match->compare[i].component = c; | |
117 | c->master = master; | |
6955b582 RK |
118 | } |
119 | return ret; | |
120 | } | |
121 | ||
ffc30b74 RK |
122 | /* Detach component from associated master */ |
123 | static void remove_component(struct master *master, struct component *c) | |
2a41e607 | 124 | { |
ffc30b74 | 125 | size_t i; |
2a41e607 | 126 | |
ffc30b74 RK |
127 | /* Detach the component from this master. */ |
128 | for (i = 0; i < master->match->num; i++) | |
129 | if (master->match->compare[i].component == c) | |
130 | master->match->compare[i].component = NULL; | |
2a41e607 RK |
131 | } |
132 | ||
133 | /* | |
134 | * Try to bring up a master. If component is NULL, we're interested in | |
135 | * this master, otherwise it's a component which must be present to try | |
136 | * and bring up the master. | |
137 | * | |
138 | * Returns 1 for successful bringup, 0 if not ready, or -ve errno. | |
139 | */ | |
140 | static int try_to_bring_up_master(struct master *master, | |
141 | struct component *component) | |
142 | { | |
c334940e RK |
143 | int ret; |
144 | ||
ffc30b74 RK |
145 | dev_dbg(master->dev, "trying to bring up master\n"); |
146 | ||
6955b582 | 147 | if (find_components(master)) { |
ffc30b74 RK |
148 | dev_dbg(master->dev, "master has incomplete components\n"); |
149 | return 0; | |
c334940e | 150 | } |
2a41e607 | 151 | |
c334940e | 152 | if (component && component->master != master) { |
ffc30b74 RK |
153 | dev_dbg(master->dev, "master is not for this component (%s)\n", |
154 | dev_name(component->dev)); | |
155 | return 0; | |
c334940e | 156 | } |
2a41e607 | 157 | |
ffc30b74 RK |
158 | if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) |
159 | return -ENOMEM; | |
2a41e607 | 160 | |
c334940e RK |
161 | /* Found all components */ |
162 | ret = master->ops->bind(master->dev); | |
163 | if (ret < 0) { | |
164 | devres_release_group(master->dev, NULL); | |
165 | dev_info(master->dev, "master bind failed: %d\n", ret); | |
ffc30b74 | 166 | return ret; |
c334940e | 167 | } |
9e1ccb4a | 168 | |
c334940e RK |
169 | master->bound = true; |
170 | return 1; | |
2a41e607 RK |
171 | } |
172 | ||
173 | static int try_to_bring_up_masters(struct component *component) | |
174 | { | |
175 | struct master *m; | |
176 | int ret = 0; | |
177 | ||
178 | list_for_each_entry(m, &masters, node) { | |
29f1c7fd RK |
179 | if (!m->bound) { |
180 | ret = try_to_bring_up_master(m, component); | |
181 | if (ret != 0) | |
182 | break; | |
183 | } | |
2a41e607 RK |
184 | } |
185 | ||
186 | return ret; | |
187 | } | |
188 | ||
189 | static void take_down_master(struct master *master) | |
190 | { | |
191 | if (master->bound) { | |
192 | master->ops->unbind(master->dev); | |
9e1ccb4a | 193 | devres_release_group(master->dev, NULL); |
2a41e607 RK |
194 | master->bound = false; |
195 | } | |
2a41e607 RK |
196 | } |
197 | ||
ce657b1c RK |
198 | static void component_match_release(struct device *master, |
199 | struct component_match *match) | |
6955b582 | 200 | { |
ce657b1c RK |
201 | unsigned int i; |
202 | ||
203 | for (i = 0; i < match->num; i++) { | |
204 | struct component_match_array *mc = &match->compare[i]; | |
205 | ||
206 | if (mc->release) | |
207 | mc->release(master, mc->data); | |
208 | } | |
9a4e7849 RK |
209 | |
210 | kfree(match->compare); | |
6955b582 RK |
211 | } |
212 | ||
ce657b1c RK |
213 | static void devm_component_match_release(struct device *dev, void *res) |
214 | { | |
215 | component_match_release(dev, res); | |
216 | } | |
217 | ||
218 | static int component_match_realloc(struct device *dev, | |
6955b582 RK |
219 | struct component_match *match, size_t num) |
220 | { | |
ce657b1c | 221 | struct component_match_array *new; |
6955b582 | 222 | |
ce657b1c RK |
223 | if (match->alloc == num) |
224 | return 0; | |
6955b582 | 225 | |
9a4e7849 | 226 | new = kmalloc_array(num, sizeof(*new), GFP_KERNEL); |
6955b582 | 227 | if (!new) |
ce657b1c | 228 | return -ENOMEM; |
6955b582 | 229 | |
ce657b1c RK |
230 | if (match->compare) { |
231 | memcpy(new, match->compare, sizeof(*new) * | |
232 | min(match->num, num)); | |
9a4e7849 | 233 | kfree(match->compare); |
6955b582 | 234 | } |
ce657b1c RK |
235 | match->compare = new; |
236 | match->alloc = num; | |
6955b582 | 237 | |
ce657b1c | 238 | return 0; |
6955b582 RK |
239 | } |
240 | ||
241 | /* | |
ce657b1c | 242 | * Add a component to be matched, with a release function. |
6955b582 RK |
243 | * |
244 | * The match array is first created or extended if necessary. | |
245 | */ | |
ce657b1c RK |
246 | void component_match_add_release(struct device *master, |
247 | struct component_match **matchptr, | |
248 | void (*release)(struct device *, void *), | |
6955b582 RK |
249 | int (*compare)(struct device *, void *), void *compare_data) |
250 | { | |
251 | struct component_match *match = *matchptr; | |
252 | ||
253 | if (IS_ERR(match)) | |
254 | return; | |
255 | ||
ce657b1c RK |
256 | if (!match) { |
257 | match = devres_alloc(devm_component_match_release, | |
258 | sizeof(*match), GFP_KERNEL); | |
259 | if (!match) { | |
260 | *matchptr = ERR_PTR(-ENOMEM); | |
261 | return; | |
262 | } | |
6955b582 | 263 | |
ce657b1c | 264 | devres_add(master, match); |
6955b582 RK |
265 | |
266 | *matchptr = match; | |
ce657b1c | 267 | } |
6955b582 | 268 | |
ce657b1c | 269 | if (match->num == match->alloc) { |
4877bb91 | 270 | size_t new_size = match->alloc + 16; |
ce657b1c RK |
271 | int ret; |
272 | ||
273 | ret = component_match_realloc(master, match, new_size); | |
274 | if (ret) { | |
275 | *matchptr = ERR_PTR(ret); | |
6955b582 | 276 | return; |
ce657b1c | 277 | } |
6955b582 RK |
278 | } |
279 | ||
ce657b1c RK |
280 | match->compare[match->num].compare = compare; |
281 | match->compare[match->num].release = release; | |
6955b582 | 282 | match->compare[match->num].data = compare_data; |
ffc30b74 | 283 | match->compare[match->num].component = NULL; |
6955b582 RK |
284 | match->num++; |
285 | } | |
ce657b1c | 286 | EXPORT_SYMBOL(component_match_add_release); |
6955b582 | 287 | |
57480484 JMT |
288 | static void free_master(struct master *master) |
289 | { | |
290 | struct component_match *match = master->match; | |
291 | int i; | |
292 | ||
293 | list_del(&master->node); | |
294 | ||
295 | if (match) { | |
296 | for (i = 0; i < match->num; i++) { | |
297 | struct component *c = match->compare[i].component; | |
298 | if (c) | |
299 | c->master = NULL; | |
300 | } | |
301 | } | |
302 | ||
303 | kfree(master); | |
304 | } | |
305 | ||
6955b582 RK |
306 | int component_master_add_with_match(struct device *dev, |
307 | const struct component_master_ops *ops, | |
308 | struct component_match *match) | |
2a41e607 RK |
309 | { |
310 | struct master *master; | |
311 | int ret; | |
312 | ||
fae9e2e0 | 313 | /* Reallocate the match array for its true size */ |
ce657b1c RK |
314 | ret = component_match_realloc(dev, match, match->num); |
315 | if (ret) | |
316 | return ret; | |
6955b582 | 317 | |
2a41e607 RK |
318 | master = kzalloc(sizeof(*master), GFP_KERNEL); |
319 | if (!master) | |
320 | return -ENOMEM; | |
321 | ||
322 | master->dev = dev; | |
323 | master->ops = ops; | |
6955b582 | 324 | master->match = match; |
2a41e607 RK |
325 | |
326 | /* Add to the list of available masters. */ | |
327 | mutex_lock(&component_mutex); | |
328 | list_add(&master->node, &masters); | |
329 | ||
330 | ret = try_to_bring_up_master(master, NULL); | |
331 | ||
57480484 JMT |
332 | if (ret < 0) |
333 | free_master(master); | |
334 | ||
2a41e607 RK |
335 | mutex_unlock(&component_mutex); |
336 | ||
337 | return ret < 0 ? ret : 0; | |
338 | } | |
6955b582 RK |
339 | EXPORT_SYMBOL_GPL(component_master_add_with_match); |
340 | ||
2a41e607 RK |
341 | void component_master_del(struct device *dev, |
342 | const struct component_master_ops *ops) | |
343 | { | |
344 | struct master *master; | |
345 | ||
346 | mutex_lock(&component_mutex); | |
347 | master = __master_find(dev, ops); | |
348 | if (master) { | |
349 | take_down_master(master); | |
57480484 | 350 | free_master(master); |
2a41e607 RK |
351 | } |
352 | mutex_unlock(&component_mutex); | |
353 | } | |
354 | EXPORT_SYMBOL_GPL(component_master_del); | |
355 | ||
356 | static void component_unbind(struct component *component, | |
357 | struct master *master, void *data) | |
358 | { | |
359 | WARN_ON(!component->bound); | |
360 | ||
361 | component->ops->unbind(component->dev, master->dev, data); | |
362 | component->bound = false; | |
363 | ||
364 | /* Release all resources claimed in the binding of this component */ | |
365 | devres_release_group(component->dev, component); | |
366 | } | |
367 | ||
368 | void component_unbind_all(struct device *master_dev, void *data) | |
369 | { | |
370 | struct master *master; | |
371 | struct component *c; | |
ffc30b74 | 372 | size_t i; |
2a41e607 RK |
373 | |
374 | WARN_ON(!mutex_is_locked(&component_mutex)); | |
375 | ||
376 | master = __master_find(master_dev, NULL); | |
377 | if (!master) | |
378 | return; | |
379 | ||
ffc30b74 RK |
380 | /* Unbind components in reverse order */ |
381 | for (i = master->match->num; i--; ) | |
382 | if (!master->match->compare[i].duplicate) { | |
383 | c = master->match->compare[i].component; | |
384 | component_unbind(c, master, data); | |
385 | } | |
2a41e607 RK |
386 | } |
387 | EXPORT_SYMBOL_GPL(component_unbind_all); | |
388 | ||
389 | static int component_bind(struct component *component, struct master *master, | |
390 | void *data) | |
391 | { | |
392 | int ret; | |
393 | ||
394 | /* | |
395 | * Each component initialises inside its own devres group. | |
396 | * This allows us to roll-back a failed component without | |
397 | * affecting anything else. | |
398 | */ | |
399 | if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) | |
400 | return -ENOMEM; | |
401 | ||
402 | /* | |
403 | * Also open a group for the device itself: this allows us | |
404 | * to release the resources claimed against the sub-device | |
405 | * at the appropriate moment. | |
406 | */ | |
407 | if (!devres_open_group(component->dev, component, GFP_KERNEL)) { | |
408 | devres_release_group(master->dev, NULL); | |
409 | return -ENOMEM; | |
410 | } | |
411 | ||
412 | dev_dbg(master->dev, "binding %s (ops %ps)\n", | |
413 | dev_name(component->dev), component->ops); | |
414 | ||
415 | ret = component->ops->bind(component->dev, master->dev, data); | |
416 | if (!ret) { | |
417 | component->bound = true; | |
418 | ||
419 | /* | |
420 | * Close the component device's group so that resources | |
421 | * allocated in the binding are encapsulated for removal | |
422 | * at unbind. Remove the group on the DRM device as we | |
423 | * can clean those resources up independently. | |
424 | */ | |
425 | devres_close_group(component->dev, NULL); | |
426 | devres_remove_group(master->dev, NULL); | |
427 | ||
428 | dev_info(master->dev, "bound %s (ops %ps)\n", | |
429 | dev_name(component->dev), component->ops); | |
430 | } else { | |
431 | devres_release_group(component->dev, NULL); | |
432 | devres_release_group(master->dev, NULL); | |
433 | ||
434 | dev_err(master->dev, "failed to bind %s (ops %ps): %d\n", | |
435 | dev_name(component->dev), component->ops, ret); | |
436 | } | |
437 | ||
438 | return ret; | |
439 | } | |
440 | ||
441 | int component_bind_all(struct device *master_dev, void *data) | |
442 | { | |
443 | struct master *master; | |
444 | struct component *c; | |
ffc30b74 | 445 | size_t i; |
2a41e607 RK |
446 | int ret = 0; |
447 | ||
448 | WARN_ON(!mutex_is_locked(&component_mutex)); | |
449 | ||
450 | master = __master_find(master_dev, NULL); | |
451 | if (!master) | |
452 | return -EINVAL; | |
453 | ||
ffc30b74 RK |
454 | /* Bind components in match order */ |
455 | for (i = 0; i < master->match->num; i++) | |
456 | if (!master->match->compare[i].duplicate) { | |
457 | c = master->match->compare[i].component; | |
458 | ret = component_bind(c, master, data); | |
459 | if (ret) | |
460 | break; | |
461 | } | |
2a41e607 RK |
462 | |
463 | if (ret != 0) { | |
ffc30b74 RK |
464 | for (; i--; ) |
465 | if (!master->match->compare[i].duplicate) { | |
466 | c = master->match->compare[i].component; | |
467 | component_unbind(c, master, data); | |
468 | } | |
2a41e607 RK |
469 | } |
470 | ||
471 | return ret; | |
472 | } | |
473 | EXPORT_SYMBOL_GPL(component_bind_all); | |
474 | ||
475 | int component_add(struct device *dev, const struct component_ops *ops) | |
476 | { | |
477 | struct component *component; | |
478 | int ret; | |
479 | ||
480 | component = kzalloc(sizeof(*component), GFP_KERNEL); | |
481 | if (!component) | |
482 | return -ENOMEM; | |
483 | ||
484 | component->ops = ops; | |
485 | component->dev = dev; | |
486 | ||
487 | dev_dbg(dev, "adding component (ops %ps)\n", ops); | |
488 | ||
489 | mutex_lock(&component_mutex); | |
490 | list_add_tail(&component->node, &component_list); | |
491 | ||
492 | ret = try_to_bring_up_masters(component); | |
493 | if (ret < 0) { | |
8e7199c2 DS |
494 | if (component->master) |
495 | remove_component(component->master, component); | |
2a41e607 RK |
496 | list_del(&component->node); |
497 | ||
498 | kfree(component); | |
499 | } | |
500 | mutex_unlock(&component_mutex); | |
501 | ||
502 | return ret < 0 ? ret : 0; | |
503 | } | |
504 | EXPORT_SYMBOL_GPL(component_add); | |
505 | ||
506 | void component_del(struct device *dev, const struct component_ops *ops) | |
507 | { | |
508 | struct component *c, *component = NULL; | |
509 | ||
510 | mutex_lock(&component_mutex); | |
511 | list_for_each_entry(c, &component_list, node) | |
512 | if (c->dev == dev && c->ops == ops) { | |
513 | list_del(&c->node); | |
514 | component = c; | |
515 | break; | |
516 | } | |
517 | ||
ffc30b74 | 518 | if (component && component->master) { |
2a41e607 | 519 | take_down_master(component->master); |
ffc30b74 RK |
520 | remove_component(component->master, component); |
521 | } | |
2a41e607 RK |
522 | |
523 | mutex_unlock(&component_mutex); | |
524 | ||
525 | WARN_ON(!component); | |
526 | kfree(component); | |
527 | } | |
528 | EXPORT_SYMBOL_GPL(component_del); | |
529 | ||
530 | MODULE_LICENSE("GPL v2"); |