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, struct device_driver *drv) |
94 | { |
95 | 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, id: 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, format: "BUS=%u" , hd->bus_id)) |
148 | return -ENOMEM; |
149 | |
150 | if (module) { |
151 | if (add_uevent_var(env, format: "MODULE=%u" , module->module_id)) |
152 | return -ENOMEM; |
153 | } |
154 | |
155 | if (intf) { |
156 | if (add_uevent_var(env, format: "INTERFACE=%u" , intf->interface_id)) |
157 | return -ENOMEM; |
158 | if (add_uevent_var(env, format: "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, format: "BUNDLE=%u" , bundle->id)) |
170 | return -ENOMEM; |
171 | if (add_uevent_var(env, format: "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, id: driver->id_table); |
204 | if (!id) |
205 | return -ENODEV; |
206 | |
207 | retval = pm_runtime_get_sync(dev: &bundle->intf->dev); |
208 | if (retval < 0) { |
209 | pm_runtime_put_noidle(dev: &bundle->intf->dev); |
210 | return retval; |
211 | } |
212 | |
213 | retval = gb_control_bundle_activate(control: bundle->intf->control, bundle_id: bundle->id); |
214 | if (retval) { |
215 | pm_runtime_put(dev: &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(control: bundle->intf->control, bundle_id: 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(dev: &bundle->intf->dev); |
246 | |
247 | return retval; |
248 | } |
249 | |
250 | pm_runtime_put(dev: &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(control: bundle->intf->control, bundle_id: 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(drv: &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(drv: &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(bus: &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(bus: &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(bus: &greybus_bus_type); |
374 | gb_debugfs_cleanup(); |
375 | tracepoint_synchronize_unregister(); |
376 | } |
377 | module_exit(gb_exit); |
378 | MODULE_LICENSE("GPL v2" ); |
379 | MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>" ); |
380 | |