1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * This file is part of wl1271 |
4 | * |
5 | * Copyright (C) 2008-2010 Nokia Corporation |
6 | * |
7 | * Contact: Luciano Coelho <luciano.coelho@nokia.com> |
8 | */ |
9 | |
10 | #include <linux/slab.h> |
11 | #include <linux/export.h> |
12 | |
13 | #include "debug.h" |
14 | #include "acx.h" |
15 | #include "boot.h" |
16 | #include "io.h" |
17 | #include "event.h" |
18 | #include "rx.h" |
19 | #include "hw_ops.h" |
20 | |
21 | static int wl1271_boot_set_ecpu_ctrl(struct wl1271 *wl, u32 flag) |
22 | { |
23 | u32 cpu_ctrl; |
24 | int ret; |
25 | |
26 | /* 10.5.0 run the firmware (I) */ |
27 | ret = wlcore_read_reg(wl, reg: REG_ECPU_CONTROL, val: &cpu_ctrl); |
28 | if (ret < 0) |
29 | goto out; |
30 | |
31 | /* 10.5.1 run the firmware (II) */ |
32 | cpu_ctrl |= flag; |
33 | ret = wlcore_write_reg(wl, reg: REG_ECPU_CONTROL, val: cpu_ctrl); |
34 | |
35 | out: |
36 | return ret; |
37 | } |
38 | |
39 | static int wlcore_boot_parse_fw_ver(struct wl1271 *wl, |
40 | struct wl1271_static_data *static_data) |
41 | { |
42 | int ret; |
43 | |
44 | strscpy(wl->chip.fw_ver_str, static_data->fw_version, |
45 | sizeof(wl->chip.fw_ver_str)); |
46 | |
47 | ret = sscanf(wl->chip.fw_ver_str + 4, "%u.%u.%u.%u.%u" , |
48 | &wl->chip.fw_ver[0], &wl->chip.fw_ver[1], |
49 | &wl->chip.fw_ver[2], &wl->chip.fw_ver[3], |
50 | &wl->chip.fw_ver[4]); |
51 | |
52 | if (ret != 5) { |
53 | wl1271_warning("fw version incorrect value" ); |
54 | memset(wl->chip.fw_ver, 0, sizeof(wl->chip.fw_ver)); |
55 | ret = -EINVAL; |
56 | goto out; |
57 | } |
58 | |
59 | ret = wlcore_identify_fw(wl); |
60 | if (ret < 0) |
61 | goto out; |
62 | out: |
63 | return ret; |
64 | } |
65 | |
66 | static int wlcore_validate_fw_ver(struct wl1271 *wl) |
67 | { |
68 | unsigned int *fw_ver = wl->chip.fw_ver; |
69 | unsigned int *min_ver = (wl->fw_type == WL12XX_FW_TYPE_MULTI) ? |
70 | wl->min_mr_fw_ver : wl->min_sr_fw_ver; |
71 | char min_fw_str[32] = "" ; |
72 | int off = 0; |
73 | int i; |
74 | |
75 | /* the chip must be exactly equal */ |
76 | if ((min_ver[FW_VER_CHIP] != WLCORE_FW_VER_IGNORE) && |
77 | (min_ver[FW_VER_CHIP] != fw_ver[FW_VER_CHIP])) |
78 | goto fail; |
79 | |
80 | /* the firmware type must be equal */ |
81 | if ((min_ver[FW_VER_IF_TYPE] != WLCORE_FW_VER_IGNORE) && |
82 | (min_ver[FW_VER_IF_TYPE] != fw_ver[FW_VER_IF_TYPE])) |
83 | goto fail; |
84 | |
85 | /* the project number must be equal */ |
86 | if ((min_ver[FW_VER_SUBTYPE] != WLCORE_FW_VER_IGNORE) && |
87 | (min_ver[FW_VER_SUBTYPE] != fw_ver[FW_VER_SUBTYPE])) |
88 | goto fail; |
89 | |
90 | /* the API version must be greater or equal */ |
91 | if ((min_ver[FW_VER_MAJOR] != WLCORE_FW_VER_IGNORE) && |
92 | (min_ver[FW_VER_MAJOR] > fw_ver[FW_VER_MAJOR])) |
93 | goto fail; |
94 | |
95 | /* if the API version is equal... */ |
96 | if (((min_ver[FW_VER_MAJOR] == WLCORE_FW_VER_IGNORE) || |
97 | (min_ver[FW_VER_MAJOR] == fw_ver[FW_VER_MAJOR])) && |
98 | /* ...the minor must be greater or equal */ |
99 | ((min_ver[FW_VER_MINOR] != WLCORE_FW_VER_IGNORE) && |
100 | (min_ver[FW_VER_MINOR] > fw_ver[FW_VER_MINOR]))) |
101 | goto fail; |
102 | |
103 | return 0; |
104 | |
105 | fail: |
106 | for (i = 0; i < NUM_FW_VER && off < sizeof(min_fw_str); i++) |
107 | if (min_ver[i] == WLCORE_FW_VER_IGNORE) |
108 | off += snprintf(buf: min_fw_str + off, |
109 | size: sizeof(min_fw_str) - off, |
110 | fmt: "*." ); |
111 | else |
112 | off += snprintf(buf: min_fw_str + off, |
113 | size: sizeof(min_fw_str) - off, |
114 | fmt: "%u." , min_ver[i]); |
115 | |
116 | wl1271_error("Your WiFi FW version (%u.%u.%u.%u.%u) is invalid.\n" |
117 | "Please use at least FW %s\n" |
118 | "You can get the latest firmwares at:\n" |
119 | "git://git.ti.com/wilink8-wlan/wl18xx_fw.git" , |
120 | fw_ver[FW_VER_CHIP], fw_ver[FW_VER_IF_TYPE], |
121 | fw_ver[FW_VER_MAJOR], fw_ver[FW_VER_SUBTYPE], |
122 | fw_ver[FW_VER_MINOR], min_fw_str); |
123 | return -EINVAL; |
124 | } |
125 | |
126 | static int wlcore_boot_static_data(struct wl1271 *wl) |
127 | { |
128 | struct wl1271_static_data *static_data; |
129 | size_t len = sizeof(*static_data) + wl->static_data_priv_len; |
130 | int ret; |
131 | |
132 | static_data = kmalloc(size: len, GFP_KERNEL); |
133 | if (!static_data) { |
134 | ret = -ENOMEM; |
135 | goto out; |
136 | } |
137 | |
138 | ret = wlcore_read(wl, addr: wl->cmd_box_addr, buf: static_data, len, fixed: false); |
139 | if (ret < 0) |
140 | goto out_free; |
141 | |
142 | ret = wlcore_boot_parse_fw_ver(wl, static_data); |
143 | if (ret < 0) |
144 | goto out_free; |
145 | |
146 | ret = wlcore_validate_fw_ver(wl); |
147 | if (ret < 0) |
148 | goto out_free; |
149 | |
150 | ret = wlcore_handle_static_data(wl, static_data); |
151 | if (ret < 0) |
152 | goto out_free; |
153 | |
154 | out_free: |
155 | kfree(objp: static_data); |
156 | out: |
157 | return ret; |
158 | } |
159 | |
160 | static int wl1271_boot_upload_firmware_chunk(struct wl1271 *wl, void *buf, |
161 | size_t fw_data_len, u32 dest) |
162 | { |
163 | struct wlcore_partition_set partition; |
164 | int addr, chunk_num, partition_limit; |
165 | u8 *p, *chunk; |
166 | int ret; |
167 | |
168 | /* whal_FwCtrl_LoadFwImageSm() */ |
169 | |
170 | wl1271_debug(DEBUG_BOOT, "starting firmware upload" ); |
171 | |
172 | wl1271_debug(DEBUG_BOOT, "fw_data_len %zd chunk_size %d" , |
173 | fw_data_len, CHUNK_SIZE); |
174 | |
175 | if ((fw_data_len % 4) != 0) { |
176 | wl1271_error("firmware length not multiple of four" ); |
177 | return -EIO; |
178 | } |
179 | |
180 | chunk = kmalloc(CHUNK_SIZE, GFP_KERNEL); |
181 | if (!chunk) { |
182 | wl1271_error("allocation for firmware upload chunk failed" ); |
183 | return -ENOMEM; |
184 | } |
185 | |
186 | memcpy(&partition, &wl->ptable[PART_DOWN], sizeof(partition)); |
187 | partition.mem.start = dest; |
188 | ret = wlcore_set_partition(wl, p: &partition); |
189 | if (ret < 0) |
190 | goto out; |
191 | |
192 | /* 10.1 set partition limit and chunk num */ |
193 | chunk_num = 0; |
194 | partition_limit = wl->ptable[PART_DOWN].mem.size; |
195 | |
196 | while (chunk_num < fw_data_len / CHUNK_SIZE) { |
197 | /* 10.2 update partition, if needed */ |
198 | addr = dest + (chunk_num + 2) * CHUNK_SIZE; |
199 | if (addr > partition_limit) { |
200 | addr = dest + chunk_num * CHUNK_SIZE; |
201 | partition_limit = chunk_num * CHUNK_SIZE + |
202 | wl->ptable[PART_DOWN].mem.size; |
203 | partition.mem.start = addr; |
204 | ret = wlcore_set_partition(wl, p: &partition); |
205 | if (ret < 0) |
206 | goto out; |
207 | } |
208 | |
209 | /* 10.3 upload the chunk */ |
210 | addr = dest + chunk_num * CHUNK_SIZE; |
211 | p = buf + chunk_num * CHUNK_SIZE; |
212 | memcpy(chunk, p, CHUNK_SIZE); |
213 | wl1271_debug(DEBUG_BOOT, "uploading fw chunk 0x%p to 0x%x" , |
214 | p, addr); |
215 | ret = wlcore_write(wl, addr, buf: chunk, CHUNK_SIZE, fixed: false); |
216 | if (ret < 0) |
217 | goto out; |
218 | |
219 | chunk_num++; |
220 | } |
221 | |
222 | /* 10.4 upload the last chunk */ |
223 | addr = dest + chunk_num * CHUNK_SIZE; |
224 | p = buf + chunk_num * CHUNK_SIZE; |
225 | memcpy(chunk, p, fw_data_len % CHUNK_SIZE); |
226 | wl1271_debug(DEBUG_BOOT, "uploading fw last chunk (%zd B) 0x%p to 0x%x" , |
227 | fw_data_len % CHUNK_SIZE, p, addr); |
228 | ret = wlcore_write(wl, addr, buf: chunk, len: fw_data_len % CHUNK_SIZE, fixed: false); |
229 | |
230 | out: |
231 | kfree(objp: chunk); |
232 | return ret; |
233 | } |
234 | |
235 | int wlcore_boot_upload_firmware(struct wl1271 *wl) |
236 | { |
237 | u32 chunks, addr, len; |
238 | int ret = 0; |
239 | u8 *fw; |
240 | |
241 | fw = wl->fw; |
242 | chunks = be32_to_cpup(p: (__be32 *) fw); |
243 | fw += sizeof(u32); |
244 | |
245 | wl1271_debug(DEBUG_BOOT, "firmware chunks to be uploaded: %u" , chunks); |
246 | |
247 | while (chunks--) { |
248 | addr = be32_to_cpup(p: (__be32 *) fw); |
249 | fw += sizeof(u32); |
250 | len = be32_to_cpup(p: (__be32 *) fw); |
251 | fw += sizeof(u32); |
252 | |
253 | if (len > 300000) { |
254 | wl1271_info("firmware chunk too long: %u" , len); |
255 | return -EINVAL; |
256 | } |
257 | wl1271_debug(DEBUG_BOOT, "chunk %d addr 0x%x len %u" , |
258 | chunks, addr, len); |
259 | ret = wl1271_boot_upload_firmware_chunk(wl, buf: fw, fw_data_len: len, dest: addr); |
260 | if (ret != 0) |
261 | break; |
262 | fw += len; |
263 | } |
264 | |
265 | return ret; |
266 | } |
267 | EXPORT_SYMBOL_GPL(wlcore_boot_upload_firmware); |
268 | |
269 | int wlcore_boot_upload_nvs(struct wl1271 *wl) |
270 | { |
271 | struct platform_device *pdev = wl->pdev; |
272 | struct wlcore_platdev_data *pdev_data = dev_get_platdata(dev: &pdev->dev); |
273 | const char *nvs_name = "unknown" ; |
274 | size_t nvs_len, burst_len; |
275 | int i; |
276 | u32 dest_addr, val; |
277 | u8 *nvs_ptr, *nvs_aligned; |
278 | int ret; |
279 | |
280 | if (wl->nvs == NULL) { |
281 | wl1271_error("NVS file is needed during boot" ); |
282 | return -ENODEV; |
283 | } |
284 | |
285 | if (pdev_data && pdev_data->family) |
286 | nvs_name = pdev_data->family->nvs_name; |
287 | |
288 | if (wl->quirks & WLCORE_QUIRK_LEGACY_NVS) { |
289 | struct wl1271_nvs_file *nvs = |
290 | (struct wl1271_nvs_file *)wl->nvs; |
291 | /* |
292 | * FIXME: the LEGACY NVS image support (NVS's missing the 5GHz |
293 | * band configurations) can be removed when those NVS files stop |
294 | * floating around. |
295 | */ |
296 | if (wl->nvs_len == sizeof(struct wl1271_nvs_file) || |
297 | wl->nvs_len == WL1271_INI_LEGACY_NVS_FILE_SIZE) { |
298 | if (nvs->general_params.dual_mode_select) |
299 | wl->enable_11a = true; |
300 | } |
301 | |
302 | if (wl->nvs_len != sizeof(struct wl1271_nvs_file) && |
303 | (wl->nvs_len != WL1271_INI_LEGACY_NVS_FILE_SIZE || |
304 | wl->enable_11a)) { |
305 | wl1271_error("%s size is not as expected: %zu != %zu" , |
306 | nvs_name, wl->nvs_len, |
307 | sizeof(struct wl1271_nvs_file)); |
308 | kfree(objp: wl->nvs); |
309 | wl->nvs = NULL; |
310 | wl->nvs_len = 0; |
311 | return -EILSEQ; |
312 | } |
313 | |
314 | /* only the first part of the NVS needs to be uploaded */ |
315 | nvs_len = sizeof(nvs->nvs); |
316 | nvs_ptr = (u8 *) nvs->nvs; |
317 | } else { |
318 | struct wl128x_nvs_file *nvs = (struct wl128x_nvs_file *)wl->nvs; |
319 | |
320 | if (wl->nvs_len == sizeof(struct wl128x_nvs_file)) { |
321 | if (nvs->general_params.dual_mode_select) |
322 | wl->enable_11a = true; |
323 | } else { |
324 | wl1271_error("%s size is not as expected: %zu != %zu" , |
325 | nvs_name, wl->nvs_len, |
326 | sizeof(struct wl128x_nvs_file)); |
327 | kfree(objp: wl->nvs); |
328 | wl->nvs = NULL; |
329 | wl->nvs_len = 0; |
330 | return -EILSEQ; |
331 | } |
332 | |
333 | /* only the first part of the NVS needs to be uploaded */ |
334 | nvs_len = sizeof(nvs->nvs); |
335 | nvs_ptr = (u8 *)nvs->nvs; |
336 | } |
337 | |
338 | /* update current MAC address to NVS */ |
339 | nvs_ptr[11] = wl->addresses[0].addr[0]; |
340 | nvs_ptr[10] = wl->addresses[0].addr[1]; |
341 | nvs_ptr[6] = wl->addresses[0].addr[2]; |
342 | nvs_ptr[5] = wl->addresses[0].addr[3]; |
343 | nvs_ptr[4] = wl->addresses[0].addr[4]; |
344 | nvs_ptr[3] = wl->addresses[0].addr[5]; |
345 | |
346 | /* |
347 | * Layout before the actual NVS tables: |
348 | * 1 byte : burst length. |
349 | * 2 bytes: destination address. |
350 | * n bytes: data to burst copy. |
351 | * |
352 | * This is ended by a 0 length, then the NVS tables. |
353 | */ |
354 | |
355 | /* FIXME: Do we need to check here whether the LSB is 1? */ |
356 | while (nvs_ptr[0]) { |
357 | burst_len = nvs_ptr[0]; |
358 | dest_addr = (nvs_ptr[1] & 0xfe) | ((u32)(nvs_ptr[2] << 8)); |
359 | |
360 | /* |
361 | * Due to our new wl1271_translate_reg_addr function, |
362 | * we need to add the register partition start address |
363 | * to the destination |
364 | */ |
365 | dest_addr += wl->curr_part.reg.start; |
366 | |
367 | /* We move our pointer to the data */ |
368 | nvs_ptr += 3; |
369 | |
370 | for (i = 0; i < burst_len; i++) { |
371 | if (nvs_ptr + 3 >= (u8 *) wl->nvs + nvs_len) |
372 | goto out_badnvs; |
373 | |
374 | val = (nvs_ptr[0] | (nvs_ptr[1] << 8) |
375 | | (nvs_ptr[2] << 16) | (nvs_ptr[3] << 24)); |
376 | |
377 | wl1271_debug(DEBUG_BOOT, |
378 | "nvs burst write 0x%x: 0x%x" , |
379 | dest_addr, val); |
380 | ret = wlcore_write32(wl, addr: dest_addr, val); |
381 | if (ret < 0) |
382 | return ret; |
383 | |
384 | nvs_ptr += 4; |
385 | dest_addr += 4; |
386 | } |
387 | |
388 | if (nvs_ptr >= (u8 *) wl->nvs + nvs_len) |
389 | goto out_badnvs; |
390 | } |
391 | |
392 | /* |
393 | * We've reached the first zero length, the first NVS table |
394 | * is located at an aligned offset which is at least 7 bytes further. |
395 | * NOTE: The wl->nvs->nvs element must be first, in order to |
396 | * simplify the casting, we assume it is at the beginning of |
397 | * the wl->nvs structure. |
398 | */ |
399 | nvs_ptr = (u8 *)wl->nvs + |
400 | ALIGN(nvs_ptr - (u8 *)wl->nvs + 7, 4); |
401 | |
402 | if (nvs_ptr >= (u8 *) wl->nvs + nvs_len) |
403 | goto out_badnvs; |
404 | |
405 | nvs_len -= nvs_ptr - (u8 *)wl->nvs; |
406 | |
407 | /* Now we must set the partition correctly */ |
408 | ret = wlcore_set_partition(wl, p: &wl->ptable[PART_WORK]); |
409 | if (ret < 0) |
410 | return ret; |
411 | |
412 | /* Copy the NVS tables to a new block to ensure alignment */ |
413 | nvs_aligned = kmemdup(p: nvs_ptr, size: nvs_len, GFP_KERNEL); |
414 | if (!nvs_aligned) |
415 | return -ENOMEM; |
416 | |
417 | /* And finally we upload the NVS tables */ |
418 | ret = wlcore_write_data(wl, reg: REG_CMD_MBOX_ADDRESS, buf: nvs_aligned, len: nvs_len, |
419 | fixed: false); |
420 | |
421 | kfree(objp: nvs_aligned); |
422 | return ret; |
423 | |
424 | out_badnvs: |
425 | wl1271_error("nvs data is malformed" ); |
426 | return -EILSEQ; |
427 | } |
428 | EXPORT_SYMBOL_GPL(wlcore_boot_upload_nvs); |
429 | |
430 | int wlcore_boot_run_firmware(struct wl1271 *wl) |
431 | { |
432 | int loop, ret; |
433 | u32 chip_id, intr; |
434 | |
435 | /* Make sure we have the boot partition */ |
436 | ret = wlcore_set_partition(wl, p: &wl->ptable[PART_BOOT]); |
437 | if (ret < 0) |
438 | return ret; |
439 | |
440 | ret = wl1271_boot_set_ecpu_ctrl(wl, ECPU_CONTROL_HALT); |
441 | if (ret < 0) |
442 | return ret; |
443 | |
444 | ret = wlcore_read_reg(wl, reg: REG_CHIP_ID_B, val: &chip_id); |
445 | if (ret < 0) |
446 | return ret; |
447 | |
448 | wl1271_debug(DEBUG_BOOT, "chip id after firmware boot: 0x%x" , chip_id); |
449 | |
450 | if (chip_id != wl->chip.id) { |
451 | wl1271_error("chip id doesn't match after firmware boot" ); |
452 | return -EIO; |
453 | } |
454 | |
455 | /* wait for init to complete */ |
456 | loop = 0; |
457 | while (loop++ < INIT_LOOP) { |
458 | udelay(INIT_LOOP_DELAY); |
459 | ret = wlcore_read_reg(wl, reg: REG_INTERRUPT_NO_CLEAR, val: &intr); |
460 | if (ret < 0) |
461 | return ret; |
462 | |
463 | if (intr == 0xffffffff) { |
464 | wl1271_error("error reading hardware complete " |
465 | "init indication" ); |
466 | return -EIO; |
467 | } |
468 | /* check that ACX_INTR_INIT_COMPLETE is enabled */ |
469 | else if (intr & WL1271_ACX_INTR_INIT_COMPLETE) { |
470 | ret = wlcore_write_reg(wl, reg: REG_INTERRUPT_ACK, |
471 | WL1271_ACX_INTR_INIT_COMPLETE); |
472 | if (ret < 0) |
473 | return ret; |
474 | break; |
475 | } |
476 | } |
477 | |
478 | if (loop > INIT_LOOP) { |
479 | wl1271_error("timeout waiting for the hardware to " |
480 | "complete initialization" ); |
481 | return -EIO; |
482 | } |
483 | |
484 | /* get hardware config command mail box */ |
485 | ret = wlcore_read_reg(wl, reg: REG_COMMAND_MAILBOX_PTR, val: &wl->cmd_box_addr); |
486 | if (ret < 0) |
487 | return ret; |
488 | |
489 | wl1271_debug(DEBUG_MAILBOX, "cmd_box_addr 0x%x" , wl->cmd_box_addr); |
490 | |
491 | /* get hardware config event mail box */ |
492 | ret = wlcore_read_reg(wl, reg: REG_EVENT_MAILBOX_PTR, val: &wl->mbox_ptr[0]); |
493 | if (ret < 0) |
494 | return ret; |
495 | |
496 | wl->mbox_ptr[1] = wl->mbox_ptr[0] + wl->mbox_size; |
497 | |
498 | wl1271_debug(DEBUG_MAILBOX, "MBOX ptrs: 0x%x 0x%x" , |
499 | wl->mbox_ptr[0], wl->mbox_ptr[1]); |
500 | |
501 | ret = wlcore_boot_static_data(wl); |
502 | if (ret < 0) { |
503 | wl1271_error("error getting static data" ); |
504 | return ret; |
505 | } |
506 | |
507 | /* |
508 | * in case of full asynchronous mode the firmware event must be |
509 | * ready to receive event from the command mailbox |
510 | */ |
511 | |
512 | /* unmask required mbox events */ |
513 | ret = wl1271_event_unmask(wl); |
514 | if (ret < 0) { |
515 | wl1271_error("EVENT mask setting failed" ); |
516 | return ret; |
517 | } |
518 | |
519 | /* set the working partition to its "running" mode offset */ |
520 | ret = wlcore_set_partition(wl, p: &wl->ptable[PART_WORK]); |
521 | |
522 | /* firmware startup completed */ |
523 | return ret; |
524 | } |
525 | EXPORT_SYMBOL_GPL(wlcore_boot_run_firmware); |
526 | |