1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Copyright (c) 2017-2026 Morse Micro
4 */
5 #include <linux/kernel.h>
6 #include <linux/firmware.h>
7 #include <linux/slab.h>
8 #include <linux/delay.h>
9 #include <linux/iopoll.h>
10 #include <linux/string_choices.h>
11 #include <net/mac80211.h>
12 #include <linux/elf.h>
13 #include <linux/crc32.h>
14 #include "fw.h"
15 #include "mac.h"
16 #include "bus.h"
17
18 /*
19 * Maximum wait time (microseconds) for firmware to boot (for host table
20 * pointer to be available)
21 */
22 #define HOST_TABLE_PTR_POLL_TIMEOUT_US 1200000
23 #define HOST_TABLE_PTR_POLL_PERIOD_US 10000
24
25 /* Number of times to attempt flashing FW */
26 #define FW_FLASH_ATTEMPT_COUNT 3
27
mm81x_fw_get_header(const u8 * data,Elf32_Ehdr * ehdr)28 static int mm81x_fw_get_header(const u8 *data, Elf32_Ehdr *ehdr)
29 {
30 const struct mm81x_elf32_ehdr *p =
31 (const struct mm81x_elf32_ehdr *)data;
32
33 /* Magic check */
34 if (p->e_ident[EI_MAG0] != ELFMAG0 || p->e_ident[EI_MAG1] != ELFMAG1 ||
35 p->e_ident[EI_MAG2] != ELFMAG2 || p->e_ident[EI_MAG3] != ELFMAG3)
36 return -EINVAL;
37
38 /* elf32 and little endian */
39 if (p->e_ident[EI_DATA] != ELFDATA2LSB ||
40 p->e_ident[EI_CLASS] != ELFCLASS32)
41 return -EINVAL;
42
43 ehdr->e_phoff = le32_to_cpu(p->e_phoff);
44 ehdr->e_phentsize = le16_to_cpu(p->e_phentsize);
45 ehdr->e_phnum = le16_to_cpu(p->e_phnum);
46 ehdr->e_shoff = le32_to_cpu(p->e_shoff);
47 ehdr->e_shentsize = le16_to_cpu(p->e_shentsize);
48 ehdr->e_shnum = le16_to_cpu(p->e_shnum);
49 ehdr->e_shstrndx = le16_to_cpu(p->e_shstrndx);
50 ehdr->e_entry = le32_to_cpu(p->e_entry);
51
52 return 0;
53 }
54
mm81x_fw_parse_info(struct mm81x * mors,const u8 * data,int length)55 static void mm81x_fw_parse_info(struct mm81x *mors, const u8 *data, int length)
56 {
57 const struct mm81x_fw_info_tlv *tlv =
58 (const struct mm81x_fw_info_tlv *)data;
59
60 while ((u8 *)tlv < (data + length)) {
61 switch (le16_to_cpu(tlv->type)) {
62 case MM81X_FW_INFO_TLV_BCF_ADDR:
63 mors->bcf_address = get_unaligned_le32(tlv->val);
64 break;
65 default:
66 break;
67 }
68 tlv = (const struct mm81x_fw_info_tlv *)((u8 *)tlv +
69 le16_to_cpu(
70 tlv->length) +
71 sizeof(*tlv));
72 }
73 }
74
mm81x_fw_get_section_header(const u8 * data,Elf32_Ehdr * ehdr,Elf32_Shdr * shdr,int i)75 static int mm81x_fw_get_section_header(const u8 *data, Elf32_Ehdr *ehdr,
76 Elf32_Shdr *shdr, int i)
77 {
78 const struct mm81x_elf32_shdr *p =
79 (void *)(data + ehdr->e_shoff + (i * ehdr->e_shentsize));
80
81 shdr->sh_name = le32_to_cpu(p->sh_name);
82 shdr->sh_type = le32_to_cpu(p->sh_type);
83 shdr->sh_offset = le32_to_cpu(p->sh_offset);
84 shdr->sh_addr = le32_to_cpu(p->sh_addr);
85 shdr->sh_size = le32_to_cpu(p->sh_size);
86 shdr->sh_flags = le32_to_cpu(p->sh_flags);
87
88 return 0;
89 }
90
mm81x_fw_set_boot_addr(struct mm81x * mors,uint32_t addr)91 static int mm81x_fw_set_boot_addr(struct mm81x *mors, uint32_t addr)
92 {
93 int status;
94
95 dev_dbg(mors->dev, "Overwriting boot address to 0x%x", addr);
96 mm81x_claim_bus(mors);
97 status = mm81x_reg32_write(mors, MM81X_REG_BOOT_ADDR(mors), addr);
98 mm81x_release_bus(mors);
99 return status;
100 }
101
mm81x_fw_load_fw(struct mm81x * mors,const struct firmware * fw)102 static int mm81x_fw_load_fw(struct mm81x *mors, const struct firmware *fw)
103 {
104 int i;
105 int ret = 0;
106 Elf32_Ehdr ehdr;
107 Elf32_Phdr phdr;
108 Elf32_Shdr shdr;
109 Elf32_Shdr sh_strtab;
110 const char *sh_strs;
111
112 u8 *fw_buf = devm_kmalloc(mors->dev, ROUND_BYTES_TO_WORD(fw->size),
113 GFP_KERNEL);
114
115 if (!fw_buf)
116 return -ENOMEM;
117
118 if (mm81x_fw_get_header(fw->data, &ehdr)) {
119 dev_err(mors->dev, "Wrong file format");
120 return -EINVAL;
121 }
122
123 if (mm81x_fw_get_section_header(fw->data, &ehdr, &sh_strtab,
124 ehdr.e_shstrndx)) {
125 dev_err(mors->dev, "Invalid firmware. Missing string table");
126 return -ENOENT;
127 }
128
129 sh_strs = (const char *)fw->data + sh_strtab.sh_offset;
130
131 for (i = 0; i < ehdr.e_phnum; i++) {
132 int status;
133 int address;
134 const struct mm81x_elf32_phdr *p =
135 (void *)(fw->data + ehdr.e_phoff +
136 i * ehdr.e_phentsize);
137
138 phdr.p_type = le32_to_cpu(p->p_type);
139 phdr.p_offset = le32_to_cpu(p->p_offset);
140 phdr.p_paddr = le32_to_cpu(p->p_paddr);
141 phdr.p_filesz = le32_to_cpu(p->p_filesz);
142 phdr.p_memsz = le32_to_cpu(p->p_memsz);
143
144 address = phdr.p_paddr;
145
146 if (phdr.p_type != PT_LOAD || !phdr.p_memsz)
147 continue;
148
149 if (phdr.p_filesz && phdr.p_offset &&
150 (phdr.p_offset + phdr.p_filesz) < fw->size) {
151 u32 padded_size = ROUND_BYTES_TO_WORD(phdr.p_filesz);
152
153 memcpy(fw_buf, fw->data + phdr.p_offset, padded_size);
154 /* Set padding to 0xff */
155 memset(fw_buf + phdr.p_filesz, 0xff,
156 padded_size - phdr.p_filesz);
157 mm81x_claim_bus(mors);
158 status = mm81x_dm_write(mors, address, fw_buf,
159 padded_size);
160 mm81x_release_bus(mors);
161 if (status) {
162 ret = -EIO;
163 break;
164 }
165 }
166 }
167
168 for (i = 0; i < ehdr.e_shnum; i++) {
169 if (mm81x_fw_get_section_header(fw->data, &ehdr, &shdr, i))
170 continue;
171
172 /* This is the firmware info. Parse it */
173 if (!strncmp(sh_strs + shdr.sh_name, ".fw_info",
174 sizeof(".fw_info")))
175 mm81x_fw_parse_info(mors, fw->data + shdr.sh_offset,
176 shdr.sh_size);
177 }
178
179 if (ehdr.e_entry)
180 ret = mm81x_fw_set_boot_addr(mors, ehdr.e_entry);
181
182 devm_kfree(mors->dev, fw_buf);
183 return ret;
184 }
185
__mm81x_fw_load_bcf(struct mm81x * mors,unsigned int addr,const void * src,size_t src_len,u8 * scratch,size_t scratch_cap)186 static int __mm81x_fw_load_bcf(struct mm81x *mors, unsigned int addr,
187 const void *src, size_t src_len, u8 *scratch,
188 size_t scratch_cap)
189 {
190 size_t rounded = ROUND_BYTES_TO_WORD(src_len);
191 int st;
192
193 if (rounded > scratch_cap)
194 return -EINVAL;
195 if (rounded > BCF_DATABASE_SIZE)
196 return -EFBIG;
197
198 memcpy(scratch, src, src_len);
199 if (rounded > src_len)
200 memset(scratch + src_len, 0xff, rounded - src_len);
201
202 mm81x_claim_bus(mors);
203 st = mm81x_dm_write(mors, addr, scratch, rounded);
204 mm81x_release_bus(mors);
205
206 return st ? -EIO : 0;
207 }
208
mm81x_fw_load_bcf(struct mm81x * mors,const struct firmware * bcf,unsigned int bcf_address)209 static int mm81x_fw_load_bcf(struct mm81x *mors, const struct firmware *bcf,
210 unsigned int bcf_address)
211 {
212 int i, ret = 0;
213 size_t reg_prefix_len, cfg_len_rounded = 0, reg_len_rounded;
214 Elf32_Ehdr ehdr;
215 Elf32_Shdr shdr, sh_strtab;
216 const char *sh_strs, *reg_prefix = ".regdom_", *reg_src;
217 size_t reg_len;
218 u8 *bcf_buf;
219
220 bcf_buf = devm_kmalloc(mors->dev, ROUND_BYTES_TO_WORD(bcf->size),
221 GFP_KERNEL);
222 if (!bcf_buf)
223 return -ENOMEM;
224
225 if (mm81x_fw_get_header(bcf->data, &ehdr)) {
226 dev_err(mors->dev, "Wrong file format");
227 ret = -EINVAL;
228 goto out_free;
229 }
230
231 if (mm81x_fw_get_section_header(bcf->data, &ehdr, &sh_strtab,
232 ehdr.e_shstrndx)) {
233 dev_err(mors->dev, "Invalid BCF - missing string table");
234 ret = -ENOENT;
235 goto out_free;
236 }
237
238 sh_strs = (const char *)bcf->data + sh_strtab.sh_offset;
239 reg_prefix_len = strlen(reg_prefix);
240
241 for (i = 0; i < ehdr.e_shnum; i++) {
242 if (mm81x_fw_get_section_header(bcf->data, &ehdr, &shdr, i))
243 continue;
244 if (strcmp(sh_strs + shdr.sh_name, ".board_config"))
245 continue;
246
247 cfg_len_rounded = ROUND_BYTES_TO_WORD(shdr.sh_size);
248 dev_dbg(mors->dev,
249 "Write BCF board_config - addr 0x%x size %zu",
250 bcf_address, cfg_len_rounded);
251
252 ret = __mm81x_fw_load_bcf(mors, bcf_address,
253 bcf->data + shdr.sh_offset,
254 shdr.sh_size, bcf_buf,
255 ROUND_BYTES_TO_WORD(bcf->size));
256 if (ret)
257 goto out_free;
258
259 bcf_address += cfg_len_rounded;
260 break;
261 }
262
263 ret = -EINVAL;
264 for (; i < ehdr.e_shnum; i++) {
265 if (mm81x_fw_get_section_header(bcf->data, &ehdr, &shdr, i))
266 continue;
267 if (strncmp(sh_strs + shdr.sh_name, reg_prefix, reg_prefix_len))
268 continue;
269 if (strncmp(sh_strs + shdr.sh_name + reg_prefix_len,
270 mors->country, 2))
271 continue;
272
273 reg_src = bcf->data + shdr.sh_offset;
274 reg_len = shdr.sh_size;
275 dev_dbg(mors->dev, "Write BCF %s - addr 0x%x size %zu",
276 sh_strs + shdr.sh_name, bcf_address,
277 ROUND_BYTES_TO_WORD(reg_len));
278 ret = 0;
279 break;
280 }
281
282 if (ret)
283 goto out_free;
284
285 reg_len_rounded = ROUND_BYTES_TO_WORD(reg_len);
286 if ((cfg_len_rounded + reg_len_rounded) > BCF_DATABASE_SIZE) {
287 ret = -EFBIG;
288 goto out_free;
289 }
290
291 ret = __mm81x_fw_load_bcf(mors, bcf_address, reg_src, reg_len, bcf_buf,
292 ROUND_BYTES_TO_WORD(bcf->size));
293
294 out_free:
295 devm_kfree(mors->dev, bcf_buf);
296 return ret;
297 }
298
mm81x_fw_clear_aon(struct mm81x * mors)299 static void mm81x_fw_clear_aon(struct mm81x *mors)
300 {
301 int idx;
302 u8 count = MM81X_REG_AON_COUNT(mors);
303 u32 address = MM81X_REG_AON_ADDR(mors);
304
305 for (idx = 0; idx < count; idx++, address += 4) {
306 if (mors->bus_type == MM81X_BUS_TYPE_USB && idx == 0)
307 /* Keep the USB power domain enabled in AON. */
308 mm81x_reg32_write(mors, address,
309 MM81X_REG_AON_USB_RESET(mors));
310 else
311 /* clear AON */
312 mm81x_reg32_write(mors, address, 0x0);
313 }
314
315 mm81x_hw_toggle_aon_latch(mors);
316 }
317
mm81x_fw_trigger(struct mm81x * mors)318 static void mm81x_fw_trigger(struct mm81x *mors)
319 {
320 const unsigned int wait_after_msi_trigger_ms = 1;
321
322 mm81x_claim_bus(mors);
323 /*
324 * If not coming from a full reset, some AON flags may be latched.
325 * Make sure to clear any hanging AON bits (can affect booting).
326 */
327 mm81x_fw_clear_aon(mors);
328
329 if (MM81X_REG_CLK_CTRL(mors))
330 mm81x_reg32_write(mors, MM81X_REG_CLK_CTRL(mors),
331 MM81X_REG_CLK_CTRL_VALUE(mors));
332
333 mm81x_reg32_write(mors, MM81X_REG_MSI(mors),
334 MM81X_REG_MSI_HOST_INT(mors));
335 mm81x_release_bus(mors);
336
337 /* Give the chip a chance to boot */
338 mdelay(wait_after_msi_trigger_ms);
339 }
340
mm81x_fw_verify_magic(struct mm81x * mors)341 static int mm81x_fw_verify_magic(struct mm81x *mors)
342 {
343 int ret = 0;
344 int magic = ~MM81X_REG_HOST_MAGIC_VALUE(mors);
345
346 mm81x_claim_bus(mors);
347 mm81x_reg32_read(mors,
348 mors->host_table_ptr +
349 offsetof(struct host_table, magic_number),
350 &magic);
351
352 if (magic != MM81X_REG_HOST_MAGIC_VALUE(mors)) {
353 dev_err(mors->dev, "FW magic mismatch 0x%08x:0x%08x",
354 MM81X_REG_HOST_MAGIC_VALUE(mors), magic);
355 ret = -EIO;
356 }
357
358 mm81x_release_bus(mors);
359 return ret;
360 }
361
mm81x_fw_get_flags(struct mm81x * mors)362 static int mm81x_fw_get_flags(struct mm81x *mors)
363 {
364 int ret = 0;
365 int fw_flags = 0;
366
367 mm81x_claim_bus(mors);
368 ret = mm81x_reg32_read(mors,
369 mors->host_table_ptr +
370 offsetof(struct host_table, fw_flags),
371 &fw_flags);
372 mors->fw_flags = fw_flags;
373 mm81x_release_bus(mors);
374
375 return ret;
376 }
377
mm81x_fw_check_compatibility(struct mm81x * mors)378 static int mm81x_fw_check_compatibility(struct mm81x *mors)
379 {
380 int ret = 0;
381 u32 fw_version;
382 u32 major;
383 u32 minor;
384 u32 patch;
385
386 mm81x_claim_bus(mors);
387 ret = mm81x_reg32_read(mors,
388 mors->host_table_ptr +
389 offsetof(struct host_table,
390 fw_version_number),
391 &fw_version);
392 mm81x_release_bus(mors);
393
394 major = MM81X_SEMVER_GET_MAJOR(fw_version);
395 minor = MM81X_SEMVER_GET_MINOR(fw_version);
396 patch = MM81X_SEMVER_GET_PATCH(fw_version);
397
398 /* Firmware on device must match the firmware file we requested */
399 if (ret == 0 && major != mors->fw_major) {
400 dev_err(mors->dev,
401 "Incompatible FW version: (Requested) v%u, (Chip) %d.%d.%d\n",
402 mors->fw_major, major, minor, patch);
403 ret = -EPERM;
404 } else if (ret == 0 && major != HOST_CMD_SEMVER_MAJOR) {
405 dev_warn(
406 mors->dev,
407 "Running FW v%d.%d.%d, driver supports up to v%d, some features might not be supported",
408 major, minor, patch, HOST_CMD_SEMVER_MAJOR);
409 } else if (ret == 0 && minor != HOST_CMD_SEMVER_MINOR) {
410 dev_warn(
411 mors->dev,
412 "FW version mismatch, some features might not be supported: (Driver) %d.%d.%d, (Chip) %d.%d.%d",
413 HOST_CMD_SEMVER_MAJOR, HOST_CMD_SEMVER_MINOR,
414 HOST_CMD_SEMVER_PATCH, major, minor, patch);
415 }
416
417 return ret;
418 }
419
mm81x_fw_invalidate_host_ptr(struct mm81x * mors)420 static int mm81x_fw_invalidate_host_ptr(struct mm81x *mors)
421 {
422 int ret;
423
424 mors->host_table_ptr = 0;
425 mm81x_claim_bus(mors);
426 ret = mm81x_reg32_write(mors, MM81X_REG_HOST_MANIFEST_PTR(mors), 0);
427 mm81x_release_bus(mors);
428 return ret;
429 }
430
mm81x_fw_get_host_table_ptr(struct mm81x * mors)431 static int mm81x_fw_get_host_table_ptr(struct mm81x *mors)
432 {
433 int ret, err;
434
435 mm81x_claim_bus(mors);
436 ret = read_poll_timeout(mm81x_reg32_read, err,
437 err || mors->host_table_ptr,
438 HOST_TABLE_PTR_POLL_PERIOD_US,
439 HOST_TABLE_PTR_POLL_TIMEOUT_US, false, mors,
440 MM81X_REG_HOST_MANIFEST_PTR(mors),
441 &mors->host_table_ptr);
442 mm81x_release_bus(mors);
443
444 return ret ? ret : err;
445 }
446
mm81x_fw_read_ext_host_table(struct mm81x * mors,struct ext_host_tbl ** ext_host_table)447 static int mm81x_fw_read_ext_host_table(struct mm81x *mors,
448 struct ext_host_tbl **ext_host_table)
449 {
450 int ret = 0;
451 u32 host_tbl_ptr = mors->host_table_ptr;
452 u32 ext_host_tbl_ptr;
453 u32 ext_host_tbl_ptr_addr =
454 host_tbl_ptr + offsetof(struct host_table, ext_host_tbl_addr);
455 u32 ext_host_tbl_len;
456 u32 ext_host_tbl_len_ptr_addr;
457 struct ext_host_tbl *host_tbl = NULL;
458
459 mm81x_claim_bus(mors);
460 ret = mm81x_reg32_read(mors, ext_host_tbl_ptr_addr, &ext_host_tbl_ptr);
461 if (ret)
462 goto exit;
463
464 if (!ext_host_tbl_ptr) {
465 ret = -ENXIO;
466 goto exit;
467 }
468
469 ext_host_tbl_len_ptr_addr =
470 ext_host_tbl_ptr +
471 offsetof(struct ext_host_tbl, ext_host_tbl_length);
472
473 ret = mm81x_reg32_read(mors, ext_host_tbl_len_ptr_addr,
474 &ext_host_tbl_len);
475 if (ret)
476 goto exit;
477
478 ext_host_tbl_len = ROUND_BYTES_TO_WORD(ext_host_tbl_len);
479 if (WARN_ON(ext_host_tbl_len == 0 || ext_host_tbl_len > INT_MAX)) {
480 ret = -EINVAL;
481 goto exit;
482 }
483
484 host_tbl = kmalloc(ext_host_tbl_len, GFP_KERNEL);
485 if (!host_tbl) {
486 ret = -ENOMEM;
487 goto exit;
488 }
489
490 ret = mm81x_dm_read(mors, ext_host_tbl_ptr, (u8 *)host_tbl,
491 (int)ext_host_tbl_len);
492 if (ret)
493 goto exit;
494
495 mm81x_release_bus(mors);
496 *ext_host_table = host_tbl;
497 return ret;
498
499 exit:
500 mm81x_release_bus(mors);
501 kfree(host_tbl);
502 return ret;
503 }
504
mm81x_fw_update_capabilities(struct mm81x * mors,struct ext_host_tbl_s1g_caps * caps)505 static void mm81x_fw_update_capabilities(struct mm81x *mors,
506 struct ext_host_tbl_s1g_caps *caps)
507 {
508 int i;
509
510 for (i = 0; i < FW_CAPABILITIES_FLAGS_WIDTH; i++) {
511 mors->fw_caps.flags[i] = le32_to_cpu(caps->flags[i]);
512 dev_dbg(mors->dev, "Firmware Manifest Flags%d: 0x%x", i,
513 le32_to_cpu(caps->flags[i]));
514 }
515 mors->fw_caps.ampdu_mss = caps->ampdu_mss;
516 mors->fw_caps.mm81x_mmss_offset = caps->mm81x_mmss_offset;
517 mors->fw_caps.beamformee_sts_capability =
518 caps->beamformee_sts_capability;
519 mors->fw_caps.maximum_ampdu_length_exponent =
520 caps->maximum_ampdu_length;
521 mors->fw_caps.number_sounding_dimensions =
522 caps->number_sounding_dimensions;
523
524 dev_dbg(mors->dev, "\tAMPDU Minimum start spacing: %u",
525 caps->ampdu_mss);
526 dev_dbg(mors->dev, "\tMorse Minimum Start Spacing offset: %u",
527 caps->mm81x_mmss_offset);
528 dev_dbg(mors->dev, "\tBeamformee STS Capability: %u",
529 caps->beamformee_sts_capability);
530 dev_dbg(mors->dev, "\tNumber of Sounding Dimensions: %u",
531 caps->number_sounding_dimensions);
532 dev_dbg(mors->dev, "\tMaximum AMPDU Length Exponent: %u",
533 caps->maximum_ampdu_length);
534 }
535
mm81x_fw_update_validate_skb_checksum(struct mm81x * mors,struct ext_host_tbl_insert_skb_checksum * validate_checksum)536 static void mm81x_fw_update_validate_skb_checksum(
537 struct mm81x *mors,
538 struct ext_host_tbl_insert_skb_checksum *validate_checksum)
539 {
540 mors->hif.validate_skb_checksum =
541 validate_checksum->insert_and_validate_checksum;
542 dev_dbg(mors->dev, "Validate checksum inserted by fw %s",
543 str_enabled_disabled(mors->hif.validate_skb_checksum));
544 }
545
mm81x_fw_parse_ext_host_tbl(struct mm81x * mors)546 int mm81x_fw_parse_ext_host_tbl(struct mm81x *mors)
547 {
548 int ret;
549 u8 *head;
550 u8 *end;
551 struct ext_host_tbl *ext_host_table = NULL;
552
553 ret = mm81x_fw_read_ext_host_table(mors, &ext_host_table);
554 if (ret || !ext_host_table)
555 goto exit;
556
557 /* Parse the TLVs */
558 head = ext_host_table->ext_host_table_data_tlvs;
559 end = ((u8 *)ext_host_table) +
560 le32_to_cpu(ext_host_table->ext_host_tbl_length);
561
562 while (head < end) {
563 struct ext_host_tbl_tlv_hdr *hdr =
564 (struct ext_host_tbl_tlv_hdr *)head;
565
566 switch (le16_to_cpu(hdr->tag)) {
567 case MM81X_FW_HOST_TABLE_TAG_S1G_CAPABILITIES:
568 mm81x_fw_update_capabilities(
569 mors, (struct ext_host_tbl_s1g_caps *)hdr);
570 break;
571
572 case MM81X_FW_HOST_TABLE_TAG_INSERT_SKB_CHECKSUM:
573 mm81x_fw_update_validate_skb_checksum(
574 mors,
575 (struct ext_host_tbl_insert_skb_checksum *)hdr);
576 break;
577
578 case MM81X_FW_HOST_TABLE_TAG_YAPS_TABLE:
579 mm81x_yaps_hw_read_table(
580 mors, &((struct ext_host_tbl_yaps_table *)hdr)
581 ->yaps_table);
582 break;
583 default:
584 break;
585 }
586
587 head += le16_to_cpu(hdr->length);
588 if (!hdr->length)
589 break;
590 }
591
592 kfree(ext_host_table);
593 return ret;
594 exit:
595 dev_err(mors->dev, "failed to parse ext host table %d", ret);
596 return ret;
597 }
598
__mm81x_fw_flash(struct mm81x * mors,const struct firmware * fw,const struct firmware * bcf,bool reset)599 static int __mm81x_fw_flash(struct mm81x *mors, const struct firmware *fw,
600 const struct firmware *bcf, bool reset)
601 {
602 int ret;
603
604 if (reset || !mors->chip_was_reset) {
605 ret = mm81x_hw_digital_reset(mors);
606 if (ret)
607 return ret;
608 }
609
610 mm81x_hw_pre_firmware_ndr_hook(mors);
611
612 ret = mm81x_fw_invalidate_host_ptr(mors);
613 if (ret)
614 return ret;
615
616 ret = mm81x_fw_load_fw(mors, fw);
617 if (ret)
618 return ret;
619
620 ret = mm81x_fw_load_bcf(mors, bcf, mors->bcf_address);
621 if (ret)
622 return ret;
623
624 mm81x_fw_trigger(mors);
625 mm81x_hw_post_firmware_ndr_hook(mors);
626
627 ret = mm81x_fw_get_host_table_ptr(mors);
628 if (ret)
629 return ret;
630
631 ret = mm81x_fw_verify_magic(mors);
632 if (ret)
633 return ret;
634
635 return mm81x_fw_check_compatibility(mors);
636 }
637
mm81x_fw_flash(struct mm81x * mors,const struct firmware * fw,const struct firmware * bcf,bool reset)638 static int mm81x_fw_flash(struct mm81x *mors, const struct firmware *fw,
639 const struct firmware *bcf, bool reset)
640 {
641 int ret;
642 int retries = FW_FLASH_ATTEMPT_COUNT;
643
644 while (retries--) {
645 ret = __mm81x_fw_flash(mors, fw, bcf, reset);
646 if (!ret)
647 return 0;
648
649 mors->chip_was_reset = false;
650 }
651
652 return ret;
653 }
654
binary_crc(const struct firmware * fw)655 static uint32_t binary_crc(const struct firmware *fw)
656 {
657 return ~crc32_le(~0, (unsigned char const *)fw->data, fw->size) &
658 0xffffffff;
659 }
660
mm81x_fw_request(struct mm81x * mors,const struct firmware ** fw)661 static int mm81x_fw_request(struct mm81x *mors, const struct firmware **fw)
662 {
663 int ret = -ENOENT;
664 int ver;
665 char *fw_path;
666
667 for (ver = MM81X_FW_VER_MAX; ver >= MM81X_FW_VER_MIN; ver--) {
668 fw_path = mm81x_core_get_fw_path(mors->chip_id, ver);
669 if (!fw_path)
670 return -ENOMEM;
671
672 ret = firmware_request_nowarn(fw, fw_path, mors->dev);
673 if (!ret) {
674 dev_info(
675 mors->dev,
676 "Loaded firmware from %s, size %zu, crc32 0x%08x\n",
677 fw_path, (*fw)->size, binary_crc(*fw));
678 mors->fw_major = ver;
679 }
680
681 kfree(fw_path);
682 if (!ret)
683 return 0;
684 }
685
686 dev_err(mors->dev, "no firmware found (tried v%d down to v%d): %d\n",
687 MM81X_FW_VER_MAX, MM81X_FW_VER_MIN, ret);
688 return ret;
689 }
690
mm81x_fw_init(struct mm81x * mors,bool reset)691 int mm81x_fw_init(struct mm81x *mors, bool reset)
692 {
693 int ret;
694 int board_id;
695 char *bcf_path = NULL;
696 const struct firmware *fw = NULL;
697 const struct firmware *bcf = NULL;
698
699 board_id = mm81x_hw_otp_get_board_type(mors);
700
701 if (!mm81x_hw_otp_valid_board_type(board_id)) {
702 dev_err(mors->dev,
703 "OTP not set, unable to determine BCF to use");
704 ret = -EINVAL;
705 goto out;
706 }
707
708 dev_dbg(mors->dev, "Using board type 0x%04x from OTP", board_id);
709
710 ret = mm81x_fw_request(mors, &fw);
711 if (ret)
712 goto out;
713
714 bcf_path = kasprintf(GFP_KERNEL,
715 MM81X_FW_DIR
716 "/v%u/bcf_boardtype_%04x" MM81X_FW_EXT,
717 mors->fw_major, board_id);
718 if (!bcf_path) {
719 ret = -ENOMEM;
720 goto out;
721 }
722
723 ret = request_firmware(&bcf, bcf_path, mors->dev);
724 if (ret) {
725 if (ret == -ENOENT)
726 dev_err(mors->dev, "BCF %s not found\n", bcf_path);
727 goto out;
728 }
729
730 dev_info(mors->dev, "Loaded BCF from %s, size %zu, crc32 0x%08x\n",
731 bcf_path, bcf->size, binary_crc(bcf));
732
733 ret = mm81x_fw_flash(mors, fw, bcf, reset);
734 if (ret) {
735 dev_err(mors->dev, "failed to flash firmware: %d", ret);
736 goto out;
737 }
738
739 ret = mm81x_fw_get_flags(mors);
740
741 out:
742 release_firmware(fw);
743 release_firmware(bcf);
744 kfree(bcf_path);
745
746 if (ret)
747 dev_err(mors->dev, "failed to init firmware: %d", ret);
748 else
749 dev_dbg(mors->dev, "firmware initialised");
750
751 return ret;
752 }
753