blob: ea23aaff946509d3f79cf184b929e5467083fe5d [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
Bryan O'Donoghue32b2b162015-09-22 18:06:38 -070016EXPORT_TRACEPOINT_SYMBOL_GPL(gb_host_device_send);
17EXPORT_TRACEPOINT_SYMBOL_GPL(gb_host_device_recv);
18
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080019/* Allow greybus to be disabled at boot if needed */
20static bool nogreybus;
21#ifdef MODULE
22module_param(nogreybus, bool, 0444);
23#else
Viresh Kumar8597e6b2014-10-20 16:45:50 +053024core_param(nogreybus, nogreybus, bool, 0444);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080025#endif
26int greybus_disabled(void)
27{
28 return nogreybus;
29}
30EXPORT_SYMBOL_GPL(greybus_disabled);
31
Alex Elder778c69c2014-09-22 19:19:03 -050032static int greybus_module_match(struct device *dev, struct device_driver *drv)
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080033{
Viresh Kumar95bd99d2014-11-14 17:24:59 +053034 struct greybus_driver *driver = to_greybus_driver(drv);
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +053035 struct gb_bundle *bundle = to_gb_bundle(dev);
36 const struct greybus_bundle_id *id;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080037
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +053038 id = gb_bundle_match_id(bundle, driver->id_table);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080039 if (id)
40 return 1;
Viresh Kumar696e0cc2014-11-21 11:26:30 +053041 /* FIXME - Dynamic ids? */
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080042 return 0;
43}
44
45static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
46{
Greg Kroah-Hartmandf671552014-12-21 14:10:26 -080047 struct gb_module *module = NULL;
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080048 struct gb_interface *intf = NULL;
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -050049 struct gb_bundle *bundle = NULL;
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -080050 struct gb_connection *connection = NULL;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080051
Greg Kroah-Hartman0f035ac2015-04-07 20:26:53 +020052 if (is_gb_endo(dev)) {
53 /*
54 * Not much to do for an endo, just fall through, as the
55 * "default" attributes are good enough for us.
56 */
57 return 0;
58 }
59
Greg Kroah-Hartmandf671552014-12-21 14:10:26 -080060 if (is_gb_module(dev)) {
61 module = to_gb_module(dev);
62 } else if (is_gb_interface(dev)) {
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080063 intf = to_gb_interface(dev);
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -050064 } else if (is_gb_bundle(dev)) {
65 bundle = to_gb_bundle(dev);
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080066 intf = bundle->intf;
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -080067 } else if (is_gb_connection(dev)) {
68 connection = to_gb_connection(dev);
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -050069 bundle = connection->bundle;
Greg Kroah-Hartman4ab9b3c2014-12-19 14:56:31 -080070 intf = bundle->intf;
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -080071 } else {
72 dev_WARN(dev, "uevent for unknown greybus device \"type\"!\n");
73 return -EINVAL;
74 }
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +080075
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -080076 if (connection) {
77 // FIXME
78 // add a uevent that can "load" a connection type
79 return 0;
80 }
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +080081
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -050082 if (bundle) {
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -080083 // FIXME
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -050084 // add a uevent that can "load" a bundle type
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -080085 // This is what we need to bind a driver to so use the info
86 // in gmod here as well
87 return 0;
88 }
89
90 // FIXME
91 // "just" a module, be vague here, nothing binds to a module except
92 // the greybus core, so there's not much, if anything, we need to
93 // advertise.
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080094 return 0;
95}
96
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +080097struct bus_type greybus_bus_type = {
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080098 .name = "greybus",
Alex Elder778c69c2014-09-22 19:19:03 -050099 .match = greybus_module_match,
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800100 .uevent = greybus_uevent,
101};
102
103static int greybus_probe(struct device *dev)
104{
105 struct greybus_driver *driver = to_greybus_driver(dev->driver);
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +0530106 struct gb_bundle *bundle = to_gb_bundle(dev);
107 const struct greybus_bundle_id *id;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800108 int retval;
109
110 /* match id */
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +0530111 id = gb_bundle_match_id(bundle, driver->id_table);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800112 if (!id)
113 return -ENODEV;
114
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +0530115 retval = driver->probe(bundle, id);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800116 if (retval)
117 return retval;
118
119 return 0;
120}
121
122static int greybus_remove(struct device *dev)
123{
124 struct greybus_driver *driver = to_greybus_driver(dev->driver);
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +0530125 struct gb_bundle *bundle = to_gb_bundle(dev);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800126
Viresh Kumar9f5f30e7122015-04-01 20:32:04 +0530127 driver->disconnect(bundle);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800128 return 0;
129}
130
131int greybus_register_driver(struct greybus_driver *driver, struct module *owner,
132 const char *mod_name)
133{
134 int retval;
135
136 if (greybus_disabled())
137 return -ENODEV;
138
139 driver->driver.name = driver->name;
140 driver->driver.probe = greybus_probe;
141 driver->driver.remove = greybus_remove;
142 driver->driver.owner = owner;
143 driver->driver.mod_name = mod_name;
144
145 retval = driver_register(&driver->driver);
146 if (retval)
147 return retval;
148
149 pr_info("registered new driver %s\n", driver->name);
150 return 0;
151}
152EXPORT_SYMBOL_GPL(greybus_register_driver);
153
Alex Elderfd1c2e52015-06-08 12:05:13 -0500154void greybus_deregister_driver(struct greybus_driver *driver)
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800155{
156 driver_unregister(&driver->driver);
157}
Alex Elderfd1c2e52015-06-08 12:05:13 -0500158EXPORT_SYMBOL_GPL(greybus_deregister_driver);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800159
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700160
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700161static DEFINE_MUTEX(hd_mutex);
162
163static void free_hd(struct kref *kref)
164{
165 struct greybus_host_device *hd;
166
167 hd = container_of(kref, struct greybus_host_device, kref);
168
Greg Kroah-Hartmanfe166c62015-07-27 14:23:44 -0700169 ida_destroy(&hd->cport_id_map);
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700170 kfree(hd);
Alex Eldera06df4b2014-10-16 06:35:26 -0500171 mutex_unlock(&hd_mutex);
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700172}
173
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700174struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver,
Johan Hovoldd9336672015-05-19 11:22:43 +0200175 struct device *parent,
Fabien Parent144670c2015-09-02 15:50:35 +0200176 size_t buffer_size_max,
177 size_t num_cports)
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700178{
179 struct greybus_host_device *hd;
180
Greg Kroah-Hartman724b6192014-10-27 13:32:27 +0800181 /*
182 * Validate that the driver implements all of the callbacks
183 * so that we don't have to every time we make them.
184 */
Greg Kroah-Hartman260998e2015-07-24 17:19:21 -0700185 if ((!driver->message_send) || (!driver->message_cancel)) {
Greg Kroah-Hartman724b6192014-10-27 13:32:27 +0800186 pr_err("Must implement all greybus_host_driver callbacks!\n");
Alex Elder8ea70fe2015-05-22 09:52:45 -0500187 return ERR_PTR(-EINVAL);
Greg Kroah-Hartman724b6192014-10-27 13:32:27 +0800188 }
189
Johan Hovold8e929a82015-05-19 11:22:45 +0200190 if (buffer_size_max < GB_OPERATION_MESSAGE_SIZE_MIN) {
191 dev_err(parent, "greybus host-device buffers too small\n");
Johan Hovold1a58a3b2015-09-02 17:37:38 +0200192 return ERR_PTR(-EINVAL);
Johan Hovold8e929a82015-05-19 11:22:45 +0200193 }
194
Fabien Parent144670c2015-09-02 15:50:35 +0200195 if (num_cports == 0 || num_cports > CPORT_ID_MAX) {
196 dev_err(parent, "Invalid number of CPorts: %zu\n", num_cports);
197 return ERR_PTR(-EINVAL);
198 }
199
Johan Hovoldd9336672015-05-19 11:22:43 +0200200 /*
201 * Make sure to never allocate messages larger than what the Greybus
202 * protocol supports.
203 */
204 if (buffer_size_max > GB_OPERATION_MESSAGE_SIZE_MAX) {
205 dev_warn(parent, "limiting buffer size to %u\n",
206 GB_OPERATION_MESSAGE_SIZE_MAX);
207 buffer_size_max = GB_OPERATION_MESSAGE_SIZE_MAX;
208 }
209
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700210 hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
211 if (!hd)
Alex Elder8ea70fe2015-05-22 09:52:45 -0500212 return ERR_PTR(-ENOMEM);
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700213
214 kref_init(&hd->kref);
Greg Kroah-Hartman772149b2014-09-14 12:27:28 -0700215 hd->parent = parent;
216 hd->driver = driver;
Greg Kroah-Hartman1cd56a82014-12-19 14:56:36 -0800217 INIT_LIST_HEAD(&hd->interfaces);
Alex Elder2c43ce42014-11-17 08:08:44 -0600218 INIT_LIST_HEAD(&hd->connections);
Greg Kroah-Hartmanfd7b4352015-06-16 19:43:05 -0700219 ida_init(&hd->cport_id_map);
Johan Hovoldd9336672015-05-19 11:22:43 +0200220 hd->buffer_size_max = buffer_size_max;
Fabien Parent144670c2015-09-02 15:50:35 +0200221 hd->num_cports = num_cports;
Alex Elder1bb3c722014-10-02 12:30:03 -0500222
Viresh Kumare75b82b2015-07-28 07:28:42 +0530223 /*
224 * Initialize AP's SVC protocol connection:
225 *
226 * This is required as part of early initialization of the host device
227 * as we need this connection in order to start any kind of message
228 * exchange between the AP and the SVC. SVC will start with a
229 * 'get-version' request followed by a 'svc-hello' message and at that
230 * time we will create a fully initialized svc-connection, as we need
231 * endo-id and AP's interface id for that.
232 */
233 if (!gb_ap_svc_connection_create(hd)) {
234 kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
235 return ERR_PTR(-ENOMEM);
236 }
237
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700238 return hd;
239}
240EXPORT_SYMBOL_GPL(greybus_create_hd);
241
Alex Eldereb765e42015-05-22 12:56:49 -0500242int greybus_endo_setup(struct greybus_host_device *hd, u16 endo_id,
243 u8 ap_intf_id)
244{
245 struct gb_endo *endo;
246
247 endo = gb_endo_create(hd, endo_id, ap_intf_id);
248 if (IS_ERR(endo))
249 return PTR_ERR(endo);
250 hd->endo = endo;
251
252 return 0;
253}
254EXPORT_SYMBOL_GPL(greybus_endo_setup);
255
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700256void greybus_remove_hd(struct greybus_host_device *hd)
257{
Greg Kroah-Hartmana4d91502015-04-07 20:27:15 +0200258 /*
259 * Tear down all interfaces, modules, and the endo that is associated
260 * with this host controller before freeing the memory associated with
261 * the host controller.
262 */
Viresh Kumar51b5d8d2015-05-20 17:33:51 +0530263 gb_interfaces_remove(hd);
Greg Kroah-Hartmana4d91502015-04-07 20:27:15 +0200264 gb_endo_remove(hd->endo);
Viresh Kumare99f3052015-07-09 10:56:30 +0530265
Viresh Kumardcd05002015-07-24 15:32:20 +0530266 /* Is the SVC still using the partially uninitialized connection ? */
Viresh Kumarfda23812015-08-31 17:21:13 +0530267 if (hd->initial_svc_connection)
Viresh Kumardcd05002015-07-24 15:32:20 +0530268 gb_connection_destroy(hd->initial_svc_connection);
Viresh Kumardcd05002015-07-24 15:32:20 +0530269
Viresh Kumare99f3052015-07-09 10:56:30 +0530270 /*
271 * Make sure there are no leftovers that can potentially corrupt sysfs.
272 */
273 if (WARN_ON(!list_empty(&hd->connections)))
274 gb_hd_connections_exit(hd);
275
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700276 kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
277}
278EXPORT_SYMBOL_GPL(greybus_remove_hd);
279
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700280static int __init gb_init(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700281{
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700282 int retval;
283
Viresh Kumar337b0682015-03-20 20:29:13 +0530284 if (greybus_disabled())
285 return -ENODEV;
286
Alex Elderfb690ca2015-06-13 11:02:09 -0500287 BUILD_BUG_ON(CPORT_ID_MAX >= (long)CPORT_ID_BAD);
Alex Elder1bb3c722014-10-02 12:30:03 -0500288
Greg Kroah-Hartman48f70472015-03-27 11:38:06 +0100289 gb_debugfs_init();
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700290
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700291 retval = bus_register(&greybus_bus_type);
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700292 if (retval) {
Alex Elderf35ab902015-06-09 17:42:51 -0500293 pr_err("bus_register failed (%d)\n", retval);
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700294 goto error_bus;
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700295 }
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700296
Alex Elder2eb585f2014-10-16 06:35:34 -0500297 retval = gb_operation_init();
298 if (retval) {
Alex Elderf35ab902015-06-09 17:42:51 -0500299 pr_err("gb_operation_init failed (%d)\n", retval);
Alex Elder2eb585f2014-10-16 06:35:34 -0500300 goto error_operation;
301 }
302
Alex Elderf35ab902015-06-09 17:42:51 -0500303 retval = gb_endo_init();
304 if (retval) {
305 pr_err("gb_endo_init failed (%d)\n", retval);
306 goto error_endo;
307 }
308
Viresh Kumarcdee4f72015-06-22 16:42:26 +0530309 retval = gb_control_protocol_init();
310 if (retval) {
311 pr_err("gb_control_protocol_init failed\n");
312 goto error_control;
313 }
314
Viresh Kumarab69c4c2015-07-03 17:00:29 +0530315 retval = gb_svc_protocol_init();
316 if (retval) {
317 pr_err("gb_svc_protocol_init failed\n");
318 goto error_svc;
319 }
320
Viresh Kumar90f1b612015-08-12 09:19:33 +0530321 retval = gb_firmware_protocol_init();
322 if (retval) {
323 pr_err("gb_firmware_protocol_init failed\n");
324 goto error_firmware;
325 }
326
Alex Elder19d03de2014-11-05 16:12:53 -0600327 return 0; /* Success */
328
Viresh Kumar90f1b612015-08-12 09:19:33 +0530329error_firmware:
330 gb_svc_protocol_exit();
Viresh Kumarab69c4c2015-07-03 17:00:29 +0530331error_svc:
332 gb_control_protocol_exit();
Viresh Kumarcdee4f72015-06-22 16:42:26 +0530333error_control:
334 gb_endo_exit();
Alex Elderf35ab902015-06-09 17:42:51 -0500335error_endo:
336 gb_operation_exit();
Alex Elder2eb585f2014-10-16 06:35:34 -0500337error_operation:
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700338 bus_unregister(&greybus_bus_type);
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700339error_bus:
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700340 gb_debugfs_cleanup();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700341
342 return retval;
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700343}
Viresh Kumard71aaf22015-03-19 17:02:49 +0530344module_init(gb_init);
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700345
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700346static void __exit gb_exit(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700347{
Viresh Kumar90f1b612015-08-12 09:19:33 +0530348 gb_firmware_protocol_exit();
Viresh Kumarab69c4c2015-07-03 17:00:29 +0530349 gb_svc_protocol_exit();
Viresh Kumarcdee4f72015-06-22 16:42:26 +0530350 gb_control_protocol_exit();
Alex Elderf35ab902015-06-09 17:42:51 -0500351 gb_endo_exit();
Alex Elder2eb585f2014-10-16 06:35:34 -0500352 gb_operation_exit();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700353 bus_unregister(&greybus_bus_type);
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700354 gb_debugfs_cleanup();
Bryan O'Donoghue5c8ad592015-09-18 16:38:45 +0100355 tracepoint_synchronize_unregister();
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700356}
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700357module_exit(gb_exit);
Greg Kroah-Hartman6cf42a42015-04-13 19:51:33 +0200358MODULE_LICENSE("GPL v2");
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800359MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");