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