]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/greybus/svc.c
greybus: svc: Move '{' to a new line in function definitions
[karo-tx-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 {
177         return route_create_operation(svc, intf1_id, dev1_id,
178                                       intf2_id, dev2_id);
179 }
180 EXPORT_SYMBOL_GPL(gb_svc_route_create);
181
182 static int gb_svc_version_request(struct gb_operation *op)
183 {
184         struct gb_connection *connection = op->connection;
185         struct gb_protocol_version_response *version;
186         struct device *dev = &connection->dev;
187
188         version = op->request->payload;
189
190         if (version->major > GB_SVC_VERSION_MAJOR) {
191                 dev_err(&connection->dev,
192                         "unsupported major version (%hhu > %hhu)\n",
193                         version->major, GB_SVC_VERSION_MAJOR);
194                 return -ENOTSUPP;
195         }
196
197         if (!gb_operation_response_alloc(op, sizeof(*version), GFP_KERNEL)) {
198                 dev_err(dev, "%s: error allocating response\n",
199                                 __func__);
200                 return -ENOMEM;
201         }
202
203         version = op->response->payload;
204         version->major = GB_SVC_VERSION_MAJOR;
205         version->minor = GB_SVC_VERSION_MINOR;
206         return 0;
207 }
208
209 static int gb_svc_hello(struct gb_operation *op)
210 {
211         struct gb_connection *connection = op->connection;
212         struct greybus_host_device *hd = connection->hd;
213         struct gb_svc_hello_request *hello_request;
214         struct device *dev = &connection->dev;
215         struct gb_interface *intf;
216         u16 endo_id;
217         u8 interface_id;
218         int ret;
219
220         /* Hello message should be received only during early bootup */
221         WARN_ON(hd->initial_svc_connection != connection);
222
223         /*
224          * SVC sends information about the endo and interface-id on the hello
225          * request, use that to create an endo.
226          */
227         if (op->request->payload_size != sizeof(*hello_request)) {
228                 dev_err(dev, "%s: Illegal size of hello request (%zu %zu)\n",
229                         __func__, op->request->payload_size,
230                         sizeof(*hello_request));
231                 return -EINVAL;
232         }
233
234         hello_request = op->request->payload;
235         endo_id = le16_to_cpu(hello_request->endo_id);
236         interface_id = hello_request->interface_id;
237
238         /* Setup Endo */
239         ret = greybus_endo_setup(hd, endo_id, interface_id);
240         if (ret)
241                 return ret;
242
243         /*
244          * Endo and its modules are ready now, fix AP's partially initialized
245          * svc protocol and its connection.
246          */
247         intf = gb_ap_interface_create(hd, connection, interface_id);
248         if (!intf) {
249                 gb_endo_remove(hd->endo);
250                 return ret;
251         }
252
253         return 0;
254 }
255
256 static int gb_svc_intf_hotplug_recv(struct gb_operation *op)
257 {
258         struct gb_message *request = op->request;
259         struct gb_svc_intf_hotplug_request *hotplug = request->payload;
260         struct gb_svc *svc = op->connection->private;
261         struct greybus_host_device *hd = op->connection->bundle->intf->hd;
262         struct device *dev = &op->connection->dev;
263         struct gb_interface *intf;
264         u8 intf_id, device_id;
265         u32 unipro_mfg_id;
266         u32 unipro_prod_id;
267         u32 ara_vend_id;
268         u32 ara_prod_id;
269         int ret;
270
271         if (request->payload_size < sizeof(*hotplug)) {
272                 dev_err(dev, "%s: short hotplug request received\n", __func__);
273                 return -EINVAL;
274         }
275
276         /*
277          * Grab the information we need.
278          *
279          * XXX I'd really like to acknowledge receipt, and then
280          * XXX continue processing the request.  There's no need
281          * XXX for the SVC to wait.  In fact, it might be best to
282          * XXX have the SVC get acknowledgement before we proceed.
283          */
284         intf_id = hotplug->intf_id;
285         unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id);
286         unipro_prod_id = le32_to_cpu(hotplug->data.unipro_prod_id);
287         ara_vend_id = le32_to_cpu(hotplug->data.ara_vend_id);
288         ara_prod_id = le32_to_cpu(hotplug->data.ara_prod_id);
289
290         // FIXME May require firmware download
291         intf = gb_interface_create(hd, intf_id);
292         if (!intf) {
293                 dev_err(dev, "%s: Failed to create interface with id %hhu\n",
294                         __func__, intf_id);
295                 return -EINVAL;
296         }
297
298         /*
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,
307                                    GB_DEVICE_ID_MODULES_START, 0, GFP_ATOMIC);
308         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
322         /*
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
341         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         }
347
348         return 0;
349
350 svc_id_free:
351         /*
352          * XXX Should we tell SVC that this id doesn't belong to interface
353          * XXX anymore.
354          */
355 ida_put:
356         ida_simple_remove(&greybus_svc_device_id_map, device_id);
357 destroy_interface:
358         gb_interface_remove(hd, intf_id);
359
360         return ret;
361 }
362
363 static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op)
364 {
365         struct gb_message *request = op->request;
366         struct gb_svc_intf_hot_unplug_request *hot_unplug = request->payload;
367         struct greybus_host_device *hd = op->connection->bundle->intf->hd;
368         struct device *dev = &op->connection->dev;
369         u8 device_id;
370         struct gb_interface *intf;
371         u8 intf_id;
372
373         if (request->payload_size < sizeof(*hot_unplug)) {
374                 dev_err(&op->connection->dev,
375                         "short hot unplug request received\n");
376                 return -EINVAL;
377         }
378
379         intf_id = hot_unplug->intf_id;
380
381         intf = gb_interface_find(hd, intf_id);
382         if (!intf) {
383                 dev_err(dev, "%s: Couldn't find interface for id %hhu\n",
384                         __func__, intf_id);
385                 return -EINVAL;
386         }
387
388         device_id = intf->device_id;
389         gb_interface_remove(hd, intf_id);
390         ida_simple_remove(&greybus_svc_device_id_map, device_id);
391
392         return 0;
393 }
394
395 static int gb_svc_intf_reset_recv(struct gb_operation *op)
396 {
397         struct gb_message *request = op->request;
398         struct gb_svc_intf_reset_request *reset;
399         u8 intf_id;
400
401         if (request->payload_size < sizeof(*reset)) {
402                 dev_err(&op->connection->dev,
403                         "short reset request received\n");
404                 return -EINVAL;
405         }
406         reset = request->payload;
407
408         intf_id = reset->intf_id;
409
410         /* FIXME Reset the interface here */
411
412         return 0;
413 }
414
415 static int gb_svc_request_recv(u8 type, struct gb_operation *op)
416 {
417         switch (type) {
418         case GB_SVC_TYPE_PROTOCOL_VERSION:
419                 return gb_svc_version_request(op);
420         case GB_SVC_TYPE_SVC_HELLO:
421                 return gb_svc_hello(op);
422         case GB_SVC_TYPE_INTF_HOTPLUG:
423                 return gb_svc_intf_hotplug_recv(op);
424         case GB_SVC_TYPE_INTF_HOT_UNPLUG:
425                 return gb_svc_intf_hot_unplug_recv(op);
426         case GB_SVC_TYPE_INTF_RESET:
427                 return gb_svc_intf_reset_recv(op);
428         default:
429                 dev_err(&op->connection->dev,
430                         "unsupported request: %hhu\n", type);
431                 return -EINVAL;
432         }
433 }
434
435 static int gb_svc_connection_init(struct gb_connection *connection)
436 {
437         struct gb_svc *svc;
438
439         svc = kzalloc(sizeof(*svc), GFP_KERNEL);
440         if (!svc)
441                 return -ENOMEM;
442
443         connection->hd->svc = svc;
444         svc->connection = connection;
445         connection->private = svc;
446
447         WARN_ON(connection->hd->initial_svc_connection);
448         connection->hd->initial_svc_connection = connection;
449
450         ida_init(&greybus_svc_device_id_map);
451
452         return 0;
453 }
454
455 static void gb_svc_connection_exit(struct gb_connection *connection)
456 {
457         struct gb_svc *svc = connection->private;
458
459         connection->hd->svc = NULL;
460         connection->private = NULL;
461         kfree(svc);
462 }
463
464 static struct gb_protocol svc_protocol = {
465         .name                   = "svc",
466         .id                     = GREYBUS_PROTOCOL_SVC,
467         .major                  = GB_SVC_VERSION_MAJOR,
468         .minor                  = GB_SVC_VERSION_MINOR,
469         .connection_init        = gb_svc_connection_init,
470         .connection_exit        = gb_svc_connection_exit,
471         .request_recv           = gb_svc_request_recv,
472 };
473 gb_builtin_protocol_driver(svc_protocol);