blob: 032710ced7cec5243545f8828c2ce9a5e500da62 [file] [log] [blame]
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +08001/*
2 * Greybus "Core"
3 *
4 * Copyright 2014 Google Inc.
Alex Eldera46e9672014-12-12 12:08:42 -06005 * Copyright 2014 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
12#include <linux/types.h>
13#include <linux/module.h>
14#include <linux/moduleparam.h>
15#include <linux/kernel.h>
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -070016#include <linux/slab.h>
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080017#include <linux/device.h>
18
19#include "greybus.h"
20
21/* 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
Alex Elder778c69c2014-09-22 19:19:03 -050034static int greybus_module_match(struct device *dev, struct device_driver *drv)
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080035{
Viresh Kumar95bd99d2014-11-14 17:24:59 +053036 struct greybus_driver *driver = to_greybus_driver(drv);
Greg Kroah-Hartman4ec7b072014-12-11 17:10:56 -050037 struct gb_interface_block *gb_ib = to_gb_interface_block(dev);
Greg Kroah-Hartman2f0c8aa2014-12-11 17:11:02 -050038 const struct greybus_interface_block_id *id;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080039
Greg Kroah-Hartman4ec7b072014-12-11 17:10:56 -050040 id = gb_ib_match_id(gb_ib, driver->id_table);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080041 if (id)
42 return 1;
Viresh Kumar696e0cc2014-11-21 11:26:30 +053043 /* FIXME - Dynamic ids? */
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080044 return 0;
45}
46
47static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
48{
Greg Kroah-Hartman4ec7b072014-12-11 17:10:56 -050049 struct gb_interface_block *gb_ib = NULL;
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -080050 struct gb_interface *interface = NULL;
51 struct gb_connection *connection = NULL;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080052
Greg Kroah-Hartman4ec7b072014-12-11 17:10:56 -050053 if (is_gb_interface_block(dev)) {
54 gb_ib = to_gb_interface_block(dev);
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -080055 } else if (is_gb_interface(dev)) {
56 interface = to_gb_interface(dev);
Greg Kroah-Hartman4ec7b072014-12-11 17:10:56 -050057 gb_ib = interface->gb_ib;
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -080058 } else if (is_gb_connection(dev)) {
59 connection = to_gb_connection(dev);
60 interface = connection->interface;
Greg Kroah-Hartman4ec7b072014-12-11 17:10:56 -050061 gb_ib = interface->gb_ib;
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -080062 } else {
63 dev_WARN(dev, "uevent for unknown greybus device \"type\"!\n");
64 return -EINVAL;
65 }
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +080066
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -080067 if (connection) {
68 // FIXME
69 // add a uevent that can "load" a connection type
70 return 0;
71 }
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +080072
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -080073 if (interface) {
74 // FIXME
75 // add a uevent that can "load" a interface type
76 // This is what we need to bind a driver to so use the info
77 // in gmod here as well
78 return 0;
79 }
80
81 // FIXME
82 // "just" a module, be vague here, nothing binds to a module except
83 // the greybus core, so there's not much, if anything, we need to
84 // advertise.
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080085 return 0;
86}
87
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +080088struct bus_type greybus_bus_type = {
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080089 .name = "greybus",
Alex Elder778c69c2014-09-22 19:19:03 -050090 .match = greybus_module_match,
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080091 .uevent = greybus_uevent,
92};
93
94static int greybus_probe(struct device *dev)
95{
96 struct greybus_driver *driver = to_greybus_driver(dev->driver);
Greg Kroah-Hartman4ec7b072014-12-11 17:10:56 -050097 struct gb_interface_block *gb_ib = to_gb_interface_block(dev);
Greg Kroah-Hartman2f0c8aa2014-12-11 17:11:02 -050098 const struct greybus_interface_block_id *id;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080099 int retval;
100
101 /* match id */
Greg Kroah-Hartman4ec7b072014-12-11 17:10:56 -0500102 id = gb_ib_match_id(gb_ib, driver->id_table);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800103 if (!id)
104 return -ENODEV;
105
Greg Kroah-Hartman4ec7b072014-12-11 17:10:56 -0500106 retval = driver->probe(gb_ib, id);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800107 if (retval)
108 return retval;
109
110 return 0;
111}
112
113static int greybus_remove(struct device *dev)
114{
115 struct greybus_driver *driver = to_greybus_driver(dev->driver);
Greg Kroah-Hartman4ec7b072014-12-11 17:10:56 -0500116 struct gb_interface_block *gb_ib = to_gb_interface_block(dev);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800117
Greg Kroah-Hartman4ec7b072014-12-11 17:10:56 -0500118 driver->disconnect(gb_ib);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800119 return 0;
120}
121
122int greybus_register_driver(struct greybus_driver *driver, struct module *owner,
123 const char *mod_name)
124{
125 int retval;
126
127 if (greybus_disabled())
128 return -ENODEV;
129
130 driver->driver.name = driver->name;
131 driver->driver.probe = greybus_probe;
132 driver->driver.remove = greybus_remove;
133 driver->driver.owner = owner;
134 driver->driver.mod_name = mod_name;
135
136 retval = driver_register(&driver->driver);
137 if (retval)
138 return retval;
139
140 pr_info("registered new driver %s\n", driver->name);
141 return 0;
142}
143EXPORT_SYMBOL_GPL(greybus_register_driver);
144
145void greybus_deregister(struct greybus_driver *driver)
146{
147 driver_unregister(&driver->driver);
148}
149EXPORT_SYMBOL_GPL(greybus_deregister);
150
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700151
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700152static DEFINE_MUTEX(hd_mutex);
153
154static void free_hd(struct kref *kref)
155{
156 struct greybus_host_device *hd;
157
158 hd = container_of(kref, struct greybus_host_device, kref);
159
160 kfree(hd);
Alex Eldera06df4b2014-10-16 06:35:26 -0500161 mutex_unlock(&hd_mutex);
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700162}
163
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700164struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver,
165 struct device *parent)
166{
167 struct greybus_host_device *hd;
168
Greg Kroah-Hartman724b6192014-10-27 13:32:27 +0800169 /*
170 * Validate that the driver implements all of the callbacks
171 * so that we don't have to every time we make them.
172 */
Alex Elder8b337302014-11-20 16:09:13 -0600173 if ((!driver->buffer_send) || (!driver->buffer_cancel) ||
Alex Eldera9163b22014-11-18 13:26:44 -0600174 (!driver->submit_svc)) {
Greg Kroah-Hartman724b6192014-10-27 13:32:27 +0800175 pr_err("Must implement all greybus_host_driver callbacks!\n");
176 return NULL;
177 }
178
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700179 hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
180 if (!hd)
181 return NULL;
182
183 kref_init(&hd->kref);
Greg Kroah-Hartman772149b2014-09-14 12:27:28 -0700184 hd->parent = parent;
185 hd->driver = driver;
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500186 INIT_LIST_HEAD(&hd->modules);
Alex Elder2c43ce42014-11-17 08:08:44 -0600187 INIT_LIST_HEAD(&hd->connections);
Alex Elder177404b2014-10-03 14:14:24 -0500188 ida_init(&hd->cport_id_map);
Alex Elder1bb3c722014-10-02 12:30:03 -0500189
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700190 return hd;
191}
192EXPORT_SYMBOL_GPL(greybus_create_hd);
193
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700194void greybus_remove_hd(struct greybus_host_device *hd)
195{
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800196 /* Tear down all modules that happen to be associated with this host
197 * controller */
Greg Kroah-Hartman63e4a8e2014-10-22 16:38:07 +0800198 gb_remove_modules(hd);
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700199 kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
200}
201EXPORT_SYMBOL_GPL(greybus_remove_hd);
202
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700203static int __init gb_init(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700204{
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700205 int retval;
206
Alex Elder1bb3c722014-10-02 12:30:03 -0500207 BUILD_BUG_ON(HOST_DEV_CPORT_ID_MAX >= (long)CPORT_ID_BAD);
Alex Elder1bb3c722014-10-02 12:30:03 -0500208
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700209 retval = gb_debugfs_init();
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700210 if (retval) {
211 pr_err("debugfs failed\n");
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700212 return retval;
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700213 }
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700214
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700215 retval = bus_register(&greybus_bus_type);
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700216 if (retval) {
217 pr_err("bus_register failed\n");
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700218 goto error_bus;
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700219 }
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700220
Greg Kroah-Hartman45f36782014-09-14 11:40:35 -0700221 retval = gb_ap_init();
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700222 if (retval) {
Greg Kroah-Hartman45f36782014-09-14 11:40:35 -0700223 pr_err("gb_ap_init failed\n");
224 goto error_ap;
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700225 }
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700226
Alex Elder2eb585f2014-10-16 06:35:34 -0500227 retval = gb_operation_init();
228 if (retval) {
229 pr_err("gb_operation_init failed\n");
230 goto error_operation;
231 }
232
Alex Elder19d03de2014-11-05 16:12:53 -0600233 if (!gb_protocol_init()) {
234 /* This only fails for duplicate protocol registration */
235 retval = -EEXIST;
236 pr_err("gb_protocol_init failed\n");
237 goto error_protocol;
238 }
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700239
Alex Elder19d03de2014-11-05 16:12:53 -0600240 return 0; /* Success */
241
242error_protocol:
243 gb_operation_exit();
Alex Elder2eb585f2014-10-16 06:35:34 -0500244error_operation:
Greg Kroah-Hartman45f36782014-09-14 11:40:35 -0700245 gb_ap_exit();
Greg Kroah-Hartman45f36782014-09-14 11:40:35 -0700246error_ap:
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700247 bus_unregister(&greybus_bus_type);
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700248error_bus:
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700249 gb_debugfs_cleanup();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700250
251 return retval;
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700252}
253
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700254static void __exit gb_exit(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700255{
Viresh Kumar35a52ca2014-11-13 18:14:33 +0530256 gb_protocol_exit();
Alex Elder2eb585f2014-10-16 06:35:34 -0500257 gb_operation_exit();
Greg Kroah-Hartman45f36782014-09-14 11:40:35 -0700258 gb_ap_exit();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700259 bus_unregister(&greybus_bus_type);
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700260 gb_debugfs_cleanup();
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700261}
262
263module_init(gb_init);
264module_exit(gb_exit);
265
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800266MODULE_LICENSE("GPL");
267MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");