Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/base/component.c
26378 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Componentized device handling.
4
*/
5
#include <linux/component.h>
6
#include <linux/device.h>
7
#include <linux/list.h>
8
#include <linux/mutex.h>
9
#include <linux/of.h>
10
#include <linux/slab.h>
11
#include <linux/debugfs.h>
12
13
/**
14
* DOC: overview
15
*
16
* The component helper allows drivers to collect a pile of sub-devices,
17
* including their bound drivers, into an aggregate driver. Various subsystems
18
* already provide functions to get hold of such components, e.g.
19
* of_clk_get_by_name(). The component helper can be used when such a
20
* subsystem-specific way to find a device is not available: The component
21
* helper fills the niche of aggregate drivers for specific hardware, where
22
* further standardization into a subsystem would not be practical. The common
23
* example is when a logical device (e.g. a DRM display driver) is spread around
24
* the SoC on various components (scanout engines, blending blocks, transcoders
25
* for various outputs and so on).
26
*
27
* The component helper also doesn't solve runtime dependencies, e.g. for system
28
* suspend and resume operations. See also :ref:`device links<device_link>`.
29
*
30
* Components are registered using component_add() and unregistered with
31
* component_del(), usually from the driver's probe and disconnect functions.
32
*
33
* Aggregate drivers first assemble a component match list of what they need
34
* using component_match_add(). This is then registered as an aggregate driver
35
* using component_master_add_with_match(), and unregistered using
36
* component_master_del().
37
*/
38
39
struct component;
40
41
struct component_match_array {
42
void *data;
43
int (*compare)(struct device *, void *);
44
int (*compare_typed)(struct device *, int, void *);
45
void (*release)(struct device *, void *);
46
struct component *component;
47
bool duplicate;
48
};
49
50
struct component_match {
51
size_t alloc;
52
size_t num;
53
struct component_match_array *compare;
54
};
55
56
struct aggregate_device {
57
struct list_head node;
58
bool bound;
59
60
const struct component_master_ops *ops;
61
struct device *parent;
62
struct component_match *match;
63
};
64
65
struct component {
66
struct list_head node;
67
struct aggregate_device *adev;
68
bool bound;
69
70
const struct component_ops *ops;
71
int subcomponent;
72
struct device *dev;
73
};
74
75
static DEFINE_MUTEX(component_mutex);
76
static LIST_HEAD(component_list);
77
static LIST_HEAD(aggregate_devices);
78
79
#ifdef CONFIG_DEBUG_FS
80
81
static struct dentry *component_debugfs_dir;
82
83
static int component_devices_show(struct seq_file *s, void *data)
84
{
85
struct aggregate_device *m = s->private;
86
struct component_match *match = m->match;
87
size_t i;
88
89
mutex_lock(&component_mutex);
90
seq_printf(s, "%-50s %20s\n", "aggregate_device name", "status");
91
seq_puts(s, "-----------------------------------------------------------------------\n");
92
seq_printf(s, "%-50s %20s\n\n",
93
dev_name(m->parent), m->bound ? "bound" : "not bound");
94
95
seq_printf(s, "%-50s %20s\n", "device name", "status");
96
seq_puts(s, "-----------------------------------------------------------------------\n");
97
for (i = 0; i < match->num; i++) {
98
struct component *component = match->compare[i].component;
99
100
seq_printf(s, "%-50s %20s\n",
101
component ? dev_name(component->dev) : "(unknown)",
102
component ? (component->bound ? "bound" : "not bound") : "not registered");
103
}
104
mutex_unlock(&component_mutex);
105
106
return 0;
107
}
108
109
DEFINE_SHOW_ATTRIBUTE(component_devices);
110
111
static int __init component_debug_init(void)
112
{
113
component_debugfs_dir = debugfs_create_dir("device_component", NULL);
114
115
return 0;
116
}
117
118
core_initcall(component_debug_init);
119
120
static void component_debugfs_add(struct aggregate_device *m)
121
{
122
debugfs_create_file(dev_name(m->parent), 0444, component_debugfs_dir, m,
123
&component_devices_fops);
124
}
125
126
static void component_debugfs_del(struct aggregate_device *m)
127
{
128
debugfs_lookup_and_remove(dev_name(m->parent), component_debugfs_dir);
129
}
130
131
#else
132
133
static void component_debugfs_add(struct aggregate_device *m)
134
{ }
135
136
static void component_debugfs_del(struct aggregate_device *m)
137
{ }
138
139
#endif
140
141
static struct aggregate_device *__aggregate_find(struct device *parent,
142
const struct component_master_ops *ops)
143
{
144
struct aggregate_device *m;
145
146
list_for_each_entry(m, &aggregate_devices, node)
147
if (m->parent == parent && (!ops || m->ops == ops))
148
return m;
149
150
return NULL;
151
}
152
153
static struct component *find_component(struct aggregate_device *adev,
154
struct component_match_array *mc)
155
{
156
struct component *c;
157
158
list_for_each_entry(c, &component_list, node) {
159
if (c->adev && c->adev != adev)
160
continue;
161
162
if (mc->compare && mc->compare(c->dev, mc->data))
163
return c;
164
165
if (mc->compare_typed &&
166
mc->compare_typed(c->dev, c->subcomponent, mc->data))
167
return c;
168
}
169
170
return NULL;
171
}
172
173
static int find_components(struct aggregate_device *adev)
174
{
175
struct component_match *match = adev->match;
176
size_t i;
177
int ret = 0;
178
179
/*
180
* Scan the array of match functions and attach
181
* any components which are found to this adev.
182
*/
183
for (i = 0; i < match->num; i++) {
184
struct component_match_array *mc = &match->compare[i];
185
struct component *c;
186
187
dev_dbg(adev->parent, "Looking for component %zu\n", i);
188
189
if (match->compare[i].component)
190
continue;
191
192
c = find_component(adev, mc);
193
if (!c) {
194
ret = -ENXIO;
195
break;
196
}
197
198
dev_dbg(adev->parent, "found component %s, duplicate %u\n",
199
dev_name(c->dev), !!c->adev);
200
201
/* Attach this component to the adev */
202
match->compare[i].duplicate = !!c->adev;
203
match->compare[i].component = c;
204
c->adev = adev;
205
}
206
return ret;
207
}
208
209
/* Detach component from associated aggregate_device */
210
static void remove_component(struct aggregate_device *adev, struct component *c)
211
{
212
size_t i;
213
214
/* Detach the component from this adev. */
215
for (i = 0; i < adev->match->num; i++)
216
if (adev->match->compare[i].component == c)
217
adev->match->compare[i].component = NULL;
218
}
219
220
/*
221
* Try to bring up an aggregate device. If component is NULL, we're interested
222
* in this aggregate device, otherwise it's a component which must be present
223
* to try and bring up the aggregate device.
224
*
225
* Returns 1 for successful bringup, 0 if not ready, or -ve errno.
226
*/
227
static int try_to_bring_up_aggregate_device(struct aggregate_device *adev,
228
struct component *component)
229
{
230
int ret;
231
232
dev_dbg(adev->parent, "trying to bring up adev\n");
233
234
if (find_components(adev)) {
235
dev_dbg(adev->parent, "master has incomplete components\n");
236
return 0;
237
}
238
239
if (component && component->adev != adev) {
240
dev_dbg(adev->parent, "master is not for this component (%s)\n",
241
dev_name(component->dev));
242
return 0;
243
}
244
245
if (!devres_open_group(adev->parent, adev, GFP_KERNEL))
246
return -ENOMEM;
247
248
/* Found all components */
249
ret = adev->ops->bind(adev->parent);
250
if (ret < 0) {
251
devres_release_group(adev->parent, NULL);
252
if (ret != -EPROBE_DEFER)
253
dev_info(adev->parent, "adev bind failed: %d\n", ret);
254
return ret;
255
}
256
257
devres_close_group(adev->parent, NULL);
258
adev->bound = true;
259
return 1;
260
}
261
262
static int try_to_bring_up_masters(struct component *component)
263
{
264
struct aggregate_device *adev;
265
int ret = 0;
266
267
list_for_each_entry(adev, &aggregate_devices, node) {
268
if (!adev->bound) {
269
ret = try_to_bring_up_aggregate_device(adev, component);
270
if (ret != 0)
271
break;
272
}
273
}
274
275
return ret;
276
}
277
278
static void take_down_aggregate_device(struct aggregate_device *adev)
279
{
280
if (adev->bound) {
281
adev->ops->unbind(adev->parent);
282
devres_release_group(adev->parent, adev);
283
adev->bound = false;
284
}
285
}
286
287
/**
288
* component_compare_of - A common component compare function for of_node
289
* @dev: component device
290
* @data: @compare_data from component_match_add_release()
291
*
292
* A common compare function when compare_data is device of_node. e.g.
293
* component_match_add_release(masterdev, &match, component_release_of,
294
* component_compare_of, component_dev_of_node)
295
*/
296
int component_compare_of(struct device *dev, void *data)
297
{
298
return device_match_of_node(dev, data);
299
}
300
EXPORT_SYMBOL_GPL(component_compare_of);
301
302
/**
303
* component_release_of - A common component release function for of_node
304
* @dev: component device
305
* @data: @compare_data from component_match_add_release()
306
*
307
* About the example, Please see component_compare_of().
308
*/
309
void component_release_of(struct device *dev, void *data)
310
{
311
of_node_put(data);
312
}
313
EXPORT_SYMBOL_GPL(component_release_of);
314
315
/**
316
* component_compare_dev - A common component compare function for dev
317
* @dev: component device
318
* @data: @compare_data from component_match_add_release()
319
*
320
* A common compare function when compare_data is struce device. e.g.
321
* component_match_add(masterdev, &match, component_compare_dev, component_dev)
322
*/
323
int component_compare_dev(struct device *dev, void *data)
324
{
325
return dev == data;
326
}
327
EXPORT_SYMBOL_GPL(component_compare_dev);
328
329
/**
330
* component_compare_dev_name - A common component compare function for device name
331
* @dev: component device
332
* @data: @compare_data from component_match_add_release()
333
*
334
* A common compare function when compare_data is device name string. e.g.
335
* component_match_add(masterdev, &match, component_compare_dev_name,
336
* "component_dev_name")
337
*/
338
int component_compare_dev_name(struct device *dev, void *data)
339
{
340
return device_match_name(dev, data);
341
}
342
EXPORT_SYMBOL_GPL(component_compare_dev_name);
343
344
static void devm_component_match_release(struct device *parent, void *res)
345
{
346
struct component_match *match = res;
347
unsigned int i;
348
349
for (i = 0; i < match->num; i++) {
350
struct component_match_array *mc = &match->compare[i];
351
352
if (mc->release)
353
mc->release(parent, mc->data);
354
}
355
356
kfree(match->compare);
357
}
358
359
static int component_match_realloc(struct component_match *match, size_t num)
360
{
361
struct component_match_array *new;
362
363
if (match->alloc == num)
364
return 0;
365
366
new = kmalloc_array(num, sizeof(*new), GFP_KERNEL);
367
if (!new)
368
return -ENOMEM;
369
370
if (match->compare) {
371
memcpy(new, match->compare, sizeof(*new) *
372
min(match->num, num));
373
kfree(match->compare);
374
}
375
match->compare = new;
376
match->alloc = num;
377
378
return 0;
379
}
380
381
static void __component_match_add(struct device *parent,
382
struct component_match **matchptr,
383
void (*release)(struct device *, void *),
384
int (*compare)(struct device *, void *),
385
int (*compare_typed)(struct device *, int, void *),
386
void *compare_data)
387
{
388
struct component_match *match = *matchptr;
389
390
if (IS_ERR(match))
391
return;
392
393
if (!match) {
394
match = devres_alloc(devm_component_match_release,
395
sizeof(*match), GFP_KERNEL);
396
if (!match) {
397
*matchptr = ERR_PTR(-ENOMEM);
398
return;
399
}
400
401
devres_add(parent, match);
402
403
*matchptr = match;
404
}
405
406
if (match->num == match->alloc) {
407
size_t new_size = match->alloc + 16;
408
int ret;
409
410
ret = component_match_realloc(match, new_size);
411
if (ret) {
412
*matchptr = ERR_PTR(ret);
413
return;
414
}
415
}
416
417
match->compare[match->num].compare = compare;
418
match->compare[match->num].compare_typed = compare_typed;
419
match->compare[match->num].release = release;
420
match->compare[match->num].data = compare_data;
421
match->compare[match->num].component = NULL;
422
match->num++;
423
}
424
425
/**
426
* component_match_add_release - add a component match entry with release callback
427
* @parent: parent device of the aggregate driver
428
* @matchptr: pointer to the list of component matches
429
* @release: release function for @compare_data
430
* @compare: compare function to match against all components
431
* @compare_data: opaque pointer passed to the @compare function
432
*
433
* Adds a new component match to the list stored in @matchptr, which the
434
* aggregate driver needs to function. The list of component matches pointed to
435
* by @matchptr must be initialized to NULL before adding the first match. This
436
* only matches against components added with component_add().
437
*
438
* The allocated match list in @matchptr is automatically released using devm
439
* actions, where upon @release will be called to free any references held by
440
* @compare_data, e.g. when @compare_data is a &device_node that must be
441
* released with of_node_put().
442
*
443
* See also component_match_add() and component_match_add_typed().
444
*/
445
void component_match_add_release(struct device *parent,
446
struct component_match **matchptr,
447
void (*release)(struct device *, void *),
448
int (*compare)(struct device *, void *), void *compare_data)
449
{
450
__component_match_add(parent, matchptr, release, compare, NULL,
451
compare_data);
452
}
453
EXPORT_SYMBOL(component_match_add_release);
454
455
/**
456
* component_match_add_typed - add a component match entry for a typed component
457
* @parent: parent device of the aggregate driver
458
* @matchptr: pointer to the list of component matches
459
* @compare_typed: compare function to match against all typed components
460
* @compare_data: opaque pointer passed to the @compare function
461
*
462
* Adds a new component match to the list stored in @matchptr, which the
463
* aggregate driver needs to function. The list of component matches pointed to
464
* by @matchptr must be initialized to NULL before adding the first match. This
465
* only matches against components added with component_add_typed().
466
*
467
* The allocated match list in @matchptr is automatically released using devm
468
* actions.
469
*
470
* See also component_match_add_release() and component_match_add_typed().
471
*/
472
void component_match_add_typed(struct device *parent,
473
struct component_match **matchptr,
474
int (*compare_typed)(struct device *, int, void *), void *compare_data)
475
{
476
__component_match_add(parent, matchptr, NULL, NULL, compare_typed,
477
compare_data);
478
}
479
EXPORT_SYMBOL(component_match_add_typed);
480
481
static void free_aggregate_device(struct aggregate_device *adev)
482
{
483
struct component_match *match = adev->match;
484
int i;
485
486
component_debugfs_del(adev);
487
list_del(&adev->node);
488
489
if (match) {
490
for (i = 0; i < match->num; i++) {
491
struct component *c = match->compare[i].component;
492
if (c)
493
c->adev = NULL;
494
}
495
}
496
497
kfree(adev);
498
}
499
500
/**
501
* component_master_add_with_match - register an aggregate driver
502
* @parent: parent device of the aggregate driver
503
* @ops: callbacks for the aggregate driver
504
* @match: component match list for the aggregate driver
505
*
506
* Registers a new aggregate driver consisting of the components added to @match
507
* by calling one of the component_match_add() functions. Once all components in
508
* @match are available, it will be assembled by calling
509
* &component_master_ops.bind from @ops. Must be unregistered by calling
510
* component_master_del().
511
*/
512
int component_master_add_with_match(struct device *parent,
513
const struct component_master_ops *ops,
514
struct component_match *match)
515
{
516
struct aggregate_device *adev;
517
int ret;
518
519
/* Reallocate the match array for its true size */
520
ret = component_match_realloc(match, match->num);
521
if (ret)
522
return ret;
523
524
adev = kzalloc(sizeof(*adev), GFP_KERNEL);
525
if (!adev)
526
return -ENOMEM;
527
528
adev->parent = parent;
529
adev->ops = ops;
530
adev->match = match;
531
532
component_debugfs_add(adev);
533
/* Add to the list of available aggregate devices. */
534
mutex_lock(&component_mutex);
535
list_add(&adev->node, &aggregate_devices);
536
537
ret = try_to_bring_up_aggregate_device(adev, NULL);
538
539
if (ret < 0)
540
free_aggregate_device(adev);
541
542
mutex_unlock(&component_mutex);
543
544
return ret < 0 ? ret : 0;
545
}
546
EXPORT_SYMBOL_GPL(component_master_add_with_match);
547
548
/**
549
* component_master_del - unregister an aggregate driver
550
* @parent: parent device of the aggregate driver
551
* @ops: callbacks for the aggregate driver
552
*
553
* Unregisters an aggregate driver registered with
554
* component_master_add_with_match(). If necessary the aggregate driver is first
555
* disassembled by calling &component_master_ops.unbind from @ops.
556
*/
557
void component_master_del(struct device *parent,
558
const struct component_master_ops *ops)
559
{
560
struct aggregate_device *adev;
561
562
mutex_lock(&component_mutex);
563
adev = __aggregate_find(parent, ops);
564
if (adev) {
565
take_down_aggregate_device(adev);
566
free_aggregate_device(adev);
567
}
568
mutex_unlock(&component_mutex);
569
}
570
EXPORT_SYMBOL_GPL(component_master_del);
571
572
bool component_master_is_bound(struct device *parent,
573
const struct component_master_ops *ops)
574
{
575
struct aggregate_device *adev;
576
577
guard(mutex)(&component_mutex);
578
adev = __aggregate_find(parent, ops);
579
if (!adev)
580
return 0;
581
582
return adev->bound;
583
}
584
EXPORT_SYMBOL_GPL(component_master_is_bound);
585
586
static void component_unbind(struct component *component,
587
struct aggregate_device *adev, void *data)
588
{
589
if (WARN_ON(!component->bound))
590
return;
591
592
dev_dbg(adev->parent, "unbinding %s component %p (ops %ps)\n",
593
dev_name(component->dev), component, component->ops);
594
595
if (component->ops && component->ops->unbind)
596
component->ops->unbind(component->dev, adev->parent, data);
597
component->bound = false;
598
599
/* Release all resources claimed in the binding of this component */
600
devres_release_group(component->dev, component);
601
}
602
603
/**
604
* component_unbind_all - unbind all components of an aggregate driver
605
* @parent: parent device of the aggregate driver
606
* @data: opaque pointer, passed to all components
607
*
608
* Unbinds all components of the aggregate device by passing @data to their
609
* &component_ops.unbind functions. Should be called from
610
* &component_master_ops.unbind.
611
*/
612
void component_unbind_all(struct device *parent, void *data)
613
{
614
struct aggregate_device *adev;
615
struct component *c;
616
size_t i;
617
618
WARN_ON(!mutex_is_locked(&component_mutex));
619
620
adev = __aggregate_find(parent, NULL);
621
if (!adev)
622
return;
623
624
/* Unbind components in reverse order */
625
for (i = adev->match->num; i--; )
626
if (!adev->match->compare[i].duplicate) {
627
c = adev->match->compare[i].component;
628
component_unbind(c, adev, data);
629
}
630
}
631
EXPORT_SYMBOL_GPL(component_unbind_all);
632
633
static int component_bind(struct component *component, struct aggregate_device *adev,
634
void *data)
635
{
636
int ret;
637
638
/*
639
* Each component initialises inside its own devres group.
640
* This allows us to roll-back a failed component without
641
* affecting anything else.
642
*/
643
if (!devres_open_group(adev->parent, NULL, GFP_KERNEL))
644
return -ENOMEM;
645
646
/*
647
* Also open a group for the device itself: this allows us
648
* to release the resources claimed against the sub-device
649
* at the appropriate moment.
650
*/
651
if (!devres_open_group(component->dev, component, GFP_KERNEL)) {
652
devres_release_group(adev->parent, NULL);
653
return -ENOMEM;
654
}
655
656
dev_dbg(adev->parent, "binding %s (ops %ps)\n",
657
dev_name(component->dev), component->ops);
658
659
ret = component->ops->bind(component->dev, adev->parent, data);
660
if (!ret) {
661
component->bound = true;
662
663
/*
664
* Close the component device's group so that resources
665
* allocated in the binding are encapsulated for removal
666
* at unbind. Remove the group on the DRM device as we
667
* can clean those resources up independently.
668
*/
669
devres_close_group(component->dev, NULL);
670
devres_remove_group(adev->parent, NULL);
671
672
dev_info(adev->parent, "bound %s (ops %ps)\n",
673
dev_name(component->dev), component->ops);
674
} else {
675
devres_release_group(component->dev, NULL);
676
devres_release_group(adev->parent, NULL);
677
678
if (ret != -EPROBE_DEFER)
679
dev_err(adev->parent, "failed to bind %s (ops %ps): %d\n",
680
dev_name(component->dev), component->ops, ret);
681
}
682
683
return ret;
684
}
685
686
/**
687
* component_bind_all - bind all components of an aggregate driver
688
* @parent: parent device of the aggregate driver
689
* @data: opaque pointer, passed to all components
690
*
691
* Binds all components of the aggregate @dev by passing @data to their
692
* &component_ops.bind functions. Should be called from
693
* &component_master_ops.bind.
694
*/
695
int component_bind_all(struct device *parent, void *data)
696
{
697
struct aggregate_device *adev;
698
struct component *c;
699
size_t i;
700
int ret = 0;
701
702
WARN_ON(!mutex_is_locked(&component_mutex));
703
704
adev = __aggregate_find(parent, NULL);
705
if (!adev)
706
return -EINVAL;
707
708
/* Bind components in match order */
709
for (i = 0; i < adev->match->num; i++)
710
if (!adev->match->compare[i].duplicate) {
711
c = adev->match->compare[i].component;
712
ret = component_bind(c, adev, data);
713
if (ret)
714
break;
715
}
716
717
if (ret != 0) {
718
for (; i > 0; i--)
719
if (!adev->match->compare[i - 1].duplicate) {
720
c = adev->match->compare[i - 1].component;
721
component_unbind(c, adev, data);
722
}
723
}
724
725
return ret;
726
}
727
EXPORT_SYMBOL_GPL(component_bind_all);
728
729
static int __component_add(struct device *dev, const struct component_ops *ops,
730
int subcomponent)
731
{
732
struct component *component;
733
int ret;
734
735
component = kzalloc(sizeof(*component), GFP_KERNEL);
736
if (!component)
737
return -ENOMEM;
738
739
component->ops = ops;
740
component->dev = dev;
741
component->subcomponent = subcomponent;
742
743
dev_dbg(dev, "adding component (ops %ps)\n", ops);
744
745
mutex_lock(&component_mutex);
746
list_add_tail(&component->node, &component_list);
747
748
ret = try_to_bring_up_masters(component);
749
if (ret < 0) {
750
if (component->adev)
751
remove_component(component->adev, component);
752
list_del(&component->node);
753
754
kfree(component);
755
}
756
mutex_unlock(&component_mutex);
757
758
return ret < 0 ? ret : 0;
759
}
760
761
/**
762
* component_add_typed - register a component
763
* @dev: component device
764
* @ops: component callbacks
765
* @subcomponent: nonzero identifier for subcomponents
766
*
767
* Register a new component for @dev. Functions in @ops will be call when the
768
* aggregate driver is ready to bind the overall driver by calling
769
* component_bind_all(). See also &struct component_ops.
770
*
771
* @subcomponent must be nonzero and is used to differentiate between multiple
772
* components registered on the same device @dev. These components are match
773
* using component_match_add_typed().
774
*
775
* The component needs to be unregistered at driver unload/disconnect by
776
* calling component_del().
777
*
778
* See also component_add().
779
*/
780
int component_add_typed(struct device *dev, const struct component_ops *ops,
781
int subcomponent)
782
{
783
if (WARN_ON(subcomponent == 0))
784
return -EINVAL;
785
786
return __component_add(dev, ops, subcomponent);
787
}
788
EXPORT_SYMBOL_GPL(component_add_typed);
789
790
/**
791
* component_add - register a component
792
* @dev: component device
793
* @ops: component callbacks
794
*
795
* Register a new component for @dev. Functions in @ops will be called when the
796
* aggregate driver is ready to bind the overall driver by calling
797
* component_bind_all(). See also &struct component_ops.
798
*
799
* The component needs to be unregistered at driver unload/disconnect by
800
* calling component_del().
801
*
802
* See also component_add_typed() for a variant that allows multiple different
803
* components on the same device.
804
*/
805
int component_add(struct device *dev, const struct component_ops *ops)
806
{
807
return __component_add(dev, ops, 0);
808
}
809
EXPORT_SYMBOL_GPL(component_add);
810
811
/**
812
* component_del - unregister a component
813
* @dev: component device
814
* @ops: component callbacks
815
*
816
* Unregister a component added with component_add(). If the component is bound
817
* into an aggregate driver, this will force the entire aggregate driver, including
818
* all its components, to be unbound.
819
*/
820
void component_del(struct device *dev, const struct component_ops *ops)
821
{
822
struct component *c, *component = NULL;
823
824
mutex_lock(&component_mutex);
825
list_for_each_entry(c, &component_list, node)
826
if (c->dev == dev && c->ops == ops) {
827
list_del(&c->node);
828
component = c;
829
break;
830
}
831
832
if (component && component->adev) {
833
take_down_aggregate_device(component->adev);
834
remove_component(component->adev, component);
835
}
836
837
mutex_unlock(&component_mutex);
838
839
WARN_ON(!component);
840
kfree(component);
841
}
842
EXPORT_SYMBOL_GPL(component_del);
843
844