blob: e546c6431877fce0ce71967ebe3b222de6d6f972 [file] [log] [blame]
Greg Kroah-Hartmaneb50fd32017-11-07 14:58:41 +01001// SPDX-License-Identifier: GPL-2.0
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +08002/*
3 * Greybus "Core"
4 *
Alex Elder4441f472015-05-22 12:59:16 -05005 * Copyright 2014-2015 Google Inc.
6 * Copyright 2014-2015 Linaro Ltd.
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +08007 */
8
9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
Bryan O'Donoghue5c8ad592015-09-18 16:38:45 +010011#define CREATE_TRACE_POINTS
Greg Kroah-Hartmanec0ad862019-08-25 07:54:27 +020012#include <linux/greybus.h>
Bryan O'Donoghue5c8ad592015-09-18 16:38:45 +010013#include "greybus_trace.h"
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080014
David Lin61e13db2016-07-14 15:13:00 -050015#define GB_BUNDLE_AUTOSUSPEND_MS 3000
16
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080017/* Allow greybus to be disabled at boot if needed */
18static bool nogreybus;
19#ifdef MODULE
20module_param(nogreybus, bool, 0444);
21#else
Viresh Kumar8597e6b2014-10-20 16:45:50 +053022core_param(nogreybus, nogreybus, bool, 0444);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080023#endif
24int greybus_disabled(void)
25{
26 return nogreybus;
27}
28EXPORT_SYMBOL_GPL(greybus_disabled);
29
Viresh Kumar129a6fb2016-06-09 16:34:36 +053030static bool greybus_match_one_id(struct gb_bundle *bundle,
Ioannis Valasakisf17f5422018-11-09 13:53:31 +000031 const struct greybus_bundle_id *id)
Johan Hovold700001a2015-11-21 10:52:03 +010032{
33 if ((id->match_flags & GREYBUS_ID_MATCH_VENDOR) &&
Johan Hovold9f592632015-11-25 15:58:56 +010034 (id->vendor != bundle->intf->vendor_id))
Viresh Kumar129a6fb2016-06-09 16:34:36 +053035 return false;
Johan Hovold700001a2015-11-21 10:52:03 +010036
37 if ((id->match_flags & GREYBUS_ID_MATCH_PRODUCT) &&
Johan Hovold9f592632015-11-25 15:58:56 +010038 (id->product != bundle->intf->product_id))
Viresh Kumar129a6fb2016-06-09 16:34:36 +053039 return false;
Johan Hovold700001a2015-11-21 10:52:03 +010040
41 if ((id->match_flags & GREYBUS_ID_MATCH_CLASS) &&
42 (id->class != bundle->class))
Viresh Kumar129a6fb2016-06-09 16:34:36 +053043 return false;
Johan Hovold700001a2015-11-21 10:52:03 +010044
Viresh Kumar129a6fb2016-06-09 16:34:36 +053045 return true;
Johan Hovold700001a2015-11-21 10:52:03 +010046}
47
48static const struct greybus_bundle_id *
49greybus_match_id(struct gb_bundle *bundle, const struct greybus_bundle_id *id)
50{
Ioannis Valasakis9b765302018-11-09 13:53:24 +000051 if (!id)
Johan Hovold700001a2015-11-21 10:52:03 +010052 return NULL;
53
54 for (; id->vendor || id->product || id->class || id->driver_info;
55 id++) {
56 if (greybus_match_one_id(bundle, id))
57 return id;
58 }
59
60 return NULL;
61}
62
Viresh Kumare4e55362016-05-12 11:34:05 +053063static int greybus_match_device(struct device *dev, struct device_driver *drv)
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080064{
Viresh Kumar95bd99d2014-11-14 17:24:59 +053065 struct greybus_driver *driver = to_greybus_driver(drv);
Johan Hovoldb77f9322016-01-08 20:13:41 +010066 struct gb_bundle *bundle;
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +053067 const struct greybus_bundle_id *id;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080068
Johan Hovoldb77f9322016-01-08 20:13:41 +010069 if (!is_gb_bundle(dev))
70 return 0;
71
72 bundle = to_gb_bundle(dev);
73
Johan Hovold700001a2015-11-21 10:52:03 +010074 id = greybus_match_id(bundle, driver->id_table);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080075 if (id)
76 return 1;
Viresh Kumar696e0cc2014-11-21 11:26:30 +053077 /* FIXME - Dynamic ids? */
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080078 return 0;
79}
80
81static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
82{
Johan Hovoldf0960d02015-12-03 19:18:02 +010083 struct gb_host_device *hd;
Johan Hovoldb15d97d2016-04-23 18:47:24 +020084 struct gb_module *module = NULL;
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080085 struct gb_interface *intf = NULL;
Johan Hovolda6e5b012016-04-13 19:19:02 +020086 struct gb_control *control = NULL;
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -050087 struct gb_bundle *bundle = NULL;
Johan Hovold88f7b962015-11-25 15:59:08 +010088 struct gb_svc *svc = NULL;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080089
Johan Hovold2adaefb2015-11-25 15:59:02 +010090 if (is_gb_host_device(dev)) {
91 hd = to_gb_host_device(dev);
Johan Hovoldb15d97d2016-04-23 18:47:24 +020092 } else if (is_gb_module(dev)) {
93 module = to_gb_module(dev);
94 hd = module->hd;
Greg Kroah-Hartmandf671552014-12-21 14:10:26 -080095 } else if (is_gb_interface(dev)) {
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080096 intf = to_gb_interface(dev);
Johan Hovoldb15d97d2016-04-23 18:47:24 +020097 module = intf->module;
Johan Hovoldf0960d02015-12-03 19:18:02 +010098 hd = intf->hd;
Johan Hovolda6e5b012016-04-13 19:19:02 +020099 } else if (is_gb_control(dev)) {
100 control = to_gb_control(dev);
101 intf = control->intf;
Sandeep Patil3e29bcf2016-05-04 16:21:29 -0700102 module = intf->module;
Johan Hovolda6e5b012016-04-13 19:19:02 +0200103 hd = intf->hd;
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -0500104 } else if (is_gb_bundle(dev)) {
105 bundle = to_gb_bundle(dev);
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800106 intf = bundle->intf;
Johan Hovoldb15d97d2016-04-23 18:47:24 +0200107 module = intf->module;
Johan Hovoldf0960d02015-12-03 19:18:02 +0100108 hd = intf->hd;
Johan Hovold88f7b962015-11-25 15:59:08 +0100109 } else if (is_gb_svc(dev)) {
110 svc = to_gb_svc(dev);
Johan Hovoldf0960d02015-12-03 19:18:02 +0100111 hd = svc->hd;
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -0800112 } else {
113 dev_WARN(dev, "uevent for unknown greybus device \"type\"!\n");
114 return -EINVAL;
115 }
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800116
Johan Hovoldf0960d02015-12-03 19:18:02 +0100117 if (add_uevent_var(env, "BUS=%u", hd->bus_id))
118 return -ENOMEM;
119
Johan Hovoldb15d97d2016-04-23 18:47:24 +0200120 if (module) {
121 if (add_uevent_var(env, "MODULE=%u", module->module_id))
122 return -ENOMEM;
123 }
124
Johan Hovoldc5e6b052015-12-03 19:18:04 +0100125 if (intf) {
126 if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id))
127 return -ENOMEM;
Greg Kroah-Hartman611924d2016-02-26 21:54:38 -0800128 if (add_uevent_var(env, "GREYBUS_ID=%08x/%08x",
129 intf->vendor_id, intf->product_id))
Greg Kroah-Hartmande141312016-01-11 19:24:54 -0800130 return -ENOMEM;
Johan Hovoldc5e6b052015-12-03 19:18:04 +0100131 }
132
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -0500133 if (bundle) {
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -0800134 // FIXME
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -0500135 // add a uevent that can "load" a bundle type
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -0800136 // This is what we need to bind a driver to so use the info
137 // in gmod here as well
Johan Hovoldc29c0162015-12-04 10:44:24 +0100138
139 if (add_uevent_var(env, "BUNDLE=%u", bundle->id))
140 return -ENOMEM;
Greg Kroah-Hartman6ce4cc22016-01-21 18:13:41 -0800141 if (add_uevent_var(env, "BUNDLE_CLASS=%02x", bundle->class))
142 return -ENOMEM;
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -0800143 }
144
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800145 return 0;
146}
147
David Lin1f77b362016-07-12 17:41:21 -0700148static void greybus_shutdown(struct device *dev)
149{
150 if (is_gb_host_device(dev)) {
151 struct gb_host_device *hd;
152
153 hd = to_gb_host_device(dev);
154 gb_hd_shutdown(hd);
155 }
156}
157
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800158struct bus_type greybus_bus_type = {
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800159 .name = "greybus",
Viresh Kumare4e55362016-05-12 11:34:05 +0530160 .match = greybus_match_device,
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800161 .uevent = greybus_uevent,
David Lin1f77b362016-07-12 17:41:21 -0700162 .shutdown = greybus_shutdown,
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800163};
164
165static int greybus_probe(struct device *dev)
166{
167 struct greybus_driver *driver = to_greybus_driver(dev->driver);
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +0530168 struct gb_bundle *bundle = to_gb_bundle(dev);
169 const struct greybus_bundle_id *id;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800170 int retval;
171
172 /* match id */
Johan Hovold700001a2015-11-21 10:52:03 +0100173 id = greybus_match_id(bundle, driver->id_table);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800174 if (!id)
175 return -ENODEV;
176
David Lin61e13db2016-07-14 15:13:00 -0500177 retval = pm_runtime_get_sync(&bundle->intf->dev);
178 if (retval < 0) {
179 pm_runtime_put_noidle(&bundle->intf->dev);
180 return retval;
181 }
182
Bartosz Golaszewskif5c93de2016-07-21 18:09:34 +0200183 retval = gb_control_bundle_activate(bundle->intf->control, bundle->id);
184 if (retval) {
185 pm_runtime_put(&bundle->intf->dev);
186 return retval;
187 }
David Lin2d48b5b2016-07-14 15:13:00 -0500188
David Lin61e13db2016-07-14 15:13:00 -0500189 /*
190 * Unbound bundle devices are always deactivated. During probe, the
191 * Runtime PM is set to enabled and active and the usage count is
192 * incremented. If the driver supports runtime PM, it should call
193 * pm_runtime_put() in its probe routine and pm_runtime_get_sync()
194 * in remove routine.
195 */
196 pm_runtime_set_autosuspend_delay(dev, GB_BUNDLE_AUTOSUSPEND_MS);
197 pm_runtime_use_autosuspend(dev);
198 pm_runtime_get_noresume(dev);
199 pm_runtime_set_active(dev);
200 pm_runtime_enable(dev);
201
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +0530202 retval = driver->probe(bundle, id);
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100203 if (retval) {
204 /*
205 * Catch buggy drivers that fail to destroy their connections.
206 */
207 WARN_ON(!list_empty(&bundle->connections));
208
David Lin2d48b5b2016-07-14 15:13:00 -0500209 gb_control_bundle_deactivate(bundle->intf->control, bundle->id);
210
David Lin61e13db2016-07-14 15:13:00 -0500211 pm_runtime_disable(dev);
212 pm_runtime_set_suspended(dev);
213 pm_runtime_put_noidle(dev);
214 pm_runtime_dont_use_autosuspend(dev);
215 pm_runtime_put(&bundle->intf->dev);
216
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800217 return retval;
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100218 }
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800219
David Lin61e13db2016-07-14 15:13:00 -0500220 pm_runtime_put(&bundle->intf->dev);
221
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800222 return 0;
223}
224
225static int greybus_remove(struct device *dev)
226{
227 struct greybus_driver *driver = to_greybus_driver(dev->driver);
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +0530228 struct gb_bundle *bundle = to_gb_bundle(dev);
Johan Hovoldfa8369c2016-01-19 12:51:09 +0100229 struct gb_connection *connection;
David Lin61e13db2016-07-14 15:13:00 -0500230 int retval;
231
232 retval = pm_runtime_get_sync(dev);
233 if (retval < 0)
234 dev_err(dev, "failed to resume bundle: %d\n", retval);
Johan Hovoldfa8369c2016-01-19 12:51:09 +0100235
Johan Hovold82278bf2016-06-09 18:42:19 +0200236 /*
237 * Disable (non-offloaded) connections early in case the interface is
238 * already gone to avoid unceccessary operation timeouts during
239 * driver disconnect. Otherwise, only disable incoming requests.
240 */
Johan Hovold47a2e672016-01-19 12:51:18 +0100241 list_for_each_entry(connection, &bundle->connections, bundle_links) {
Johan Hovold82278bf2016-06-09 18:42:19 +0200242 if (gb_connection_is_offloaded(connection))
243 continue;
244
Johan Hovold47a2e672016-01-19 12:51:18 +0100245 if (bundle->intf->disconnected)
Johan Hovold7aefe792016-05-27 17:26:22 +0200246 gb_connection_disable_forced(connection);
Johan Hovold47a2e672016-01-19 12:51:18 +0100247 else
248 gb_connection_disable_rx(connection);
249 }
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800250
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +0530251 driver->disconnect(bundle);
Johan Hovold02a54dd2016-01-19 12:51:10 +0100252
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100253 /* Catch buggy drivers that fail to destroy their connections. */
254 WARN_ON(!list_empty(&bundle->connections));
Johan Hovold02a54dd2016-01-19 12:51:10 +0100255
David Lin2d48b5b2016-07-14 15:13:00 -0500256 if (!bundle->intf->disconnected)
257 gb_control_bundle_deactivate(bundle->intf->control, bundle->id);
258
David Lin61e13db2016-07-14 15:13:00 -0500259 pm_runtime_put_noidle(dev);
260 pm_runtime_disable(dev);
261 pm_runtime_set_suspended(dev);
262 pm_runtime_dont_use_autosuspend(dev);
263 pm_runtime_put_noidle(dev);
264
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800265 return 0;
266}
267
268int greybus_register_driver(struct greybus_driver *driver, struct module *owner,
Bhanusree Polab7417e32019-02-26 06:02:44 +0530269 const char *mod_name)
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800270{
271 int retval;
272
273 if (greybus_disabled())
274 return -ENODEV;
275
Johan Hovold3c48d1b2016-01-08 20:13:42 +0100276 driver->driver.bus = &greybus_bus_type;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800277 driver->driver.name = driver->name;
278 driver->driver.probe = greybus_probe;
279 driver->driver.remove = greybus_remove;
280 driver->driver.owner = owner;
281 driver->driver.mod_name = mod_name;
282
283 retval = driver_register(&driver->driver);
284 if (retval)
285 return retval;
286
287 pr_info("registered new driver %s\n", driver->name);
288 return 0;
289}
290EXPORT_SYMBOL_GPL(greybus_register_driver);
291
Alex Elderfd1c2e52015-06-08 12:05:13 -0500292void greybus_deregister_driver(struct greybus_driver *driver)
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800293{
294 driver_unregister(&driver->driver);
295}
Alex Elderfd1c2e52015-06-08 12:05:13 -0500296EXPORT_SYMBOL_GPL(greybus_deregister_driver);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800297
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700298static int __init gb_init(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700299{
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700300 int retval;
301
Viresh Kumar337b0682015-03-20 20:29:13 +0530302 if (greybus_disabled())
303 return -ENODEV;
304
Alex Elderfb690ca2015-06-13 11:02:09 -0500305 BUILD_BUG_ON(CPORT_ID_MAX >= (long)CPORT_ID_BAD);
Alex Elder1bb3c722014-10-02 12:30:03 -0500306
Greg Kroah-Hartman48f70472015-03-27 11:38:06 +0100307 gb_debugfs_init();
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700308
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700309 retval = bus_register(&greybus_bus_type);
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700310 if (retval) {
Alex Elderf35ab902015-06-09 17:42:51 -0500311 pr_err("bus_register failed (%d)\n", retval);
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700312 goto error_bus;
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700313 }
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700314
Johan Hovold2adaefb2015-11-25 15:59:02 +0100315 retval = gb_hd_init();
316 if (retval) {
317 pr_err("gb_hd_init failed (%d)\n", retval);
318 goto error_hd;
319 }
320
Alex Elder2eb585f2014-10-16 06:35:34 -0500321 retval = gb_operation_init();
322 if (retval) {
Alex Elderf35ab902015-06-09 17:42:51 -0500323 pr_err("gb_operation_init failed (%d)\n", retval);
Alex Elder2eb585f2014-10-16 06:35:34 -0500324 goto error_operation;
325 }
Alex Elder19d03de2014-11-05 16:12:53 -0600326 return 0; /* Success */
327
Alex Elder2eb585f2014-10-16 06:35:34 -0500328error_operation:
Johan Hovold2adaefb2015-11-25 15:59:02 +0100329 gb_hd_exit();
330error_hd:
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700331 bus_unregister(&greybus_bus_type);
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700332error_bus:
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700333 gb_debugfs_cleanup();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700334
335 return retval;
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700336}
Viresh Kumard71aaf22015-03-19 17:02:49 +0530337module_init(gb_init);
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700338
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700339static void __exit gb_exit(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700340{
Alex Elder2eb585f2014-10-16 06:35:34 -0500341 gb_operation_exit();
Johan Hovold2adaefb2015-11-25 15:59:02 +0100342 gb_hd_exit();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700343 bus_unregister(&greybus_bus_type);
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700344 gb_debugfs_cleanup();
Bryan O'Donoghue5c8ad592015-09-18 16:38:45 +0100345 tracepoint_synchronize_unregister();
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700346}
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700347module_exit(gb_exit);
Greg Kroah-Hartman6cf42a42015-04-13 19:51:33 +0200348MODULE_LICENSE("GPL v2");
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800349MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");