greybus: initialize svc connection while creating hd
[cascardo/linux.git] / drivers / staging / greybus / svc.c
1 /*
2  * SVC Greybus driver.
3  *
4  * Copyright 2015 Google Inc.
5  * Copyright 2015 Linaro Ltd.
6  *
7  * Released under the GPLv2 only.
8  */
9
10 #include "greybus.h"
11
12 #define CPORT_FLAGS_E2EFC       (1)
13 #define CPORT_FLAGS_CSD_N       (2)
14 #define CPORT_FLAGS_CSV_N       (4)
15
16 struct gb_svc {
17         struct gb_connection    *connection;
18         u8                      version_major;
19         u8                      version_minor;
20 };
21
22 static struct ida greybus_svc_device_id_map;
23
24 /*
25  * AP's SVC cport is required early to get messages from the SVC. This happens
26  * even before the Endo is created and hence any modules or interfaces.
27  *
28  * This is a temporary connection, used only at initial bootup.
29  */
30 struct gb_connection *
31 gb_ap_svc_connection_create(struct greybus_host_device *hd)
32 {
33         struct gb_connection *connection;
34
35         connection = gb_connection_create_range(hd, NULL, hd->parent,
36                                                 GB_SVC_CPORT_ID,
37                                                 GREYBUS_PROTOCOL_SVC,
38                                                 GB_SVC_CPORT_ID,
39                                                 GB_SVC_CPORT_ID + 1);
40
41         return connection;
42 }
43
44 /*
45  * We know endo-type and AP's interface id now, lets create a proper svc
46  * connection (and its interface/bundle) now and get rid of the initial
47  * 'partially' initialized one svc connection.
48  */
49 static struct gb_interface *
50 gb_ap_interface_create(struct greybus_host_device *hd,
51                        struct gb_connection *connection, u8 interface_id)
52 {
53         struct gb_interface *intf;
54         struct device *dev = &hd->endo->dev;
55
56         intf = gb_interface_create(hd, interface_id);
57         if (!intf) {
58                 dev_err(dev, "%s: Failed to create interface with id %hhu\n",
59                         __func__, interface_id);
60                 return NULL;
61         }
62
63         intf->device_id = GB_DEVICE_ID_AP;
64         svc_update_connection(intf, connection);
65
66         /* Its no longer a partially initialized connection */
67         hd->initial_svc_connection = NULL;
68
69         return intf;
70 }
71
72 static int intf_device_id_operation(struct gb_svc *svc,
73                                 u8 intf_id, u8 device_id)
74 {
75         struct gb_svc_intf_device_id_request request;
76
77         request.intf_id = intf_id;
78         request.device_id = device_id;
79
80         return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_DEVICE_ID,
81                                  &request, sizeof(request), NULL, 0);
82 }
83
84 static int intf_reset_operation(struct gb_svc *svc, u8 intf_id)
85 {
86         struct gb_svc_intf_reset_request request;
87
88         request.intf_id = intf_id;
89
90         return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_RESET,
91                                  &request, sizeof(request), NULL, 0);
92 }
93
94 static int connection_create_operation(struct gb_svc *svc,
95                                 u8 intf1_id, u16 cport1_id,
96                                 u8 intf2_id, u16 cport2_id)
97 {
98         struct gb_svc_conn_create_request request;
99
100         request.intf1_id = intf1_id;
101         request.cport1_id = cport1_id;
102         request.intf2_id = intf2_id;
103         request.cport2_id = cport2_id;
104         /*
105          * XXX: fix connections paramaters to TC0 and all CPort flags
106          * for now.
107          */
108         request.tc = 0;
109         request.flags = CPORT_FLAGS_CSV_N | CPORT_FLAGS_E2EFC;
110
111         return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE,
112                                  &request, sizeof(request), NULL, 0);
113 }
114
115 static int connection_destroy_operation(struct gb_svc *svc,
116                                 u8 intf1_id, u16 cport1_id,
117                                 u8 intf2_id, u16 cport2_id)
118 {
119         struct gb_svc_conn_destroy_request request;
120
121         request.intf1_id = intf1_id;
122         request.cport1_id = cport1_id;
123         request.intf2_id = intf2_id;
124         request.cport2_id = cport2_id;
125
126         return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_DESTROY,
127                                  &request, sizeof(request), NULL, 0);
128 }
129
130 static int route_create_operation(struct gb_svc *svc, u8 intf1_id, u8 dev1_id,
131                                   u8 intf2_id, u8 dev2_id)
132 {
133         struct gb_svc_route_create_request request;
134
135         request.intf1_id = intf1_id;
136         request.dev1_id = dev1_id;
137         request.intf2_id = intf2_id;
138         request.dev2_id = dev2_id;
139
140         return gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_CREATE,
141                                  &request, sizeof(request), NULL, 0);
142 }
143
144 int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id)
145 {
146         return intf_device_id_operation(svc, intf_id, device_id);
147 }
148 EXPORT_SYMBOL_GPL(gb_svc_intf_device_id);
149
150 int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id)
151 {
152         return intf_reset_operation(svc, intf_id);
153 }
154 EXPORT_SYMBOL_GPL(gb_svc_intf_reset);
155
156 int gb_svc_connection_create(struct gb_svc *svc,
157                                 u8 intf1_id, u16 cport1_id,
158                                 u8 intf2_id, u16 cport2_id)
159 {
160         return connection_create_operation(svc, intf1_id, cport1_id,
161                                                 intf2_id, cport2_id);
162 }
163 EXPORT_SYMBOL_GPL(gb_svc_connection_create);
164
165 int gb_svc_connection_destroy(struct gb_svc *svc,
166                                 u8 intf1_id, u16 cport1_id,
167                                 u8 intf2_id, u16 cport2_id)
168 {
169         return connection_destroy_operation(svc, intf1_id, cport1_id,
170                                                 intf2_id, cport2_id);
171 }
172 EXPORT_SYMBOL_GPL(gb_svc_connection_destroy);
173
174 int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id,
175                         u8 intf2_id, u8 dev2_id) {
176         return route_create_operation(svc, intf1_id, dev1_id,
177                                       intf2_id, dev2_id);
178 }
179 EXPORT_SYMBOL_GPL(gb_svc_route_create);
180
181 static int gb_svc_version_request(struct gb_operation *op)
182 {
183         struct gb_connection *connection = op->connection;
184         struct gb_protocol_version_response *version;
185         struct device *dev = &connection->dev;
186
187         version = op->request->payload;
188
189         if (version->major > GB_SVC_VERSION_MAJOR) {
190                 dev_err(&connection->dev,
191                         "unsupported major version (%hhu > %hhu)\n",
192                         version->major, GB_SVC_VERSION_MAJOR);
193                 return -ENOTSUPP;
194         }
195
196         if (!gb_operation_response_alloc(op, sizeof(*version), GFP_KERNEL)) {
197                 dev_err(dev, "%s: error allocating response\n",
198                                 __func__);
199                 return -ENOMEM;
200         }
201
202         version = op->response->payload;
203         version->major = GB_SVC_VERSION_MAJOR;
204         version->minor = GB_SVC_VERSION_MINOR;
205         return 0;
206 }
207
208 static int gb_svc_hello(struct gb_operation *op)
209 {
210         struct gb_connection *connection = op->connection;
211         struct greybus_host_device *hd = connection->hd;
212         struct gb_svc_hello_request *hello_request;
213         struct device *dev = &connection->dev;
214         struct gb_interface *intf;
215         u16 endo_id;
216         u8 interface_id;
217         int ret;
218
219         /* Hello message should be received only during early bootup */
220         WARN_ON(hd->initial_svc_connection != connection);
221
222         /*
223          * SVC sends information about the endo and interface-id on the hello
224          * request, use that to create an endo.
225          */
226         if (op->request->payload_size != sizeof(*hello_request)) {
227                 dev_err(dev, "%s: Illegal size of hello request (%zu %zu)\n",
228                         __func__, op->request->payload_size,
229                         sizeof(*hello_request));
230                 return -EINVAL;
231         }
232
233         hello_request = op->request->payload;
234         endo_id = le16_to_cpu(hello_request->endo_id);
235         interface_id = hello_request->interface_id;
236
237         /* Setup Endo */
238         ret = greybus_endo_setup(hd, endo_id, interface_id);
239         if (ret)
240                 return ret;
241
242         /*
243          * Endo and its modules are ready now, fix AP's partially initialized
244          * svc protocol and its connection.
245          */
246         intf = gb_ap_interface_create(hd, connection, interface_id);
247         if (!intf) {
248                 gb_endo_remove(hd->endo);
249                 return ret;
250         }
251
252         return 0;
253 }
254
255 static int gb_svc_intf_hotplug_recv(struct gb_operation *op)
256 {
257         struct gb_message *request = op->request;
258         struct gb_svc_intf_hotplug_request *hotplug = request->payload;
259         struct gb_svc *svc = op->connection->private;
260         struct greybus_host_device *hd = op->connection->bundle->intf->hd;
261         struct device *dev = &op->connection->dev;
262         struct gb_interface *intf;
263         u8 intf_id, device_id;
264         u32 unipro_mfg_id;
265         u32 unipro_prod_id;
266         u32 ara_vend_id;
267         u32 ara_prod_id;
268         int ret;
269
270         if (request->payload_size < sizeof(*hotplug)) {
271                 dev_err(dev, "%s: short hotplug request received\n", __func__);
272                 return -EINVAL;
273         }
274
275         /*
276          * Grab the information we need.
277          *
278          * XXX I'd really like to acknowledge receipt, and then
279          * XXX continue processing the request.  There's no need
280          * XXX for the SVC to wait.  In fact, it might be best to
281          * XXX have the SVC get acknowledgement before we proceed.
282          */
283         intf_id = hotplug->intf_id;
284         unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id);
285         unipro_prod_id = le32_to_cpu(hotplug->data.unipro_prod_id);
286         ara_vend_id = le32_to_cpu(hotplug->data.ara_vend_id);
287         ara_prod_id = le32_to_cpu(hotplug->data.ara_prod_id);
288
289         // FIXME May require firmware download
290         intf = gb_interface_create(hd, intf_id);
291         if (!intf) {
292                 dev_err(dev, "%s: Failed to create interface with id %hhu\n",
293                         __func__, intf_id);
294                 return -EINVAL;
295         }
296
297         /*
298          * Create a device id for the interface:
299          * - device id 0 (GB_DEVICE_ID_SVC) belongs to the SVC
300          * - device id 1 (GB_DEVICE_ID_AP) belongs to the AP
301          *
302          * XXX Do we need to allocate device ID for SVC or the AP here? And what
303          * XXX about an AP with multiple interface blocks?
304          */
305         device_id = ida_simple_get(&greybus_svc_device_id_map,
306                                    GB_DEVICE_ID_MODULES_START, 0, GFP_ATOMIC);
307         if (device_id < 0) {
308                 ret = device_id;
309                 dev_err(dev, "%s: Failed to allocate device id for interface with id %hhu (%d)\n",
310                         __func__, intf_id, ret);
311                 goto destroy_interface;
312         }
313
314         ret = intf_device_id_operation(svc, intf_id, device_id);
315         if (ret) {
316                 dev_err(dev, "%s: Device id operation failed, interface %hhu device_id %hhu (%d)\n",
317                         __func__, intf_id, device_id, ret);
318                 goto ida_put;
319         }
320
321         /*
322          * Create a two-way route between the AP and the new interface
323          */
324         ret = route_create_operation(svc, hd->endo->ap_intf_id,
325                                      GB_DEVICE_ID_AP, intf_id, device_id);
326         if (ret) {
327                 dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n",
328                         __func__, intf_id, device_id, ret);
329                 goto ida_put;
330         }
331
332         ret = route_create_operation(svc, intf_id, device_id,
333                                      hd->endo->ap_intf_id, GB_DEVICE_ID_AP);
334         if (ret) {
335                 dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n",
336                         __func__, intf_id, device_id, ret);
337                 goto ida_put;
338         }
339
340         ret = gb_interface_init(intf, device_id);
341         if (ret) {
342                 dev_err(dev, "%s: Failed to initialize interface, interface %hhu device_id %hhu (%d)\n",
343                         __func__, intf_id, device_id, ret);
344                 goto svc_id_free;
345         }
346
347         return 0;
348
349 svc_id_free:
350         /*
351          * XXX Should we tell SVC that this id doesn't belong to interface
352          * XXX anymore.
353          */
354 ida_put:
355         ida_simple_remove(&greybus_svc_device_id_map, device_id);
356 destroy_interface:
357         gb_interface_remove(hd, intf_id);
358
359         return ret;
360 }
361
362 static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op)
363 {
364         struct gb_message *request = op->request;
365         struct gb_svc_intf_hot_unplug_request *hot_unplug = request->payload;
366         struct greybus_host_device *hd = op->connection->bundle->intf->hd;
367         struct device *dev = &op->connection->dev;
368         u8 device_id;
369         struct gb_interface *intf;
370         u8 intf_id;
371
372         if (request->payload_size < sizeof(*hot_unplug)) {
373                 dev_err(&op->connection->dev,
374                         "short hot unplug request received\n");
375                 return -EINVAL;
376         }
377
378         intf_id = hot_unplug->intf_id;
379
380         intf = gb_interface_find(hd, intf_id);
381         if (!intf) {
382                 dev_err(dev, "%s: Couldn't find interface for id %hhu\n",
383                         __func__, intf_id);
384                 return -EINVAL;
385         }
386
387         device_id = intf->device_id;
388         gb_interface_remove(hd, intf_id);
389         ida_simple_remove(&greybus_svc_device_id_map, device_id);
390
391         return 0;
392 }
393
394 static int gb_svc_intf_reset_recv(struct gb_operation *op)
395 {
396         struct gb_message *request = op->request;
397         struct gb_svc_intf_reset_request *reset;
398         u8 intf_id;
399
400         if (request->payload_size < sizeof(*reset)) {
401                 dev_err(&op->connection->dev,
402                         "short reset request received\n");
403                 return -EINVAL;
404         }
405         reset = request->payload;
406
407         intf_id = reset->intf_id;
408
409         /* FIXME Reset the interface here */
410
411         return 0;
412 }
413
414 static int gb_svc_request_recv(u8 type, struct gb_operation *op)
415 {
416         switch (type) {
417         case GB_SVC_TYPE_PROTOCOL_VERSION:
418                 return gb_svc_version_request(op);
419         case GB_SVC_TYPE_SVC_HELLO:
420                 return gb_svc_hello(op);
421         case GB_SVC_TYPE_INTF_HOTPLUG:
422                 return gb_svc_intf_hotplug_recv(op);
423         case GB_SVC_TYPE_INTF_HOT_UNPLUG:
424                 return gb_svc_intf_hot_unplug_recv(op);
425         case GB_SVC_TYPE_INTF_RESET:
426                 return gb_svc_intf_reset_recv(op);
427         default:
428                 dev_err(&op->connection->dev,
429                         "unsupported request: %hhu\n", type);
430                 return -EINVAL;
431         }
432 }
433
434 static int gb_svc_connection_init(struct gb_connection *connection)
435 {
436         struct gb_svc *svc;
437
438         svc = kzalloc(sizeof(*svc), GFP_KERNEL);
439         if (!svc)
440                 return -ENOMEM;
441
442         connection->hd->svc = svc;
443         svc->connection = connection;
444         connection->private = svc;
445
446         WARN_ON(connection->hd->initial_svc_connection);
447         connection->hd->initial_svc_connection = connection;
448
449         ida_init(&greybus_svc_device_id_map);
450
451         return 0;
452 }
453
454 static void gb_svc_connection_exit(struct gb_connection *connection)
455 {
456         struct gb_svc *svc = connection->private;
457
458         connection->hd->svc = NULL;
459         connection->private = NULL;
460         kfree(svc);
461 }
462
463 static struct gb_protocol svc_protocol = {
464         .name                   = "svc",
465         .id                     = GREYBUS_PROTOCOL_SVC,
466         .major                  = GB_SVC_VERSION_MAJOR,
467         .minor                  = GB_SVC_VERSION_MINOR,
468         .connection_init        = gb_svc_connection_init,
469         .connection_exit        = gb_svc_connection_exit,
470         .request_recv           = gb_svc_request_recv,
471 };
472 gb_builtin_protocol_driver(svc_protocol);