blob: 2fb95744e01c3a17411a2bc74f9d41e917ea3ac7 [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
Johan Hovold8ec589b2016-01-29 15:42:31 +010013#include "firmware.h"
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080014#include "greybus.h"
Bryan O'Donoghue5c8ad592015-09-18 16:38:45 +010015#include "greybus_trace.h"
Johan Hovold5dda7e52016-01-19 12:50:59 +010016#include "legacy.h"
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080017
Bryan O'Donoghue32b2b162015-09-22 18:06:38 -070018EXPORT_TRACEPOINT_SYMBOL_GPL(gb_host_device_send);
19EXPORT_TRACEPOINT_SYMBOL_GPL(gb_host_device_recv);
20
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080021/* Allow greybus to be disabled at boot if needed */
22static bool nogreybus;
23#ifdef MODULE
24module_param(nogreybus, bool, 0444);
25#else
Viresh Kumar8597e6b2014-10-20 16:45:50 +053026core_param(nogreybus, nogreybus, bool, 0444);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080027#endif
28int greybus_disabled(void)
29{
30 return nogreybus;
31}
32EXPORT_SYMBOL_GPL(greybus_disabled);
33
Johan Hovold700001a2015-11-21 10:52:03 +010034static int greybus_match_one_id(struct gb_bundle *bundle,
35 const struct greybus_bundle_id *id)
36{
37 if ((id->match_flags & GREYBUS_ID_MATCH_VENDOR) &&
Johan Hovold9f592632015-11-25 15:58:56 +010038 (id->vendor != bundle->intf->vendor_id))
Johan Hovold700001a2015-11-21 10:52:03 +010039 return 0;
40
41 if ((id->match_flags & GREYBUS_ID_MATCH_PRODUCT) &&
Johan Hovold9f592632015-11-25 15:58:56 +010042 (id->product != bundle->intf->product_id))
Johan Hovold700001a2015-11-21 10:52:03 +010043 return 0;
44
45 if ((id->match_flags & GREYBUS_ID_MATCH_CLASS) &&
46 (id->class != bundle->class))
47 return 0;
48
49 return 1;
50}
51
52static const struct greybus_bundle_id *
53greybus_match_id(struct gb_bundle *bundle, const struct greybus_bundle_id *id)
54{
55 if (id == NULL)
56 return NULL;
57
58 for (; id->vendor || id->product || id->class || id->driver_info;
59 id++) {
60 if (greybus_match_one_id(bundle, id))
61 return id;
62 }
63
64 return NULL;
65}
66
Alex Elder778c69c2014-09-22 19:19:03 -050067static int greybus_module_match(struct device *dev, struct device_driver *drv)
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080068{
Viresh Kumar95bd99d2014-11-14 17:24:59 +053069 struct greybus_driver *driver = to_greybus_driver(drv);
Johan Hovoldb77f9322016-01-08 20:13:41 +010070 struct gb_bundle *bundle;
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +053071 const struct greybus_bundle_id *id;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080072
Johan Hovoldb77f9322016-01-08 20:13:41 +010073 if (!is_gb_bundle(dev))
74 return 0;
75
76 bundle = to_gb_bundle(dev);
77
Johan Hovold700001a2015-11-21 10:52:03 +010078 id = greybus_match_id(bundle, driver->id_table);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080079 if (id)
80 return 1;
Viresh Kumar696e0cc2014-11-21 11:26:30 +053081 /* FIXME - Dynamic ids? */
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080082 return 0;
83}
84
85static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
86{
Johan Hovoldf0960d02015-12-03 19:18:02 +010087 struct gb_host_device *hd;
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080088 struct gb_interface *intf = NULL;
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -050089 struct gb_bundle *bundle = NULL;
Johan Hovold88f7b962015-11-25 15:59:08 +010090 struct gb_svc *svc = NULL;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080091
Johan Hovold2adaefb2015-11-25 15:59:02 +010092 if (is_gb_host_device(dev)) {
93 hd = to_gb_host_device(dev);
Greg Kroah-Hartmandf671552014-12-21 14:10:26 -080094 } else if (is_gb_interface(dev)) {
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080095 intf = to_gb_interface(dev);
Johan Hovoldf0960d02015-12-03 19:18:02 +010096 hd = intf->hd;
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -050097 } else if (is_gb_bundle(dev)) {
98 bundle = to_gb_bundle(dev);
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080099 intf = bundle->intf;
Johan Hovoldf0960d02015-12-03 19:18:02 +0100100 hd = intf->hd;
Johan Hovold88f7b962015-11-25 15:59:08 +0100101 } else if (is_gb_svc(dev)) {
102 svc = to_gb_svc(dev);
Johan Hovoldf0960d02015-12-03 19:18:02 +0100103 hd = svc->hd;
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -0800104 } else {
105 dev_WARN(dev, "uevent for unknown greybus device \"type\"!\n");
106 return -EINVAL;
107 }
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800108
Johan Hovoldf0960d02015-12-03 19:18:02 +0100109 if (add_uevent_var(env, "BUS=%u", hd->bus_id))
110 return -ENOMEM;
111
Johan Hovoldc5e6b052015-12-03 19:18:04 +0100112 if (intf) {
113 if (add_uevent_var(env, "INTERFACE=%u", intf->interface_id))
114 return -ENOMEM;
Greg Kroah-Hartmande141312016-01-11 19:24:54 -0800115 if (add_uevent_var(env, "GREYBUS_ID=%04x/%04x",
116 (u16)(intf->vendor_id & 0xffff),
117 (u16)(intf->product_id & 0xffff)))
118 return -ENOMEM;
Johan Hovoldc5e6b052015-12-03 19:18:04 +0100119 }
120
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -0500121 if (bundle) {
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -0800122 // FIXME
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -0500123 // add a uevent that can "load" a bundle type
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -0800124 // This is what we need to bind a driver to so use the info
125 // in gmod here as well
Johan Hovoldc29c0162015-12-04 10:44:24 +0100126
127 if (add_uevent_var(env, "BUNDLE=%u", bundle->id))
128 return -ENOMEM;
Greg Kroah-Hartman6ce4cc22016-01-21 18:13:41 -0800129 if (add_uevent_var(env, "BUNDLE_CLASS=%02x", bundle->class))
130 return -ENOMEM;
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -0800131 }
132
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800133 return 0;
134}
135
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800136struct bus_type greybus_bus_type = {
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800137 .name = "greybus",
Alex Elder778c69c2014-09-22 19:19:03 -0500138 .match = greybus_module_match,
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800139 .uevent = greybus_uevent,
140};
141
142static int greybus_probe(struct device *dev)
143{
144 struct greybus_driver *driver = to_greybus_driver(dev->driver);
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +0530145 struct gb_bundle *bundle = to_gb_bundle(dev);
146 const struct greybus_bundle_id *id;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800147 int retval;
148
149 /* match id */
Johan Hovold700001a2015-11-21 10:52:03 +0100150 id = greybus_match_id(bundle, driver->id_table);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800151 if (!id)
152 return -ENODEV;
153
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +0530154 retval = driver->probe(bundle, id);
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100155 if (retval) {
156 /*
157 * Catch buggy drivers that fail to destroy their connections.
158 */
159 WARN_ON(!list_empty(&bundle->connections));
160
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800161 return retval;
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100162 }
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800163
164 return 0;
165}
166
167static int greybus_remove(struct device *dev)
168{
169 struct greybus_driver *driver = to_greybus_driver(dev->driver);
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +0530170 struct gb_bundle *bundle = to_gb_bundle(dev);
Johan Hovoldfa8369c2016-01-19 12:51:09 +0100171 struct gb_connection *connection;
172
Johan Hovold47a2e672016-01-19 12:51:18 +0100173 list_for_each_entry(connection, &bundle->connections, bundle_links) {
174 if (bundle->intf->disconnected)
175 gb_connection_disable(connection);
176 else
177 gb_connection_disable_rx(connection);
178 }
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800179
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +0530180 driver->disconnect(bundle);
Johan Hovold02a54dd2016-01-19 12:51:10 +0100181
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100182 /* Catch buggy drivers that fail to destroy their connections. */
183 WARN_ON(!list_empty(&bundle->connections));
Johan Hovold02a54dd2016-01-19 12:51:10 +0100184
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800185 return 0;
186}
187
188int greybus_register_driver(struct greybus_driver *driver, struct module *owner,
189 const char *mod_name)
190{
191 int retval;
192
193 if (greybus_disabled())
194 return -ENODEV;
195
Johan Hovold3c48d1b2016-01-08 20:13:42 +0100196 driver->driver.bus = &greybus_bus_type;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800197 driver->driver.name = driver->name;
198 driver->driver.probe = greybus_probe;
199 driver->driver.remove = greybus_remove;
200 driver->driver.owner = owner;
201 driver->driver.mod_name = mod_name;
202
203 retval = driver_register(&driver->driver);
204 if (retval)
205 return retval;
206
207 pr_info("registered new driver %s\n", driver->name);
208 return 0;
209}
210EXPORT_SYMBOL_GPL(greybus_register_driver);
211
Alex Elderfd1c2e52015-06-08 12:05:13 -0500212void greybus_deregister_driver(struct greybus_driver *driver)
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800213{
214 driver_unregister(&driver->driver);
215}
Alex Elderfd1c2e52015-06-08 12:05:13 -0500216EXPORT_SYMBOL_GPL(greybus_deregister_driver);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800217
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700218static int __init gb_init(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700219{
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700220 int retval;
221
Viresh Kumar337b0682015-03-20 20:29:13 +0530222 if (greybus_disabled())
223 return -ENODEV;
224
Alex Elderfb690ca2015-06-13 11:02:09 -0500225 BUILD_BUG_ON(CPORT_ID_MAX >= (long)CPORT_ID_BAD);
Alex Elder1bb3c722014-10-02 12:30:03 -0500226
Greg Kroah-Hartman48f70472015-03-27 11:38:06 +0100227 gb_debugfs_init();
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700228
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700229 retval = bus_register(&greybus_bus_type);
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700230 if (retval) {
Alex Elderf35ab902015-06-09 17:42:51 -0500231 pr_err("bus_register failed (%d)\n", retval);
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700232 goto error_bus;
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700233 }
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700234
Johan Hovold2adaefb2015-11-25 15:59:02 +0100235 retval = gb_hd_init();
236 if (retval) {
237 pr_err("gb_hd_init failed (%d)\n", retval);
238 goto error_hd;
239 }
240
Alex Elder2eb585f2014-10-16 06:35:34 -0500241 retval = gb_operation_init();
242 if (retval) {
Alex Elderf35ab902015-06-09 17:42:51 -0500243 pr_err("gb_operation_init failed (%d)\n", retval);
Alex Elder2eb585f2014-10-16 06:35:34 -0500244 goto error_operation;
245 }
246
Johan Hovold8ec589b2016-01-29 15:42:31 +0100247 retval = gb_firmware_init();
Viresh Kumar90f1b612015-08-12 09:19:33 +0530248 if (retval) {
Johan Hovold8ec589b2016-01-29 15:42:31 +0100249 pr_err("gb_firmware_init failed\n");
Viresh Kumar90f1b612015-08-12 09:19:33 +0530250 goto error_firmware;
251 }
252
Johan Hovold5dda7e52016-01-19 12:50:59 +0100253 retval = gb_legacy_init();
254 if (retval) {
255 pr_err("gb_legacy_init failed\n");
256 goto error_legacy;
257 }
258
Alex Elder19d03de2014-11-05 16:12:53 -0600259 return 0; /* Success */
260
Johan Hovold5dda7e52016-01-19 12:50:59 +0100261error_legacy:
Johan Hovold8ec589b2016-01-29 15:42:31 +0100262 gb_firmware_exit();
Viresh Kumar90f1b612015-08-12 09:19:33 +0530263error_firmware:
Alex Elderf35ab902015-06-09 17:42:51 -0500264 gb_operation_exit();
Alex Elder2eb585f2014-10-16 06:35:34 -0500265error_operation:
Johan Hovold2adaefb2015-11-25 15:59:02 +0100266 gb_hd_exit();
267error_hd:
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700268 bus_unregister(&greybus_bus_type);
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700269error_bus:
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700270 gb_debugfs_cleanup();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700271
272 return retval;
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700273}
Viresh Kumard71aaf22015-03-19 17:02:49 +0530274module_init(gb_init);
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700275
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700276static void __exit gb_exit(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700277{
Johan Hovold5dda7e52016-01-19 12:50:59 +0100278 gb_legacy_exit();
Johan Hovold8ec589b2016-01-29 15:42:31 +0100279 gb_firmware_exit();
Alex Elder2eb585f2014-10-16 06:35:34 -0500280 gb_operation_exit();
Johan Hovold2adaefb2015-11-25 15:59:02 +0100281 gb_hd_exit();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700282 bus_unregister(&greybus_bus_type);
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700283 gb_debugfs_cleanup();
Bryan O'Donoghue5c8ad592015-09-18 16:38:45 +0100284 tracepoint_synchronize_unregister();
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700285}
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700286module_exit(gb_exit);
Greg Kroah-Hartman6cf42a42015-04-13 19:51:33 +0200287MODULE_LICENSE("GPL v2");
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800288MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");