Commit | Line | Data |
---|---|---|
90f1b617 | 1 | /* |
5a53e02e | 2 | * BOOTROM Greybus driver. |
90f1b617 | 3 | * |
5a53e02e VK |
4 | * Copyright 2016 Google Inc. |
5 | * Copyright 2016 Linaro Ltd. | |
90f1b617 VK |
6 | * |
7 | * Released under the GPLv2 only. | |
8 | */ | |
9 | ||
10 | #include <linux/firmware.h> | |
a956d939 VK |
11 | #include <linux/jiffies.h> |
12 | #include <linux/mutex.h> | |
13 | #include <linux/workqueue.h> | |
90f1b617 VK |
14 | |
15 | #include "greybus.h" | |
8a704565 | 16 | #include "firmware.h" |
90f1b617 | 17 | |
a956d939 | 18 | /* Timeout, in jiffies, within which the next request must be received */ |
dbb8cfeb | 19 | #define NEXT_REQ_TIMEOUT_MS 1000 |
8ec589b9 | 20 | |
326f98ac VK |
21 | /* |
22 | * FIXME: Reduce this timeout once svc core handles parallel processing of | |
23 | * events from the SVC, which are handled sequentially today. | |
24 | */ | |
25 | #define MODE_SWITCH_TIMEOUT_MS 10000 | |
26 | ||
a4293e1d VK |
27 | enum next_request_type { |
28 | NEXT_REQ_FIRMWARE_SIZE, | |
29 | NEXT_REQ_GET_FIRMWARE, | |
30 | NEXT_REQ_READY_TO_BOOT, | |
31 | NEXT_REQ_MODE_SWITCH, | |
32 | }; | |
33 | ||
5a53e02e | 34 | struct gb_bootrom { |
90f1b617 VK |
35 | struct gb_connection *connection; |
36 | const struct firmware *fw; | |
8ec589b9 JH |
37 | u8 protocol_major; |
38 | u8 protocol_minor; | |
a4293e1d | 39 | enum next_request_type next_request; |
a956d939 VK |
40 | struct delayed_work dwork; |
41 | struct mutex mutex; /* Protects bootrom->fw */ | |
90f1b617 VK |
42 | }; |
43 | ||
5a53e02e | 44 | static void free_firmware(struct gb_bootrom *bootrom) |
90f1b617 | 45 | { |
a956d939 VK |
46 | if (!bootrom->fw) |
47 | return; | |
48 | ||
5a53e02e VK |
49 | release_firmware(bootrom->fw); |
50 | bootrom->fw = NULL; | |
90f1b617 VK |
51 | } |
52 | ||
a956d939 VK |
53 | static void gb_bootrom_timedout(struct work_struct *work) |
54 | { | |
55 | struct delayed_work *dwork = to_delayed_work(work); | |
461ab807 GK |
56 | struct gb_bootrom *bootrom = container_of(dwork, |
57 | struct gb_bootrom, dwork); | |
a956d939 | 58 | struct device *dev = &bootrom->connection->bundle->dev; |
a4293e1d VK |
59 | const char *reason; |
60 | ||
61 | switch (bootrom->next_request) { | |
62 | case NEXT_REQ_FIRMWARE_SIZE: | |
63 | reason = "Firmware Size Request"; | |
64 | break; | |
65 | case NEXT_REQ_GET_FIRMWARE: | |
66 | reason = "Get Firmware Request"; | |
67 | break; | |
68 | case NEXT_REQ_READY_TO_BOOT: | |
69 | reason = "Ready to Boot Request"; | |
70 | break; | |
71 | case NEXT_REQ_MODE_SWITCH: | |
72 | reason = "Interface Mode Switch"; | |
73 | break; | |
74 | default: | |
75 | reason = NULL; | |
76 | dev_err(dev, "Invalid next-request: %u", bootrom->next_request); | |
77 | break; | |
78 | } | |
a956d939 | 79 | |
a4293e1d | 80 | dev_err(dev, "Timed out waiting for %s from the Module\n", reason); |
a956d939 VK |
81 | |
82 | mutex_lock(&bootrom->mutex); | |
83 | free_firmware(bootrom); | |
84 | mutex_unlock(&bootrom->mutex); | |
85 | ||
86 | /* TODO: Power-off Module ? */ | |
87 | } | |
88 | ||
a4293e1d VK |
89 | static void gb_bootrom_set_timeout(struct gb_bootrom *bootrom, |
90 | enum next_request_type next, unsigned long timeout) | |
91 | { | |
92 | bootrom->next_request = next; | |
dbb8cfeb | 93 | schedule_delayed_work(&bootrom->dwork, msecs_to_jiffies(timeout)); |
a4293e1d VK |
94 | } |
95 | ||
40d276ed VK |
96 | static void gb_bootrom_cancel_timeout(struct gb_bootrom *bootrom) |
97 | { | |
98 | cancel_delayed_work_sync(&bootrom->dwork); | |
99 | } | |
100 | ||
f1e941a6 VK |
101 | /* |
102 | * The es2 chip doesn't have VID/PID programmed into the hardware and we need to | |
103 | * hack that up to distinguish different modules and their firmware blobs. | |
104 | * | |
5a53e02e | 105 | * This fetches VID/PID (over bootrom protocol) for es2 chip only, when VID/PID |
f1e941a6 VK |
106 | * already sent during hotplug are 0. |
107 | * | |
f3e6c097 | 108 | * Otherwise, we keep intf->vendor_id/product_id same as what's passed |
f1e941a6 VK |
109 | * during hotplug. |
110 | */ | |
5a53e02e | 111 | static void bootrom_es2_fixup_vid_pid(struct gb_bootrom *bootrom) |
f1e941a6 | 112 | { |
5a53e02e VK |
113 | struct gb_bootrom_get_vid_pid_response response; |
114 | struct gb_connection *connection = bootrom->connection; | |
f1e941a6 VK |
115 | struct gb_interface *intf = connection->bundle->intf; |
116 | int ret; | |
117 | ||
23931ffb | 118 | if (!(intf->quirks & GB_INTERFACE_QUIRK_NO_GMP_IDS)) |
f1e941a6 VK |
119 | return; |
120 | ||
5a53e02e | 121 | ret = gb_operation_sync(connection, GB_BOOTROM_TYPE_GET_VID_PID, |
f1e941a6 VK |
122 | NULL, 0, &response, sizeof(response)); |
123 | if (ret) { | |
124 | dev_err(&connection->bundle->dev, | |
5a53e02e | 125 | "Bootrom get vid/pid operation failed (%d)\n", ret); |
f1e941a6 VK |
126 | return; |
127 | } | |
128 | ||
f3e6c097 VK |
129 | /* |
130 | * NOTE: This is hacked, so that the same values of VID/PID can be used | |
131 | * by next firmware level as well. The uevent for bootrom will still | |
132 | * have VID/PID as 0, though after this point the sysfs files will start | |
133 | * showing the updated values. But yeah, that's a bit racy as the same | |
134 | * sysfs files would be showing 0 before this point. | |
135 | */ | |
136 | intf->vendor_id = le32_to_cpu(response.vendor_id); | |
137 | intf->product_id = le32_to_cpu(response.product_id); | |
fc41c2da | 138 | |
5a53e02e | 139 | dev_dbg(&connection->bundle->dev, "Bootrom got vid (0x%x)/pid (0x%x)\n", |
f3e6c097 | 140 | intf->vendor_id, intf->product_id); |
f1e941a6 VK |
141 | } |
142 | ||
90f1b617 | 143 | /* This returns path of the firmware blob on the disk */ |
68793c4c | 144 | static int find_firmware(struct gb_bootrom *bootrom, u8 stage) |
90f1b617 | 145 | { |
5a53e02e | 146 | struct gb_connection *connection = bootrom->connection; |
90f1b617 | 147 | struct gb_interface *intf = connection->bundle->intf; |
56c78715 | 148 | char firmware_name[49]; |
fc41c2da | 149 | int rc; |
90f1b617 VK |
150 | |
151 | /* Already have a firmware, free it */ | |
a956d939 | 152 | free_firmware(bootrom); |
90f1b617 | 153 | |
56c78715 VK |
154 | /* Bootrom protocol is only supported for loading Stage 2 firmware */ |
155 | if (stage != 2) { | |
156 | dev_err(&connection->bundle->dev, "Invalid boot stage: %u\n", | |
157 | stage); | |
158 | return -EINVAL; | |
159 | } | |
160 | ||
90f1b617 VK |
161 | /* |
162 | * Create firmware name | |
163 | * | |
164 | * XXX Name it properly.. | |
165 | */ | |
af0b4d5a | 166 | snprintf(firmware_name, sizeof(firmware_name), |
8a704565 | 167 | FW_NAME_PREFIX "%08x_%08x_%08x_%08x_s2l.tftf", |
b32a5c53 | 168 | intf->ddbl1_manufacturer_id, intf->ddbl1_product_id, |
56c78715 | 169 | intf->vendor_id, intf->product_id); |
90f1b617 | 170 | |
eb8fafdf GKH |
171 | // FIXME: |
172 | // Turn to dev_dbg later after everyone has valid bootloaders with good | |
173 | // ids, but leave this as dev_info for now to make it easier to track | |
174 | // down "empty" vid/pid modules. | |
175 | dev_info(&connection->bundle->dev, "Firmware file '%s' requested\n", | |
176 | firmware_name); | |
177 | ||
5a53e02e | 178 | rc = request_firmware(&bootrom->fw, firmware_name, |
fc41c2da | 179 | &connection->bundle->dev); |
2d6f1c29 | 180 | if (rc) { |
68793c4c VK |
181 | dev_err(&connection->bundle->dev, |
182 | "failed to find %s firmware (%d)\n", firmware_name, rc); | |
2d6f1c29 VK |
183 | } |
184 | ||
fc41c2da | 185 | return rc; |
90f1b617 VK |
186 | } |
187 | ||
5a53e02e | 188 | static int gb_bootrom_firmware_size_request(struct gb_operation *op) |
90f1b617 | 189 | { |
5a53e02e | 190 | struct gb_bootrom *bootrom = gb_connection_get_data(op->connection); |
461ab807 GK |
191 | struct gb_bootrom_firmware_size_request *size_request = |
192 | op->request->payload; | |
5a53e02e | 193 | struct gb_bootrom_firmware_size_response *size_response; |
0ec30632 | 194 | struct device *dev = &op->connection->bundle->dev; |
90f1b617 VK |
195 | int ret; |
196 | ||
a956d939 | 197 | /* Disable timeouts */ |
40d276ed | 198 | gb_bootrom_cancel_timeout(bootrom); |
a956d939 | 199 | |
90f1b617 VK |
200 | if (op->request->payload_size != sizeof(*size_request)) { |
201 | dev_err(dev, "%s: illegal size of firmware size request (%zu != %zu)\n", | |
202 | __func__, op->request->payload_size, | |
203 | sizeof(*size_request)); | |
a956d939 VK |
204 | ret = -EINVAL; |
205 | goto queue_work; | |
90f1b617 VK |
206 | } |
207 | ||
a956d939 VK |
208 | mutex_lock(&bootrom->mutex); |
209 | ||
68793c4c | 210 | ret = find_firmware(bootrom, size_request->stage); |
2d6f1c29 | 211 | if (ret) |
a956d939 | 212 | goto unlock; |
90f1b617 VK |
213 | |
214 | if (!gb_operation_response_alloc(op, sizeof(*size_response), | |
215 | GFP_KERNEL)) { | |
216 | dev_err(dev, "%s: error allocating response\n", __func__); | |
5a53e02e | 217 | free_firmware(bootrom); |
a956d939 VK |
218 | ret = -ENOMEM; |
219 | goto unlock; | |
90f1b617 VK |
220 | } |
221 | ||
222 | size_response = op->response->payload; | |
5a53e02e | 223 | size_response->size = cpu_to_le32(bootrom->fw->size); |
90f1b617 | 224 | |
461ab807 GK |
225 | dev_dbg(dev, "%s: firmware size %d bytes\n", |
226 | __func__, size_response->size); | |
fc41c2da | 227 | |
a956d939 VK |
228 | unlock: |
229 | mutex_unlock(&bootrom->mutex); | |
230 | ||
231 | queue_work: | |
cc90d6fc VK |
232 | if (!ret) { |
233 | /* Refresh timeout */ | |
234 | gb_bootrom_set_timeout(bootrom, NEXT_REQ_GET_FIRMWARE, | |
235 | NEXT_REQ_TIMEOUT_MS); | |
236 | } | |
a956d939 VK |
237 | |
238 | return ret; | |
90f1b617 VK |
239 | } |
240 | ||
5a53e02e | 241 | static int gb_bootrom_get_firmware(struct gb_operation *op) |
90f1b617 | 242 | { |
5a53e02e | 243 | struct gb_bootrom *bootrom = gb_connection_get_data(op->connection); |
a956d939 | 244 | const struct firmware *fw; |
5a53e02e VK |
245 | struct gb_bootrom_get_firmware_request *firmware_request; |
246 | struct gb_bootrom_get_firmware_response *firmware_response; | |
0ec30632 | 247 | struct device *dev = &op->connection->bundle->dev; |
90f1b617 | 248 | unsigned int offset, size; |
a4293e1d | 249 | enum next_request_type next_request; |
a956d939 VK |
250 | int ret = 0; |
251 | ||
252 | /* Disable timeouts */ | |
40d276ed | 253 | gb_bootrom_cancel_timeout(bootrom); |
90f1b617 VK |
254 | |
255 | if (op->request->payload_size != sizeof(*firmware_request)) { | |
256 | dev_err(dev, "%s: Illegal size of get firmware request (%zu %zu)\n", | |
257 | __func__, op->request->payload_size, | |
258 | sizeof(*firmware_request)); | |
a956d939 VK |
259 | ret = -EINVAL; |
260 | goto queue_work; | |
90f1b617 VK |
261 | } |
262 | ||
a956d939 VK |
263 | mutex_lock(&bootrom->mutex); |
264 | ||
265 | fw = bootrom->fw; | |
98645a9c | 266 | if (!fw) { |
90f1b617 | 267 | dev_err(dev, "%s: firmware not available\n", __func__); |
a956d939 VK |
268 | ret = -EINVAL; |
269 | goto unlock; | |
90f1b617 VK |
270 | } |
271 | ||
87f6c976 | 272 | firmware_request = op->request->payload; |
90f1b617 VK |
273 | offset = le32_to_cpu(firmware_request->offset); |
274 | size = le32_to_cpu(firmware_request->size); | |
275 | ||
98645a9c JH |
276 | if (offset >= fw->size || size > fw->size - offset) { |
277 | dev_warn(dev, "bad firmware request (offs = %u, size = %u)\n", | |
278 | offset, size); | |
a956d939 VK |
279 | ret = -EINVAL; |
280 | goto unlock; | |
98645a9c JH |
281 | } |
282 | ||
90f1b617 VK |
283 | if (!gb_operation_response_alloc(op, sizeof(*firmware_response) + size, |
284 | GFP_KERNEL)) { | |
285 | dev_err(dev, "%s: error allocating response\n", __func__); | |
a956d939 VK |
286 | ret = -ENOMEM; |
287 | goto unlock; | |
90f1b617 VK |
288 | } |
289 | ||
290 | firmware_response = op->response->payload; | |
98645a9c | 291 | memcpy(firmware_response->data, fw->data + offset, size); |
90f1b617 | 292 | |
461ab807 GK |
293 | dev_dbg(dev, "responding with firmware (offs = %u, size = %u)\n", |
294 | offset, size); | |
fc41c2da | 295 | |
a956d939 VK |
296 | unlock: |
297 | mutex_unlock(&bootrom->mutex); | |
298 | ||
299 | queue_work: | |
300 | /* Refresh timeout */ | |
a4293e1d VK |
301 | if (!ret && (offset + size == fw->size)) |
302 | next_request = NEXT_REQ_READY_TO_BOOT; | |
303 | else | |
304 | next_request = NEXT_REQ_GET_FIRMWARE; | |
305 | ||
dbb8cfeb | 306 | gb_bootrom_set_timeout(bootrom, next_request, NEXT_REQ_TIMEOUT_MS); |
a956d939 VK |
307 | |
308 | return ret; | |
90f1b617 VK |
309 | } |
310 | ||
5a53e02e | 311 | static int gb_bootrom_ready_to_boot(struct gb_operation *op) |
90f1b617 VK |
312 | { |
313 | struct gb_connection *connection = op->connection; | |
a956d939 | 314 | struct gb_bootrom *bootrom = gb_connection_get_data(connection); |
5a53e02e | 315 | struct gb_bootrom_ready_to_boot_request *rtb_request; |
0a72bd36 | 316 | struct device *dev = &connection->bundle->dev; |
06986a2c | 317 | u8 status; |
a956d939 VK |
318 | int ret = 0; |
319 | ||
320 | /* Disable timeouts */ | |
40d276ed | 321 | gb_bootrom_cancel_timeout(bootrom); |
90f1b617 VK |
322 | |
323 | if (op->request->payload_size != sizeof(*rtb_request)) { | |
324 | dev_err(dev, "%s: Illegal size of ready to boot request (%zu %zu)\n", | |
325 | __func__, op->request->payload_size, | |
326 | sizeof(*rtb_request)); | |
a956d939 VK |
327 | ret = -EINVAL; |
328 | goto queue_work; | |
90f1b617 VK |
329 | } |
330 | ||
87f6c976 | 331 | rtb_request = op->request->payload; |
90f1b617 VK |
332 | status = rtb_request->status; |
333 | ||
334 | /* Return error if the blob was invalid */ | |
a956d939 VK |
335 | if (status == GB_BOOTROM_BOOT_STATUS_INVALID) { |
336 | ret = -EINVAL; | |
337 | goto queue_work; | |
338 | } | |
90f1b617 VK |
339 | |
340 | /* | |
341 | * XXX Should we return error for insecure firmware? | |
342 | */ | |
fc41c2da | 343 | dev_dbg(dev, "ready to boot: 0x%x, 0\n", status); |
90f1b617 | 344 | |
a956d939 VK |
345 | queue_work: |
346 | /* | |
347 | * Refresh timeout, the Interface shall load the new personality and | |
348 | * send a new hotplug request, which shall get rid of the bootrom | |
349 | * connection. As that can take some time, increase the timeout a bit. | |
350 | */ | |
a4293e1d | 351 | gb_bootrom_set_timeout(bootrom, NEXT_REQ_MODE_SWITCH, |
326f98ac | 352 | MODE_SWITCH_TIMEOUT_MS); |
a956d939 VK |
353 | |
354 | return ret; | |
90f1b617 VK |
355 | } |
356 | ||
5a53e02e | 357 | static int gb_bootrom_request_handler(struct gb_operation *op) |
90f1b617 | 358 | { |
8ec589b9 JH |
359 | u8 type = op->type; |
360 | ||
90f1b617 | 361 | switch (type) { |
5a53e02e VK |
362 | case GB_BOOTROM_TYPE_FIRMWARE_SIZE: |
363 | return gb_bootrom_firmware_size_request(op); | |
364 | case GB_BOOTROM_TYPE_GET_FIRMWARE: | |
365 | return gb_bootrom_get_firmware(op); | |
366 | case GB_BOOTROM_TYPE_READY_TO_BOOT: | |
367 | return gb_bootrom_ready_to_boot(op); | |
90f1b617 | 368 | default: |
0a72bd36 | 369 | dev_err(&op->connection->bundle->dev, |
2f3db927 | 370 | "unsupported request: %u\n", type); |
90f1b617 VK |
371 | return -EINVAL; |
372 | } | |
373 | } | |
374 | ||
5a53e02e | 375 | static int gb_bootrom_get_version(struct gb_bootrom *bootrom) |
90f1b617 | 376 | { |
5a53e02e VK |
377 | struct gb_bundle *bundle = bootrom->connection->bundle; |
378 | struct gb_bootrom_version_request request; | |
379 | struct gb_bootrom_version_response response; | |
8ec589b9 JH |
380 | int ret; |
381 | ||
5a53e02e VK |
382 | request.major = GB_BOOTROM_VERSION_MAJOR; |
383 | request.minor = GB_BOOTROM_VERSION_MINOR; | |
8ec589b9 | 384 | |
5a53e02e VK |
385 | ret = gb_operation_sync(bootrom->connection, |
386 | GB_BOOTROM_TYPE_VERSION, | |
8ec589b9 JH |
387 | &request, sizeof(request), &response, |
388 | sizeof(response)); | |
389 | if (ret) { | |
390 | dev_err(&bundle->dev, | |
391 | "failed to get protocol version: %d\n", | |
392 | ret); | |
393 | return ret; | |
394 | } | |
395 | ||
396 | if (response.major > request.major) { | |
397 | dev_err(&bundle->dev, | |
398 | "unsupported major protocol version (%u > %u)\n", | |
399 | response.major, request.major); | |
400 | return -ENOTSUPP; | |
401 | } | |
402 | ||
5a53e02e VK |
403 | bootrom->protocol_major = response.major; |
404 | bootrom->protocol_minor = response.minor; | |
8ec589b9 JH |
405 | |
406 | dev_dbg(&bundle->dev, "%s - %u.%u\n", __func__, response.major, | |
407 | response.minor); | |
408 | ||
409 | return 0; | |
410 | } | |
411 | ||
5a53e02e | 412 | static int gb_bootrom_probe(struct gb_bundle *bundle, |
8ec589b9 JH |
413 | const struct greybus_bundle_id *id) |
414 | { | |
415 | struct greybus_descriptor_cport *cport_desc; | |
416 | struct gb_connection *connection; | |
5a53e02e | 417 | struct gb_bootrom *bootrom; |
4c9e2284 | 418 | int ret; |
90f1b617 | 419 | |
8ec589b9 JH |
420 | if (bundle->num_cports != 1) |
421 | return -ENODEV; | |
422 | ||
423 | cport_desc = &bundle->cport_desc[0]; | |
5a53e02e | 424 | if (cport_desc->protocol_id != GREYBUS_PROTOCOL_BOOTROM) |
8ec589b9 JH |
425 | return -ENODEV; |
426 | ||
5a53e02e VK |
427 | bootrom = kzalloc(sizeof(*bootrom), GFP_KERNEL); |
428 | if (!bootrom) | |
90f1b617 VK |
429 | return -ENOMEM; |
430 | ||
8ec589b9 JH |
431 | connection = gb_connection_create(bundle, |
432 | le16_to_cpu(cport_desc->id), | |
5a53e02e | 433 | gb_bootrom_request_handler); |
8ec589b9 JH |
434 | if (IS_ERR(connection)) { |
435 | ret = PTR_ERR(connection); | |
5a53e02e | 436 | goto err_free_bootrom; |
8ec589b9 JH |
437 | } |
438 | ||
5a53e02e | 439 | gb_connection_set_data(connection, bootrom); |
90f1b617 | 440 | |
5a53e02e | 441 | bootrom->connection = connection; |
8ec589b9 | 442 | |
a956d939 VK |
443 | mutex_init(&bootrom->mutex); |
444 | INIT_DELAYED_WORK(&bootrom->dwork, gb_bootrom_timedout); | |
5a53e02e | 445 | greybus_set_drvdata(bundle, bootrom); |
8ec589b9 JH |
446 | |
447 | ret = gb_connection_enable_tx(connection); | |
448 | if (ret) | |
449 | goto err_connection_destroy; | |
450 | ||
5a53e02e | 451 | ret = gb_bootrom_get_version(bootrom); |
8ec589b9 JH |
452 | if (ret) |
453 | goto err_connection_disable; | |
454 | ||
5a53e02e | 455 | bootrom_es2_fixup_vid_pid(bootrom); |
f1e941a6 | 456 | |
8ec589b9 JH |
457 | ret = gb_connection_enable(connection); |
458 | if (ret) | |
459 | goto err_connection_disable; | |
460 | ||
6d9e6ffc VK |
461 | /* Refresh timeout */ |
462 | gb_bootrom_set_timeout(bootrom, NEXT_REQ_FIRMWARE_SIZE, | |
463 | NEXT_REQ_TIMEOUT_MS); | |
464 | ||
8eff5109 | 465 | /* Tell bootrom we're ready. */ |
5a53e02e | 466 | ret = gb_operation_sync(connection, GB_BOOTROM_TYPE_AP_READY, NULL, 0, |
4c9e2284 | 467 | NULL, 0); |
87f6c976 JH |
468 | if (ret) { |
469 | dev_err(&connection->bundle->dev, | |
470 | "failed to send AP READY: %d\n", ret); | |
6d9e6ffc | 471 | goto err_cancel_timeout; |
87f6c976 | 472 | } |
4c9e2284 | 473 | |
8ec589b9 | 474 | dev_dbg(&bundle->dev, "AP_READY sent\n"); |
fc41c2da | 475 | |
90f1b617 | 476 | return 0; |
8eff5109 | 477 | |
6d9e6ffc | 478 | err_cancel_timeout: |
40d276ed | 479 | gb_bootrom_cancel_timeout(bootrom); |
8ec589b9 JH |
480 | err_connection_disable: |
481 | gb_connection_disable(connection); | |
482 | err_connection_destroy: | |
483 | gb_connection_destroy(connection); | |
5a53e02e VK |
484 | err_free_bootrom: |
485 | kfree(bootrom); | |
8eff5109 JH |
486 | |
487 | return ret; | |
90f1b617 VK |
488 | } |
489 | ||
5a53e02e | 490 | static void gb_bootrom_disconnect(struct gb_bundle *bundle) |
90f1b617 | 491 | { |
5a53e02e | 492 | struct gb_bootrom *bootrom = greybus_get_drvdata(bundle); |
8ec589b9 JH |
493 | |
494 | dev_dbg(&bundle->dev, "%s\n", __func__); | |
495 | ||
5a53e02e | 496 | gb_connection_disable(bootrom->connection); |
90f1b617 | 497 | |
a956d939 | 498 | /* Disable timeouts */ |
40d276ed | 499 | gb_bootrom_cancel_timeout(bootrom); |
a956d939 VK |
500 | |
501 | /* | |
502 | * Release firmware: | |
503 | * | |
504 | * As the connection and the delayed work are already disabled, we don't | |
505 | * need to lock access to bootrom->fw here. | |
506 | */ | |
507 | free_firmware(bootrom); | |
90f1b617 | 508 | |
5a53e02e VK |
509 | gb_connection_destroy(bootrom->connection); |
510 | kfree(bootrom); | |
90f1b617 VK |
511 | } |
512 | ||
5a53e02e VK |
513 | static const struct greybus_bundle_id gb_bootrom_id_table[] = { |
514 | { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_BOOTROM) }, | |
8ec589b9 | 515 | { } |
90f1b617 | 516 | }; |
8ec589b9 | 517 | |
5a53e02e VK |
518 | static struct greybus_driver gb_bootrom_driver = { |
519 | .name = "bootrom", | |
520 | .probe = gb_bootrom_probe, | |
521 | .disconnect = gb_bootrom_disconnect, | |
522 | .id_table = gb_bootrom_id_table, | |
8ec589b9 JH |
523 | }; |
524 | ||
aa62b5e4 | 525 | module_greybus_driver(gb_bootrom_driver); |
8ec589b9 | 526 | |
aa62b5e4 | 527 | MODULE_LICENSE("GPL v2"); |