xref: /linux/drivers/greybus/core.c (revision 94c37d42cb7ca362aee9633bec2dbeed787edf3e)
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 static int greybus_probe(struct device *dev)
189 {
190 	struct greybus_driver *driver = to_greybus_driver(dev->driver);
191 	struct gb_bundle *bundle = to_gb_bundle(dev);
192 	const struct greybus_bundle_id *id;
193 	int retval;
194 
195 	/* match id */
196 	id = greybus_match_id(bundle, driver->id_table);
197 	if (!id)
198 		return -ENODEV;
199 
200 	retval = pm_runtime_get_sync(&bundle->intf->dev);
201 	if (retval < 0) {
202 		pm_runtime_put_noidle(&bundle->intf->dev);
203 		return retval;
204 	}
205 
206 	retval = gb_control_bundle_activate(bundle->intf->control, bundle->id);
207 	if (retval) {
208 		pm_runtime_put(&bundle->intf->dev);
209 		return retval;
210 	}
211 
212 	/*
213 	 * Unbound bundle devices are always deactivated. During probe, the
214 	 * Runtime PM is set to enabled and active and the usage count is
215 	 * incremented. If the driver supports runtime PM, it should call
216 	 * pm_runtime_put() in its probe routine and pm_runtime_get_sync()
217 	 * in remove routine.
218 	 */
219 	pm_runtime_set_autosuspend_delay(dev, GB_BUNDLE_AUTOSUSPEND_MS);
220 	pm_runtime_use_autosuspend(dev);
221 	pm_runtime_get_noresume(dev);
222 	pm_runtime_set_active(dev);
223 	pm_runtime_enable(dev);
224 
225 	retval = driver->probe(bundle, id);
226 	if (retval) {
227 		/*
228 		 * Catch buggy drivers that fail to destroy their connections.
229 		 */
230 		WARN_ON(!list_empty(&bundle->connections));
231 
232 		gb_control_bundle_deactivate(bundle->intf->control, bundle->id);
233 
234 		pm_runtime_disable(dev);
235 		pm_runtime_set_suspended(dev);
236 		pm_runtime_put_noidle(dev);
237 		pm_runtime_dont_use_autosuspend(dev);
238 		pm_runtime_put(&bundle->intf->dev);
239 
240 		return retval;
241 	}
242 
243 	pm_runtime_put(&bundle->intf->dev);
244 
245 	return 0;
246 }
247 
248 static void greybus_remove(struct device *dev)
249 {
250 	struct greybus_driver *driver = to_greybus_driver(dev->driver);
251 	struct gb_bundle *bundle = to_gb_bundle(dev);
252 	struct gb_connection *connection;
253 	int retval;
254 
255 	retval = pm_runtime_get_sync(dev);
256 	if (retval < 0)
257 		dev_err(dev, "failed to resume bundle: %d\n", retval);
258 
259 	/*
260 	 * Disable (non-offloaded) connections early in case the interface is
261 	 * already gone to avoid unceccessary operation timeouts during
262 	 * driver disconnect. Otherwise, only disable incoming requests.
263 	 */
264 	list_for_each_entry(connection, &bundle->connections, bundle_links) {
265 		if (gb_connection_is_offloaded(connection))
266 			continue;
267 
268 		if (bundle->intf->disconnected)
269 			gb_connection_disable_forced(connection);
270 		else
271 			gb_connection_disable_rx(connection);
272 	}
273 
274 	driver->disconnect(bundle);
275 
276 	/* Catch buggy drivers that fail to destroy their connections. */
277 	WARN_ON(!list_empty(&bundle->connections));
278 
279 	if (!bundle->intf->disconnected)
280 		gb_control_bundle_deactivate(bundle->intf->control, bundle->id);
281 
282 	pm_runtime_put_noidle(dev);
283 	pm_runtime_disable(dev);
284 	pm_runtime_set_suspended(dev);
285 	pm_runtime_dont_use_autosuspend(dev);
286 	pm_runtime_put_noidle(dev);
287 }
288 
289 const struct bus_type greybus_bus_type = {
290 	.name =		"greybus",
291 	.match =	greybus_match_device,
292 	.uevent =	greybus_uevent,
293 	.probe =	greybus_probe,
294 	.remove =	greybus_remove,
295 	.shutdown =	greybus_shutdown,
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.owner = owner;
309 	driver->driver.mod_name = mod_name;
310 
311 	retval = driver_register(&driver->driver);
312 	if (retval)
313 		return retval;
314 
315 	pr_info("registered new driver %s\n", driver->name);
316 	return 0;
317 }
318 EXPORT_SYMBOL_GPL(greybus_register_driver);
319 
320 void greybus_deregister_driver(struct greybus_driver *driver)
321 {
322 	driver_unregister(&driver->driver);
323 }
324 EXPORT_SYMBOL_GPL(greybus_deregister_driver);
325 
326 static int __init gb_init(void)
327 {
328 	int retval;
329 
330 	if (greybus_disabled())
331 		return -ENODEV;
332 
333 	BUILD_BUG_ON(CPORT_ID_MAX >= (long)CPORT_ID_BAD);
334 
335 	gb_debugfs_init();
336 
337 	retval = bus_register(&greybus_bus_type);
338 	if (retval) {
339 		pr_err("bus_register failed (%d)\n", retval);
340 		goto error_bus;
341 	}
342 
343 	retval = gb_hd_init();
344 	if (retval) {
345 		pr_err("gb_hd_init failed (%d)\n", retval);
346 		goto error_hd;
347 	}
348 
349 	retval = gb_operation_init();
350 	if (retval) {
351 		pr_err("gb_operation_init failed (%d)\n", retval);
352 		goto error_operation;
353 	}
354 	return 0;	/* Success */
355 
356 error_operation:
357 	gb_hd_exit();
358 error_hd:
359 	bus_unregister(&greybus_bus_type);
360 error_bus:
361 	gb_debugfs_cleanup();
362 
363 	return retval;
364 }
365 module_init(gb_init);
366 
367 static void __exit gb_exit(void)
368 {
369 	gb_operation_exit();
370 	gb_hd_exit();
371 	bus_unregister(&greybus_bus_type);
372 	gb_debugfs_cleanup();
373 	tracepoint_synchronize_unregister();
374 }
375 module_exit(gb_exit);
376 MODULE_DESCRIPTION("Greybus core driver");
377 MODULE_LICENSE("GPL v2");
378 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");
379