blob: ac8ff44f3a8979fc92b4e1d45643f3797b363396 [file] [log] [blame]
Alex Elder30c6d9d2015-05-22 13:02:08 -05001/*
2 * SVC Greybus driver.
3 *
4 * Copyright 2015 Google Inc.
5 * Copyright 2015 Linaro Ltd.
6 *
7 * Released under the GPLv2 only.
8 */
9
Alex Elder30c6d9d2015-05-22 13:02:08 -050010#include "greybus.h"
Viresh Kumar067906f2015-08-06 12:44:55 +053011#include <linux/workqueue.h>
Alex Elder30c6d9d2015-05-22 13:02:08 -050012
Perry Hung0b226492015-07-24 19:02:34 -040013#define CPORT_FLAGS_E2EFC (1)
14#define CPORT_FLAGS_CSD_N (2)
15#define CPORT_FLAGS_CSV_N (4)
16
Viresh Kumarb45864d2015-07-24 15:32:21 +053017struct gb_svc {
18 struct gb_connection *connection;
Viresh Kumarb45864d2015-07-24 15:32:21 +053019};
20
Viresh Kumar067906f2015-08-06 12:44:55 +053021struct svc_hotplug {
22 struct work_struct work;
23 struct gb_connection *connection;
24 struct gb_svc_intf_hotplug_request data;
25};
26
Viresh Kumaread35462015-07-21 17:44:19 +053027static struct ida greybus_svc_device_id_map;
28
Viresh Kumard3d44842015-07-21 17:44:18 +053029/*
30 * AP's SVC cport is required early to get messages from the SVC. This happens
31 * even before the Endo is created and hence any modules or interfaces.
32 *
33 * This is a temporary connection, used only at initial bootup.
34 */
35struct gb_connection *
36gb_ap_svc_connection_create(struct greybus_host_device *hd)
37{
38 struct gb_connection *connection;
39
40 connection = gb_connection_create_range(hd, NULL, hd->parent,
41 GB_SVC_CPORT_ID,
42 GREYBUS_PROTOCOL_SVC,
43 GB_SVC_CPORT_ID,
44 GB_SVC_CPORT_ID + 1);
45
46 return connection;
47}
Viresh Kumard3d44842015-07-21 17:44:18 +053048
49/*
50 * We know endo-type and AP's interface id now, lets create a proper svc
51 * connection (and its interface/bundle) now and get rid of the initial
52 * 'partially' initialized one svc connection.
53 */
54static struct gb_interface *
55gb_ap_interface_create(struct greybus_host_device *hd,
56 struct gb_connection *connection, u8 interface_id)
57{
58 struct gb_interface *intf;
59 struct device *dev = &hd->endo->dev;
Viresh Kumard3d44842015-07-21 17:44:18 +053060
61 intf = gb_interface_create(hd, interface_id);
62 if (!intf) {
63 dev_err(dev, "%s: Failed to create interface with id %hhu\n",
64 __func__, interface_id);
65 return NULL;
66 }
67
68 intf->device_id = GB_DEVICE_ID_AP;
Viresh Kumar67c93ae2015-07-24 15:32:19 +053069 svc_update_connection(intf, connection);
Viresh Kumard3d44842015-07-21 17:44:18 +053070
Viresh Kumardcd05002015-07-24 15:32:20 +053071 /* Its no longer a partially initialized connection */
72 hd->initial_svc_connection = NULL;
73
Viresh Kumard3d44842015-07-21 17:44:18 +053074 return intf;
75}
76
Alex Elder30c6d9d2015-05-22 13:02:08 -050077static int intf_device_id_operation(struct gb_svc *svc,
78 u8 intf_id, u8 device_id)
79{
80 struct gb_svc_intf_device_id_request request;
81
82 request.intf_id = intf_id;
83 request.device_id = device_id;
84
85 return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_DEVICE_ID,
86 &request, sizeof(request), NULL, 0);
87}
88
89static int intf_reset_operation(struct gb_svc *svc, u8 intf_id)
90{
91 struct gb_svc_intf_reset_request request;
92
93 request.intf_id = intf_id;
94
95 return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_RESET,
96 &request, sizeof(request), NULL, 0);
97}
98
99static int connection_create_operation(struct gb_svc *svc,
100 u8 intf1_id, u16 cport1_id,
101 u8 intf2_id, u16 cport2_id)
102{
103 struct gb_svc_conn_create_request request;
104
105 request.intf1_id = intf1_id;
106 request.cport1_id = cport1_id;
107 request.intf2_id = intf2_id;
108 request.cport2_id = cport2_id;
Perry Hung0b226492015-07-24 19:02:34 -0400109 /*
110 * XXX: fix connections paramaters to TC0 and all CPort flags
111 * for now.
112 */
113 request.tc = 0;
114 request.flags = CPORT_FLAGS_CSV_N | CPORT_FLAGS_E2EFC;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500115
116 return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE,
117 &request, sizeof(request), NULL, 0);
118}
119
120static int connection_destroy_operation(struct gb_svc *svc,
121 u8 intf1_id, u16 cport1_id,
122 u8 intf2_id, u16 cport2_id)
123{
124 struct gb_svc_conn_destroy_request request;
125
126 request.intf1_id = intf1_id;
127 request.cport1_id = cport1_id;
128 request.intf2_id = intf2_id;
129 request.cport2_id = cport2_id;
130
131 return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_DESTROY,
132 &request, sizeof(request), NULL, 0);
133}
134
Perry Hunge08aaa42015-07-24 19:02:31 -0400135static int route_create_operation(struct gb_svc *svc, u8 intf1_id, u8 dev1_id,
136 u8 intf2_id, u8 dev2_id)
137{
138 struct gb_svc_route_create_request request;
139
140 request.intf1_id = intf1_id;
141 request.dev1_id = dev1_id;
142 request.intf2_id = intf2_id;
143 request.dev2_id = dev2_id;
144
145 return gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_CREATE,
146 &request, sizeof(request), NULL, 0);
147}
148
Alex Elder30c6d9d2015-05-22 13:02:08 -0500149int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id)
150{
151 return intf_device_id_operation(svc, intf_id, device_id);
152}
153EXPORT_SYMBOL_GPL(gb_svc_intf_device_id);
154
155int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id)
156{
157 return intf_reset_operation(svc, intf_id);
158}
159EXPORT_SYMBOL_GPL(gb_svc_intf_reset);
160
161int gb_svc_connection_create(struct gb_svc *svc,
162 u8 intf1_id, u16 cport1_id,
163 u8 intf2_id, u16 cport2_id)
164{
165 return connection_create_operation(svc, intf1_id, cport1_id,
166 intf2_id, cport2_id);
167}
168EXPORT_SYMBOL_GPL(gb_svc_connection_create);
169
170int gb_svc_connection_destroy(struct gb_svc *svc,
171 u8 intf1_id, u16 cport1_id,
172 u8 intf2_id, u16 cport2_id)
173{
174 return connection_destroy_operation(svc, intf1_id, cport1_id,
175 intf2_id, cport2_id);
176}
177EXPORT_SYMBOL_GPL(gb_svc_connection_destroy);
178
Perry Hunge08aaa42015-07-24 19:02:31 -0400179int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id,
Viresh Kumar03cb4fa2015-07-29 11:44:08 +0530180 u8 intf2_id, u8 dev2_id)
181{
Perry Hunge08aaa42015-07-24 19:02:31 -0400182 return route_create_operation(svc, intf1_id, dev1_id,
183 intf2_id, dev2_id);
184}
185EXPORT_SYMBOL_GPL(gb_svc_route_create);
186
Viresh Kumaread35462015-07-21 17:44:19 +0530187static int gb_svc_version_request(struct gb_operation *op)
188{
189 struct gb_connection *connection = op->connection;
190 struct gb_protocol_version_response *version;
191 struct device *dev = &connection->dev;
192
193 version = op->request->payload;
194
195 if (version->major > GB_SVC_VERSION_MAJOR) {
196 dev_err(&connection->dev,
197 "unsupported major version (%hhu > %hhu)\n",
198 version->major, GB_SVC_VERSION_MAJOR);
199 return -ENOTSUPP;
200 }
201
Viresh Kumar3ea959e32015-08-11 07:36:14 +0530202 connection->module_major = version->major;
203 connection->module_minor = version->minor;
204
Viresh Kumaread35462015-07-21 17:44:19 +0530205 if (!gb_operation_response_alloc(op, sizeof(*version), GFP_KERNEL)) {
206 dev_err(dev, "%s: error allocating response\n",
207 __func__);
208 return -ENOMEM;
209 }
210
211 version = op->response->payload;
212 version->major = GB_SVC_VERSION_MAJOR;
213 version->minor = GB_SVC_VERSION_MINOR;
214 return 0;
215}
216
217static int gb_svc_hello(struct gb_operation *op)
218{
219 struct gb_connection *connection = op->connection;
220 struct greybus_host_device *hd = connection->hd;
221 struct gb_svc_hello_request *hello_request;
222 struct device *dev = &connection->dev;
223 struct gb_interface *intf;
224 u16 endo_id;
225 u8 interface_id;
226 int ret;
227
228 /* Hello message should be received only during early bootup */
229 WARN_ON(hd->initial_svc_connection != connection);
230
231 /*
232 * SVC sends information about the endo and interface-id on the hello
233 * request, use that to create an endo.
234 */
Viresh Kumar0c32d2a2015-08-11 07:29:19 +0530235 if (op->request->payload_size < sizeof(*hello_request)) {
236 dev_err(dev, "%s: Illegal size of hello request (%zu < %zu)\n",
Viresh Kumaread35462015-07-21 17:44:19 +0530237 __func__, op->request->payload_size,
238 sizeof(*hello_request));
239 return -EINVAL;
240 }
241
242 hello_request = op->request->payload;
243 endo_id = le16_to_cpu(hello_request->endo_id);
244 interface_id = hello_request->interface_id;
245
246 /* Setup Endo */
247 ret = greybus_endo_setup(hd, endo_id, interface_id);
248 if (ret)
249 return ret;
250
251 /*
252 * Endo and its modules are ready now, fix AP's partially initialized
253 * svc protocol and its connection.
254 */
255 intf = gb_ap_interface_create(hd, connection, interface_id);
256 if (!intf) {
257 gb_endo_remove(hd->endo);
258 return ret;
259 }
260
261 return 0;
262}
263
Viresh Kumar067906f2015-08-06 12:44:55 +0530264/*
265 * 'struct svc_hotplug' should be freed by svc_process_hotplug() before it
266 * returns, irrespective of success or Failure in bringing up the module.
267 */
268static void svc_process_hotplug(struct work_struct *work)
Alex Elder30c6d9d2015-05-22 13:02:08 -0500269{
Viresh Kumar067906f2015-08-06 12:44:55 +0530270 struct svc_hotplug *svc_hotplug = container_of(work, struct svc_hotplug,
271 work);
272 struct gb_svc_intf_hotplug_request *hotplug = &svc_hotplug->data;
273 struct gb_connection *connection = svc_hotplug->connection;
274 struct gb_svc *svc = connection->private;
Viresh Kumarb9fb7042015-09-01 17:16:16 +0530275 struct greybus_host_device *hd = connection->hd;
Viresh Kumar067906f2015-08-06 12:44:55 +0530276 struct device *dev = &connection->dev;
Viresh Kumaread35462015-07-21 17:44:19 +0530277 struct gb_interface *intf;
278 u8 intf_id, device_id;
Viresh Kumaread35462015-07-21 17:44:19 +0530279 int ret;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500280
Alex Elder30c6d9d2015-05-22 13:02:08 -0500281 /*
282 * Grab the information we need.
Viresh Kumar7eb89192015-07-01 12:13:50 +0530283 */
Alex Elder30c6d9d2015-05-22 13:02:08 -0500284 intf_id = hotplug->intf_id;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500285
Viresh Kumaread35462015-07-21 17:44:19 +0530286 intf = gb_interface_create(hd, intf_id);
287 if (!intf) {
288 dev_err(dev, "%s: Failed to create interface with id %hhu\n",
289 __func__, intf_id);
Viresh Kumar067906f2015-08-06 12:44:55 +0530290 goto free_svc_hotplug;
Viresh Kumaread35462015-07-21 17:44:19 +0530291 }
292
Viresh Kumar3944a452015-08-12 09:19:31 +0530293 intf->unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id);
294 intf->unipro_prod_id = le32_to_cpu(hotplug->data.unipro_prod_id);
295 intf->ara_vend_id = le32_to_cpu(hotplug->data.ara_vend_id);
296 intf->ara_prod_id = le32_to_cpu(hotplug->data.ara_prod_id);
297
Viresh Kumaread35462015-07-21 17:44:19 +0530298 /*
299 * Create a device id for the interface:
300 * - device id 0 (GB_DEVICE_ID_SVC) belongs to the SVC
301 * - device id 1 (GB_DEVICE_ID_AP) belongs to the AP
302 *
303 * XXX Do we need to allocate device ID for SVC or the AP here? And what
304 * XXX about an AP with multiple interface blocks?
305 */
306 device_id = ida_simple_get(&greybus_svc_device_id_map,
Johan Hovold89f637f2015-09-01 12:25:25 +0200307 GB_DEVICE_ID_MODULES_START, 0, GFP_KERNEL);
Viresh Kumaread35462015-07-21 17:44:19 +0530308 if (device_id < 0) {
309 ret = device_id;
310 dev_err(dev, "%s: Failed to allocate device id for interface with id %hhu (%d)\n",
311 __func__, intf_id, ret);
312 goto destroy_interface;
313 }
314
315 ret = intf_device_id_operation(svc, intf_id, device_id);
316 if (ret) {
317 dev_err(dev, "%s: Device id operation failed, interface %hhu device_id %hhu (%d)\n",
318 __func__, intf_id, device_id, ret);
319 goto ida_put;
320 }
321
Perry Hung7e275462015-07-24 19:02:32 -0400322 /*
323 * Create a two-way route between the AP and the new interface
324 */
325 ret = route_create_operation(svc, hd->endo->ap_intf_id,
326 GB_DEVICE_ID_AP, intf_id, device_id);
327 if (ret) {
328 dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n",
329 __func__, intf_id, device_id, ret);
330 goto ida_put;
331 }
332
333 ret = route_create_operation(svc, intf_id, device_id,
334 hd->endo->ap_intf_id, GB_DEVICE_ID_AP);
335 if (ret) {
336 dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n",
337 __func__, intf_id, device_id, ret);
338 goto ida_put;
339 }
340
Viresh Kumaread35462015-07-21 17:44:19 +0530341 ret = gb_interface_init(intf, device_id);
342 if (ret) {
343 dev_err(dev, "%s: Failed to initialize interface, interface %hhu device_id %hhu (%d)\n",
344 __func__, intf_id, device_id, ret);
345 goto svc_id_free;
346 }
Alex Elder30c6d9d2015-05-22 13:02:08 -0500347
Viresh Kumar067906f2015-08-06 12:44:55 +0530348 goto free_svc_hotplug;
Viresh Kumaread35462015-07-21 17:44:19 +0530349
350svc_id_free:
351 /*
352 * XXX Should we tell SVC that this id doesn't belong to interface
353 * XXX anymore.
354 */
355ida_put:
356 ida_simple_remove(&greybus_svc_device_id_map, device_id);
357destroy_interface:
358 gb_interface_remove(hd, intf_id);
Viresh Kumar067906f2015-08-06 12:44:55 +0530359free_svc_hotplug:
360 kfree(svc_hotplug);
361}
Viresh Kumaread35462015-07-21 17:44:19 +0530362
Viresh Kumar067906f2015-08-06 12:44:55 +0530363/*
364 * Bringing up a module can be time consuming, as that may require lots of
365 * initialization on the module side. Over that, we may also need to download
366 * the firmware first and flash that on the module.
367 *
368 * In order to make other hotplug events to not wait for all this to finish,
369 * handle most of module hotplug stuff outside of the hotplug callback, with
370 * help of a workqueue.
371 */
372static int gb_svc_intf_hotplug_recv(struct gb_operation *op)
373{
374 struct gb_message *request = op->request;
375 struct svc_hotplug *svc_hotplug;
376
377 if (request->payload_size < sizeof(svc_hotplug->data)) {
378 dev_err(&op->connection->dev,
379 "%s: short hotplug request received (%zu < %zu)\n",
380 __func__, request->payload_size,
381 sizeof(svc_hotplug->data));
382 return -EINVAL;
383 }
384
Johan Hovold287bba82015-09-01 12:25:26 +0200385 svc_hotplug = kmalloc(sizeof(*svc_hotplug), GFP_KERNEL);
Viresh Kumar067906f2015-08-06 12:44:55 +0530386 if (!svc_hotplug)
387 return -ENOMEM;
388
389 svc_hotplug->connection = op->connection;
390 memcpy(&svc_hotplug->data, op->request->payload, sizeof(svc_hotplug->data));
391
392 INIT_WORK(&svc_hotplug->work, svc_process_hotplug);
393 queue_work(system_unbound_wq, &svc_hotplug->work);
394
395 return 0;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500396}
397
398static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op)
399{
400 struct gb_message *request = op->request;
Viresh Kumaread35462015-07-21 17:44:19 +0530401 struct gb_svc_intf_hot_unplug_request *hot_unplug = request->payload;
Viresh Kumarb9fb7042015-09-01 17:16:16 +0530402 struct greybus_host_device *hd = op->connection->hd;
Viresh Kumaread35462015-07-21 17:44:19 +0530403 struct device *dev = &op->connection->dev;
404 u8 device_id;
405 struct gb_interface *intf;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500406 u8 intf_id;
407
408 if (request->payload_size < sizeof(*hot_unplug)) {
409 dev_err(&op->connection->dev,
Viresh Kumar6d05ad32015-08-06 12:44:54 +0530410 "short hot unplug request received (%zu < %zu)\n",
411 request->payload_size, sizeof(*hot_unplug));
Alex Elder30c6d9d2015-05-22 13:02:08 -0500412 return -EINVAL;
413 }
Alex Elder30c6d9d2015-05-22 13:02:08 -0500414
415 intf_id = hot_unplug->intf_id;
416
Viresh Kumaread35462015-07-21 17:44:19 +0530417 intf = gb_interface_find(hd, intf_id);
418 if (!intf) {
419 dev_err(dev, "%s: Couldn't find interface for id %hhu\n",
420 __func__, intf_id);
421 return -EINVAL;
422 }
423
424 device_id = intf->device_id;
425 gb_interface_remove(hd, intf_id);
426 ida_simple_remove(&greybus_svc_device_id_map, device_id);
Alex Elder30c6d9d2015-05-22 13:02:08 -0500427
428 return 0;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500429}
430
431static int gb_svc_intf_reset_recv(struct gb_operation *op)
432{
433 struct gb_message *request = op->request;
434 struct gb_svc_intf_reset_request *reset;
435 u8 intf_id;
436
437 if (request->payload_size < sizeof(*reset)) {
438 dev_err(&op->connection->dev,
Viresh Kumar6d05ad32015-08-06 12:44:54 +0530439 "short reset request received (%zu < %zu)\n",
440 request->payload_size, sizeof(*reset));
Alex Elder30c6d9d2015-05-22 13:02:08 -0500441 return -EINVAL;
442 }
443 reset = request->payload;
444
445 intf_id = reset->intf_id;
446
447 /* FIXME Reset the interface here */
448
449 return 0;
450}
451
452static int gb_svc_request_recv(u8 type, struct gb_operation *op)
453{
454 switch (type) {
Viresh Kumar0e2462d2015-08-14 07:57:38 +0530455 case GB_REQUEST_TYPE_PROTOCOL_VERSION:
Viresh Kumaread35462015-07-21 17:44:19 +0530456 return gb_svc_version_request(op);
457 case GB_SVC_TYPE_SVC_HELLO:
458 return gb_svc_hello(op);
Alex Elder30c6d9d2015-05-22 13:02:08 -0500459 case GB_SVC_TYPE_INTF_HOTPLUG:
460 return gb_svc_intf_hotplug_recv(op);
461 case GB_SVC_TYPE_INTF_HOT_UNPLUG:
462 return gb_svc_intf_hot_unplug_recv(op);
463 case GB_SVC_TYPE_INTF_RESET:
464 return gb_svc_intf_reset_recv(op);
465 default:
466 dev_err(&op->connection->dev,
467 "unsupported request: %hhu\n", type);
468 return -EINVAL;
469 }
470}
471
Alex Elder30c6d9d2015-05-22 13:02:08 -0500472static int gb_svc_connection_init(struct gb_connection *connection)
473{
474 struct gb_svc *svc;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500475
476 svc = kzalloc(sizeof(*svc), GFP_KERNEL);
477 if (!svc)
478 return -ENOMEM;
479
Perry Hung75a60ed2015-07-24 19:02:33 -0400480 connection->hd->svc = svc;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500481 svc->connection = connection;
482 connection->private = svc;
Viresh Kumard3d44842015-07-21 17:44:18 +0530483
Viresh Kumardcd05002015-07-24 15:32:20 +0530484 WARN_ON(connection->hd->initial_svc_connection);
485 connection->hd->initial_svc_connection = connection;
Viresh Kumard3d44842015-07-21 17:44:18 +0530486
487 ida_init(&greybus_svc_device_id_map);
488
Viresh Kumar18d777c2015-07-21 17:44:20 +0530489 return 0;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500490}
491
492static void gb_svc_connection_exit(struct gb_connection *connection)
493{
494 struct gb_svc *svc = connection->private;
495
Perry Hung75a60ed2015-07-24 19:02:33 -0400496 connection->hd->svc = NULL;
Viresh Kumard3d44842015-07-21 17:44:18 +0530497 connection->private = NULL;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500498 kfree(svc);
499}
500
501static struct gb_protocol svc_protocol = {
502 .name = "svc",
503 .id = GREYBUS_PROTOCOL_SVC,
Viresh Kumar06e305f2015-07-01 12:13:51 +0530504 .major = GB_SVC_VERSION_MAJOR,
505 .minor = GB_SVC_VERSION_MINOR,
Alex Elder30c6d9d2015-05-22 13:02:08 -0500506 .connection_init = gb_svc_connection_init,
507 .connection_exit = gb_svc_connection_exit,
508 .request_recv = gb_svc_request_recv,
509};
Viresh Kumarab69c4c2015-07-03 17:00:29 +0530510gb_builtin_protocol_driver(svc_protocol);