]>
Commit | Line | Data |
---|---|---|
30c6d9d7 AE |
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 | ||
9504677c | 10 | #include <linux/debugfs.h> |
067906f6 | 11 | #include <linux/workqueue.h> |
30c6d9d7 | 12 | |
f66427ad VK |
13 | #include "greybus.h" |
14 | ||
140026b3 JC |
15 | #define SVC_INTF_EJECT_TIMEOUT 9000 |
16 | #define SVC_INTF_ACTIVATE_TIMEOUT 6000 | |
1f3e09e7 | 17 | #define SVC_INTF_RESUME_TIMEOUT 3000 |
d18da86b | 18 | |
9ae4109e | 19 | struct gb_svc_deferred_request { |
067906f6 | 20 | struct work_struct work; |
9ae4109e | 21 | struct gb_operation *operation; |
067906f6 VK |
22 | }; |
23 | ||
ead35460 | 24 | |
ee2f2074 MT |
25 | static int gb_svc_queue_deferred_request(struct gb_operation *operation); |
26 | ||
66069fb0 JH |
27 | static ssize_t endo_id_show(struct device *dev, |
28 | struct device_attribute *attr, char *buf) | |
29 | { | |
30 | struct gb_svc *svc = to_gb_svc(dev); | |
31 | ||
32 | return sprintf(buf, "0x%04x\n", svc->endo_id); | |
33 | } | |
34 | static DEVICE_ATTR_RO(endo_id); | |
35 | ||
36 | static ssize_t ap_intf_id_show(struct device *dev, | |
37 | struct device_attribute *attr, char *buf) | |
38 | { | |
39 | struct gb_svc *svc = to_gb_svc(dev); | |
40 | ||
41 | return sprintf(buf, "%u\n", svc->ap_intf_id); | |
42 | } | |
43 | static DEVICE_ATTR_RO(ap_intf_id); | |
44 | ||
2c92bd52 RMS |
45 | // FIXME |
46 | // This is a hack, we need to do this "right" and clean the interface up | |
47 | // properly, not just forcibly yank the thing out of the system and hope for the | |
48 | // best. But for now, people want their modules to come out without having to | |
49 | // throw the thing to the ground or get out a screwdriver. | |
50 | static ssize_t intf_eject_store(struct device *dev, | |
51 | struct device_attribute *attr, const char *buf, | |
52 | size_t len) | |
53 | { | |
54 | struct gb_svc *svc = to_gb_svc(dev); | |
55 | unsigned short intf_id; | |
56 | int ret; | |
57 | ||
58 | ret = kstrtou16(buf, 10, &intf_id); | |
59 | if (ret < 0) | |
60 | return ret; | |
61 | ||
62 | dev_warn(dev, "Forcibly trying to eject interface %d\n", intf_id); | |
63 | ||
64 | ret = gb_svc_intf_eject(svc, intf_id); | |
65 | if (ret < 0) | |
66 | return ret; | |
67 | ||
68 | return len; | |
69 | } | |
70 | static DEVICE_ATTR_WO(intf_eject); | |
71 | ||
d562853d GKH |
72 | static ssize_t watchdog_show(struct device *dev, struct device_attribute *attr, |
73 | char *buf) | |
74 | { | |
75 | struct gb_svc *svc = to_gb_svc(dev); | |
76 | ||
77 | return sprintf(buf, "%s\n", | |
78 | gb_svc_watchdog_enabled(svc) ? "enabled" : "disabled"); | |
79 | } | |
80 | ||
81 | static ssize_t watchdog_store(struct device *dev, | |
82 | struct device_attribute *attr, const char *buf, | |
83 | size_t len) | |
84 | { | |
85 | struct gb_svc *svc = to_gb_svc(dev); | |
86 | int retval; | |
87 | bool user_request; | |
88 | ||
89 | retval = strtobool(buf, &user_request); | |
90 | if (retval) | |
91 | return retval; | |
92 | ||
93 | if (user_request) | |
94 | retval = gb_svc_watchdog_enable(svc); | |
95 | else | |
96 | retval = gb_svc_watchdog_disable(svc); | |
97 | if (retval) | |
98 | return retval; | |
99 | return len; | |
100 | } | |
101 | static DEVICE_ATTR_RW(watchdog); | |
102 | ||
7c4a0edb DL |
103 | static ssize_t watchdog_action_show(struct device *dev, |
104 | struct device_attribute *attr, char *buf) | |
105 | { | |
106 | struct gb_svc *svc = to_gb_svc(dev); | |
107 | ||
108 | if (svc->action == GB_SVC_WATCHDOG_BITE_PANIC_KERNEL) | |
109 | return sprintf(buf, "panic\n"); | |
110 | else if (svc->action == GB_SVC_WATCHDOG_BITE_RESET_UNIPRO) | |
111 | return sprintf(buf, "reset\n"); | |
112 | ||
113 | return -EINVAL; | |
114 | } | |
115 | ||
116 | static ssize_t watchdog_action_store(struct device *dev, | |
117 | struct device_attribute *attr, | |
118 | const char *buf, size_t len) | |
119 | { | |
120 | struct gb_svc *svc = to_gb_svc(dev); | |
121 | ||
122 | if (sysfs_streq(buf, "panic")) | |
123 | svc->action = GB_SVC_WATCHDOG_BITE_PANIC_KERNEL; | |
124 | else if (sysfs_streq(buf, "reset")) | |
125 | svc->action = GB_SVC_WATCHDOG_BITE_RESET_UNIPRO; | |
126 | else | |
127 | return -EINVAL; | |
128 | ||
129 | return len; | |
130 | } | |
131 | static DEVICE_ATTR_RW(watchdog_action); | |
132 | ||
9504677c DL |
133 | static int gb_svc_pwrmon_rail_count_get(struct gb_svc *svc, u8 *value) |
134 | { | |
135 | struct gb_svc_pwrmon_rail_count_get_response response; | |
136 | int ret; | |
137 | ||
138 | ret = gb_operation_sync(svc->connection, | |
139 | GB_SVC_TYPE_PWRMON_RAIL_COUNT_GET, NULL, 0, | |
140 | &response, sizeof(response)); | |
141 | if (ret) { | |
89f2df43 | 142 | dev_err(&svc->dev, "failed to get rail count: %d\n", ret); |
9504677c DL |
143 | return ret; |
144 | } | |
145 | ||
146 | *value = response.rail_count; | |
147 | ||
148 | return 0; | |
149 | } | |
150 | ||
151 | static int gb_svc_pwrmon_rail_names_get(struct gb_svc *svc, | |
f35fdb2f JH |
152 | struct gb_svc_pwrmon_rail_names_get_response *response, |
153 | size_t bufsize) | |
9504677c DL |
154 | { |
155 | int ret; | |
156 | ||
157 | ret = gb_operation_sync(svc->connection, | |
158 | GB_SVC_TYPE_PWRMON_RAIL_NAMES_GET, NULL, 0, | |
159 | response, bufsize); | |
160 | if (ret) { | |
89f2df43 | 161 | dev_err(&svc->dev, "failed to get rail names: %d\n", ret); |
9504677c DL |
162 | return ret; |
163 | } | |
164 | ||
8fb76c3c DL |
165 | if (response->status != GB_SVC_OP_SUCCESS) { |
166 | dev_err(&svc->dev, | |
167 | "SVC error while getting rail names: %u\n", | |
168 | response->status); | |
169 | return -EREMOTEIO; | |
170 | } | |
171 | ||
9504677c DL |
172 | return 0; |
173 | } | |
174 | ||
175 | static int gb_svc_pwrmon_sample_get(struct gb_svc *svc, u8 rail_id, | |
176 | u8 measurement_type, u32 *value) | |
177 | { | |
178 | struct gb_svc_pwrmon_sample_get_request request; | |
179 | struct gb_svc_pwrmon_sample_get_response response; | |
180 | int ret; | |
181 | ||
182 | request.rail_id = rail_id; | |
183 | request.measurement_type = measurement_type; | |
184 | ||
185 | ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_PWRMON_SAMPLE_GET, | |
186 | &request, sizeof(request), | |
187 | &response, sizeof(response)); | |
188 | if (ret) { | |
89f2df43 | 189 | dev_err(&svc->dev, "failed to get rail sample: %d\n", ret); |
9504677c DL |
190 | return ret; |
191 | } | |
192 | ||
193 | if (response.result) { | |
194 | dev_err(&svc->dev, | |
195 | "UniPro error while getting rail power sample (%d %d): %d\n", | |
196 | rail_id, measurement_type, response.result); | |
197 | switch (response.result) { | |
198 | case GB_SVC_PWRMON_GET_SAMPLE_INVAL: | |
199 | return -EINVAL; | |
200 | case GB_SVC_PWRMON_GET_SAMPLE_NOSUPP: | |
5b35ef95 | 201 | return -ENOMSG; |
9504677c | 202 | default: |
4aea5a15 | 203 | return -EREMOTEIO; |
9504677c DL |
204 | } |
205 | } | |
206 | ||
207 | *value = le32_to_cpu(response.measurement); | |
208 | ||
209 | return 0; | |
210 | } | |
211 | ||
ddb10c8a DL |
212 | int gb_svc_pwrmon_intf_sample_get(struct gb_svc *svc, u8 intf_id, |
213 | u8 measurement_type, u32 *value) | |
214 | { | |
215 | struct gb_svc_pwrmon_intf_sample_get_request request; | |
216 | struct gb_svc_pwrmon_intf_sample_get_response response; | |
217 | int ret; | |
218 | ||
219 | request.intf_id = intf_id; | |
220 | request.measurement_type = measurement_type; | |
221 | ||
222 | ret = gb_operation_sync(svc->connection, | |
223 | GB_SVC_TYPE_PWRMON_INTF_SAMPLE_GET, | |
224 | &request, sizeof(request), | |
225 | &response, sizeof(response)); | |
226 | if (ret) { | |
89f2df43 | 227 | dev_err(&svc->dev, "failed to get intf sample: %d\n", ret); |
ddb10c8a DL |
228 | return ret; |
229 | } | |
230 | ||
231 | if (response.result) { | |
232 | dev_err(&svc->dev, | |
233 | "UniPro error while getting intf power sample (%d %d): %d\n", | |
234 | intf_id, measurement_type, response.result); | |
235 | switch (response.result) { | |
236 | case GB_SVC_PWRMON_GET_SAMPLE_INVAL: | |
237 | return -EINVAL; | |
238 | case GB_SVC_PWRMON_GET_SAMPLE_NOSUPP: | |
0bba4fb5 | 239 | return -ENOMSG; |
ddb10c8a | 240 | default: |
4aea5a15 | 241 | return -EREMOTEIO; |
ddb10c8a DL |
242 | } |
243 | } | |
244 | ||
245 | *value = le32_to_cpu(response.measurement); | |
246 | ||
247 | return 0; | |
248 | } | |
249 | ||
66069fb0 JH |
250 | static struct attribute *svc_attrs[] = { |
251 | &dev_attr_endo_id.attr, | |
252 | &dev_attr_ap_intf_id.attr, | |
2c92bd52 | 253 | &dev_attr_intf_eject.attr, |
d562853d | 254 | &dev_attr_watchdog.attr, |
7c4a0edb | 255 | &dev_attr_watchdog_action.attr, |
66069fb0 JH |
256 | NULL, |
257 | }; | |
258 | ATTRIBUTE_GROUPS(svc); | |
259 | ||
4d5f6218 | 260 | int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id) |
30c6d9d7 AE |
261 | { |
262 | struct gb_svc_intf_device_id_request request; | |
263 | ||
264 | request.intf_id = intf_id; | |
265 | request.device_id = device_id; | |
266 | ||
267 | return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_DEVICE_ID, | |
268 | &request, sizeof(request), NULL, 0); | |
269 | } | |
270 | ||
c5d55fb3 RMS |
271 | int gb_svc_intf_eject(struct gb_svc *svc, u8 intf_id) |
272 | { | |
273 | struct gb_svc_intf_eject_request request; | |
e676ccd7 | 274 | int ret; |
c5d55fb3 RMS |
275 | |
276 | request.intf_id = intf_id; | |
277 | ||
278 | /* | |
279 | * The pulse width for module release in svc is long so we need to | |
280 | * increase the timeout so the operation will not return to soon. | |
281 | */ | |
e676ccd7 JH |
282 | ret = gb_operation_sync_timeout(svc->connection, |
283 | GB_SVC_TYPE_INTF_EJECT, &request, | |
284 | sizeof(request), NULL, 0, | |
d18da86b | 285 | SVC_INTF_EJECT_TIMEOUT); |
e676ccd7 JH |
286 | if (ret) { |
287 | dev_err(&svc->dev, "failed to eject interface %u\n", intf_id); | |
288 | return ret; | |
289 | } | |
290 | ||
291 | return 0; | |
c5d55fb3 | 292 | } |
c5d55fb3 | 293 | |
017482b2 JH |
294 | int gb_svc_intf_vsys_set(struct gb_svc *svc, u8 intf_id, bool enable) |
295 | { | |
144763bf JC |
296 | struct gb_svc_intf_vsys_request request; |
297 | struct gb_svc_intf_vsys_response response; | |
298 | int type, ret; | |
299 | ||
300 | request.intf_id = intf_id; | |
017482b2 | 301 | |
144763bf JC |
302 | if (enable) |
303 | type = GB_SVC_TYPE_INTF_VSYS_ENABLE; | |
304 | else | |
305 | type = GB_SVC_TYPE_INTF_VSYS_DISABLE; | |
306 | ||
307 | ret = gb_operation_sync(svc->connection, type, | |
308 | &request, sizeof(request), | |
309 | &response, sizeof(response)); | |
310 | if (ret < 0) | |
311 | return ret; | |
312 | if (response.result_code != GB_SVC_INTF_VSYS_OK) | |
313 | return -EREMOTEIO; | |
017482b2 JH |
314 | return 0; |
315 | } | |
316 | ||
317 | int gb_svc_intf_refclk_set(struct gb_svc *svc, u8 intf_id, bool enable) | |
318 | { | |
144763bf JC |
319 | struct gb_svc_intf_refclk_request request; |
320 | struct gb_svc_intf_refclk_response response; | |
321 | int type, ret; | |
017482b2 | 322 | |
144763bf JC |
323 | request.intf_id = intf_id; |
324 | ||
325 | if (enable) | |
326 | type = GB_SVC_TYPE_INTF_REFCLK_ENABLE; | |
327 | else | |
328 | type = GB_SVC_TYPE_INTF_REFCLK_DISABLE; | |
329 | ||
330 | ret = gb_operation_sync(svc->connection, type, | |
331 | &request, sizeof(request), | |
332 | &response, sizeof(response)); | |
333 | if (ret < 0) | |
334 | return ret; | |
335 | if (response.result_code != GB_SVC_INTF_REFCLK_OK) | |
336 | return -EREMOTEIO; | |
017482b2 JH |
337 | return 0; |
338 | } | |
339 | ||
340 | int gb_svc_intf_unipro_set(struct gb_svc *svc, u8 intf_id, bool enable) | |
341 | { | |
144763bf JC |
342 | struct gb_svc_intf_unipro_request request; |
343 | struct gb_svc_intf_unipro_response response; | |
344 | int type, ret; | |
345 | ||
346 | request.intf_id = intf_id; | |
347 | ||
348 | if (enable) | |
349 | type = GB_SVC_TYPE_INTF_UNIPRO_ENABLE; | |
350 | else | |
351 | type = GB_SVC_TYPE_INTF_UNIPRO_DISABLE; | |
017482b2 | 352 | |
144763bf JC |
353 | ret = gb_operation_sync(svc->connection, type, |
354 | &request, sizeof(request), | |
355 | &response, sizeof(response)); | |
356 | if (ret < 0) | |
357 | return ret; | |
358 | if (response.result_code != GB_SVC_INTF_UNIPRO_OK) | |
359 | return -EREMOTEIO; | |
017482b2 JH |
360 | return 0; |
361 | } | |
362 | ||
1e8e22b5 JH |
363 | int gb_svc_intf_activate(struct gb_svc *svc, u8 intf_id, u8 *intf_type) |
364 | { | |
140026b3 JC |
365 | struct gb_svc_intf_activate_request request; |
366 | struct gb_svc_intf_activate_response response; | |
367 | int ret; | |
368 | ||
369 | request.intf_id = intf_id; | |
370 | ||
371 | ret = gb_operation_sync_timeout(svc->connection, | |
372 | GB_SVC_TYPE_INTF_ACTIVATE, | |
373 | &request, sizeof(request), | |
374 | &response, sizeof(response), | |
375 | SVC_INTF_ACTIVATE_TIMEOUT); | |
376 | if (ret < 0) | |
377 | return ret; | |
03fba180 JC |
378 | if (response.status != GB_SVC_OP_SUCCESS) { |
379 | dev_err(&svc->dev, "failed to activate interface %u: %u\n", | |
380 | intf_id, response.status); | |
381 | return -EREMOTEIO; | |
382 | } | |
1e8e22b5 | 383 | |
140026b3 | 384 | *intf_type = response.intf_type; |
1e8e22b5 JH |
385 | |
386 | return 0; | |
387 | } | |
388 | ||
fc8a4027 DL |
389 | int gb_svc_intf_resume(struct gb_svc *svc, u8 intf_id) |
390 | { | |
391 | struct gb_svc_intf_resume_request request; | |
392 | struct gb_svc_intf_resume_response response; | |
393 | int ret; | |
394 | ||
395 | request.intf_id = intf_id; | |
396 | ||
397 | ret = gb_operation_sync_timeout(svc->connection, | |
398 | GB_SVC_TYPE_INTF_RESUME, | |
399 | &request, sizeof(request), | |
400 | &response, sizeof(response), | |
401 | SVC_INTF_RESUME_TIMEOUT); | |
402 | if (ret < 0) { | |
403 | dev_err(&svc->dev, "failed to send interface resume %u: %d\n", | |
404 | intf_id, ret); | |
405 | return ret; | |
406 | } | |
407 | ||
408 | if (response.status != GB_SVC_OP_SUCCESS) { | |
409 | dev_err(&svc->dev, "failed to resume interface %u: %u\n", | |
410 | intf_id, response.status); | |
411 | return -EREMOTEIO; | |
412 | } | |
413 | ||
414 | return 0; | |
415 | } | |
416 | ||
19151c3d VK |
417 | int gb_svc_dme_peer_get(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, |
418 | u32 *value) | |
419 | { | |
420 | struct gb_svc_dme_peer_get_request request; | |
421 | struct gb_svc_dme_peer_get_response response; | |
422 | u16 result; | |
423 | int ret; | |
424 | ||
425 | request.intf_id = intf_id; | |
426 | request.attr = cpu_to_le16(attr); | |
427 | request.selector = cpu_to_le16(selector); | |
428 | ||
429 | ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_DME_PEER_GET, | |
430 | &request, sizeof(request), | |
431 | &response, sizeof(response)); | |
432 | if (ret) { | |
b933fa4a | 433 | dev_err(&svc->dev, "failed to get DME attribute (%u 0x%04x %u): %d\n", |
684156a9 | 434 | intf_id, attr, selector, ret); |
19151c3d VK |
435 | return ret; |
436 | } | |
437 | ||
438 | result = le16_to_cpu(response.result_code); | |
439 | if (result) { | |
b933fa4a | 440 | dev_err(&svc->dev, "UniPro error while getting DME attribute (%u 0x%04x %u): %u\n", |
684156a9 | 441 | intf_id, attr, selector, result); |
4aea5a15 | 442 | return -EREMOTEIO; |
19151c3d VK |
443 | } |
444 | ||
445 | if (value) | |
446 | *value = le32_to_cpu(response.attr_value); | |
447 | ||
448 | return 0; | |
449 | } | |
19151c3d VK |
450 | |
451 | int gb_svc_dme_peer_set(struct gb_svc *svc, u8 intf_id, u16 attr, u16 selector, | |
452 | u32 value) | |
453 | { | |
454 | struct gb_svc_dme_peer_set_request request; | |
455 | struct gb_svc_dme_peer_set_response response; | |
456 | u16 result; | |
457 | int ret; | |
458 | ||
459 | request.intf_id = intf_id; | |
460 | request.attr = cpu_to_le16(attr); | |
461 | request.selector = cpu_to_le16(selector); | |
462 | request.value = cpu_to_le32(value); | |
463 | ||
464 | ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_DME_PEER_SET, | |
465 | &request, sizeof(request), | |
466 | &response, sizeof(response)); | |
467 | if (ret) { | |
b933fa4a | 468 | dev_err(&svc->dev, "failed to set DME attribute (%u 0x%04x %u %u): %d\n", |
684156a9 | 469 | intf_id, attr, selector, value, ret); |
19151c3d VK |
470 | return ret; |
471 | } | |
472 | ||
473 | result = le16_to_cpu(response.result_code); | |
474 | if (result) { | |
b933fa4a | 475 | dev_err(&svc->dev, "UniPro error while setting DME attribute (%u 0x%04x %u %u): %u\n", |
684156a9 | 476 | intf_id, attr, selector, value, result); |
4aea5a15 | 477 | return -EREMOTEIO; |
19151c3d VK |
478 | } |
479 | ||
480 | return 0; | |
481 | } | |
19151c3d | 482 | |
3f0e9183 | 483 | int gb_svc_connection_create(struct gb_svc *svc, |
30c6d9d7 | 484 | u8 intf1_id, u16 cport1_id, |
1575ef18 | 485 | u8 intf2_id, u16 cport2_id, |
27f25c17 | 486 | u8 cport_flags) |
30c6d9d7 AE |
487 | { |
488 | struct gb_svc_conn_create_request request; | |
489 | ||
490 | request.intf1_id = intf1_id; | |
2498050b | 491 | request.cport1_id = cpu_to_le16(cport1_id); |
30c6d9d7 | 492 | request.intf2_id = intf2_id; |
2498050b | 493 | request.cport2_id = cpu_to_le16(cport2_id); |
34145b60 | 494 | request.tc = 0; /* TC0 */ |
27f25c17 | 495 | request.flags = cport_flags; |
30c6d9d7 AE |
496 | |
497 | return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE, | |
498 | &request, sizeof(request), NULL, 0); | |
499 | } | |
500 | ||
3f0e9183 VK |
501 | void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id, |
502 | u8 intf2_id, u16 cport2_id) | |
30c6d9d7 AE |
503 | { |
504 | struct gb_svc_conn_destroy_request request; | |
d9fcffff VK |
505 | struct gb_connection *connection = svc->connection; |
506 | int ret; | |
30c6d9d7 AE |
507 | |
508 | request.intf1_id = intf1_id; | |
2498050b | 509 | request.cport1_id = cpu_to_le16(cport1_id); |
30c6d9d7 | 510 | request.intf2_id = intf2_id; |
2498050b | 511 | request.cport2_id = cpu_to_le16(cport2_id); |
30c6d9d7 | 512 | |
d9fcffff VK |
513 | ret = gb_operation_sync(connection, GB_SVC_TYPE_CONN_DESTROY, |
514 | &request, sizeof(request), NULL, 0); | |
684156a9 | 515 | if (ret) { |
2f3db927 | 516 | dev_err(&svc->dev, "failed to destroy connection (%u:%u %u:%u): %d\n", |
684156a9 JH |
517 | intf1_id, cport1_id, intf2_id, cport2_id, ret); |
518 | } | |
30c6d9d7 AE |
519 | } |
520 | ||
5705020f BD |
521 | int gb_svc_timesync_enable(struct gb_svc *svc, u8 count, u64 frame_time, |
522 | u32 strobe_delay, u32 refclk) | |
523 | { | |
524 | struct gb_connection *connection = svc->connection; | |
525 | struct gb_svc_timesync_enable_request request; | |
526 | ||
527 | request.count = count; | |
528 | request.frame_time = cpu_to_le64(frame_time); | |
529 | request.strobe_delay = cpu_to_le32(strobe_delay); | |
530 | request.refclk = cpu_to_le32(refclk); | |
531 | return gb_operation_sync(connection, | |
532 | GB_SVC_TYPE_TIMESYNC_ENABLE, | |
533 | &request, sizeof(request), NULL, 0); | |
534 | } | |
5705020f BD |
535 | |
536 | int gb_svc_timesync_disable(struct gb_svc *svc) | |
537 | { | |
538 | struct gb_connection *connection = svc->connection; | |
539 | ||
540 | return gb_operation_sync(connection, | |
541 | GB_SVC_TYPE_TIMESYNC_DISABLE, | |
542 | NULL, 0, NULL, 0); | |
543 | } | |
5705020f BD |
544 | |
545 | int gb_svc_timesync_authoritative(struct gb_svc *svc, u64 *frame_time) | |
546 | { | |
547 | struct gb_connection *connection = svc->connection; | |
548 | struct gb_svc_timesync_authoritative_response response; | |
549 | int ret, i; | |
550 | ||
551 | ret = gb_operation_sync(connection, | |
552 | GB_SVC_TYPE_TIMESYNC_AUTHORITATIVE, NULL, 0, | |
553 | &response, sizeof(response)); | |
554 | if (ret < 0) | |
555 | return ret; | |
556 | ||
557 | for (i = 0; i < GB_TIMESYNC_MAX_STROBES; i++) | |
558 | frame_time[i] = le64_to_cpu(response.frame_time[i]); | |
559 | return 0; | |
560 | } | |
5705020f BD |
561 | |
562 | int gb_svc_timesync_ping(struct gb_svc *svc, u64 *frame_time) | |
563 | { | |
564 | struct gb_connection *connection = svc->connection; | |
565 | struct gb_svc_timesync_ping_response response; | |
566 | int ret; | |
567 | ||
568 | ret = gb_operation_sync(connection, | |
569 | GB_SVC_TYPE_TIMESYNC_PING, | |
570 | NULL, 0, | |
571 | &response, sizeof(response)); | |
572 | if (ret < 0) | |
573 | return ret; | |
574 | ||
575 | *frame_time = le64_to_cpu(response.frame_time); | |
576 | return 0; | |
577 | } | |
5705020f BD |
578 | |
579 | int gb_svc_timesync_wake_pins_acquire(struct gb_svc *svc, u32 strobe_mask) | |
580 | { | |
581 | struct gb_connection *connection = svc->connection; | |
582 | struct gb_svc_timesync_wake_pins_acquire_request request; | |
583 | ||
584 | request.strobe_mask = cpu_to_le32(strobe_mask); | |
585 | return gb_operation_sync(connection, | |
586 | GB_SVC_TYPE_TIMESYNC_WAKE_PINS_ACQUIRE, | |
587 | &request, sizeof(request), | |
588 | NULL, 0); | |
589 | } | |
5705020f BD |
590 | |
591 | int gb_svc_timesync_wake_pins_release(struct gb_svc *svc) | |
592 | { | |
593 | struct gb_connection *connection = svc->connection; | |
594 | ||
595 | return gb_operation_sync(connection, | |
596 | GB_SVC_TYPE_TIMESYNC_WAKE_PINS_RELEASE, | |
597 | NULL, 0, NULL, 0); | |
598 | } | |
5705020f | 599 | |
bb106852 | 600 | /* Creates bi-directional routes between the devices */ |
4d5f6218 | 601 | int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id, |
505f16cc | 602 | u8 intf2_id, u8 dev2_id) |
e08aaa49 PH |
603 | { |
604 | struct gb_svc_route_create_request request; | |
605 | ||
606 | request.intf1_id = intf1_id; | |
607 | request.dev1_id = dev1_id; | |
608 | request.intf2_id = intf2_id; | |
609 | request.dev2_id = dev2_id; | |
610 | ||
611 | return gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_CREATE, | |
612 | &request, sizeof(request), NULL, 0); | |
613 | } | |
e08aaa49 | 614 | |
0a020570 | 615 | /* Destroys bi-directional routes between the devices */ |
4d5f6218 | 616 | void gb_svc_route_destroy(struct gb_svc *svc, u8 intf1_id, u8 intf2_id) |
0a020570 VK |
617 | { |
618 | struct gb_svc_route_destroy_request request; | |
619 | int ret; | |
620 | ||
621 | request.intf1_id = intf1_id; | |
622 | request.intf2_id = intf2_id; | |
623 | ||
624 | ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_DESTROY, | |
625 | &request, sizeof(request), NULL, 0); | |
684156a9 | 626 | if (ret) { |
2f3db927 | 627 | dev_err(&svc->dev, "failed to destroy route (%u %u): %d\n", |
684156a9 JH |
628 | intf1_id, intf2_id, ret); |
629 | } | |
0a020570 VK |
630 | } |
631 | ||
aab4a1a3 LP |
632 | int gb_svc_intf_set_power_mode(struct gb_svc *svc, u8 intf_id, u8 hs_series, |
633 | u8 tx_mode, u8 tx_gear, u8 tx_nlanes, | |
8c2522d8 | 634 | u8 tx_amplitude, u8 tx_hs_equalizer, |
aab4a1a3 | 635 | u8 rx_mode, u8 rx_gear, u8 rx_nlanes, |
8c2522d8 ES |
636 | u8 flags, u32 quirks, |
637 | struct gb_svc_l2_timer_cfg *local, | |
638 | struct gb_svc_l2_timer_cfg *remote) | |
784f8761 | 639 | { |
aab4a1a3 LP |
640 | struct gb_svc_intf_set_pwrm_request request; |
641 | struct gb_svc_intf_set_pwrm_response response; | |
642 | int ret; | |
8c2522d8 ES |
643 | u16 result_code; |
644 | ||
645 | memset(&request, 0, sizeof(request)); | |
784f8761 LP |
646 | |
647 | request.intf_id = intf_id; | |
aab4a1a3 LP |
648 | request.hs_series = hs_series; |
649 | request.tx_mode = tx_mode; | |
650 | request.tx_gear = tx_gear; | |
651 | request.tx_nlanes = tx_nlanes; | |
8c2522d8 ES |
652 | request.tx_amplitude = tx_amplitude; |
653 | request.tx_hs_equalizer = tx_hs_equalizer; | |
aab4a1a3 LP |
654 | request.rx_mode = rx_mode; |
655 | request.rx_gear = rx_gear; | |
656 | request.rx_nlanes = rx_nlanes; | |
784f8761 | 657 | request.flags = flags; |
aab4a1a3 | 658 | request.quirks = cpu_to_le32(quirks); |
8c2522d8 ES |
659 | if (local) |
660 | request.local_l2timerdata = *local; | |
661 | if (remote) | |
662 | request.remote_l2timerdata = *remote; | |
784f8761 | 663 | |
aab4a1a3 LP |
664 | ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_SET_PWRM, |
665 | &request, sizeof(request), | |
666 | &response, sizeof(response)); | |
667 | if (ret < 0) | |
668 | return ret; | |
669 | ||
8c2522d8 ES |
670 | result_code = response.result_code; |
671 | if (result_code != GB_SVC_SETPWRM_PWR_LOCAL) { | |
672 | dev_err(&svc->dev, "set power mode = %d\n", result_code); | |
673 | return -EIO; | |
674 | } | |
675 | ||
676 | return 0; | |
784f8761 | 677 | } |
46bb647b | 678 | EXPORT_SYMBOL_GPL(gb_svc_intf_set_power_mode); |
784f8761 | 679 | |
c7dc28ff DL |
680 | int gb_svc_intf_set_power_mode_hibernate(struct gb_svc *svc, u8 intf_id) |
681 | { | |
682 | struct gb_svc_intf_set_pwrm_request request; | |
683 | struct gb_svc_intf_set_pwrm_response response; | |
684 | int ret; | |
685 | u16 result_code; | |
686 | ||
687 | memset(&request, 0, sizeof(request)); | |
688 | ||
689 | request.intf_id = intf_id; | |
690 | request.hs_series = GB_SVC_UNIPRO_HS_SERIES_A; | |
691 | request.tx_mode = GB_SVC_UNIPRO_HIBERNATE_MODE; | |
692 | request.rx_mode = GB_SVC_UNIPRO_HIBERNATE_MODE; | |
693 | ||
694 | ret = gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_SET_PWRM, | |
695 | &request, sizeof(request), | |
696 | &response, sizeof(response)); | |
697 | if (ret < 0) { | |
698 | dev_err(&svc->dev, | |
699 | "failed to send set power mode operation to interface %u: %d\n", | |
700 | intf_id, ret); | |
701 | return ret; | |
702 | } | |
703 | ||
704 | result_code = response.result_code; | |
705 | if (result_code != GB_SVC_SETPWRM_PWR_OK) { | |
706 | dev_err(&svc->dev, | |
707 | "failed to hibernate the link for interface %u: %u\n", | |
708 | intf_id, result_code); | |
709 | return -EIO; | |
710 | } | |
711 | ||
712 | return 0; | |
713 | } | |
714 | ||
55ec09e8 GKH |
715 | int gb_svc_ping(struct gb_svc *svc) |
716 | { | |
839ac5b9 GKH |
717 | return gb_operation_sync_timeout(svc->connection, GB_SVC_TYPE_PING, |
718 | NULL, 0, NULL, 0, | |
719 | GB_OPERATION_TIMEOUT_DEFAULT * 2); | |
55ec09e8 | 720 | } |
55ec09e8 | 721 | |
ead35460 VK |
722 | static int gb_svc_version_request(struct gb_operation *op) |
723 | { | |
724 | struct gb_connection *connection = op->connection; | |
0ec30632 | 725 | struct gb_svc *svc = gb_connection_get_data(connection); |
a2cf2e59 JH |
726 | struct gb_svc_version_request *request; |
727 | struct gb_svc_version_response *response; | |
ead35460 | 728 | |
55510843 | 729 | if (op->request->payload_size < sizeof(*request)) { |
684156a9 | 730 | dev_err(&svc->dev, "short version request (%zu < %zu)\n", |
55510843 JH |
731 | op->request->payload_size, |
732 | sizeof(*request)); | |
733 | return -EINVAL; | |
734 | } | |
735 | ||
cfb16906 | 736 | request = op->request->payload; |
ead35460 | 737 | |
cfb16906 | 738 | if (request->major > GB_SVC_VERSION_MAJOR) { |
2f3db927 | 739 | dev_warn(&svc->dev, "unsupported major version (%u > %u)\n", |
684156a9 | 740 | request->major, GB_SVC_VERSION_MAJOR); |
ead35460 VK |
741 | return -ENOTSUPP; |
742 | } | |
743 | ||
357de006 JH |
744 | svc->protocol_major = request->major; |
745 | svc->protocol_minor = request->minor; | |
3ea959e3 | 746 | |
684156a9 | 747 | if (!gb_operation_response_alloc(op, sizeof(*response), GFP_KERNEL)) |
ead35460 | 748 | return -ENOMEM; |
ead35460 | 749 | |
cfb16906 | 750 | response = op->response->payload; |
357de006 JH |
751 | response->major = svc->protocol_major; |
752 | response->minor = svc->protocol_minor; | |
59832931 | 753 | |
ead35460 VK |
754 | return 0; |
755 | } | |
756 | ||
9504677c DL |
757 | static ssize_t pwr_debugfs_voltage_read(struct file *file, char __user *buf, |
758 | size_t len, loff_t *offset) | |
759 | { | |
45063097 | 760 | struct svc_debugfs_pwrmon_rail *pwrmon_rails = file_inode(file)->i_private; |
9504677c DL |
761 | struct gb_svc *svc = pwrmon_rails->svc; |
762 | int ret, desc; | |
763 | u32 value; | |
764 | char buff[16]; | |
765 | ||
766 | ret = gb_svc_pwrmon_sample_get(svc, pwrmon_rails->id, | |
767 | GB_SVC_PWRMON_TYPE_VOL, &value); | |
768 | if (ret) { | |
769 | dev_err(&svc->dev, | |
89f2df43 JH |
770 | "failed to get voltage sample %u: %d\n", |
771 | pwrmon_rails->id, ret); | |
9504677c DL |
772 | return ret; |
773 | } | |
774 | ||
775 | desc = scnprintf(buff, sizeof(buff), "%u\n", value); | |
776 | ||
777 | return simple_read_from_buffer(buf, len, offset, buff, desc); | |
778 | } | |
779 | ||
780 | static ssize_t pwr_debugfs_current_read(struct file *file, char __user *buf, | |
781 | size_t len, loff_t *offset) | |
782 | { | |
45063097 | 783 | struct svc_debugfs_pwrmon_rail *pwrmon_rails = file_inode(file)->i_private; |
9504677c DL |
784 | struct gb_svc *svc = pwrmon_rails->svc; |
785 | int ret, desc; | |
786 | u32 value; | |
787 | char buff[16]; | |
788 | ||
789 | ret = gb_svc_pwrmon_sample_get(svc, pwrmon_rails->id, | |
790 | GB_SVC_PWRMON_TYPE_CURR, &value); | |
791 | if (ret) { | |
792 | dev_err(&svc->dev, | |
89f2df43 JH |
793 | "failed to get current sample %u: %d\n", |
794 | pwrmon_rails->id, ret); | |
9504677c DL |
795 | return ret; |
796 | } | |
797 | ||
798 | desc = scnprintf(buff, sizeof(buff), "%u\n", value); | |
799 | ||
800 | return simple_read_from_buffer(buf, len, offset, buff, desc); | |
801 | } | |
802 | ||
803 | static ssize_t pwr_debugfs_power_read(struct file *file, char __user *buf, | |
804 | size_t len, loff_t *offset) | |
805 | { | |
45063097 | 806 | struct svc_debugfs_pwrmon_rail *pwrmon_rails = file_inode(file)->i_private; |
9504677c DL |
807 | struct gb_svc *svc = pwrmon_rails->svc; |
808 | int ret, desc; | |
809 | u32 value; | |
810 | char buff[16]; | |
811 | ||
812 | ret = gb_svc_pwrmon_sample_get(svc, pwrmon_rails->id, | |
813 | GB_SVC_PWRMON_TYPE_PWR, &value); | |
814 | if (ret) { | |
89f2df43 JH |
815 | dev_err(&svc->dev, "failed to get power sample %u: %d\n", |
816 | pwrmon_rails->id, ret); | |
9504677c DL |
817 | return ret; |
818 | } | |
819 | ||
820 | desc = scnprintf(buff, sizeof(buff), "%u\n", value); | |
821 | ||
822 | return simple_read_from_buffer(buf, len, offset, buff, desc); | |
823 | } | |
824 | ||
825 | static const struct file_operations pwrmon_debugfs_voltage_fops = { | |
826 | .read = pwr_debugfs_voltage_read, | |
827 | }; | |
828 | ||
829 | static const struct file_operations pwrmon_debugfs_current_fops = { | |
830 | .read = pwr_debugfs_current_read, | |
831 | }; | |
832 | ||
833 | static const struct file_operations pwrmon_debugfs_power_fops = { | |
834 | .read = pwr_debugfs_power_read, | |
835 | }; | |
836 | ||
12185197 | 837 | static void gb_svc_pwrmon_debugfs_init(struct gb_svc *svc) |
9504677c DL |
838 | { |
839 | int i; | |
840 | size_t bufsize; | |
841 | struct dentry *dent; | |
3fd747a6 DL |
842 | struct gb_svc_pwrmon_rail_names_get_response *rail_names; |
843 | u8 rail_count; | |
9504677c DL |
844 | |
845 | dent = debugfs_create_dir("pwrmon", svc->debugfs_dentry); | |
846 | if (IS_ERR_OR_NULL(dent)) | |
847 | return; | |
848 | ||
3fd747a6 | 849 | if (gb_svc_pwrmon_rail_count_get(svc, &rail_count)) |
9504677c DL |
850 | goto err_pwrmon_debugfs; |
851 | ||
3fd747a6 | 852 | if (!rail_count || rail_count > GB_SVC_PWRMON_MAX_RAIL_COUNT) |
9504677c DL |
853 | goto err_pwrmon_debugfs; |
854 | ||
8fb76c3c DL |
855 | bufsize = sizeof(*rail_names) + |
856 | GB_SVC_PWRMON_RAIL_NAME_BUFSIZE * rail_count; | |
9504677c | 857 | |
3fd747a6 DL |
858 | rail_names = kzalloc(bufsize, GFP_KERNEL); |
859 | if (!rail_names) | |
9504677c DL |
860 | goto err_pwrmon_debugfs; |
861 | ||
3fd747a6 | 862 | svc->pwrmon_rails = kcalloc(rail_count, sizeof(*svc->pwrmon_rails), |
9504677c DL |
863 | GFP_KERNEL); |
864 | if (!svc->pwrmon_rails) | |
865 | goto err_pwrmon_debugfs_free; | |
866 | ||
3fd747a6 | 867 | if (gb_svc_pwrmon_rail_names_get(svc, rail_names, bufsize)) |
9504677c DL |
868 | goto err_pwrmon_debugfs_free; |
869 | ||
3fd747a6 | 870 | for (i = 0; i < rail_count; i++) { |
9504677c DL |
871 | struct dentry *dir; |
872 | struct svc_debugfs_pwrmon_rail *rail = &svc->pwrmon_rails[i]; | |
873 | char fname[GB_SVC_PWRMON_RAIL_NAME_BUFSIZE]; | |
874 | ||
875 | snprintf(fname, sizeof(fname), "%s", | |
3fd747a6 | 876 | (char *)&rail_names->name[i]); |
9504677c DL |
877 | |
878 | rail->id = i; | |
879 | rail->svc = svc; | |
880 | ||
881 | dir = debugfs_create_dir(fname, dent); | |
882 | debugfs_create_file("voltage_now", S_IRUGO, dir, rail, | |
883 | &pwrmon_debugfs_voltage_fops); | |
884 | debugfs_create_file("current_now", S_IRUGO, dir, rail, | |
885 | &pwrmon_debugfs_current_fops); | |
886 | debugfs_create_file("power_now", S_IRUGO, dir, rail, | |
887 | &pwrmon_debugfs_power_fops); | |
898d75f4 | 888 | } |
3fd747a6 DL |
889 | |
890 | kfree(rail_names); | |
9504677c DL |
891 | return; |
892 | ||
893 | err_pwrmon_debugfs_free: | |
3fd747a6 | 894 | kfree(rail_names); |
9504677c DL |
895 | kfree(svc->pwrmon_rails); |
896 | svc->pwrmon_rails = NULL; | |
897 | ||
898 | err_pwrmon_debugfs: | |
899 | debugfs_remove(dent); | |
900 | } | |
901 | ||
12185197 | 902 | static void gb_svc_debugfs_init(struct gb_svc *svc) |
9504677c DL |
903 | { |
904 | svc->debugfs_dentry = debugfs_create_dir(dev_name(&svc->dev), | |
905 | gb_debugfs_get()); | |
12185197 | 906 | gb_svc_pwrmon_debugfs_init(svc); |
9504677c DL |
907 | } |
908 | ||
12185197 | 909 | static void gb_svc_debugfs_exit(struct gb_svc *svc) |
9504677c DL |
910 | { |
911 | debugfs_remove_recursive(svc->debugfs_dentry); | |
9983ea6b DL |
912 | kfree(svc->pwrmon_rails); |
913 | svc->pwrmon_rails = NULL; | |
9504677c DL |
914 | } |
915 | ||
ead35460 VK |
916 | static int gb_svc_hello(struct gb_operation *op) |
917 | { | |
918 | struct gb_connection *connection = op->connection; | |
0ec30632 | 919 | struct gb_svc *svc = gb_connection_get_data(connection); |
ead35460 | 920 | struct gb_svc_hello_request *hello_request; |
ead35460 VK |
921 | int ret; |
922 | ||
0c32d2a5 | 923 | if (op->request->payload_size < sizeof(*hello_request)) { |
684156a9 JH |
924 | dev_warn(&svc->dev, "short hello request (%zu < %zu)\n", |
925 | op->request->payload_size, | |
926 | sizeof(*hello_request)); | |
ead35460 VK |
927 | return -EINVAL; |
928 | } | |
929 | ||
930 | hello_request = op->request->payload; | |
66069fb0 JH |
931 | svc->endo_id = le16_to_cpu(hello_request->endo_id); |
932 | svc->ap_intf_id = hello_request->interface_id; | |
ead35460 | 933 | |
88f7b96d JH |
934 | ret = device_add(&svc->dev); |
935 | if (ret) { | |
936 | dev_err(&svc->dev, "failed to register svc device: %d\n", ret); | |
937 | return ret; | |
938 | } | |
939 | ||
ed7279ae GKH |
940 | ret = gb_svc_watchdog_create(svc); |
941 | if (ret) { | |
942 | dev_err(&svc->dev, "failed to create watchdog: %d\n", ret); | |
4a448427 | 943 | goto err_unregister_device; |
ed7279ae GKH |
944 | } |
945 | ||
12185197 | 946 | gb_svc_debugfs_init(svc); |
9504677c | 947 | |
4a448427 BD |
948 | ret = gb_timesync_svc_add(svc); |
949 | if (ret) { | |
950 | dev_err(&svc->dev, "failed to add SVC to timesync: %d\n", ret); | |
951 | gb_svc_debugfs_exit(svc); | |
952 | goto err_unregister_device; | |
953 | } | |
954 | ||
ee2f2074 | 955 | return gb_svc_queue_deferred_request(op); |
4a448427 BD |
956 | |
957 | err_unregister_device: | |
958 | gb_svc_watchdog_destroy(svc); | |
4a448427 BD |
959 | device_del(&svc->dev); |
960 | return ret; | |
ead35460 VK |
961 | } |
962 | ||
b482b0d6 JH |
963 | static struct gb_interface *gb_svc_interface_lookup(struct gb_svc *svc, |
964 | u8 intf_id) | |
965 | { | |
966 | struct gb_host_device *hd = svc->hd; | |
967 | struct gb_module *module; | |
968 | size_t num_interfaces; | |
969 | u8 module_id; | |
970 | ||
971 | list_for_each_entry(module, &hd->modules, hd_node) { | |
972 | module_id = module->module_id; | |
973 | num_interfaces = module->num_interfaces; | |
974 | ||
975 | if (intf_id >= module_id && | |
976 | intf_id < module_id + num_interfaces) { | |
977 | return module->interfaces[intf_id - module_id]; | |
978 | } | |
979 | } | |
980 | ||
981 | return NULL; | |
982 | } | |
983 | ||
b15d97d7 JH |
984 | static struct gb_module *gb_svc_module_lookup(struct gb_svc *svc, u8 module_id) |
985 | { | |
986 | struct gb_host_device *hd = svc->hd; | |
987 | struct gb_module *module; | |
988 | ||
989 | list_for_each_entry(module, &hd->modules, hd_node) { | |
990 | if (module->module_id == module_id) | |
991 | return module; | |
992 | } | |
993 | ||
994 | return NULL; | |
995 | } | |
996 | ||
ee2f2074 MT |
997 | static void gb_svc_process_hello_deferred(struct gb_operation *operation) |
998 | { | |
999 | struct gb_connection *connection = operation->connection; | |
1000 | struct gb_svc *svc = gb_connection_get_data(connection); | |
1001 | int ret; | |
1002 | ||
1003 | /* | |
1004 | * XXX This is a hack/work-around to reconfigure the APBridgeA-Switch | |
1005 | * link to PWM G2, 1 Lane, Slow Auto, so that it has sufficient | |
1006 | * bandwidth for 3 audio streams plus boot-over-UniPro of a hot-plugged | |
1007 | * module. | |
1008 | * | |
1009 | * The code should be removed once SW-2217, Heuristic for UniPro | |
1010 | * Power Mode Changes is resolved. | |
1011 | */ | |
1012 | ret = gb_svc_intf_set_power_mode(svc, svc->ap_intf_id, | |
1013 | GB_SVC_UNIPRO_HS_SERIES_A, | |
1014 | GB_SVC_UNIPRO_SLOW_AUTO_MODE, | |
1015 | 2, 1, | |
8c2522d8 | 1016 | GB_SVC_SMALL_AMPLITUDE, GB_SVC_NO_DE_EMPHASIS, |
ee2f2074 MT |
1017 | GB_SVC_UNIPRO_SLOW_AUTO_MODE, |
1018 | 2, 1, | |
8c2522d8 ES |
1019 | 0, 0, |
1020 | NULL, NULL); | |
ee2f2074 MT |
1021 | |
1022 | if (ret) | |
1023 | dev_warn(&svc->dev, | |
1024 | "power mode change failed on AP to switch link: %d\n", | |
1025 | ret); | |
1026 | } | |
1027 | ||
22bb9380 JH |
1028 | static void gb_svc_process_module_inserted(struct gb_operation *operation) |
1029 | { | |
1030 | struct gb_svc_module_inserted_request *request; | |
1031 | struct gb_connection *connection = operation->connection; | |
1032 | struct gb_svc *svc = gb_connection_get_data(connection); | |
1033 | struct gb_host_device *hd = svc->hd; | |
1034 | struct gb_module *module; | |
1035 | size_t num_interfaces; | |
1036 | u8 module_id; | |
1037 | u16 flags; | |
1038 | int ret; | |
1039 | ||
1040 | /* The request message size has already been verified. */ | |
1041 | request = operation->request->payload; | |
1042 | module_id = request->primary_intf_id; | |
1043 | num_interfaces = request->intf_count; | |
1044 | flags = le16_to_cpu(request->flags); | |
1045 | ||
1046 | dev_dbg(&svc->dev, "%s - id = %u, num_interfaces = %zu, flags = 0x%04x\n", | |
1047 | __func__, module_id, num_interfaces, flags); | |
1048 | ||
1049 | if (flags & GB_SVC_MODULE_INSERTED_FLAG_NO_PRIMARY) { | |
1050 | dev_warn(&svc->dev, "no primary interface detected on module %u\n", | |
1051 | module_id); | |
1052 | } | |
1053 | ||
1054 | module = gb_svc_module_lookup(svc, module_id); | |
1055 | if (module) { | |
1056 | dev_warn(&svc->dev, "unexpected module-inserted event %u\n", | |
1057 | module_id); | |
1058 | return; | |
1059 | } | |
1060 | ||
1061 | module = gb_module_create(hd, module_id, num_interfaces); | |
1062 | if (!module) { | |
1063 | dev_err(&svc->dev, "failed to create module\n"); | |
1064 | return; | |
1065 | } | |
1066 | ||
1067 | ret = gb_module_add(module); | |
1068 | if (ret) { | |
1069 | gb_module_put(module); | |
1070 | return; | |
1071 | } | |
1072 | ||
1073 | list_add(&module->hd_node, &hd->modules); | |
1074 | } | |
1075 | ||
1076 | static void gb_svc_process_module_removed(struct gb_operation *operation) | |
1077 | { | |
1078 | struct gb_svc_module_removed_request *request; | |
1079 | struct gb_connection *connection = operation->connection; | |
1080 | struct gb_svc *svc = gb_connection_get_data(connection); | |
1081 | struct gb_module *module; | |
1082 | u8 module_id; | |
1083 | ||
1084 | /* The request message size has already been verified. */ | |
1085 | request = operation->request->payload; | |
1086 | module_id = request->primary_intf_id; | |
1087 | ||
1088 | dev_dbg(&svc->dev, "%s - id = %u\n", __func__, module_id); | |
1089 | ||
1090 | module = gb_svc_module_lookup(svc, module_id); | |
1091 | if (!module) { | |
1092 | dev_warn(&svc->dev, "unexpected module-removed event %u\n", | |
1093 | module_id); | |
1094 | return; | |
1095 | } | |
1096 | ||
1097 | module->disconnected = true; | |
1098 | ||
1099 | gb_module_del(module); | |
1100 | list_del(&module->hd_node); | |
1101 | gb_module_put(module); | |
1102 | } | |
1103 | ||
57fa2de1 GD |
1104 | static void gb_svc_process_intf_oops(struct gb_operation *operation) |
1105 | { | |
1106 | struct gb_svc_intf_oops_request *request; | |
1107 | struct gb_connection *connection = operation->connection; | |
1108 | struct gb_svc *svc = gb_connection_get_data(connection); | |
1109 | struct gb_interface *intf; | |
1110 | u8 intf_id; | |
1111 | u8 reason; | |
1112 | ||
1113 | /* The request message size has already been verified. */ | |
1114 | request = operation->request->payload; | |
1115 | intf_id = request->intf_id; | |
1116 | reason = request->reason; | |
1117 | ||
1118 | intf = gb_svc_interface_lookup(svc, intf_id); | |
1119 | if (!intf) { | |
1120 | dev_warn(&svc->dev, "unexpected interface-oops event %u\n", | |
1121 | intf_id); | |
1122 | return; | |
1123 | } | |
1124 | ||
1125 | dev_info(&svc->dev, "Deactivating interface %u, interface oops reason = %u\n", | |
1126 | intf_id, reason); | |
1127 | ||
1128 | mutex_lock(&intf->mutex); | |
1129 | intf->disconnected = true; | |
1130 | gb_interface_disable(intf); | |
1131 | gb_interface_deactivate(intf); | |
1132 | mutex_unlock(&intf->mutex); | |
1133 | } | |
1134 | ||
b482b0d6 JH |
1135 | static void gb_svc_process_intf_mailbox_event(struct gb_operation *operation) |
1136 | { | |
1137 | struct gb_svc_intf_mailbox_event_request *request; | |
1138 | struct gb_connection *connection = operation->connection; | |
1139 | struct gb_svc *svc = gb_connection_get_data(connection); | |
1140 | struct gb_interface *intf; | |
1141 | u8 intf_id; | |
1142 | u16 result_code; | |
1143 | u32 mailbox; | |
1144 | ||
1145 | /* The request message size has already been verified. */ | |
1146 | request = operation->request->payload; | |
1147 | intf_id = request->intf_id; | |
1148 | result_code = le16_to_cpu(request->result_code); | |
1149 | mailbox = le32_to_cpu(request->mailbox); | |
1150 | ||
1151 | dev_dbg(&svc->dev, "%s - id = %u, result = 0x%04x, mailbox = 0x%08x\n", | |
1152 | __func__, intf_id, result_code, mailbox); | |
1153 | ||
1154 | intf = gb_svc_interface_lookup(svc, intf_id); | |
1155 | if (!intf) { | |
1156 | dev_warn(&svc->dev, "unexpected mailbox event %u\n", intf_id); | |
1157 | return; | |
1158 | } | |
1159 | ||
55742d2a | 1160 | gb_interface_mailbox_event(intf, result_code, mailbox); |
b482b0d6 JH |
1161 | } |
1162 | ||
9ae4109e JH |
1163 | static void gb_svc_process_deferred_request(struct work_struct *work) |
1164 | { | |
1165 | struct gb_svc_deferred_request *dr; | |
1166 | struct gb_operation *operation; | |
1167 | struct gb_svc *svc; | |
1168 | u8 type; | |
1169 | ||
1170 | dr = container_of(work, struct gb_svc_deferred_request, work); | |
1171 | operation = dr->operation; | |
0ec30632 | 1172 | svc = gb_connection_get_data(operation->connection); |
9ae4109e JH |
1173 | type = operation->request->header->type; |
1174 | ||
1175 | switch (type) { | |
ee2f2074 MT |
1176 | case GB_SVC_TYPE_SVC_HELLO: |
1177 | gb_svc_process_hello_deferred(operation); | |
1178 | break; | |
22bb9380 JH |
1179 | case GB_SVC_TYPE_MODULE_INSERTED: |
1180 | gb_svc_process_module_inserted(operation); | |
1181 | break; | |
1182 | case GB_SVC_TYPE_MODULE_REMOVED: | |
1183 | gb_svc_process_module_removed(operation); | |
1184 | break; | |
b482b0d6 JH |
1185 | case GB_SVC_TYPE_INTF_MAILBOX_EVENT: |
1186 | gb_svc_process_intf_mailbox_event(operation); | |
1187 | break; | |
57fa2de1 GD |
1188 | case GB_SVC_TYPE_INTF_OOPS: |
1189 | gb_svc_process_intf_oops(operation); | |
1190 | break; | |
9ae4109e | 1191 | default: |
b933fa4a | 1192 | dev_err(&svc->dev, "bad deferred request type: 0x%02x\n", type); |
9ae4109e JH |
1193 | } |
1194 | ||
1195 | gb_operation_put(operation); | |
1196 | kfree(dr); | |
1197 | } | |
1198 | ||
1199 | static int gb_svc_queue_deferred_request(struct gb_operation *operation) | |
1200 | { | |
0ec30632 | 1201 | struct gb_svc *svc = gb_connection_get_data(operation->connection); |
9ae4109e JH |
1202 | struct gb_svc_deferred_request *dr; |
1203 | ||
1204 | dr = kmalloc(sizeof(*dr), GFP_KERNEL); | |
1205 | if (!dr) | |
1206 | return -ENOMEM; | |
1207 | ||
1208 | gb_operation_get(operation); | |
1209 | ||
1210 | dr->operation = operation; | |
1211 | INIT_WORK(&dr->work, gb_svc_process_deferred_request); | |
1212 | ||
3e48acac | 1213 | queue_work(svc->wq, &dr->work); |
9ae4109e JH |
1214 | |
1215 | return 0; | |
067906f6 | 1216 | } |
ead35460 | 1217 | |
30c6d9d7 AE |
1218 | static int gb_svc_intf_reset_recv(struct gb_operation *op) |
1219 | { | |
0ec30632 | 1220 | struct gb_svc *svc = gb_connection_get_data(op->connection); |
30c6d9d7 AE |
1221 | struct gb_message *request = op->request; |
1222 | struct gb_svc_intf_reset_request *reset; | |
1223 | u8 intf_id; | |
1224 | ||
1225 | if (request->payload_size < sizeof(*reset)) { | |
684156a9 JH |
1226 | dev_warn(&svc->dev, "short reset request received (%zu < %zu)\n", |
1227 | request->payload_size, sizeof(*reset)); | |
30c6d9d7 AE |
1228 | return -EINVAL; |
1229 | } | |
1230 | reset = request->payload; | |
1231 | ||
1232 | intf_id = reset->intf_id; | |
1233 | ||
1234 | /* FIXME Reset the interface here */ | |
1235 | ||
1236 | return 0; | |
1237 | } | |
1238 | ||
22bb9380 JH |
1239 | static int gb_svc_module_inserted_recv(struct gb_operation *op) |
1240 | { | |
1241 | struct gb_svc *svc = gb_connection_get_data(op->connection); | |
1242 | struct gb_svc_module_inserted_request *request; | |
1243 | ||
1244 | if (op->request->payload_size < sizeof(*request)) { | |
1245 | dev_warn(&svc->dev, "short module-inserted request received (%zu < %zu)\n", | |
1246 | op->request->payload_size, sizeof(*request)); | |
1247 | return -EINVAL; | |
1248 | } | |
1249 | ||
1250 | request = op->request->payload; | |
1251 | ||
1252 | dev_dbg(&svc->dev, "%s - id = %u\n", __func__, | |
1253 | request->primary_intf_id); | |
1254 | ||
1255 | return gb_svc_queue_deferred_request(op); | |
1256 | } | |
1257 | ||
1258 | static int gb_svc_module_removed_recv(struct gb_operation *op) | |
1259 | { | |
1260 | struct gb_svc *svc = gb_connection_get_data(op->connection); | |
1261 | struct gb_svc_module_removed_request *request; | |
1262 | ||
1263 | if (op->request->payload_size < sizeof(*request)) { | |
1264 | dev_warn(&svc->dev, "short module-removed request received (%zu < %zu)\n", | |
1265 | op->request->payload_size, sizeof(*request)); | |
1266 | return -EINVAL; | |
1267 | } | |
1268 | ||
1269 | request = op->request->payload; | |
1270 | ||
1271 | dev_dbg(&svc->dev, "%s - id = %u\n", __func__, | |
1272 | request->primary_intf_id); | |
1273 | ||
1274 | return gb_svc_queue_deferred_request(op); | |
1275 | } | |
1276 | ||
57fa2de1 GD |
1277 | static int gb_svc_intf_oops_recv(struct gb_operation *op) |
1278 | { | |
1279 | struct gb_svc *svc = gb_connection_get_data(op->connection); | |
1280 | struct gb_svc_intf_oops_request *request; | |
1281 | ||
1282 | if (op->request->payload_size < sizeof(*request)) { | |
1283 | dev_warn(&svc->dev, "short intf-oops request received (%zu < %zu)\n", | |
1284 | op->request->payload_size, sizeof(*request)); | |
1285 | return -EINVAL; | |
1286 | } | |
1287 | ||
1288 | return gb_svc_queue_deferred_request(op); | |
1289 | } | |
1290 | ||
b482b0d6 JH |
1291 | static int gb_svc_intf_mailbox_event_recv(struct gb_operation *op) |
1292 | { | |
1293 | struct gb_svc *svc = gb_connection_get_data(op->connection); | |
1294 | struct gb_svc_intf_mailbox_event_request *request; | |
1295 | ||
1296 | if (op->request->payload_size < sizeof(*request)) { | |
1297 | dev_warn(&svc->dev, "short mailbox request received (%zu < %zu)\n", | |
1298 | op->request->payload_size, sizeof(*request)); | |
1299 | return -EINVAL; | |
1300 | } | |
1301 | ||
1302 | request = op->request->payload; | |
1303 | ||
1304 | dev_dbg(&svc->dev, "%s - id = %u\n", __func__, request->intf_id); | |
1305 | ||
1306 | return gb_svc_queue_deferred_request(op); | |
1307 | } | |
1308 | ||
84427943 | 1309 | static int gb_svc_request_handler(struct gb_operation *op) |
30c6d9d7 | 1310 | { |
3ccb1600 | 1311 | struct gb_connection *connection = op->connection; |
0ec30632 | 1312 | struct gb_svc *svc = gb_connection_get_data(connection); |
84427943 | 1313 | u8 type = op->type; |
3ccb1600 VK |
1314 | int ret = 0; |
1315 | ||
1316 | /* | |
1317 | * SVC requests need to follow a specific order (at least initially) and | |
1318 | * below code takes care of enforcing that. The expected order is: | |
1319 | * - PROTOCOL_VERSION | |
1320 | * - SVC_HELLO | |
1321 | * - Any other request, but the earlier two. | |
1322 | * | |
1323 | * Incoming requests are guaranteed to be serialized and so we don't | |
1324 | * need to protect 'state' for any races. | |
1325 | */ | |
30c6d9d7 | 1326 | switch (type) { |
a2cf2e59 | 1327 | case GB_SVC_TYPE_PROTOCOL_VERSION: |
3ccb1600 VK |
1328 | if (svc->state != GB_SVC_STATE_RESET) |
1329 | ret = -EINVAL; | |
1330 | break; | |
ead35460 | 1331 | case GB_SVC_TYPE_SVC_HELLO: |
3ccb1600 VK |
1332 | if (svc->state != GB_SVC_STATE_PROTOCOL_VERSION) |
1333 | ret = -EINVAL; | |
1334 | break; | |
1335 | default: | |
1336 | if (svc->state != GB_SVC_STATE_SVC_HELLO) | |
1337 | ret = -EINVAL; | |
1338 | break; | |
1339 | } | |
1340 | ||
1341 | if (ret) { | |
684156a9 JH |
1342 | dev_warn(&svc->dev, "unexpected request 0x%02x received (state %u)\n", |
1343 | type, svc->state); | |
3ccb1600 VK |
1344 | return ret; |
1345 | } | |
1346 | ||
1347 | switch (type) { | |
a2cf2e59 | 1348 | case GB_SVC_TYPE_PROTOCOL_VERSION: |
3ccb1600 VK |
1349 | ret = gb_svc_version_request(op); |
1350 | if (!ret) | |
1351 | svc->state = GB_SVC_STATE_PROTOCOL_VERSION; | |
1352 | return ret; | |
1353 | case GB_SVC_TYPE_SVC_HELLO: | |
1354 | ret = gb_svc_hello(op); | |
1355 | if (!ret) | |
1356 | svc->state = GB_SVC_STATE_SVC_HELLO; | |
1357 | return ret; | |
30c6d9d7 AE |
1358 | case GB_SVC_TYPE_INTF_RESET: |
1359 | return gb_svc_intf_reset_recv(op); | |
22bb9380 JH |
1360 | case GB_SVC_TYPE_MODULE_INSERTED: |
1361 | return gb_svc_module_inserted_recv(op); | |
1362 | case GB_SVC_TYPE_MODULE_REMOVED: | |
1363 | return gb_svc_module_removed_recv(op); | |
b482b0d6 JH |
1364 | case GB_SVC_TYPE_INTF_MAILBOX_EVENT: |
1365 | return gb_svc_intf_mailbox_event_recv(op); | |
57fa2de1 GD |
1366 | case GB_SVC_TYPE_INTF_OOPS: |
1367 | return gb_svc_intf_oops_recv(op); | |
30c6d9d7 | 1368 | default: |
684156a9 | 1369 | dev_warn(&svc->dev, "unsupported request 0x%02x\n", type); |
30c6d9d7 AE |
1370 | return -EINVAL; |
1371 | } | |
1372 | } | |
1373 | ||
efe6ef76 JH |
1374 | static void gb_svc_release(struct device *dev) |
1375 | { | |
88f7b96d | 1376 | struct gb_svc *svc = to_gb_svc(dev); |
efe6ef76 | 1377 | |
7adeaae7 JH |
1378 | if (svc->connection) |
1379 | gb_connection_destroy(svc->connection); | |
efe6ef76 | 1380 | ida_destroy(&svc->device_id_map); |
3e48acac | 1381 | destroy_workqueue(svc->wq); |
efe6ef76 JH |
1382 | kfree(svc); |
1383 | } | |
1384 | ||
1385 | struct device_type greybus_svc_type = { | |
1386 | .name = "greybus_svc", | |
1387 | .release = gb_svc_release, | |
1388 | }; | |
1389 | ||
7adeaae7 | 1390 | struct gb_svc *gb_svc_create(struct gb_host_device *hd) |
30c6d9d7 AE |
1391 | { |
1392 | struct gb_svc *svc; | |
30c6d9d7 AE |
1393 | |
1394 | svc = kzalloc(sizeof(*svc), GFP_KERNEL); | |
1395 | if (!svc) | |
7adeaae7 | 1396 | return NULL; |
30c6d9d7 | 1397 | |
3e48acac JH |
1398 | svc->wq = alloc_workqueue("%s:svc", WQ_UNBOUND, 1, dev_name(&hd->dev)); |
1399 | if (!svc->wq) { | |
1400 | kfree(svc); | |
7adeaae7 | 1401 | return NULL; |
3e48acac JH |
1402 | } |
1403 | ||
efe6ef76 JH |
1404 | svc->dev.parent = &hd->dev; |
1405 | svc->dev.bus = &greybus_bus_type; | |
1406 | svc->dev.type = &greybus_svc_type; | |
66069fb0 | 1407 | svc->dev.groups = svc_groups; |
efe6ef76 JH |
1408 | svc->dev.dma_mask = svc->dev.parent->dma_mask; |
1409 | device_initialize(&svc->dev); | |
1410 | ||
1411 | dev_set_name(&svc->dev, "%d-svc", hd->bus_id); | |
1412 | ||
6106e51b | 1413 | ida_init(&svc->device_id_map); |
3ccb1600 | 1414 | svc->state = GB_SVC_STATE_RESET; |
f0960d05 | 1415 | svc->hd = hd; |
d3d44840 | 1416 | |
f7ee081e JH |
1417 | svc->connection = gb_connection_create_static(hd, GB_SVC_CPORT_ID, |
1418 | gb_svc_request_handler); | |
24e094d6 JH |
1419 | if (IS_ERR(svc->connection)) { |
1420 | dev_err(&svc->dev, "failed to create connection: %ld\n", | |
1421 | PTR_ERR(svc->connection)); | |
1beb1fae | 1422 | goto err_put_device; |
7adeaae7 JH |
1423 | } |
1424 | ||
0ec30632 | 1425 | gb_connection_set_data(svc->connection, svc); |
efe6ef76 | 1426 | |
7adeaae7 | 1427 | return svc; |
ebe99d61 | 1428 | |
ebe99d61 RMS |
1429 | err_put_device: |
1430 | put_device(&svc->dev); | |
1431 | return NULL; | |
30c6d9d7 AE |
1432 | } |
1433 | ||
7adeaae7 | 1434 | int gb_svc_add(struct gb_svc *svc) |
30c6d9d7 | 1435 | { |
7adeaae7 | 1436 | int ret; |
30c6d9d7 | 1437 | |
7adeaae7 JH |
1438 | /* |
1439 | * The SVC protocol is currently driven by the SVC, so the SVC device | |
1440 | * is added from the connection request handler when enough | |
1441 | * information has been received. | |
1442 | */ | |
f7ee081e | 1443 | ret = gb_connection_enable(svc->connection); |
7adeaae7 JH |
1444 | if (ret) |
1445 | return ret; | |
1446 | ||
1447 | return 0; | |
1448 | } | |
1449 | ||
b15d97d7 | 1450 | static void gb_svc_remove_modules(struct gb_svc *svc) |
66d674cf | 1451 | { |
b15d97d7 JH |
1452 | struct gb_host_device *hd = svc->hd; |
1453 | struct gb_module *module, *tmp; | |
66d674cf | 1454 | |
b15d97d7 JH |
1455 | list_for_each_entry_safe(module, tmp, &hd->modules, hd_node) { |
1456 | gb_module_del(module); | |
1457 | list_del(&module->hd_node); | |
1458 | gb_module_put(module); | |
629c0d00 | 1459 | } |
66d674cf JH |
1460 | } |
1461 | ||
7adeaae7 JH |
1462 | void gb_svc_del(struct gb_svc *svc) |
1463 | { | |
d1a8c36e | 1464 | gb_connection_disable_rx(svc->connection); |
ebe99d61 | 1465 | |
7adeaae7 | 1466 | /* |
1beb1fae | 1467 | * The SVC device may have been registered from the request handler. |
7adeaae7 | 1468 | */ |
ebe99d61 | 1469 | if (device_is_registered(&svc->dev)) { |
4a448427 | 1470 | gb_timesync_svc_remove(svc); |
12185197 | 1471 | gb_svc_debugfs_exit(svc); |
ed7279ae | 1472 | gb_svc_watchdog_destroy(svc); |
88f7b96d | 1473 | device_del(&svc->dev); |
ebe99d61 | 1474 | } |
1cacb456 | 1475 | |
7adeaae7 | 1476 | flush_workqueue(svc->wq); |
66d674cf | 1477 | |
b15d97d7 | 1478 | gb_svc_remove_modules(svc); |
d1a8c36e VK |
1479 | |
1480 | gb_connection_disable(svc->connection); | |
7adeaae7 | 1481 | } |
efe6ef76 | 1482 | |
7adeaae7 JH |
1483 | void gb_svc_put(struct gb_svc *svc) |
1484 | { | |
efe6ef76 | 1485 | put_device(&svc->dev); |
30c6d9d7 | 1486 | } |