blob: 1049e9c0edb07e0922de238bd26c6c11cfa2d757 [file] [log] [blame]
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +08001/*
2 * Greybus "Core"
3 *
Alex Elder4441f472015-05-22 12:59:16 -05004 * Copyright 2014-2015 Google Inc.
5 * Copyright 2014-2015 Linaro Ltd.
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +08006 *
7 * Released under the GPLv2 only.
8 */
9
10#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
11
Bryan O'Donoghue5c8ad592015-09-18 16:38:45 +010012#define CREATE_TRACE_POINTS
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080013#include "greybus.h"
Bryan O'Donoghue5c8ad592015-09-18 16:38:45 +010014#include "greybus_trace.h"
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080015
David Lin61e13db2016-07-14 15:13:00 -050016#define GB_BUNDLE_AUTOSUSPEND_MS 3000
17
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080018/* Allow greybus to be disabled at boot if needed */
19static bool nogreybus;
20#ifdef MODULE
21module_param(nogreybus, bool, 0444);
22#else
Viresh Kumar8597e6b2014-10-20 16:45:50 +053023core_param(nogreybus, nogreybus, bool, 0444);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080024#endif
25int greybus_disabled(void)
26{
27 return nogreybus;
28}
29EXPORT_SYMBOL_GPL(greybus_disabled);
30
Viresh Kumar129a6fb2016-06-09 16:34:36 +053031static bool greybus_match_one_id(struct gb_bundle *bundle,
Johan Hovold700001a2015-11-21 10:52:03 +010032 const struct greybus_bundle_id *id)
33{
34 if ((id->match_flags & GREYBUS_ID_MATCH_VENDOR) &&
Johan Hovold9f592632015-11-25 15:58:56 +010035 (id->vendor != bundle->intf->vendor_id))
Viresh Kumar129a6fb2016-06-09 16:34:36 +053036 return false;
Johan Hovold700001a2015-11-21 10:52:03 +010037
38 if ((id->match_flags & GREYBUS_ID_MATCH_PRODUCT) &&
Johan Hovold9f592632015-11-25 15:58:56 +010039 (id->product != bundle->intf->product_id))
Viresh Kumar129a6fb2016-06-09 16:34:36 +053040 return false;
Johan Hovold700001a2015-11-21 10:52:03 +010041
42 if ((id->match_flags & GREYBUS_ID_MATCH_CLASS) &&
43 (id->class != bundle->class))
Viresh Kumar129a6fb2016-06-09 16:34:36 +053044 return false;
Johan Hovold700001a2015-11-21 10:52:03 +010045
Viresh Kumar129a6fb2016-06-09 16:34:36 +053046 return true;
Johan Hovold700001a2015-11-21 10:52:03 +010047}
48
49static const struct greybus_bundle_id *
50greybus_match_id(struct gb_bundle *bundle, const struct greybus_bundle_id *id)
51{
52 if (id == NULL)
53 return NULL;
54
55 for (; id->vendor || id->product || id->class || id->driver_info;
56 id++) {
57 if (greybus_match_one_id(bundle, id))
58 return id;
59 }
60
61 return NULL;
62}
63
Viresh Kumare4e55362016-05-12 11:34:05 +053064static int greybus_match_device(struct device *dev, struct device_driver *drv)
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080065{
Viresh Kumar95bd99d2014-11-14 17:24:59 +053066 struct greybus_driver *driver = to_greybus_driver(drv);
Johan Hovoldb77f9322016-01-08 20:13:41 +010067 struct gb_bundle *bundle;
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +053068 const struct greybus_bundle_id *id;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080069
Johan Hovoldb77f9322016-01-08 20:13:41 +010070 if (!is_gb_bundle(dev))
71 return 0;
72
73 bundle = to_gb_bundle(dev);
74
Johan Hovold700001a2015-11-21 10:52:03 +010075 id = greybus_match_id(bundle, driver->id_table);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080076 if (id)
77 return 1;
Viresh Kumar696e0cc2014-11-21 11:26:30 +053078 /* FIXME - Dynamic ids? */
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080079 return 0;
80}
81
82static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
83{
Johan Hovoldf0960d02015-12-03 19:18:02 +010084 struct gb_host_device *hd;
Johan Hovoldb15d97d2016-04-23 18:47:24 +020085 struct gb_module *module = NULL;
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080086 struct gb_interface *intf = NULL;
Johan Hovolda6e5b012016-04-13 19:19:02 +020087 struct gb_control *control = NULL;
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -050088 struct gb_bundle *bundle = NULL;
Johan Hovold88f7b962015-11-25 15:59:08 +010089 struct gb_svc *svc = NULL;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080090
Johan Hovold2adaefb2015-11-25 15:59:02 +010091 if (is_gb_host_device(dev)) {
92 hd = to_gb_host_device(dev);
Johan Hovoldb15d97d2016-04-23 18:47:24 +020093 } else if (is_gb_module(dev)) {
94 module = to_gb_module(dev);
95 hd = module->hd;
Greg Kroah-Hartmandf671552014-12-21 14:10:26 -080096 } else if (is_gb_interface(dev)) {
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080097 intf = to_gb_interface(dev);
Johan Hovoldb15d97d2016-04-23 18:47:24 +020098 module = intf->module;
Johan Hovoldf0960d02015-12-03 19:18:02 +010099 hd = intf->hd;
Johan Hovolda6e5b012016-04-13 19:19:02 +0200100 } else if (is_gb_control(dev)) {
101 control = to_gb_control(dev);
102 intf = control->intf;
Sandeep Patil3e29bcf2016-05-04 16:21:29 -0700103 module = intf->module;
Johan Hovolda6e5b012016-04-13 19:19:02 +0200104 hd = intf->hd;
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -0500105 } else if (is_gb_bundle(dev)) {
106 bundle = to_gb_bundle(dev);
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -0800107 intf = bundle->intf;
Johan Hovoldb15d97d2016-04-23 18:47:24 +0200108 module = intf->module;
Johan Hovoldf0960d02015-12-03 19:18:02 +0100109 hd = intf->hd;
Johan Hovold88f7b962015-11-25 15:59:08 +0100110 } else if (is_gb_svc(dev)) {
111 svc = to_gb_svc(dev);
Johan Hovoldf0960d02015-12-03 19:18:02 +0100112 hd = svc->hd;
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -0800113 } else {
114 dev_WARN(dev, "uevent for unknown greybus device \"type\"!\n");
115 return -EINVAL;
116 }
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800117
Johan Hovoldf0960d02015-12-03 19:18:02 +0100118 if (add_uevent_var(env, "BUS=%u", hd->bus_id))
119 return -ENOMEM;
120
Johan Hovoldb15d97d2016-04-23 18:47:24 +0200121 if (module) {
122 if (add_uevent_var(env, "MODULE=%u", module->module_id))
123 return -ENOMEM;
124 }
125
Johan Hovoldc5e6b052015-12-03 19:18:04 +0100126 if (intf) {
127 if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id))
128 return -ENOMEM;
Greg Kroah-Hartman611924d2016-02-26 21:54:38 -0800129 if (add_uevent_var(env, "GREYBUS_ID=%08x/%08x",
130 intf->vendor_id, intf->product_id))
Greg Kroah-Hartmande141312016-01-11 19:24:54 -0800131 return -ENOMEM;
Johan Hovoldc5e6b052015-12-03 19:18:04 +0100132 }
133
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -0500134 if (bundle) {
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -0800135 // FIXME
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -0500136 // add a uevent that can "load" a bundle type
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -0800137 // This is what we need to bind a driver to so use the info
138 // in gmod here as well
Johan Hovoldc29c0162015-12-04 10:44:24 +0100139
140 if (add_uevent_var(env, "BUNDLE=%u", bundle->id))
141 return -ENOMEM;
Greg Kroah-Hartman6ce4cc22016-01-21 18:13:41 -0800142 if (add_uevent_var(env, "BUNDLE_CLASS=%02x", bundle->class))
143 return -ENOMEM;
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -0800144 }
145
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800146 return 0;
147}
148
David Lin1f77b362016-07-12 17:41:21 -0700149static void greybus_shutdown(struct device *dev)
150{
151 if (is_gb_host_device(dev)) {
152 struct gb_host_device *hd;
153
154 hd = to_gb_host_device(dev);
155 gb_hd_shutdown(hd);
156 }
157}
158
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800159struct bus_type greybus_bus_type = {
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800160 .name = "greybus",
Viresh Kumare4e55362016-05-12 11:34:05 +0530161 .match = greybus_match_device,
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800162 .uevent = greybus_uevent,
David Lin1f77b362016-07-12 17:41:21 -0700163 .shutdown = greybus_shutdown,
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800164};
165
166static int greybus_probe(struct device *dev)
167{
168 struct greybus_driver *driver = to_greybus_driver(dev->driver);
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +0530169 struct gb_bundle *bundle = to_gb_bundle(dev);
170 const struct greybus_bundle_id *id;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800171 int retval;
172
173 /* match id */
Johan Hovold700001a2015-11-21 10:52:03 +0100174 id = greybus_match_id(bundle, driver->id_table);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800175 if (!id)
176 return -ENODEV;
177
David Lin61e13db2016-07-14 15:13:00 -0500178 retval = pm_runtime_get_sync(&bundle->intf->dev);
179 if (retval < 0) {
180 pm_runtime_put_noidle(&bundle->intf->dev);
181 return retval;
182 }
183
Bartosz Golaszewskif5c93de2016-07-21 18:09:34 +0200184 retval = gb_control_bundle_activate(bundle->intf->control, bundle->id);
185 if (retval) {
186 pm_runtime_put(&bundle->intf->dev);
187 return retval;
188 }
David Lin2d48b5b2016-07-14 15:13:00 -0500189
David Lin61e13db2016-07-14 15:13:00 -0500190 /*
191 * Unbound bundle devices are always deactivated. During probe, the
192 * Runtime PM is set to enabled and active and the usage count is
193 * incremented. If the driver supports runtime PM, it should call
194 * pm_runtime_put() in its probe routine and pm_runtime_get_sync()
195 * in remove routine.
196 */
197 pm_runtime_set_autosuspend_delay(dev, GB_BUNDLE_AUTOSUSPEND_MS);
198 pm_runtime_use_autosuspend(dev);
199 pm_runtime_get_noresume(dev);
200 pm_runtime_set_active(dev);
201 pm_runtime_enable(dev);
202
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +0530203 retval = driver->probe(bundle, id);
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100204 if (retval) {
205 /*
206 * Catch buggy drivers that fail to destroy their connections.
207 */
208 WARN_ON(!list_empty(&bundle->connections));
209
David Lin2d48b5b2016-07-14 15:13:00 -0500210 gb_control_bundle_deactivate(bundle->intf->control, bundle->id);
211
David Lin61e13db2016-07-14 15:13:00 -0500212 pm_runtime_disable(dev);
213 pm_runtime_set_suspended(dev);
214 pm_runtime_put_noidle(dev);
215 pm_runtime_dont_use_autosuspend(dev);
216 pm_runtime_put(&bundle->intf->dev);
217
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800218 return retval;
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100219 }
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800220
Bryan O'Donoghue10a24b72016-07-20 19:31:18 +0100221 gb_timesync_schedule_synchronous(bundle->intf);
Bryan O'Donoghue4a448422016-06-05 14:03:27 +0100222
David Lin61e13db2016-07-14 15:13:00 -0500223 pm_runtime_put(&bundle->intf->dev);
224
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800225 return 0;
226}
227
228static int greybus_remove(struct device *dev)
229{
230 struct greybus_driver *driver = to_greybus_driver(dev->driver);
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +0530231 struct gb_bundle *bundle = to_gb_bundle(dev);
Johan Hovoldfa8369c2016-01-19 12:51:09 +0100232 struct gb_connection *connection;
David Lin61e13db2016-07-14 15:13:00 -0500233 int retval;
234
235 retval = pm_runtime_get_sync(dev);
236 if (retval < 0)
237 dev_err(dev, "failed to resume bundle: %d\n", retval);
Johan Hovoldfa8369c2016-01-19 12:51:09 +0100238
Johan Hovold82278bf2016-06-09 18:42:19 +0200239 /*
240 * Disable (non-offloaded) connections early in case the interface is
241 * already gone to avoid unceccessary operation timeouts during
242 * driver disconnect. Otherwise, only disable incoming requests.
243 */
Johan Hovold47a2e672016-01-19 12:51:18 +0100244 list_for_each_entry(connection, &bundle->connections, bundle_links) {
Johan Hovold82278bf2016-06-09 18:42:19 +0200245 if (gb_connection_is_offloaded(connection))
246 continue;
247
Johan Hovold47a2e672016-01-19 12:51:18 +0100248 if (bundle->intf->disconnected)
Johan Hovold7aefe792016-05-27 17:26:22 +0200249 gb_connection_disable_forced(connection);
Johan Hovold47a2e672016-01-19 12:51:18 +0100250 else
251 gb_connection_disable_rx(connection);
252 }
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800253
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +0530254 driver->disconnect(bundle);
Johan Hovold02a54dd2016-01-19 12:51:10 +0100255
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100256 /* Catch buggy drivers that fail to destroy their connections. */
257 WARN_ON(!list_empty(&bundle->connections));
Johan Hovold02a54dd2016-01-19 12:51:10 +0100258
David Lin2d48b5b2016-07-14 15:13:00 -0500259 if (!bundle->intf->disconnected)
260 gb_control_bundle_deactivate(bundle->intf->control, bundle->id);
261
David Lin61e13db2016-07-14 15:13:00 -0500262 pm_runtime_put_noidle(dev);
263 pm_runtime_disable(dev);
264 pm_runtime_set_suspended(dev);
265 pm_runtime_dont_use_autosuspend(dev);
266 pm_runtime_put_noidle(dev);
267
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800268 return 0;
269}
270
271int greybus_register_driver(struct greybus_driver *driver, struct module *owner,
272 const char *mod_name)
273{
274 int retval;
275
276 if (greybus_disabled())
277 return -ENODEV;
278
Johan Hovold3c48d1b2016-01-08 20:13:42 +0100279 driver->driver.bus = &greybus_bus_type;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800280 driver->driver.name = driver->name;
281 driver->driver.probe = greybus_probe;
282 driver->driver.remove = greybus_remove;
283 driver->driver.owner = owner;
284 driver->driver.mod_name = mod_name;
285
286 retval = driver_register(&driver->driver);
287 if (retval)
288 return retval;
289
290 pr_info("registered new driver %s\n", driver->name);
291 return 0;
292}
293EXPORT_SYMBOL_GPL(greybus_register_driver);
294
Alex Elderfd1c2e52015-06-08 12:05:13 -0500295void greybus_deregister_driver(struct greybus_driver *driver)
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800296{
297 driver_unregister(&driver->driver);
298}
Alex Elderfd1c2e52015-06-08 12:05:13 -0500299EXPORT_SYMBOL_GPL(greybus_deregister_driver);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800300
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700301static int __init gb_init(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700302{
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700303 int retval;
304
Viresh Kumar337b0682015-03-20 20:29:13 +0530305 if (greybus_disabled())
306 return -ENODEV;
307
Alex Elderfb690ca2015-06-13 11:02:09 -0500308 BUILD_BUG_ON(CPORT_ID_MAX >= (long)CPORT_ID_BAD);
Alex Elder1bb3c722014-10-02 12:30:03 -0500309
Greg Kroah-Hartman48f70472015-03-27 11:38:06 +0100310 gb_debugfs_init();
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700311
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700312 retval = bus_register(&greybus_bus_type);
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700313 if (retval) {
Alex Elderf35ab902015-06-09 17:42:51 -0500314 pr_err("bus_register failed (%d)\n", retval);
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700315 goto error_bus;
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700316 }
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700317
Johan Hovold2adaefb2015-11-25 15:59:02 +0100318 retval = gb_hd_init();
319 if (retval) {
320 pr_err("gb_hd_init failed (%d)\n", retval);
321 goto error_hd;
322 }
323
Alex Elder2eb585f2014-10-16 06:35:34 -0500324 retval = gb_operation_init();
325 if (retval) {
Alex Elderf35ab902015-06-09 17:42:51 -0500326 pr_err("gb_operation_init failed (%d)\n", retval);
Alex Elder2eb585f2014-10-16 06:35:34 -0500327 goto error_operation;
328 }
329
Bryan O'Donoghue4a448422016-06-05 14:03:27 +0100330 retval = gb_timesync_init();
331 if (retval) {
332 pr_err("gb_timesync_init failed\n");
333 goto error_timesync;
334 }
Alex Elder19d03de2014-11-05 16:12:53 -0600335 return 0; /* Success */
336
Bryan O'Donoghue4a448422016-06-05 14:03:27 +0100337error_timesync:
Alex Elderf35ab902015-06-09 17:42:51 -0500338 gb_operation_exit();
Alex Elder2eb585f2014-10-16 06:35:34 -0500339error_operation:
Johan Hovold2adaefb2015-11-25 15:59:02 +0100340 gb_hd_exit();
341error_hd:
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700342 bus_unregister(&greybus_bus_type);
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700343error_bus:
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700344 gb_debugfs_cleanup();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700345
346 return retval;
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700347}
Viresh Kumard71aaf22015-03-19 17:02:49 +0530348module_init(gb_init);
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700349
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700350static void __exit gb_exit(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700351{
Bryan O'Donoghue4a448422016-06-05 14:03:27 +0100352 gb_timesync_exit();
Alex Elder2eb585f2014-10-16 06:35:34 -0500353 gb_operation_exit();
Johan Hovold2adaefb2015-11-25 15:59:02 +0100354 gb_hd_exit();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700355 bus_unregister(&greybus_bus_type);
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700356 gb_debugfs_cleanup();
Bryan O'Donoghue5c8ad592015-09-18 16:38:45 +0100357 tracepoint_synchronize_unregister();
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700358}
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700359module_exit(gb_exit);
Greg Kroah-Hartman6cf42a42015-04-13 19:51:33 +0200360MODULE_LICENSE("GPL v2");
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800361MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");