blob: fab5c45c0701c096a3c071461a4856a3f8cf9160 [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
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -070010#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
Viresh Kumar067906f2015-08-06 12:44:55 +053011#include <linux/workqueue.h>
Alex Elder30c6d9d2015-05-22 13:02:08 -050012
Viresh Kumarf66427a2015-09-02 21:27:13 +053013#include "greybus.h"
14
Johan Hovoldf6c6c132015-11-11 10:07:08 +010015#define CPORT_FLAGS_E2EFC BIT(0)
16#define CPORT_FLAGS_CSD_N BIT(1)
17#define CPORT_FLAGS_CSV_N BIT(2)
Perry Hung0b226492015-07-24 19:02:34 -040018
Viresh Kumarb45864d2015-07-24 15:32:21 +053019
Viresh Kumar067906f2015-08-06 12:44:55 +053020struct svc_hotplug {
21 struct work_struct work;
22 struct gb_connection *connection;
23 struct gb_svc_intf_hotplug_request data;
24};
25
Viresh Kumaread35462015-07-21 17:44:19 +053026
Viresh Kumard3d44842015-07-21 17:44:18 +053027/*
28 * AP's SVC cport is required early to get messages from the SVC. This happens
29 * even before the Endo is created and hence any modules or interfaces.
30 *
31 * This is a temporary connection, used only at initial bootup.
32 */
33struct gb_connection *
Johan Hovold25376362015-11-03 18:03:23 +010034gb_ap_svc_connection_create(struct gb_host_device *hd)
Viresh Kumard3d44842015-07-21 17:44:18 +053035{
36 struct gb_connection *connection;
37
Johan Hovold582b3a12015-11-25 15:59:03 +010038 connection = gb_connection_create_range(hd, NULL,
Viresh Kumard3d44842015-07-21 17:44:18 +053039 GB_SVC_CPORT_ID,
40 GREYBUS_PROTOCOL_SVC,
41 GB_SVC_CPORT_ID,
42 GB_SVC_CPORT_ID + 1);
43
44 return connection;
45}
Viresh Kumard3d44842015-07-21 17:44:18 +053046
47/*
48 * We know endo-type and AP's interface id now, lets create a proper svc
49 * connection (and its interface/bundle) now and get rid of the initial
50 * 'partially' initialized one svc connection.
51 */
52static struct gb_interface *
Johan Hovold25376362015-11-03 18:03:23 +010053gb_ap_interface_create(struct gb_host_device *hd,
Viresh Kumard3d44842015-07-21 17:44:18 +053054 struct gb_connection *connection, u8 interface_id)
55{
56 struct gb_interface *intf;
57 struct device *dev = &hd->endo->dev;
Viresh Kumard3d44842015-07-21 17:44:18 +053058
59 intf = gb_interface_create(hd, interface_id);
60 if (!intf) {
61 dev_err(dev, "%s: Failed to create interface with id %hhu\n",
62 __func__, interface_id);
63 return NULL;
64 }
65
66 intf->device_id = GB_DEVICE_ID_AP;
Viresh Kumar67c93ae2015-07-24 15:32:19 +053067 svc_update_connection(intf, connection);
Viresh Kumard3d44842015-07-21 17:44:18 +053068
Viresh Kumardcd05002015-07-24 15:32:20 +053069 /* Its no longer a partially initialized connection */
70 hd->initial_svc_connection = NULL;
71
Viresh Kumard3d44842015-07-21 17:44:18 +053072 return intf;
73}
74
Viresh Kumar505f16c2015-08-31 17:21:07 +053075static int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id)
Alex Elder30c6d9d2015-05-22 13:02:08 -050076{
77 struct gb_svc_intf_device_id_request request;
78
79 request.intf_id = intf_id;
80 request.device_id = device_id;
81
82 return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_DEVICE_ID,
83 &request, sizeof(request), NULL, 0);
84}
85
Viresh Kumar3f0e9182015-08-31 17:21:06 +053086int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id)
Alex Elder30c6d9d2015-05-22 13:02:08 -050087{
88 struct gb_svc_intf_reset_request request;
89
90 request.intf_id = intf_id;
91
92 return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_RESET,
93 &request, sizeof(request), NULL, 0);
94}
Viresh Kumar3f0e9182015-08-31 17:21:06 +053095EXPORT_SYMBOL_GPL(gb_svc_intf_reset);
Alex Elder30c6d9d2015-05-22 13:02:08 -050096
Viresh Kumar19151c32015-09-09 21:08:29 +053097int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector,
98 u32 *value)
99{
100 struct gb_svc_dme_peer_get_request request;
101 struct gb_svc_dme_peer_get_response response;
102 u16 result;
103 int ret;
104
105 request.intf_id = intf_id;
106 request.attr = cpu_to_le16(attr);
107 request.selector = cpu_to_le16(selector);
108
109 ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_DME_PEER_GET,
110 &request, sizeof(request),
111 &response, sizeof(response));
112 if (ret) {
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700113 pr_err("failed to get DME attribute (%hhu %hx %hu) %d\n",
114 intf_id, attr, selector, ret);
Viresh Kumar19151c32015-09-09 21:08:29 +0530115 return ret;
116 }
117
118 result = le16_to_cpu(response.result_code);
119 if (result) {
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700120 pr_err("Unipro error %hu while getting DME attribute (%hhu %hx %hu)\n",
121 result, intf_id, attr, selector);
Viresh Kumar19151c32015-09-09 21:08:29 +0530122 return -EINVAL;
123 }
124
125 if (value)
126 *value = le32_to_cpu(response.attr_value);
127
128 return 0;
129}
130EXPORT_SYMBOL_GPL(gb_svc_dme_peer_get);
131
132int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector,
133 u32 value)
134{
135 struct gb_svc_dme_peer_set_request request;
136 struct gb_svc_dme_peer_set_response response;
137 u16 result;
138 int ret;
139
140 request.intf_id = intf_id;
141 request.attr = cpu_to_le16(attr);
142 request.selector = cpu_to_le16(selector);
143 request.value = cpu_to_le32(value);
144
145 ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_DME_PEER_SET,
146 &request, sizeof(request),
147 &response, sizeof(response));
148 if (ret) {
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700149 pr_err("failed to set DME attribute (%hhu %hx %hu %u) %d\n",
150 intf_id, attr, selector, value, ret);
Viresh Kumar19151c32015-09-09 21:08:29 +0530151 return ret;
152 }
153
154 result = le16_to_cpu(response.result_code);
155 if (result) {
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700156 pr_err("Unipro error %hu while setting DME attribute (%hhu %hx %hu %u)\n",
157 result, intf_id, attr, selector, value);
Viresh Kumar19151c32015-09-09 21:08:29 +0530158 return -EINVAL;
159 }
160
161 return 0;
162}
163EXPORT_SYMBOL_GPL(gb_svc_dme_peer_set);
164
Viresh Kumar6bec5c72015-09-24 14:40:29 -0700165/*
166 * T_TstSrcIncrement is written by the module on ES2 as a stand-in for boot
167 * status attribute. AP needs to read and clear it, after reading a non-zero
168 * value from it.
169 *
170 * FIXME: This is module-hardware dependent and needs to be extended for every
171 * type of module we want to support.
172 */
173static int gb_svc_read_and_clear_module_boot_status(struct gb_interface *intf)
174{
Johan Hovold25376362015-11-03 18:03:23 +0100175 struct gb_host_device *hd = intf->hd;
Viresh Kumar6bec5c72015-09-24 14:40:29 -0700176 int ret;
177 u32 value;
178
179 /* Read and clear boot status in T_TstSrcIncrement */
180 ret = gb_svc_dme_peer_get(hd->svc, intf->interface_id,
181 DME_ATTR_T_TST_SRC_INCREMENT,
182 DME_ATTR_SELECTOR_INDEX, &value);
183
184 if (ret)
185 return ret;
186
187 /*
188 * A nonzero boot status indicates the module has finished
189 * booting. Clear it.
190 */
191 if (!value) {
192 dev_err(&intf->dev, "Module not ready yet\n");
193 return -ENODEV;
194 }
195
Viresh Kumar1575ef12015-10-07 15:40:24 -0400196 /*
197 * Check if the module needs to boot from unipro.
198 * For ES2: We need to check lowest 8 bits of 'value'.
199 * For ES3: We need to check highest 8 bits out of 32 of 'value'.
200 *
201 * FIXME: Add code to find if we are on ES2 or ES3 to have separate
202 * checks.
203 */
204 if (value == DME_TSI_UNIPRO_BOOT_STARTED ||
205 value == DME_TSI_FALLBACK_UNIPRO_BOOT_STARTED)
206 intf->boot_over_unipro = true;
207
Viresh Kumar6bec5c72015-09-24 14:40:29 -0700208 return gb_svc_dme_peer_set(hd->svc, intf->interface_id,
209 DME_ATTR_T_TST_SRC_INCREMENT,
210 DME_ATTR_SELECTOR_INDEX, 0);
211}
212
Viresh Kumar3f0e9182015-08-31 17:21:06 +0530213int gb_svc_connection_create(struct gb_svc *svc,
Alex Elder30c6d9d2015-05-22 13:02:08 -0500214 u8 intf1_id, u16 cport1_id,
Viresh Kumar1575ef12015-10-07 15:40:24 -0400215 u8 intf2_id, u16 cport2_id,
216 bool boot_over_unipro)
Alex Elder30c6d9d2015-05-22 13:02:08 -0500217{
218 struct gb_svc_conn_create_request request;
219
220 request.intf1_id = intf1_id;
Rui Miguel Silva24980502015-09-15 15:33:51 +0100221 request.cport1_id = cpu_to_le16(cport1_id);
Alex Elder30c6d9d2015-05-22 13:02:08 -0500222 request.intf2_id = intf2_id;
Rui Miguel Silva24980502015-09-15 15:33:51 +0100223 request.cport2_id = cpu_to_le16(cport2_id);
Perry Hung0b226492015-07-24 19:02:34 -0400224 /*
225 * XXX: fix connections paramaters to TC0 and all CPort flags
226 * for now.
227 */
228 request.tc = 0;
Viresh Kumar1575ef12015-10-07 15:40:24 -0400229
230 /*
231 * We need to skip setting E2EFC and other flags to the connection
232 * create request, for all cports, on an interface that need to boot
233 * over unipro, i.e. interfaces required to download firmware.
234 */
235 if (boot_over_unipro)
236 request.flags = CPORT_FLAGS_CSV_N | CPORT_FLAGS_CSD_N;
237 else
238 request.flags = CPORT_FLAGS_CSV_N | CPORT_FLAGS_E2EFC;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500239
240 return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE,
241 &request, sizeof(request), NULL, 0);
242}
Viresh Kumar3f0e9182015-08-31 17:21:06 +0530243EXPORT_SYMBOL_GPL(gb_svc_connection_create);
Alex Elder30c6d9d2015-05-22 13:02:08 -0500244
Viresh Kumar3f0e9182015-08-31 17:21:06 +0530245void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id,
246 u8 intf2_id, u16 cport2_id)
Alex Elder30c6d9d2015-05-22 13:02:08 -0500247{
248 struct gb_svc_conn_destroy_request request;
Viresh Kumard9fcfff2015-08-31 17:21:05 +0530249 struct gb_connection *connection = svc->connection;
250 int ret;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500251
252 request.intf1_id = intf1_id;
Rui Miguel Silva24980502015-09-15 15:33:51 +0100253 request.cport1_id = cpu_to_le16(cport1_id);
Alex Elder30c6d9d2015-05-22 13:02:08 -0500254 request.intf2_id = intf2_id;
Rui Miguel Silva24980502015-09-15 15:33:51 +0100255 request.cport2_id = cpu_to_le16(cport2_id);
Alex Elder30c6d9d2015-05-22 13:02:08 -0500256
Viresh Kumard9fcfff2015-08-31 17:21:05 +0530257 ret = gb_operation_sync(connection, GB_SVC_TYPE_CONN_DESTROY,
258 &request, sizeof(request), NULL, 0);
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700259 if (ret)
Johan Hovold35a84ba2015-11-11 10:07:07 +0100260 pr_err("failed to destroy connection (%hhu:%hu %hhu:%hu) %d\n",
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700261 intf1_id, cport1_id, intf2_id, cport2_id, ret);
Alex Elder30c6d9d2015-05-22 13:02:08 -0500262}
Viresh Kumar3f0e9182015-08-31 17:21:06 +0530263EXPORT_SYMBOL_GPL(gb_svc_connection_destroy);
Alex Elder30c6d9d2015-05-22 13:02:08 -0500264
Viresh Kumarbb106852015-09-07 16:01:25 +0530265/* Creates bi-directional routes between the devices */
Viresh Kumar505f16c2015-08-31 17:21:07 +0530266static int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id,
267 u8 intf2_id, u8 dev2_id)
Perry Hunge08aaa42015-07-24 19:02:31 -0400268{
269 struct gb_svc_route_create_request request;
270
271 request.intf1_id = intf1_id;
272 request.dev1_id = dev1_id;
273 request.intf2_id = intf2_id;
274 request.dev2_id = dev2_id;
275
276 return gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_CREATE,
277 &request, sizeof(request), NULL, 0);
278}
Perry Hunge08aaa42015-07-24 19:02:31 -0400279
Viresh Kumar0a020572015-09-07 18:05:26 +0530280/* Destroys bi-directional routes between the devices */
281static void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id)
282{
283 struct gb_svc_route_destroy_request request;
284 int ret;
285
286 request.intf1_id = intf1_id;
287 request.intf2_id = intf2_id;
288
289 ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_DESTROY,
290 &request, sizeof(request), NULL, 0);
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700291 if (ret)
Johan Hovold35a84ba2015-11-11 10:07:07 +0100292 pr_err("failed to destroy route (%hhu %hhu) %d\n",
Viresh Kumar0a020572015-09-07 18:05:26 +0530293 intf1_id, intf2_id, ret);
Viresh Kumar0a020572015-09-07 18:05:26 +0530294}
295
Viresh Kumaread35462015-07-21 17:44:19 +0530296static int gb_svc_version_request(struct gb_operation *op)
297{
298 struct gb_connection *connection = op->connection;
Johan Hovoldcfb16902015-09-15 10:48:01 +0200299 struct gb_protocol_version_request *request;
300 struct gb_protocol_version_response *response;
Viresh Kumaread35462015-07-21 17:44:19 +0530301
Johan Hovold55510842015-11-19 18:28:01 +0100302 if (op->request->payload_size < sizeof(*request)) {
303 pr_err("%d: short version request (%zu < %zu)\n",
304 connection->intf_cport_id,
305 op->request->payload_size,
306 sizeof(*request));
307 return -EINVAL;
308 }
309
Johan Hovoldcfb16902015-09-15 10:48:01 +0200310 request = op->request->payload;
Viresh Kumaread35462015-07-21 17:44:19 +0530311
Johan Hovoldcfb16902015-09-15 10:48:01 +0200312 if (request->major > GB_SVC_VERSION_MAJOR) {
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700313 pr_err("%d: unsupported major version (%hhu > %hhu)\n",
314 connection->intf_cport_id, request->major,
315 GB_SVC_VERSION_MAJOR);
Viresh Kumaread35462015-07-21 17:44:19 +0530316 return -ENOTSUPP;
317 }
318
Johan Hovoldcfb16902015-09-15 10:48:01 +0200319 connection->module_major = request->major;
320 connection->module_minor = request->minor;
Viresh Kumar3ea959e32015-08-11 07:36:14 +0530321
Johan Hovoldcfb16902015-09-15 10:48:01 +0200322 if (!gb_operation_response_alloc(op, sizeof(*response), GFP_KERNEL)) {
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700323 pr_err("%d: error allocating response\n",
324 connection->intf_cport_id);
Viresh Kumaread35462015-07-21 17:44:19 +0530325 return -ENOMEM;
326 }
327
Johan Hovoldcfb16902015-09-15 10:48:01 +0200328 response = op->response->payload;
329 response->major = connection->module_major;
330 response->minor = connection->module_minor;
Johan Hovold59832932015-09-15 10:48:00 +0200331
Viresh Kumaread35462015-07-21 17:44:19 +0530332 return 0;
333}
334
335static int gb_svc_hello(struct gb_operation *op)
336{
337 struct gb_connection *connection = op->connection;
Johan Hovold88f7b962015-11-25 15:59:08 +0100338 struct gb_svc *svc = connection->private;
Johan Hovold25376362015-11-03 18:03:23 +0100339 struct gb_host_device *hd = connection->hd;
Viresh Kumaread35462015-07-21 17:44:19 +0530340 struct gb_svc_hello_request *hello_request;
Viresh Kumaread35462015-07-21 17:44:19 +0530341 struct gb_interface *intf;
342 u16 endo_id;
343 u8 interface_id;
344 int ret;
345
Viresh Kumaread35462015-07-21 17:44:19 +0530346 /*
347 * SVC sends information about the endo and interface-id on the hello
348 * request, use that to create an endo.
349 */
Viresh Kumar0c32d2a2015-08-11 07:29:19 +0530350 if (op->request->payload_size < sizeof(*hello_request)) {
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700351 pr_err("%d: Illegal size of hello request (%zu < %zu)\n",
352 connection->intf_cport_id, op->request->payload_size,
353 sizeof(*hello_request));
Viresh Kumaread35462015-07-21 17:44:19 +0530354 return -EINVAL;
355 }
356
357 hello_request = op->request->payload;
358 endo_id = le16_to_cpu(hello_request->endo_id);
359 interface_id = hello_request->interface_id;
360
Johan Hovold88f7b962015-11-25 15:59:08 +0100361 ret = device_add(&svc->dev);
362 if (ret) {
363 dev_err(&svc->dev, "failed to register svc device: %d\n", ret);
364 return ret;
365 }
366
Viresh Kumaread35462015-07-21 17:44:19 +0530367 /* Setup Endo */
368 ret = greybus_endo_setup(hd, endo_id, interface_id);
369 if (ret)
370 return ret;
371
372 /*
373 * Endo and its modules are ready now, fix AP's partially initialized
374 * svc protocol and its connection.
375 */
376 intf = gb_ap_interface_create(hd, connection, interface_id);
377 if (!intf) {
378 gb_endo_remove(hd->endo);
379 return ret;
380 }
381
382 return 0;
383}
384
Viresh Kumarbbaca712015-09-23 16:48:08 -0700385static void svc_intf_remove(struct gb_connection *connection,
386 struct gb_interface *intf)
387{
Johan Hovold25376362015-11-03 18:03:23 +0100388 struct gb_host_device *hd = connection->hd;
Viresh Kumarbbaca712015-09-23 16:48:08 -0700389 struct gb_svc *svc = connection->private;
390 u8 intf_id = intf->interface_id;
391 u8 device_id;
392
393 device_id = intf->device_id;
Viresh Kumar80d1ede2015-09-23 16:48:10 -0700394 gb_interface_remove(intf);
Viresh Kumarbbaca712015-09-23 16:48:08 -0700395
396 /*
397 * Destroy the two-way route between the AP and the interface.
398 */
399 gb_svc_route_destroy(svc, hd->endo->ap_intf_id, intf_id);
400
401 ida_simple_remove(&svc->device_id_map, device_id);
402}
403
Viresh Kumar067906f2015-08-06 12:44:55 +0530404/*
405 * 'struct svc_hotplug' should be freed by svc_process_hotplug() before it
406 * returns, irrespective of success or Failure in bringing up the module.
407 */
408static void svc_process_hotplug(struct work_struct *work)
Alex Elder30c6d9d2015-05-22 13:02:08 -0500409{
Viresh Kumar067906f2015-08-06 12:44:55 +0530410 struct svc_hotplug *svc_hotplug = container_of(work, struct svc_hotplug,
411 work);
412 struct gb_svc_intf_hotplug_request *hotplug = &svc_hotplug->data;
413 struct gb_connection *connection = svc_hotplug->connection;
414 struct gb_svc *svc = connection->private;
Johan Hovold25376362015-11-03 18:03:23 +0100415 struct gb_host_device *hd = connection->hd;
Viresh Kumaread35462015-07-21 17:44:19 +0530416 struct gb_interface *intf;
417 u8 intf_id, device_id;
Viresh Kumaread35462015-07-21 17:44:19 +0530418 int ret;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500419
Alex Elder30c6d9d2015-05-22 13:02:08 -0500420 /*
421 * Grab the information we need.
Viresh Kumar7eb89192015-07-01 12:13:50 +0530422 */
Alex Elder30c6d9d2015-05-22 13:02:08 -0500423 intf_id = hotplug->intf_id;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500424
Viresh Kumarbbaca712015-09-23 16:48:08 -0700425 intf = gb_interface_find(hd, intf_id);
426 if (intf) {
427 /*
428 * We have received a hotplug request for an interface that
429 * already exists.
430 *
431 * This can happen in cases like:
432 * - bootrom loading the firmware image and booting into that,
433 * which only generates a hotplug event. i.e. no hot-unplug
434 * event.
435 * - Or the firmware on the module crashed and sent hotplug
436 * request again to the SVC, which got propagated to AP.
437 *
438 * Remove the interface and add it again, and let user know
439 * about this with a print message.
440 */
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700441 pr_info("%d: Removed interface (%hhu) to add it again\n",
442 connection->intf_cport_id, intf_id);
Viresh Kumarbbaca712015-09-23 16:48:08 -0700443 svc_intf_remove(connection, intf);
444 }
445
Viresh Kumaread35462015-07-21 17:44:19 +0530446 intf = gb_interface_create(hd, intf_id);
447 if (!intf) {
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700448 pr_err("%d: Failed to create interface with id %hhu\n",
449 connection->intf_cport_id, intf_id);
Viresh Kumar067906f2015-08-06 12:44:55 +0530450 goto free_svc_hotplug;
Viresh Kumaread35462015-07-21 17:44:19 +0530451 }
452
Viresh Kumar6bec5c72015-09-24 14:40:29 -0700453 ret = gb_svc_read_and_clear_module_boot_status(intf);
454 if (ret)
455 goto destroy_interface;
456
Viresh Kumar3944a452015-08-12 09:19:31 +0530457 intf->unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id);
458 intf->unipro_prod_id = le32_to_cpu(hotplug->data.unipro_prod_id);
Johan Hovold9f592632015-11-25 15:58:56 +0100459 intf->vendor_id = le32_to_cpu(hotplug->data.ara_vend_id);
460 intf->product_id = le32_to_cpu(hotplug->data.ara_prod_id);
Viresh Kumar3944a452015-08-12 09:19:31 +0530461
Viresh Kumaread35462015-07-21 17:44:19 +0530462 /*
463 * Create a device id for the interface:
464 * - device id 0 (GB_DEVICE_ID_SVC) belongs to the SVC
465 * - device id 1 (GB_DEVICE_ID_AP) belongs to the AP
466 *
467 * XXX Do we need to allocate device ID for SVC or the AP here? And what
468 * XXX about an AP with multiple interface blocks?
469 */
Johan Hovoldc09db182015-09-15 09:18:08 +0200470 device_id = ida_simple_get(&svc->device_id_map,
Johan Hovold89f637f2015-09-01 12:25:25 +0200471 GB_DEVICE_ID_MODULES_START, 0, GFP_KERNEL);
Viresh Kumaread35462015-07-21 17:44:19 +0530472 if (device_id < 0) {
473 ret = device_id;
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700474 pr_err("%d: Failed to allocate device id for interface with id %hhu (%d)\n",
475 connection->intf_cport_id, intf_id, ret);
Viresh Kumaread35462015-07-21 17:44:19 +0530476 goto destroy_interface;
477 }
478
Viresh Kumar3f0e9182015-08-31 17:21:06 +0530479 ret = gb_svc_intf_device_id(svc, intf_id, device_id);
Viresh Kumaread35462015-07-21 17:44:19 +0530480 if (ret) {
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700481 pr_err("%d: Device id operation failed, interface %hhu device_id %hhu (%d)\n",
482 connection->intf_cport_id, intf_id, device_id, ret);
Viresh Kumaread35462015-07-21 17:44:19 +0530483 goto ida_put;
484 }
485
Perry Hung7e275462015-07-24 19:02:32 -0400486 /*
487 * Create a two-way route between the AP and the new interface
488 */
Viresh Kumar3f0e9182015-08-31 17:21:06 +0530489 ret = gb_svc_route_create(svc, hd->endo->ap_intf_id, GB_DEVICE_ID_AP,
490 intf_id, device_id);
Perry Hung7e275462015-07-24 19:02:32 -0400491 if (ret) {
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700492 pr_err("%d: Route create operation failed, interface %hhu device_id %hhu (%d)\n",
493 connection->intf_cport_id, intf_id, device_id, ret);
Viresh Kumar0a020572015-09-07 18:05:26 +0530494 goto svc_id_free;
Perry Hung7e275462015-07-24 19:02:32 -0400495 }
496
Viresh Kumaread35462015-07-21 17:44:19 +0530497 ret = gb_interface_init(intf, device_id);
498 if (ret) {
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700499 pr_err("%d: Failed to initialize interface, interface %hhu device_id %hhu (%d)\n",
500 connection->intf_cport_id, intf_id, device_id, ret);
Viresh Kumar0a020572015-09-07 18:05:26 +0530501 goto destroy_route;
Viresh Kumaread35462015-07-21 17:44:19 +0530502 }
Alex Elder30c6d9d2015-05-22 13:02:08 -0500503
Viresh Kumar067906f2015-08-06 12:44:55 +0530504 goto free_svc_hotplug;
Viresh Kumaread35462015-07-21 17:44:19 +0530505
Viresh Kumar0a020572015-09-07 18:05:26 +0530506destroy_route:
507 gb_svc_route_destroy(svc, hd->endo->ap_intf_id, intf_id);
Viresh Kumaread35462015-07-21 17:44:19 +0530508svc_id_free:
509 /*
510 * XXX Should we tell SVC that this id doesn't belong to interface
511 * XXX anymore.
512 */
513ida_put:
Johan Hovoldc09db182015-09-15 09:18:08 +0200514 ida_simple_remove(&svc->device_id_map, device_id);
Viresh Kumaread35462015-07-21 17:44:19 +0530515destroy_interface:
Viresh Kumar80d1ede2015-09-23 16:48:10 -0700516 gb_interface_remove(intf);
Viresh Kumar067906f2015-08-06 12:44:55 +0530517free_svc_hotplug:
518 kfree(svc_hotplug);
519}
Viresh Kumaread35462015-07-21 17:44:19 +0530520
Viresh Kumar067906f2015-08-06 12:44:55 +0530521/*
522 * Bringing up a module can be time consuming, as that may require lots of
523 * initialization on the module side. Over that, we may also need to download
524 * the firmware first and flash that on the module.
525 *
526 * In order to make other hotplug events to not wait for all this to finish,
527 * handle most of module hotplug stuff outside of the hotplug callback, with
528 * help of a workqueue.
529 */
530static int gb_svc_intf_hotplug_recv(struct gb_operation *op)
531{
532 struct gb_message *request = op->request;
533 struct svc_hotplug *svc_hotplug;
534
535 if (request->payload_size < sizeof(svc_hotplug->data)) {
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700536 pr_err("%d: short hotplug request received (%zu < %zu)\n",
537 op->connection->intf_cport_id, request->payload_size,
538 sizeof(svc_hotplug->data));
Viresh Kumar067906f2015-08-06 12:44:55 +0530539 return -EINVAL;
540 }
541
Johan Hovold287bba82015-09-01 12:25:26 +0200542 svc_hotplug = kmalloc(sizeof(*svc_hotplug), GFP_KERNEL);
Viresh Kumar067906f2015-08-06 12:44:55 +0530543 if (!svc_hotplug)
544 return -ENOMEM;
545
546 svc_hotplug->connection = op->connection;
547 memcpy(&svc_hotplug->data, op->request->payload, sizeof(svc_hotplug->data));
548
549 INIT_WORK(&svc_hotplug->work, svc_process_hotplug);
550 queue_work(system_unbound_wq, &svc_hotplug->work);
551
552 return 0;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500553}
554
555static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op)
556{
557 struct gb_message *request = op->request;
Viresh Kumaread35462015-07-21 17:44:19 +0530558 struct gb_svc_intf_hot_unplug_request *hot_unplug = request->payload;
Johan Hovold25376362015-11-03 18:03:23 +0100559 struct gb_host_device *hd = op->connection->hd;
Viresh Kumaread35462015-07-21 17:44:19 +0530560 struct gb_interface *intf;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500561 u8 intf_id;
562
563 if (request->payload_size < sizeof(*hot_unplug)) {
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700564 pr_err("connection %d: short hot unplug request received (%zu < %zu)\n",
565 op->connection->intf_cport_id, request->payload_size,
566 sizeof(*hot_unplug));
Alex Elder30c6d9d2015-05-22 13:02:08 -0500567 return -EINVAL;
568 }
Alex Elder30c6d9d2015-05-22 13:02:08 -0500569
570 intf_id = hot_unplug->intf_id;
571
Viresh Kumaread35462015-07-21 17:44:19 +0530572 intf = gb_interface_find(hd, intf_id);
573 if (!intf) {
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700574 pr_err("connection %d: Couldn't find interface for id %hhu\n",
575 op->connection->intf_cport_id, intf_id);
Viresh Kumaread35462015-07-21 17:44:19 +0530576 return -EINVAL;
577 }
578
Viresh Kumarbbaca712015-09-23 16:48:08 -0700579 svc_intf_remove(op->connection, intf);
Alex Elder30c6d9d2015-05-22 13:02:08 -0500580
581 return 0;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500582}
583
584static int gb_svc_intf_reset_recv(struct gb_operation *op)
585{
586 struct gb_message *request = op->request;
587 struct gb_svc_intf_reset_request *reset;
588 u8 intf_id;
589
590 if (request->payload_size < sizeof(*reset)) {
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700591 pr_err("connection %d: short reset request received (%zu < %zu)\n",
592 op->connection->intf_cport_id, request->payload_size,
593 sizeof(*reset));
Alex Elder30c6d9d2015-05-22 13:02:08 -0500594 return -EINVAL;
595 }
596 reset = request->payload;
597
598 intf_id = reset->intf_id;
599
600 /* FIXME Reset the interface here */
601
602 return 0;
603}
604
605static int gb_svc_request_recv(u8 type, struct gb_operation *op)
606{
Viresh Kumar3ccb1602015-09-03 15:42:22 +0530607 struct gb_connection *connection = op->connection;
608 struct gb_svc *svc = connection->private;
609 int ret = 0;
610
611 /*
612 * SVC requests need to follow a specific order (at least initially) and
613 * below code takes care of enforcing that. The expected order is:
614 * - PROTOCOL_VERSION
615 * - SVC_HELLO
616 * - Any other request, but the earlier two.
617 *
618 * Incoming requests are guaranteed to be serialized and so we don't
619 * need to protect 'state' for any races.
620 */
Alex Elder30c6d9d2015-05-22 13:02:08 -0500621 switch (type) {
Viresh Kumar0e2462d2015-08-14 07:57:38 +0530622 case GB_REQUEST_TYPE_PROTOCOL_VERSION:
Viresh Kumar3ccb1602015-09-03 15:42:22 +0530623 if (svc->state != GB_SVC_STATE_RESET)
624 ret = -EINVAL;
625 break;
Viresh Kumaread35462015-07-21 17:44:19 +0530626 case GB_SVC_TYPE_SVC_HELLO:
Viresh Kumar3ccb1602015-09-03 15:42:22 +0530627 if (svc->state != GB_SVC_STATE_PROTOCOL_VERSION)
628 ret = -EINVAL;
629 break;
630 default:
631 if (svc->state != GB_SVC_STATE_SVC_HELLO)
632 ret = -EINVAL;
633 break;
634 }
635
636 if (ret) {
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700637 pr_warn("connection %d: unexpected SVC request 0x%02x received (state %u)\n",
638 connection->intf_cport_id, type, svc->state);
Viresh Kumar3ccb1602015-09-03 15:42:22 +0530639 return ret;
640 }
641
642 switch (type) {
643 case GB_REQUEST_TYPE_PROTOCOL_VERSION:
644 ret = gb_svc_version_request(op);
645 if (!ret)
646 svc->state = GB_SVC_STATE_PROTOCOL_VERSION;
647 return ret;
648 case GB_SVC_TYPE_SVC_HELLO:
649 ret = gb_svc_hello(op);
650 if (!ret)
651 svc->state = GB_SVC_STATE_SVC_HELLO;
652 return ret;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500653 case GB_SVC_TYPE_INTF_HOTPLUG:
654 return gb_svc_intf_hotplug_recv(op);
655 case GB_SVC_TYPE_INTF_HOT_UNPLUG:
656 return gb_svc_intf_hot_unplug_recv(op);
657 case GB_SVC_TYPE_INTF_RESET:
658 return gb_svc_intf_reset_recv(op);
659 default:
Greg Kroah-Hartmanb50a24e2015-10-16 16:53:31 -0700660 pr_err("connection %d: unsupported request: %hhu\n",
661 connection->intf_cport_id, type);
Alex Elder30c6d9d2015-05-22 13:02:08 -0500662 return -EINVAL;
663 }
664}
665
Johan Hovoldefe6ef72015-11-25 15:59:06 +0100666static void gb_svc_release(struct device *dev)
667{
Johan Hovold88f7b962015-11-25 15:59:08 +0100668 struct gb_svc *svc = to_gb_svc(dev);
Johan Hovoldefe6ef72015-11-25 15:59:06 +0100669
670 ida_destroy(&svc->device_id_map);
671 kfree(svc);
672}
673
674struct device_type greybus_svc_type = {
675 .name = "greybus_svc",
676 .release = gb_svc_release,
677};
678
Alex Elder30c6d9d2015-05-22 13:02:08 -0500679static int gb_svc_connection_init(struct gb_connection *connection)
680{
Johan Hovoldefe6ef72015-11-25 15:59:06 +0100681 struct gb_host_device *hd = connection->hd;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500682 struct gb_svc *svc;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500683
684 svc = kzalloc(sizeof(*svc), GFP_KERNEL);
685 if (!svc)
686 return -ENOMEM;
687
Johan Hovoldefe6ef72015-11-25 15:59:06 +0100688 svc->dev.parent = &hd->dev;
689 svc->dev.bus = &greybus_bus_type;
690 svc->dev.type = &greybus_svc_type;
691 svc->dev.dma_mask = svc->dev.parent->dma_mask;
692 device_initialize(&svc->dev);
693
694 dev_set_name(&svc->dev, "%d-svc", hd->bus_id);
695
Johan Hovold6106e512015-11-25 15:59:07 +0100696 ida_init(&svc->device_id_map);
Viresh Kumar3ccb1602015-09-03 15:42:22 +0530697 svc->state = GB_SVC_STATE_RESET;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500698 svc->connection = connection;
699 connection->private = svc;
Viresh Kumard3d44842015-07-21 17:44:18 +0530700
Johan Hovoldefe6ef72015-11-25 15:59:06 +0100701 hd->svc = svc;
702
Viresh Kumardcd05002015-07-24 15:32:20 +0530703 WARN_ON(connection->hd->initial_svc_connection);
704 connection->hd->initial_svc_connection = connection;
Viresh Kumard3d44842015-07-21 17:44:18 +0530705
Viresh Kumar18d777c2015-07-21 17:44:20 +0530706 return 0;
Alex Elder30c6d9d2015-05-22 13:02:08 -0500707}
708
709static void gb_svc_connection_exit(struct gb_connection *connection)
710{
711 struct gb_svc *svc = connection->private;
712
Johan Hovold88f7b962015-11-25 15:59:08 +0100713 if (device_is_registered(&svc->dev))
714 device_del(&svc->dev);
715
Perry Hung75a60ed2015-07-24 19:02:33 -0400716 connection->hd->svc = NULL;
Viresh Kumard3d44842015-07-21 17:44:18 +0530717 connection->private = NULL;
Johan Hovoldefe6ef72015-11-25 15:59:06 +0100718
719 put_device(&svc->dev);
Alex Elder30c6d9d2015-05-22 13:02:08 -0500720}
721
722static struct gb_protocol svc_protocol = {
723 .name = "svc",
724 .id = GREYBUS_PROTOCOL_SVC,
Viresh Kumar06e305f2015-07-01 12:13:51 +0530725 .major = GB_SVC_VERSION_MAJOR,
726 .minor = GB_SVC_VERSION_MINOR,
Alex Elder30c6d9d2015-05-22 13:02:08 -0500727 .connection_init = gb_svc_connection_init,
728 .connection_exit = gb_svc_connection_exit,
729 .request_recv = gb_svc_request_recv,
Viresh Kumar5a5296b2015-09-07 16:01:24 +0530730 .flags = GB_PROTOCOL_SKIP_CONTROL_CONNECTED |
731 GB_PROTOCOL_SKIP_CONTROL_DISCONNECTED |
732 GB_PROTOCOL_NO_BUNDLE |
733 GB_PROTOCOL_SKIP_VERSION |
734 GB_PROTOCOL_SKIP_SVC_CONNECTION,
Alex Elder30c6d9d2015-05-22 13:02:08 -0500735};
Viresh Kumarab69c4c2015-07-03 17:00:29 +0530736gb_builtin_protocol_driver(svc_protocol);