Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/greybus/core.c
26278 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Greybus "Core"
4
*
5
* Copyright 2014-2015 Google Inc.
6
* Copyright 2014-2015 Linaro Ltd.
7
*/
8
9
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
11
#define CREATE_TRACE_POINTS
12
#include <linux/greybus.h>
13
#include "greybus_trace.h"
14
15
#define GB_BUNDLE_AUTOSUSPEND_MS 3000
16
17
/* Allow greybus to be disabled at boot if needed */
18
static bool nogreybus;
19
#ifdef MODULE
20
module_param(nogreybus, bool, 0444);
21
#else
22
core_param(nogreybus, nogreybus, bool, 0444);
23
#endif
24
int greybus_disabled(void)
25
{
26
return nogreybus;
27
}
28
EXPORT_SYMBOL_GPL(greybus_disabled);
29
30
static int is_gb_host_device(const struct device *dev)
31
{
32
return dev->type == &greybus_hd_type;
33
}
34
35
static int is_gb_module(const struct device *dev)
36
{
37
return dev->type == &greybus_module_type;
38
}
39
40
static int is_gb_interface(const struct device *dev)
41
{
42
return dev->type == &greybus_interface_type;
43
}
44
45
static int is_gb_control(const struct device *dev)
46
{
47
return dev->type == &greybus_control_type;
48
}
49
50
static int is_gb_bundle(const struct device *dev)
51
{
52
return dev->type == &greybus_bundle_type;
53
}
54
55
static int is_gb_svc(const struct device *dev)
56
{
57
return dev->type == &greybus_svc_type;
58
}
59
60
static bool greybus_match_one_id(struct gb_bundle *bundle,
61
const struct greybus_bundle_id *id)
62
{
63
if ((id->match_flags & GREYBUS_ID_MATCH_VENDOR) &&
64
(id->vendor != bundle->intf->vendor_id))
65
return false;
66
67
if ((id->match_flags & GREYBUS_ID_MATCH_PRODUCT) &&
68
(id->product != bundle->intf->product_id))
69
return false;
70
71
if ((id->match_flags & GREYBUS_ID_MATCH_CLASS) &&
72
(id->class != bundle->class))
73
return false;
74
75
return true;
76
}
77
78
static const struct greybus_bundle_id *
79
greybus_match_id(struct gb_bundle *bundle, const struct greybus_bundle_id *id)
80
{
81
if (!id)
82
return NULL;
83
84
for (; id->vendor || id->product || id->class || id->driver_info;
85
id++) {
86
if (greybus_match_one_id(bundle, id))
87
return id;
88
}
89
90
return NULL;
91
}
92
93
static int greybus_match_device(struct device *dev, const struct device_driver *drv)
94
{
95
const struct greybus_driver *driver = to_greybus_driver(drv);
96
struct gb_bundle *bundle;
97
const struct greybus_bundle_id *id;
98
99
if (!is_gb_bundle(dev))
100
return 0;
101
102
bundle = to_gb_bundle(dev);
103
104
id = greybus_match_id(bundle, driver->id_table);
105
if (id)
106
return 1;
107
/* FIXME - Dynamic ids? */
108
return 0;
109
}
110
111
static int greybus_uevent(const struct device *dev, struct kobj_uevent_env *env)
112
{
113
const struct gb_host_device *hd;
114
const struct gb_module *module = NULL;
115
const struct gb_interface *intf = NULL;
116
const struct gb_control *control = NULL;
117
const struct gb_bundle *bundle = NULL;
118
const struct gb_svc *svc = NULL;
119
120
if (is_gb_host_device(dev)) {
121
hd = to_gb_host_device(dev);
122
} else if (is_gb_module(dev)) {
123
module = to_gb_module(dev);
124
hd = module->hd;
125
} else if (is_gb_interface(dev)) {
126
intf = to_gb_interface(dev);
127
module = intf->module;
128
hd = intf->hd;
129
} else if (is_gb_control(dev)) {
130
control = to_gb_control(dev);
131
intf = control->intf;
132
module = intf->module;
133
hd = intf->hd;
134
} else if (is_gb_bundle(dev)) {
135
bundle = to_gb_bundle(dev);
136
intf = bundle->intf;
137
module = intf->module;
138
hd = intf->hd;
139
} else if (is_gb_svc(dev)) {
140
svc = to_gb_svc(dev);
141
hd = svc->hd;
142
} else {
143
dev_WARN(dev, "uevent for unknown greybus device \"type\"!\n");
144
return -EINVAL;
145
}
146
147
if (add_uevent_var(env, "BUS=%u", hd->bus_id))
148
return -ENOMEM;
149
150
if (module) {
151
if (add_uevent_var(env, "MODULE=%u", module->module_id))
152
return -ENOMEM;
153
}
154
155
if (intf) {
156
if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id))
157
return -ENOMEM;
158
if (add_uevent_var(env, "GREYBUS_ID=%08x/%08x",
159
intf->vendor_id, intf->product_id))
160
return -ENOMEM;
161
}
162
163
if (bundle) {
164
// FIXME
165
// add a uevent that can "load" a bundle type
166
// This is what we need to bind a driver to so use the info
167
// in gmod here as well
168
169
if (add_uevent_var(env, "BUNDLE=%u", bundle->id))
170
return -ENOMEM;
171
if (add_uevent_var(env, "BUNDLE_CLASS=%02x", bundle->class))
172
return -ENOMEM;
173
}
174
175
return 0;
176
}
177
178
static void greybus_shutdown(struct device *dev)
179
{
180
if (is_gb_host_device(dev)) {
181
struct gb_host_device *hd;
182
183
hd = to_gb_host_device(dev);
184
gb_hd_shutdown(hd);
185
}
186
}
187
188
const struct bus_type greybus_bus_type = {
189
.name = "greybus",
190
.match = greybus_match_device,
191
.uevent = greybus_uevent,
192
.shutdown = greybus_shutdown,
193
};
194
195
static int greybus_probe(struct device *dev)
196
{
197
struct greybus_driver *driver = to_greybus_driver(dev->driver);
198
struct gb_bundle *bundle = to_gb_bundle(dev);
199
const struct greybus_bundle_id *id;
200
int retval;
201
202
/* match id */
203
id = greybus_match_id(bundle, driver->id_table);
204
if (!id)
205
return -ENODEV;
206
207
retval = pm_runtime_get_sync(&bundle->intf->dev);
208
if (retval < 0) {
209
pm_runtime_put_noidle(&bundle->intf->dev);
210
return retval;
211
}
212
213
retval = gb_control_bundle_activate(bundle->intf->control, bundle->id);
214
if (retval) {
215
pm_runtime_put(&bundle->intf->dev);
216
return retval;
217
}
218
219
/*
220
* Unbound bundle devices are always deactivated. During probe, the
221
* Runtime PM is set to enabled and active and the usage count is
222
* incremented. If the driver supports runtime PM, it should call
223
* pm_runtime_put() in its probe routine and pm_runtime_get_sync()
224
* in remove routine.
225
*/
226
pm_runtime_set_autosuspend_delay(dev, GB_BUNDLE_AUTOSUSPEND_MS);
227
pm_runtime_use_autosuspend(dev);
228
pm_runtime_get_noresume(dev);
229
pm_runtime_set_active(dev);
230
pm_runtime_enable(dev);
231
232
retval = driver->probe(bundle, id);
233
if (retval) {
234
/*
235
* Catch buggy drivers that fail to destroy their connections.
236
*/
237
WARN_ON(!list_empty(&bundle->connections));
238
239
gb_control_bundle_deactivate(bundle->intf->control, bundle->id);
240
241
pm_runtime_disable(dev);
242
pm_runtime_set_suspended(dev);
243
pm_runtime_put_noidle(dev);
244
pm_runtime_dont_use_autosuspend(dev);
245
pm_runtime_put(&bundle->intf->dev);
246
247
return retval;
248
}
249
250
pm_runtime_put(&bundle->intf->dev);
251
252
return 0;
253
}
254
255
static int greybus_remove(struct device *dev)
256
{
257
struct greybus_driver *driver = to_greybus_driver(dev->driver);
258
struct gb_bundle *bundle = to_gb_bundle(dev);
259
struct gb_connection *connection;
260
int retval;
261
262
retval = pm_runtime_get_sync(dev);
263
if (retval < 0)
264
dev_err(dev, "failed to resume bundle: %d\n", retval);
265
266
/*
267
* Disable (non-offloaded) connections early in case the interface is
268
* already gone to avoid unceccessary operation timeouts during
269
* driver disconnect. Otherwise, only disable incoming requests.
270
*/
271
list_for_each_entry(connection, &bundle->connections, bundle_links) {
272
if (gb_connection_is_offloaded(connection))
273
continue;
274
275
if (bundle->intf->disconnected)
276
gb_connection_disable_forced(connection);
277
else
278
gb_connection_disable_rx(connection);
279
}
280
281
driver->disconnect(bundle);
282
283
/* Catch buggy drivers that fail to destroy their connections. */
284
WARN_ON(!list_empty(&bundle->connections));
285
286
if (!bundle->intf->disconnected)
287
gb_control_bundle_deactivate(bundle->intf->control, bundle->id);
288
289
pm_runtime_put_noidle(dev);
290
pm_runtime_disable(dev);
291
pm_runtime_set_suspended(dev);
292
pm_runtime_dont_use_autosuspend(dev);
293
pm_runtime_put_noidle(dev);
294
295
return 0;
296
}
297
298
int greybus_register_driver(struct greybus_driver *driver, struct module *owner,
299
const char *mod_name)
300
{
301
int retval;
302
303
if (greybus_disabled())
304
return -ENODEV;
305
306
driver->driver.bus = &greybus_bus_type;
307
driver->driver.name = driver->name;
308
driver->driver.probe = greybus_probe;
309
driver->driver.remove = greybus_remove;
310
driver->driver.owner = owner;
311
driver->driver.mod_name = mod_name;
312
313
retval = driver_register(&driver->driver);
314
if (retval)
315
return retval;
316
317
pr_info("registered new driver %s\n", driver->name);
318
return 0;
319
}
320
EXPORT_SYMBOL_GPL(greybus_register_driver);
321
322
void greybus_deregister_driver(struct greybus_driver *driver)
323
{
324
driver_unregister(&driver->driver);
325
}
326
EXPORT_SYMBOL_GPL(greybus_deregister_driver);
327
328
static int __init gb_init(void)
329
{
330
int retval;
331
332
if (greybus_disabled())
333
return -ENODEV;
334
335
BUILD_BUG_ON(CPORT_ID_MAX >= (long)CPORT_ID_BAD);
336
337
gb_debugfs_init();
338
339
retval = bus_register(&greybus_bus_type);
340
if (retval) {
341
pr_err("bus_register failed (%d)\n", retval);
342
goto error_bus;
343
}
344
345
retval = gb_hd_init();
346
if (retval) {
347
pr_err("gb_hd_init failed (%d)\n", retval);
348
goto error_hd;
349
}
350
351
retval = gb_operation_init();
352
if (retval) {
353
pr_err("gb_operation_init failed (%d)\n", retval);
354
goto error_operation;
355
}
356
return 0; /* Success */
357
358
error_operation:
359
gb_hd_exit();
360
error_hd:
361
bus_unregister(&greybus_bus_type);
362
error_bus:
363
gb_debugfs_cleanup();
364
365
return retval;
366
}
367
module_init(gb_init);
368
369
static void __exit gb_exit(void)
370
{
371
gb_operation_exit();
372
gb_hd_exit();
373
bus_unregister(&greybus_bus_type);
374
gb_debugfs_cleanup();
375
tracepoint_synchronize_unregister();
376
}
377
module_exit(gb_exit);
378
MODULE_DESCRIPTION("Greybus core driver");
379
MODULE_LICENSE("GPL v2");
380
MODULE_AUTHOR("Greg Kroah-Hartman <[email protected]>");
381
382