xref: /linux/drivers/net/wireless/morsemicro/mm81x/fw.c (revision 91ec2035134982b98fab0609a9fd8480e8217dc1)
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