blob: f4b562c709d97b9c31da7ca05f98639efd4ceb6f [file] [log] [blame]
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +08001/*
2 * Greybus "Core"
3 *
4 * Copyright 2014 Google Inc.
5 *
6 * Released under the GPLv2 only.
7 */
8
9#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
11#include <linux/types.h>
12#include <linux/module.h>
13#include <linux/moduleparam.h>
14#include <linux/kernel.h>
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -070015#include <linux/slab.h>
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -070016#include <linux/kthread.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
26core_param(nogreybus, bool, 0444);
27#endif
28int greybus_disabled(void)
29{
30 return nogreybus;
31}
32EXPORT_SYMBOL_GPL(greybus_disabled);
33
34static int greybus_match_one_id(struct greybus_device *gdev,
Greg Kroah-Hartman6584c8a2014-09-01 13:31:31 -070035 const struct greybus_module_id *id)
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080036{
Greg Kroah-Hartman6dca7b92014-09-01 13:42:43 -070037 struct greybus_descriptor_module_id *module_id;
38 struct greybus_descriptor_serial_number *serial_num;
39
40 module_id = &gdev->module_id;
41 serial_num = &gdev->serial_number;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080042
43 if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) &&
Greg Kroah-Hartman6dca7b92014-09-01 13:42:43 -070044 (id->vendor != le16_to_cpu(module_id->vendor)))
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080045 return 0;
46
47 if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_PRODUCT) &&
Greg Kroah-Hartman6dca7b92014-09-01 13:42:43 -070048 (id->product != le16_to_cpu(module_id->product)))
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080049 return 0;
50
51 if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) &&
Greg Kroah-Hartman6dca7b92014-09-01 13:42:43 -070052 (id->serial_number != le64_to_cpu(serial_num->serial_number)))
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080053 return 0;
54
55 return 1;
56}
57
Greg Kroah-Hartman6584c8a2014-09-01 13:31:31 -070058static const struct greybus_module_id *greybus_match_id(
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080059 struct greybus_device *gdev,
Greg Kroah-Hartman6584c8a2014-09-01 13:31:31 -070060 const struct greybus_module_id *id)
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080061{
62 if (id == NULL)
63 return NULL;
64
Greg Kroah-Hartman6dca7b92014-09-01 13:42:43 -070065 for (; id->vendor || id->product || id->serial_number ||
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080066 id->driver_info ; id++) {
67 if (greybus_match_one_id(gdev, id))
68 return id;
69 }
70
71 return NULL;
72}
73
74static int greybus_device_match(struct device *dev, struct device_driver *drv)
75{
76 struct greybus_driver *driver = to_greybus_driver(dev->driver);
77 struct greybus_device *gdev = to_greybus_device(dev);
Greg Kroah-Hartman6584c8a2014-09-01 13:31:31 -070078 const struct greybus_module_id *id;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080079
80 id = greybus_match_id(gdev, driver->id_table);
81 if (id)
82 return 1;
83 /* FIXME - Dyanmic ids? */
84 return 0;
85}
86
87static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
88{
89 /* struct greybus_device *gdev = to_greybus_device(dev); */
90
91 /* FIXME - add some uevents here... */
92 return 0;
93}
94
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -070095static struct bus_type greybus_bus_type = {
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080096 .name = "greybus",
97 .match = greybus_device_match,
98 .uevent = greybus_uevent,
99};
100
101static int greybus_probe(struct device *dev)
102{
103 struct greybus_driver *driver = to_greybus_driver(dev->driver);
104 struct greybus_device *gdev = to_greybus_device(dev);
Greg Kroah-Hartman6584c8a2014-09-01 13:31:31 -0700105 const struct greybus_module_id *id;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800106 int retval;
107
108 /* match id */
109 id = greybus_match_id(gdev, driver->id_table);
110 if (!id)
111 return -ENODEV;
112
113 retval = driver->probe(gdev, id);
114 if (retval)
115 return retval;
116
117 return 0;
118}
119
120static int greybus_remove(struct device *dev)
121{
122 struct greybus_driver *driver = to_greybus_driver(dev->driver);
123 struct greybus_device *gdev = to_greybus_device(dev);
124
125 driver->disconnect(gdev);
126 return 0;
127}
128
129int greybus_register_driver(struct greybus_driver *driver, struct module *owner,
130 const char *mod_name)
131{
132 int retval;
133
134 if (greybus_disabled())
135 return -ENODEV;
136
137 driver->driver.name = driver->name;
138 driver->driver.probe = greybus_probe;
139 driver->driver.remove = greybus_remove;
140 driver->driver.owner = owner;
141 driver->driver.mod_name = mod_name;
142
143 retval = driver_register(&driver->driver);
144 if (retval)
145 return retval;
146
147 pr_info("registered new driver %s\n", driver->name);
148 return 0;
149}
150EXPORT_SYMBOL_GPL(greybus_register_driver);
151
152void greybus_deregister(struct greybus_driver *driver)
153{
154 driver_unregister(&driver->driver);
155}
156EXPORT_SYMBOL_GPL(greybus_deregister);
157
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700158
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700159static int gb_init_subdevs(struct greybus_device *gdev,
160 const struct greybus_module_id *id)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700161{
162 int retval;
163
164 /* Allocate all of the different "sub device types" for this device */
165 retval = gb_i2c_probe(gdev, id);
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700166 if (retval)
167 goto error_i2c;
168
169 retval = gb_gpio_probe(gdev, id);
170 if (retval)
171 goto error_gpio;
172
173 retval = gb_sdio_probe(gdev, id);
174 if (retval)
175 goto error_sdio;
176
177 retval = gb_tty_probe(gdev, id);
178 if (retval)
179 goto error_tty;
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700180 return 0;
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700181
182error_tty:
183 gb_sdio_disconnect(gdev);
184
185error_sdio:
186 gb_gpio_disconnect(gdev);
187
188error_gpio:
189 gb_i2c_disconnect(gdev);
190
191error_i2c:
192 return retval;
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700193}
194
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700195static const struct greybus_module_id fake_gb_id =
196 { GREYBUS_DEVICE(0x42, 0x42) };
197
198/**
199 * greybus_new_device:
200 *
201 * Pass in a buffer that _should_ be a set of greybus descriptor fields and spit
202 * out a greybus device structure.
203 */
204struct greybus_device *greybus_new_device(int module_number, u8 *data, int size)
205{
206 struct greybus_device *gdev;
207 struct greybus_descriptor_block_header *block;
208 struct greybus_descriptor *desc;
209 int retval;
210 int overall_size;
211 int header_size;
212 int desc_size;
213 u8 version_major;
214 u8 version_minor;
215
216 /* we have to have at _least_ the block header */
217 if (size <= sizeof(struct greybus_descriptor_block_header))
218 return NULL;
219
220 gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
221 if (!gdev)
222 return NULL;
223
224 gdev->module_number = module_number;
225
226 block = (struct greybus_descriptor_block_header *)data;
227 overall_size = le16_to_cpu(block->size);
228 if (overall_size != size) {
229 pr_err("size != block header size, %d != %d\n", size,
230 overall_size);
231 goto error;
232 }
233
234 version_major = block->version_major;
235 version_minor = block->version_minor;
236
237 // FIXME - check version major/minor here!
238
239 size -= sizeof(struct greybus_descriptor_block_header);
240 data += sizeof(struct greybus_descriptor_block_header);
241 while (size > 0) {
242 desc = (struct greybus_descriptor *)data;
243 desc_size = le16_to_cpu(desc->header.size);
244
245 switch (desc->header.type) {
246 case GREYBUS_TYPE_FUNCTION:
247 header_size =
248 sizeof(struct greybus_descriptor_function);
249 if (desc_size != header_size) {
250 pr_err("invalid function header size %d\n",
251 desc_size);
252 goto error;
253 }
254 memcpy(&gdev->function, &desc->function, header_size);
255 size -= header_size;
256 data += header_size;
257 break;
258
259 case GREYBUS_TYPE_MODULE_ID:
260 header_size =
261 sizeof(struct greybus_descriptor_module_id);
262 if (desc_size != header_size) {
263 pr_err("invalid module header size %d\n",
264 desc_size);
265 goto error;
266 }
267 memcpy(&gdev->module_id, &desc->module_id, header_size);
268 size -= header_size;
269 data += header_size;
270 break;
271
272 case GREYBUS_TYPE_SERIAL_NUMBER:
273 header_size =
274 sizeof(struct greybus_descriptor_serial_number);
275 if (desc_size != header_size) {
276 pr_err("invalid serial number header size %d\n",
277 desc_size);
278 goto error;
279 }
280 memcpy(&gdev->serial_number, &desc->serial_number,
281 header_size);
282 size -= header_size;
283 data += header_size;
284 break;
285
286 case GREYBUS_TYPE_DEVICE_STRING:
287 case GREYBUS_TYPE_CPORT:
288 case GREYBUS_TYPE_INVALID:
289 default:
290 pr_err("invalid descriptor type %d\n", desc->header.type);
291 goto error;
292 }
293#if 0
294 struct greybus_descriptor_string string;
295 struct greybus_descriptor_cport cport;
296#endif
297 }
298 retval = gb_init_subdevs(gdev, &fake_gb_id);
299 if (retval)
300 goto error;
301 return gdev;
302error:
303 kfree(gdev);
304 return NULL;
305}
306
Greg Kroah-Hartman6dca7b92014-09-01 13:42:43 -0700307void remove_device(struct greybus_device *gdev)
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700308{
309 /* tear down all of the "sub device types" for this device */
310 gb_i2c_disconnect(gdev);
311 gb_gpio_disconnect(gdev);
312 gb_sdio_disconnect(gdev);
313 gb_tty_disconnect(gdev);
314}
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700315
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700316static int __init gb_init(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700317{
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700318 int retval;
319
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700320 retval = gb_debugfs_init();
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700321 if (retval)
322 return retval;
323
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700324 retval = bus_register(&greybus_bus_type);
325 if (retval)
326 goto error_bus;
327
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700328 retval = gb_thread_init();
329 if (retval)
330 goto error_thread;
331
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700332 // FIXME - more gb core init goes here
333
334 retval = gb_tty_init();
335 if (retval)
336 goto error_tty;
337
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700338 return 0;
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700339
340error_tty:
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700341 gb_thread_destroy();
342
343error_thread:
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700344 bus_unregister(&greybus_bus_type);
345
346error_bus:
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700347 gb_debugfs_cleanup();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700348
349 return retval;
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700350}
351
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700352static void __exit gb_exit(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700353{
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700354 gb_tty_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();
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700357}
358
359module_init(gb_init);
360module_exit(gb_exit);
361
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800362MODULE_LICENSE("GPL");
363MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");