1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (c) 2023, Intel Corporation. |
4 | * Intel Visual Sensing Controller Transport Layer Linux driver |
5 | */ |
6 | |
7 | #include <linux/acpi.h> |
8 | #include <linux/align.h> |
9 | #include <linux/bitfield.h> |
10 | #include <linux/bits.h> |
11 | #include <linux/cleanup.h> |
12 | #include <linux/firmware.h> |
13 | #include <linux/sizes.h> |
14 | #include <linux/slab.h> |
15 | #include <linux/string_helpers.h> |
16 | #include <linux/types.h> |
17 | |
18 | #include <asm-generic/unaligned.h> |
19 | |
20 | #include "vsc-tp.h" |
21 | |
22 | #define VSC_MAGIC_NUM 0x49505343 /* IPSC */ |
23 | #define VSC_MAGIC_FW 0x49574653 /* IWFS */ |
24 | #define VSC_MAGIC_FILE 0x46564353 /* FVCS */ |
25 | |
26 | #define VSC_ADDR_BASE 0xE0030000 |
27 | #define VSC_EFUSE_ADDR (VSC_ADDR_BASE + 0x038) |
28 | #define VSC_STRAP_ADDR (VSC_ADDR_BASE + 0x100) |
29 | |
30 | #define VSC_MAINSTEPPING_VERSION_MASK GENMASK(7, 4) |
31 | #define VSC_MAINSTEPPING_VERSION_A 0 |
32 | |
33 | #define VSC_SUBSTEPPING_VERSION_MASK GENMASK(3, 0) |
34 | #define VSC_SUBSTEPPING_VERSION_0 0 |
35 | #define VSC_SUBSTEPPING_VERSION_1 2 |
36 | |
37 | #define VSC_BOOT_IMG_OPTION_MASK GENMASK(15, 0) |
38 | |
39 | #define VSC_SKU_CFG_LOCATION 0x5001A000 |
40 | #define VSC_SKU_MAX_SIZE 4100u |
41 | |
42 | #define VSC_ACE_IMG_CNT 2 |
43 | #define VSC_CSI_IMG_CNT 4 |
44 | #define VSC_IMG_CNT_MAX 6 |
45 | |
46 | #define VSC_ROM_PKG_SIZE 256u |
47 | #define VSC_FW_PKG_SIZE 512u |
48 | |
49 | #define VSC_IMAGE_DIR "intel/vsc/" |
50 | |
51 | #define VSC_CSI_IMAGE_NAME VSC_IMAGE_DIR "ivsc_fw.bin" |
52 | #define VSC_ACE_IMAGE_NAME_FMT VSC_IMAGE_DIR "ivsc_pkg_%s_0.bin" |
53 | #define VSC_CFG_IMAGE_NAME_FMT VSC_IMAGE_DIR "ivsc_skucfg_%s_0_1.bin" |
54 | |
55 | #define VSC_IMAGE_PATH_MAX_LEN 64 |
56 | |
57 | #define VSC_SENSOR_NAME_MAX_LEN 16 |
58 | |
59 | /* command id */ |
60 | enum { |
61 | VSC_CMD_QUERY = 0, |
62 | VSC_CMD_DL_SET = 1, |
63 | VSC_CMD_DL_START = 2, |
64 | VSC_CMD_DL_CONT = 3, |
65 | VSC_CMD_DUMP_MEM = 4, |
66 | VSC_CMD_GET_CONT = 8, |
67 | VSC_CMD_CAM_BOOT = 10, |
68 | }; |
69 | |
70 | /* command ack token */ |
71 | enum { |
72 | VSC_TOKEN_BOOTLOADER_REQ = 1, |
73 | VSC_TOKEN_DUMP_RESP = 4, |
74 | VSC_TOKEN_ERROR = 7, |
75 | }; |
76 | |
77 | /* image type */ |
78 | enum { |
79 | VSC_IMG_BOOTLOADER_TYPE = 1, |
80 | VSC_IMG_CSI_EM7D_TYPE, |
81 | VSC_IMG_CSI_SEM_TYPE, |
82 | VSC_IMG_CSI_RUNTIME_TYPE, |
83 | VSC_IMG_ACE_VISION_TYPE, |
84 | VSC_IMG_ACE_CFG_TYPE, |
85 | VSC_IMG_SKU_CFG_TYPE, |
86 | }; |
87 | |
88 | /* image fragments */ |
89 | enum { |
90 | VSC_IMG_BOOTLOADER_FRAG, |
91 | VSC_IMG_CSI_SEM_FRAG, |
92 | VSC_IMG_CSI_RUNTIME_FRAG, |
93 | VSC_IMG_ACE_VISION_FRAG, |
94 | VSC_IMG_ACE_CFG_FRAG, |
95 | VSC_IMG_CSI_EM7D_FRAG, |
96 | VSC_IMG_SKU_CFG_FRAG, |
97 | VSC_IMG_FRAG_MAX |
98 | }; |
99 | |
100 | struct vsc_rom_cmd { |
101 | __le32 magic; |
102 | __u8 cmd_id; |
103 | union { |
104 | /* download start */ |
105 | struct { |
106 | __u8 img_type; |
107 | __le16 option; |
108 | __le32 img_len; |
109 | __le32 img_loc; |
110 | __le32 crc; |
111 | DECLARE_FLEX_ARRAY(__u8, res); |
112 | } __packed dl_start; |
113 | /* download set */ |
114 | struct { |
115 | __u8 option; |
116 | __le16 img_cnt; |
117 | DECLARE_FLEX_ARRAY(__le32, payload); |
118 | } __packed dl_set; |
119 | /* download continue */ |
120 | struct { |
121 | __u8 end_flag; |
122 | __le16 len; |
123 | /* 8 is the offset of payload */ |
124 | __u8 payload[VSC_ROM_PKG_SIZE - 8]; |
125 | } __packed dl_cont; |
126 | /* dump memory */ |
127 | struct { |
128 | __u8 res; |
129 | __le16 len; |
130 | __le32 addr; |
131 | DECLARE_FLEX_ARRAY(__u8, payload); |
132 | } __packed dump_mem; |
133 | /* 5 is the offset of padding */ |
134 | __u8 padding[VSC_ROM_PKG_SIZE - 5]; |
135 | } data; |
136 | }; |
137 | |
138 | struct vsc_rom_cmd_ack { |
139 | __le32 magic; |
140 | __u8 token; |
141 | __u8 type; |
142 | __u8 res[2]; |
143 | __u8 payload[]; |
144 | }; |
145 | |
146 | struct vsc_fw_cmd { |
147 | __le32 magic; |
148 | __u8 cmd_id; |
149 | union { |
150 | struct { |
151 | __le16 option; |
152 | __u8 img_type; |
153 | __le32 img_len; |
154 | __le32 img_loc; |
155 | __le32 crc; |
156 | DECLARE_FLEX_ARRAY(__u8, res); |
157 | } __packed dl_start; |
158 | struct { |
159 | __le16 option; |
160 | __u8 img_cnt; |
161 | DECLARE_FLEX_ARRAY(__le32, payload); |
162 | } __packed dl_set; |
163 | struct { |
164 | __le32 addr; |
165 | __u8 len; |
166 | DECLARE_FLEX_ARRAY(__u8, payload); |
167 | } __packed dump_mem; |
168 | struct { |
169 | __u8 resv[3]; |
170 | __le32 crc; |
171 | DECLARE_FLEX_ARRAY(__u8, payload); |
172 | } __packed boot; |
173 | /* 5 is the offset of padding */ |
174 | __u8 padding[VSC_FW_PKG_SIZE - 5]; |
175 | } data; |
176 | }; |
177 | |
178 | struct vsc_img { |
179 | __le32 magic; |
180 | __le32 option; |
181 | __le32 image_count; |
182 | __le32 image_location[VSC_IMG_CNT_MAX]; |
183 | }; |
184 | |
185 | struct vsc_fw_sign { |
186 | __le32 magic; |
187 | __le32 image_size; |
188 | __u8 image[]; |
189 | }; |
190 | |
191 | struct vsc_image_code_data { |
192 | /* fragment index */ |
193 | u8 frag_index; |
194 | /* image type */ |
195 | u8 image_type; |
196 | }; |
197 | |
198 | struct vsc_img_frag { |
199 | u8 type; |
200 | u32 location; |
201 | const u8 *data; |
202 | u32 size; |
203 | }; |
204 | |
205 | /** |
206 | * struct vsc_fw_loader - represent vsc firmware loader |
207 | * @dev: device used to request fimware |
208 | * @tp: transport layer used with the firmware loader |
209 | * @csi: CSI image |
210 | * @ace: ACE image |
211 | * @cfg: config image |
212 | * @tx_buf: tx buffer |
213 | * @rx_buf: rx buffer |
214 | * @option: command option |
215 | * @count: total image count |
216 | * @sensor_name: camera sensor name |
217 | * @frags: image fragments |
218 | */ |
219 | struct vsc_fw_loader { |
220 | struct device *dev; |
221 | struct vsc_tp *tp; |
222 | |
223 | const struct firmware *csi; |
224 | const struct firmware *ace; |
225 | const struct firmware *cfg; |
226 | |
227 | void *tx_buf; |
228 | void *rx_buf; |
229 | |
230 | u16 option; |
231 | u16 count; |
232 | |
233 | char sensor_name[VSC_SENSOR_NAME_MAX_LEN]; |
234 | |
235 | struct vsc_img_frag frags[VSC_IMG_FRAG_MAX]; |
236 | }; |
237 | |
238 | static inline u32 vsc_sum_crc(void *data, size_t size) |
239 | { |
240 | u32 crc = 0; |
241 | size_t i; |
242 | |
243 | for (i = 0; i < size; i++) |
244 | crc += *((u8 *)data + i); |
245 | |
246 | return crc; |
247 | } |
248 | |
249 | /* get sensor name to construct image name */ |
250 | static int vsc_get_sensor_name(struct vsc_fw_loader *fw_loader, |
251 | struct device *dev) |
252 | { |
253 | struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER }; |
254 | union acpi_object obj = { |
255 | .type = ACPI_TYPE_INTEGER, |
256 | .integer.value = 1, |
257 | }; |
258 | struct acpi_object_list arg_list = { |
259 | .count = 1, |
260 | .pointer = &obj, |
261 | }; |
262 | union acpi_object *ret_obj; |
263 | acpi_handle handle; |
264 | acpi_status status; |
265 | int ret = 0; |
266 | |
267 | handle = ACPI_HANDLE(dev); |
268 | if (!handle) |
269 | return -EINVAL; |
270 | |
271 | status = acpi_evaluate_object(object: handle, pathname: "SID" , parameter_objects: &arg_list, return_object_buffer: &buffer); |
272 | if (ACPI_FAILURE(status)) { |
273 | dev_err(dev, "can't evaluate SID method: %d\n" , status); |
274 | return -ENODEV; |
275 | } |
276 | |
277 | ret_obj = buffer.pointer; |
278 | if (!ret_obj) { |
279 | dev_err(dev, "can't locate ACPI buffer\n" ); |
280 | return -ENODEV; |
281 | } |
282 | |
283 | if (ret_obj->type != ACPI_TYPE_STRING) { |
284 | dev_err(dev, "found non-string entry\n" ); |
285 | ret = -ENODEV; |
286 | goto out_free_buff; |
287 | } |
288 | |
289 | /* string length excludes trailing NUL */ |
290 | if (ret_obj->string.length >= sizeof(fw_loader->sensor_name)) { |
291 | dev_err(dev, "sensor name buffer too small\n" ); |
292 | ret = -EINVAL; |
293 | goto out_free_buff; |
294 | } |
295 | |
296 | memcpy(fw_loader->sensor_name, ret_obj->string.pointer, |
297 | ret_obj->string.length); |
298 | |
299 | string_lower(dst: fw_loader->sensor_name, src: fw_loader->sensor_name); |
300 | |
301 | out_free_buff: |
302 | ACPI_FREE(buffer.pointer); |
303 | |
304 | return ret; |
305 | } |
306 | |
307 | static int vsc_identify_silicon(struct vsc_fw_loader *fw_loader) |
308 | { |
309 | struct vsc_rom_cmd_ack *ack = fw_loader->rx_buf; |
310 | struct vsc_rom_cmd *cmd = fw_loader->tx_buf; |
311 | u8 version, sub_version; |
312 | int ret; |
313 | |
314 | /* identify stepping information */ |
315 | cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); |
316 | cmd->cmd_id = VSC_CMD_DUMP_MEM; |
317 | cmd->data.dump_mem.addr = cpu_to_le32(VSC_EFUSE_ADDR); |
318 | cmd->data.dump_mem.len = cpu_to_le16(sizeof(__le32)); |
319 | ret = vsc_tp_rom_xfer(tp: fw_loader->tp, obuf: cmd, ibuf: ack, VSC_ROM_PKG_SIZE); |
320 | if (ret) |
321 | return ret; |
322 | if (ack->token == VSC_TOKEN_ERROR) |
323 | return -EINVAL; |
324 | |
325 | cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); |
326 | cmd->cmd_id = VSC_CMD_GET_CONT; |
327 | ret = vsc_tp_rom_xfer(tp: fw_loader->tp, obuf: cmd, ibuf: ack, VSC_ROM_PKG_SIZE); |
328 | if (ret) |
329 | return ret; |
330 | if (ack->token != VSC_TOKEN_DUMP_RESP) |
331 | return -EINVAL; |
332 | |
333 | version = FIELD_GET(VSC_MAINSTEPPING_VERSION_MASK, ack->payload[0]); |
334 | sub_version = FIELD_GET(VSC_SUBSTEPPING_VERSION_MASK, ack->payload[0]); |
335 | |
336 | if (version != VSC_MAINSTEPPING_VERSION_A) |
337 | return -EINVAL; |
338 | |
339 | if (sub_version != VSC_SUBSTEPPING_VERSION_0 && |
340 | sub_version != VSC_SUBSTEPPING_VERSION_1) |
341 | return -EINVAL; |
342 | |
343 | dev_info(fw_loader->dev, "silicon stepping version is %u:%u\n" , |
344 | version, sub_version); |
345 | |
346 | /* identify strap information */ |
347 | cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); |
348 | cmd->cmd_id = VSC_CMD_DUMP_MEM; |
349 | cmd->data.dump_mem.addr = cpu_to_le32(VSC_STRAP_ADDR); |
350 | cmd->data.dump_mem.len = cpu_to_le16(sizeof(__le32)); |
351 | ret = vsc_tp_rom_xfer(tp: fw_loader->tp, obuf: cmd, ibuf: ack, VSC_ROM_PKG_SIZE); |
352 | if (ret) |
353 | return ret; |
354 | if (ack->token == VSC_TOKEN_ERROR) |
355 | return -EINVAL; |
356 | |
357 | cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); |
358 | cmd->cmd_id = VSC_CMD_GET_CONT; |
359 | ret = vsc_tp_rom_xfer(tp: fw_loader->tp, obuf: cmd, ibuf: ack, VSC_ROM_PKG_SIZE); |
360 | if (ret) |
361 | return ret; |
362 | if (ack->token != VSC_TOKEN_DUMP_RESP) |
363 | return -EINVAL; |
364 | |
365 | return 0; |
366 | } |
367 | |
368 | static int vsc_identify_csi_image(struct vsc_fw_loader *fw_loader) |
369 | { |
370 | const struct firmware *image; |
371 | struct vsc_fw_sign *sign; |
372 | struct vsc_img *img; |
373 | unsigned int i; |
374 | int ret; |
375 | |
376 | ret = request_firmware(fw: &image, VSC_CSI_IMAGE_NAME, device: fw_loader->dev); |
377 | if (ret) |
378 | return ret; |
379 | |
380 | img = (struct vsc_img *)image->data; |
381 | if (!img) { |
382 | ret = -ENOENT; |
383 | goto err_release_image; |
384 | } |
385 | |
386 | if (le32_to_cpu(img->magic) != VSC_MAGIC_FILE) { |
387 | ret = -EINVAL; |
388 | goto err_release_image; |
389 | } |
390 | |
391 | if (le32_to_cpu(img->image_count) != VSC_CSI_IMG_CNT) { |
392 | ret = -EINVAL; |
393 | goto err_release_image; |
394 | } |
395 | fw_loader->count += le32_to_cpu(img->image_count) - 1; |
396 | |
397 | fw_loader->option = |
398 | FIELD_GET(VSC_BOOT_IMG_OPTION_MASK, le32_to_cpu(img->option)); |
399 | |
400 | sign = (struct vsc_fw_sign *) |
401 | (img->image_location + le32_to_cpu(img->image_count)); |
402 | |
403 | for (i = 0; i < VSC_CSI_IMG_CNT; i++) { |
404 | /* mapping from CSI image index to image code data */ |
405 | static const struct vsc_image_code_data csi_image_map[] = { |
406 | { VSC_IMG_BOOTLOADER_FRAG, VSC_IMG_BOOTLOADER_TYPE }, |
407 | { VSC_IMG_CSI_SEM_FRAG, VSC_IMG_CSI_SEM_TYPE }, |
408 | { VSC_IMG_CSI_RUNTIME_FRAG, VSC_IMG_CSI_RUNTIME_TYPE }, |
409 | { VSC_IMG_CSI_EM7D_FRAG, VSC_IMG_CSI_EM7D_TYPE }, |
410 | }; |
411 | struct vsc_img_frag *frag; |
412 | |
413 | if ((u8 *)sign + sizeof(*sign) > image->data + image->size) { |
414 | ret = -EINVAL; |
415 | goto err_release_image; |
416 | } |
417 | |
418 | if (le32_to_cpu(sign->magic) != VSC_MAGIC_FW) { |
419 | ret = -EINVAL; |
420 | goto err_release_image; |
421 | } |
422 | |
423 | if (!le32_to_cpu(img->image_location[i])) { |
424 | ret = -EINVAL; |
425 | goto err_release_image; |
426 | } |
427 | |
428 | frag = &fw_loader->frags[csi_image_map[i].frag_index]; |
429 | |
430 | frag->data = sign->image; |
431 | frag->size = le32_to_cpu(sign->image_size); |
432 | frag->location = le32_to_cpu(img->image_location[i]); |
433 | frag->type = csi_image_map[i].image_type; |
434 | |
435 | sign = (struct vsc_fw_sign *) |
436 | (sign->image + le32_to_cpu(sign->image_size)); |
437 | } |
438 | |
439 | fw_loader->csi = image; |
440 | |
441 | return 0; |
442 | |
443 | err_release_image: |
444 | release_firmware(fw: image); |
445 | |
446 | return ret; |
447 | } |
448 | |
449 | static int vsc_identify_ace_image(struct vsc_fw_loader *fw_loader) |
450 | { |
451 | char path[VSC_IMAGE_PATH_MAX_LEN]; |
452 | const struct firmware *image; |
453 | struct vsc_fw_sign *sign; |
454 | struct vsc_img *img; |
455 | unsigned int i; |
456 | int ret; |
457 | |
458 | snprintf(buf: path, size: sizeof(path), VSC_ACE_IMAGE_NAME_FMT, |
459 | fw_loader->sensor_name); |
460 | |
461 | ret = request_firmware(fw: &image, name: path, device: fw_loader->dev); |
462 | if (ret) |
463 | return ret; |
464 | |
465 | img = (struct vsc_img *)image->data; |
466 | if (!img) { |
467 | ret = -ENOENT; |
468 | goto err_release_image; |
469 | } |
470 | |
471 | if (le32_to_cpu(img->magic) != VSC_MAGIC_FILE) { |
472 | ret = -EINVAL; |
473 | goto err_release_image; |
474 | } |
475 | |
476 | if (le32_to_cpu(img->image_count) != VSC_ACE_IMG_CNT) { |
477 | ret = -EINVAL; |
478 | goto err_release_image; |
479 | } |
480 | fw_loader->count += le32_to_cpu(img->image_count); |
481 | |
482 | sign = (struct vsc_fw_sign *) |
483 | (img->image_location + le32_to_cpu(img->image_count)); |
484 | |
485 | for (i = 0; i < VSC_ACE_IMG_CNT; i++) { |
486 | /* mapping from ACE image index to image code data */ |
487 | static const struct vsc_image_code_data ace_image_map[] = { |
488 | { VSC_IMG_ACE_VISION_FRAG, VSC_IMG_ACE_VISION_TYPE }, |
489 | { VSC_IMG_ACE_CFG_FRAG, VSC_IMG_ACE_CFG_TYPE }, |
490 | }; |
491 | struct vsc_img_frag *frag, *last_frag; |
492 | u8 frag_index; |
493 | |
494 | if ((u8 *)sign + sizeof(*sign) > image->data + image->size) { |
495 | ret = -EINVAL; |
496 | goto err_release_image; |
497 | } |
498 | |
499 | if (le32_to_cpu(sign->magic) != VSC_MAGIC_FW) { |
500 | ret = -EINVAL; |
501 | goto err_release_image; |
502 | } |
503 | |
504 | frag_index = ace_image_map[i].frag_index; |
505 | frag = &fw_loader->frags[frag_index]; |
506 | |
507 | frag->data = sign->image; |
508 | frag->size = le32_to_cpu(sign->image_size); |
509 | frag->location = le32_to_cpu(img->image_location[i]); |
510 | frag->type = ace_image_map[i].image_type; |
511 | |
512 | if (!frag->location) { |
513 | last_frag = &fw_loader->frags[frag_index - 1]; |
514 | frag->location = |
515 | ALIGN(last_frag->location + last_frag->size, SZ_4K); |
516 | } |
517 | |
518 | sign = (struct vsc_fw_sign *) |
519 | (sign->image + le32_to_cpu(sign->image_size)); |
520 | } |
521 | |
522 | fw_loader->ace = image; |
523 | |
524 | return 0; |
525 | |
526 | err_release_image: |
527 | release_firmware(fw: image); |
528 | |
529 | return ret; |
530 | } |
531 | |
532 | static int vsc_identify_cfg_image(struct vsc_fw_loader *fw_loader) |
533 | { |
534 | struct vsc_img_frag *frag = &fw_loader->frags[VSC_IMG_SKU_CFG_FRAG]; |
535 | char path[VSC_IMAGE_PATH_MAX_LEN]; |
536 | const struct firmware *image; |
537 | u32 size; |
538 | int ret; |
539 | |
540 | snprintf(buf: path, size: sizeof(path), VSC_CFG_IMAGE_NAME_FMT, |
541 | fw_loader->sensor_name); |
542 | |
543 | ret = request_firmware(fw: &image, name: path, device: fw_loader->dev); |
544 | if (ret) |
545 | return ret; |
546 | |
547 | /* identify image size */ |
548 | if (image->size <= sizeof(u32) || image->size > VSC_SKU_MAX_SIZE) { |
549 | ret = -EINVAL; |
550 | goto err_release_image; |
551 | } |
552 | |
553 | size = le32_to_cpu(*((__le32 *)image->data)) + sizeof(u32); |
554 | if (image->size != size) { |
555 | ret = -EINVAL; |
556 | goto err_release_image; |
557 | } |
558 | |
559 | frag->data = image->data; |
560 | frag->size = image->size; |
561 | frag->type = VSC_IMG_SKU_CFG_TYPE; |
562 | frag->location = VSC_SKU_CFG_LOCATION; |
563 | |
564 | fw_loader->cfg = image; |
565 | |
566 | return 0; |
567 | |
568 | err_release_image: |
569 | release_firmware(fw: image); |
570 | |
571 | return ret; |
572 | } |
573 | |
574 | static int vsc_download_bootloader(struct vsc_fw_loader *fw_loader) |
575 | { |
576 | struct vsc_img_frag *frag = &fw_loader->frags[VSC_IMG_BOOTLOADER_FRAG]; |
577 | struct vsc_rom_cmd_ack *ack = fw_loader->rx_buf; |
578 | struct vsc_rom_cmd *cmd = fw_loader->tx_buf; |
579 | u32 len, c_len; |
580 | size_t remain; |
581 | const u8 *p; |
582 | int ret; |
583 | |
584 | cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); |
585 | cmd->cmd_id = VSC_CMD_QUERY; |
586 | ret = vsc_tp_rom_xfer(tp: fw_loader->tp, obuf: cmd, ibuf: ack, VSC_ROM_PKG_SIZE); |
587 | if (ret) |
588 | return ret; |
589 | if (ack->token != VSC_TOKEN_DUMP_RESP && |
590 | ack->token != VSC_TOKEN_BOOTLOADER_REQ) |
591 | return -EINVAL; |
592 | |
593 | cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); |
594 | cmd->cmd_id = VSC_CMD_DL_START; |
595 | cmd->data.dl_start.option = cpu_to_le16(fw_loader->option); |
596 | cmd->data.dl_start.img_type = frag->type; |
597 | cmd->data.dl_start.img_len = cpu_to_le32(frag->size); |
598 | cmd->data.dl_start.img_loc = cpu_to_le32(frag->location); |
599 | |
600 | c_len = offsetof(struct vsc_rom_cmd, data.dl_start.crc); |
601 | cmd->data.dl_start.crc = cpu_to_le32(vsc_sum_crc(cmd, c_len)); |
602 | |
603 | ret = vsc_tp_rom_xfer(tp: fw_loader->tp, obuf: cmd, NULL, VSC_ROM_PKG_SIZE); |
604 | if (ret) |
605 | return ret; |
606 | |
607 | p = frag->data; |
608 | remain = frag->size; |
609 | |
610 | /* download image data */ |
611 | while (remain > 0) { |
612 | len = min(remain, sizeof(cmd->data.dl_cont.payload)); |
613 | |
614 | cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); |
615 | cmd->cmd_id = VSC_CMD_DL_CONT; |
616 | cmd->data.dl_cont.len = cpu_to_le16(len); |
617 | cmd->data.dl_cont.end_flag = remain == len; |
618 | memcpy(cmd->data.dl_cont.payload, p, len); |
619 | |
620 | ret = vsc_tp_rom_xfer(tp: fw_loader->tp, obuf: cmd, NULL, VSC_ROM_PKG_SIZE); |
621 | if (ret) |
622 | return ret; |
623 | |
624 | p += len; |
625 | remain -= len; |
626 | } |
627 | |
628 | return 0; |
629 | } |
630 | |
631 | static int vsc_download_firmware(struct vsc_fw_loader *fw_loader) |
632 | { |
633 | struct vsc_fw_cmd *cmd = fw_loader->tx_buf; |
634 | unsigned int i, index = 0; |
635 | u32 c_len; |
636 | int ret; |
637 | |
638 | cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); |
639 | cmd->cmd_id = VSC_CMD_DL_SET; |
640 | cmd->data.dl_set.img_cnt = cpu_to_le16(fw_loader->count); |
641 | put_unaligned_le16(val: fw_loader->option, p: &cmd->data.dl_set.option); |
642 | |
643 | for (i = VSC_IMG_CSI_SEM_FRAG; i <= VSC_IMG_CSI_EM7D_FRAG; i++) { |
644 | struct vsc_img_frag *frag = &fw_loader->frags[i]; |
645 | |
646 | cmd->data.dl_set.payload[index++] = cpu_to_le32(frag->location); |
647 | cmd->data.dl_set.payload[index++] = cpu_to_le32(frag->size); |
648 | } |
649 | |
650 | c_len = offsetof(struct vsc_fw_cmd, data.dl_set.payload[index]); |
651 | cmd->data.dl_set.payload[index] = cpu_to_le32(vsc_sum_crc(cmd, c_len)); |
652 | |
653 | ret = vsc_tp_rom_xfer(tp: fw_loader->tp, obuf: cmd, NULL, VSC_FW_PKG_SIZE); |
654 | if (ret) |
655 | return ret; |
656 | |
657 | for (i = VSC_IMG_CSI_SEM_FRAG; i < VSC_IMG_FRAG_MAX; i++) { |
658 | struct vsc_img_frag *frag = &fw_loader->frags[i]; |
659 | const u8 *p; |
660 | u32 remain; |
661 | |
662 | cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); |
663 | cmd->cmd_id = VSC_CMD_DL_START; |
664 | cmd->data.dl_start.img_type = frag->type; |
665 | cmd->data.dl_start.img_len = cpu_to_le32(frag->size); |
666 | cmd->data.dl_start.img_loc = cpu_to_le32(frag->location); |
667 | put_unaligned_le16(val: fw_loader->option, p: &cmd->data.dl_start.option); |
668 | |
669 | c_len = offsetof(struct vsc_fw_cmd, data.dl_start.crc); |
670 | cmd->data.dl_start.crc = cpu_to_le32(vsc_sum_crc(cmd, c_len)); |
671 | |
672 | ret = vsc_tp_rom_xfer(tp: fw_loader->tp, obuf: cmd, NULL, VSC_FW_PKG_SIZE); |
673 | if (ret) |
674 | return ret; |
675 | |
676 | p = frag->data; |
677 | remain = frag->size; |
678 | |
679 | /* download image data */ |
680 | while (remain > 0) { |
681 | u32 len = min(remain, VSC_FW_PKG_SIZE); |
682 | |
683 | memcpy(fw_loader->tx_buf, p, len); |
684 | memset(fw_loader->tx_buf + len, 0, VSC_FW_PKG_SIZE - len); |
685 | |
686 | ret = vsc_tp_rom_xfer(tp: fw_loader->tp, obuf: fw_loader->tx_buf, |
687 | NULL, VSC_FW_PKG_SIZE); |
688 | if (ret) |
689 | break; |
690 | |
691 | p += len; |
692 | remain -= len; |
693 | } |
694 | } |
695 | |
696 | cmd->magic = cpu_to_le32(VSC_MAGIC_NUM); |
697 | cmd->cmd_id = VSC_CMD_CAM_BOOT; |
698 | |
699 | c_len = offsetof(struct vsc_fw_cmd, data.dl_start.crc); |
700 | cmd->data.boot.crc = cpu_to_le32(vsc_sum_crc(cmd, c_len)); |
701 | |
702 | return vsc_tp_rom_xfer(tp: fw_loader->tp, obuf: cmd, NULL, VSC_FW_PKG_SIZE); |
703 | } |
704 | |
705 | /** |
706 | * vsc_tp_init - init vsc_tp |
707 | * @tp: vsc_tp device handle |
708 | * @dev: device node for mei vsc device |
709 | * Return: 0 in case of success, negative value in case of error |
710 | */ |
711 | int vsc_tp_init(struct vsc_tp *tp, struct device *dev) |
712 | { |
713 | struct vsc_fw_loader *fw_loader __free(kfree) = NULL; |
714 | void *tx_buf __free(kfree) = NULL; |
715 | void *rx_buf __free(kfree) = NULL; |
716 | int ret; |
717 | |
718 | fw_loader = kzalloc(size: sizeof(*fw_loader), GFP_KERNEL); |
719 | if (!fw_loader) |
720 | return -ENOMEM; |
721 | |
722 | tx_buf = kzalloc(VSC_FW_PKG_SIZE, GFP_KERNEL); |
723 | if (!tx_buf) |
724 | return -ENOMEM; |
725 | |
726 | rx_buf = kzalloc(VSC_FW_PKG_SIZE, GFP_KERNEL); |
727 | if (!rx_buf) |
728 | return -ENOMEM; |
729 | |
730 | fw_loader->tx_buf = tx_buf; |
731 | fw_loader->rx_buf = rx_buf; |
732 | |
733 | fw_loader->tp = tp; |
734 | fw_loader->dev = dev; |
735 | |
736 | ret = vsc_get_sensor_name(fw_loader, dev); |
737 | if (ret) |
738 | return ret; |
739 | |
740 | ret = vsc_identify_silicon(fw_loader); |
741 | if (ret) |
742 | return ret; |
743 | |
744 | ret = vsc_identify_csi_image(fw_loader); |
745 | if (ret) |
746 | return ret; |
747 | |
748 | ret = vsc_identify_ace_image(fw_loader); |
749 | if (ret) |
750 | goto err_release_csi; |
751 | |
752 | ret = vsc_identify_cfg_image(fw_loader); |
753 | if (ret) |
754 | goto err_release_ace; |
755 | |
756 | ret = vsc_download_bootloader(fw_loader); |
757 | if (!ret) |
758 | ret = vsc_download_firmware(fw_loader); |
759 | |
760 | release_firmware(fw: fw_loader->cfg); |
761 | |
762 | err_release_ace: |
763 | release_firmware(fw: fw_loader->ace); |
764 | |
765 | err_release_csi: |
766 | release_firmware(fw: fw_loader->csi); |
767 | |
768 | return ret; |
769 | } |
770 | EXPORT_SYMBOL_NS_GPL(vsc_tp_init, VSC_TP); |
771 | |