2 * Copyright (c) 2014-2016 Qualcomm Atheros, Inc.
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 /* Algorithmic part of the firmware download.
18 * To be included in the container file providing framework
21 #define wil_err_fw(wil, fmt, arg...) wil_err(wil, "ERR[ FW ]" fmt, ##arg)
22 #define wil_dbg_fw(wil, fmt, arg...) wil_dbg(wil, "DBG[ FW ]" fmt, ##arg)
23 #define wil_hex_dump_fw(prefix_str, prefix_type, rowsize, \
24 groupsize, buf, len, ascii) \
25 print_hex_dump_debug("DBG[ FW ]" prefix_str, \
26 prefix_type, rowsize, \
27 groupsize, buf, len, ascii)
29 #define FW_ADDR_CHECK(ioaddr, val, msg) do { \
30 ioaddr = wmi_buffer(wil, val); \
32 wil_err_fw(wil, "bad " msg ": 0x%08x\n", \
39 * wil_fw_verify - verify firmware file validity
41 * perform various checks for the firmware file header.
42 * records are not validated.
44 * Return file size or negative error
46 static int wil_fw_verify(struct wil6210_priv
*wil
, const u8
*data
, size_t size
)
48 const struct wil_fw_record_head
*hdr
= (const void *)data
;
49 struct wil_fw_record_file_header fh
;
50 const struct wil_fw_record_file_header
*fh_
;
55 wil_err_fw(wil
, "image size not aligned: %zu\n", size
);
58 /* have enough data for the file header? */
59 if (size
< sizeof(*hdr
) + sizeof(fh
)) {
60 wil_err_fw(wil
, "file too short: %zu bytes\n", size
);
64 /* start with the file header? */
65 if (le16_to_cpu(hdr
->type
) != wil_fw_type_file_header
) {
66 wil_err_fw(wil
, "no file header\n");
71 fh_
= (struct wil_fw_record_file_header
*)&hdr
[1];
72 dlen
= le32_to_cpu(fh_
->data_len
);
74 wil_err_fw(wil
, "data length not aligned: %lu\n", (ulong
)dlen
);
78 wil_err_fw(wil
, "file truncated at %zu/%lu\n",
82 if (dlen
< sizeof(*hdr
) + sizeof(fh
)) {
83 wil_err_fw(wil
, "data length too short: %lu\n", (ulong
)dlen
);
88 if (le32_to_cpu(fh_
->signature
) != WIL_FW_SIGNATURE
) {
89 wil_err_fw(wil
, "bad header signature: 0x%08x\n",
90 le32_to_cpu(fh_
->signature
));
95 if (le32_to_cpu(fh_
->version
) > WIL_FW_FMT_VERSION
) {
96 wil_err_fw(wil
, "unsupported header version: %d\n",
97 le32_to_cpu(fh_
->version
));
101 /* checksum. ~crc32(~0, data, size) when fh.crc set to 0*/
105 crc
= crc32_le(~0, (unsigned char const *)hdr
, sizeof(*hdr
));
106 crc
= crc32_le(crc
, (unsigned char const *)&fh
, sizeof(fh
));
107 crc
= crc32_le(crc
, (unsigned char const *)&fh_
[1],
108 dlen
- sizeof(*hdr
) - sizeof(fh
));
111 if (crc
!= le32_to_cpu(fh_
->crc
)) {
112 wil_err_fw(wil
, "checksum mismatch:"
113 " calculated for %lu bytes 0x%08x != 0x%08x\n",
114 (ulong
)dlen
, crc
, le32_to_cpu(fh_
->crc
));
121 static int fw_ignore_section(struct wil6210_priv
*wil
, const void *data
,
127 static int fw_handle_comment(struct wil6210_priv
*wil
, const void *data
,
130 wil_hex_dump_fw("", DUMP_PREFIX_OFFSET
, 16, 1, data
, size
, true);
136 fw_handle_capabilities(struct wil6210_priv
*wil
, const void *data
,
139 const struct wil_fw_record_capabilities
*rec
= data
;
142 if (size
< sizeof(*rec
) ||
143 le32_to_cpu(rec
->magic
) != WIL_FW_CAPABILITIES_MAGIC
)
146 capa_size
= size
- offsetof(struct wil_fw_record_capabilities
,
148 bitmap_zero(wil
->fw_capabilities
, WMI_FW_CAPABILITY_MAX
);
149 memcpy(wil
->fw_capabilities
, rec
->capabilities
,
150 min(sizeof(wil
->fw_capabilities
), capa_size
));
151 wil_hex_dump_fw("CAPA", DUMP_PREFIX_OFFSET
, 16, 1,
152 rec
->capabilities
, capa_size
, false);
156 static int fw_handle_data(struct wil6210_priv
*wil
, const void *data
,
159 const struct wil_fw_record_data
*d
= data
;
161 size_t s
= size
- sizeof(*d
);
163 if (size
< sizeof(*d
) + sizeof(u32
)) {
164 wil_err_fw(wil
, "data record too short: %zu\n", size
);
168 FW_ADDR_CHECK(dst
, d
->addr
, "address");
169 wil_dbg_fw(wil
, "write [0x%08x] <== %zu bytes\n", le32_to_cpu(d
->addr
),
171 wil_memcpy_toio_32(dst
, d
->data
, s
);
172 wmb(); /* finish before processing next record */
177 static int fw_handle_fill(struct wil6210_priv
*wil
, const void *data
,
180 const struct wil_fw_record_fill
*d
= data
;
183 size_t s
= (size_t)le32_to_cpu(d
->size
);
185 if (size
!= sizeof(*d
)) {
186 wil_err_fw(wil
, "bad size for fill record: %zu\n", size
);
190 if (s
< sizeof(u32
)) {
191 wil_err_fw(wil
, "fill size too short: %zu\n", s
);
195 if (s
% sizeof(u32
)) {
196 wil_err_fw(wil
, "fill size not aligned: %zu\n", s
);
200 FW_ADDR_CHECK(dst
, d
->addr
, "address");
202 v
= le32_to_cpu(d
->value
);
203 wil_dbg_fw(wil
, "fill [0x%08x] <== 0x%08x, %zu bytes\n",
204 le32_to_cpu(d
->addr
), v
, s
);
205 wil_memset_toio_32(dst
, v
, s
);
206 wmb(); /* finish before processing next record */
211 static int fw_handle_file_header(struct wil6210_priv
*wil
, const void *data
,
214 const struct wil_fw_record_file_header
*d
= data
;
216 if (size
!= sizeof(*d
)) {
217 wil_err_fw(wil
, "file header length incorrect: %zu\n", size
);
221 wil_dbg_fw(wil
, "new file, ver. %d, %i bytes\n",
222 d
->version
, d
->data_len
);
223 wil_hex_dump_fw("", DUMP_PREFIX_OFFSET
, 16, 1, d
->comment
,
224 sizeof(d
->comment
), true);
226 if (!memcmp(d
->comment
, WIL_FW_VERSION_PREFIX
,
227 WIL_FW_VERSION_PREFIX_LEN
))
228 memcpy(wil
->fw_version
,
229 d
->comment
+ WIL_FW_VERSION_PREFIX_LEN
,
230 min(sizeof(d
->comment
) - WIL_FW_VERSION_PREFIX_LEN
,
231 sizeof(wil
->fw_version
) - 1));
236 static int fw_handle_direct_write(struct wil6210_priv
*wil
, const void *data
,
239 const struct wil_fw_record_direct_write
*d
= data
;
240 const struct wil_fw_data_dwrite
*block
= d
->data
;
243 if (size
% sizeof(*block
)) {
244 wil_err_fw(wil
, "record size not aligned on %zu: %zu\n",
245 sizeof(*block
), size
);
248 n
= size
/ sizeof(*block
);
250 for (i
= 0; i
< n
; i
++) {
252 u32 m
= le32_to_cpu(block
[i
].mask
);
253 u32 v
= le32_to_cpu(block
[i
].value
);
256 FW_ADDR_CHECK(dst
, block
[i
].addr
, "address");
259 y
= (x
& m
) | (v
& ~m
);
260 wil_dbg_fw(wil
, "write [0x%08x] <== 0x%08x "
261 "(old 0x%08x val 0x%08x mask 0x%08x)\n",
262 le32_to_cpu(block
[i
].addr
), y
, x
, v
, m
);
264 wmb(); /* finish before processing next record */
270 static int gw_write(struct wil6210_priv
*wil
, void __iomem
*gwa_addr
,
271 void __iomem
*gwa_cmd
, void __iomem
*gwa_ctl
, u32 gw_cmd
,
277 writel(gw_cmd
, gwa_cmd
);
278 wmb(); /* finish before activate gw */
280 writel(WIL_FW_GW_CTL_RUN
, gwa_ctl
); /* activate gw */
282 udelay(1); /* typical time is few usec */
284 wil_err_fw(wil
, "gw timeout\n");
287 } while (readl(gwa_ctl
) & WIL_FW_GW_CTL_BUSY
); /* gw done? */
292 static int fw_handle_gateway_data(struct wil6210_priv
*wil
, const void *data
,
295 const struct wil_fw_record_gateway_data
*d
= data
;
296 const struct wil_fw_data_gw
*block
= d
->data
;
297 void __iomem
*gwa_addr
;
298 void __iomem
*gwa_val
;
299 void __iomem
*gwa_cmd
;
300 void __iomem
*gwa_ctl
;
304 if (size
< sizeof(*d
) + sizeof(*block
)) {
305 wil_err_fw(wil
, "gateway record too short: %zu\n", size
);
309 if ((size
- sizeof(*d
)) % sizeof(*block
)) {
310 wil_err_fw(wil
, "gateway record data size"
311 " not aligned on %zu: %zu\n",
312 sizeof(*block
), size
- sizeof(*d
));
315 n
= (size
- sizeof(*d
)) / sizeof(*block
);
317 gw_cmd
= le32_to_cpu(d
->command
);
319 wil_dbg_fw(wil
, "gw write record [%3d] blocks, cmd 0x%08x\n",
322 FW_ADDR_CHECK(gwa_addr
, d
->gateway_addr_addr
, "gateway_addr_addr");
323 FW_ADDR_CHECK(gwa_val
, d
->gateway_value_addr
, "gateway_value_addr");
324 FW_ADDR_CHECK(gwa_cmd
, d
->gateway_cmd_addr
, "gateway_cmd_addr");
325 FW_ADDR_CHECK(gwa_ctl
, d
->gateway_ctrl_address
, "gateway_ctrl_address");
327 wil_dbg_fw(wil
, "gw addresses: addr 0x%08x val 0x%08x"
328 " cmd 0x%08x ctl 0x%08x\n",
329 le32_to_cpu(d
->gateway_addr_addr
),
330 le32_to_cpu(d
->gateway_value_addr
),
331 le32_to_cpu(d
->gateway_cmd_addr
),
332 le32_to_cpu(d
->gateway_ctrl_address
));
334 for (i
= 0; i
< n
; i
++) {
336 u32 a
= le32_to_cpu(block
[i
].addr
);
337 u32 v
= le32_to_cpu(block
[i
].value
);
339 wil_dbg_fw(wil
, " gw write[%3d] [0x%08x] <== 0x%08x\n",
343 rc
= gw_write(wil
, gwa_addr
, gwa_cmd
, gwa_ctl
, gw_cmd
, a
);
351 static int fw_handle_gateway_data4(struct wil6210_priv
*wil
, const void *data
,
354 const struct wil_fw_record_gateway_data4
*d
= data
;
355 const struct wil_fw_data_gw4
*block
= d
->data
;
356 void __iomem
*gwa_addr
;
357 void __iomem
*gwa_val
[ARRAY_SIZE(block
->value
)];
358 void __iomem
*gwa_cmd
;
359 void __iomem
*gwa_ctl
;
363 if (size
< sizeof(*d
) + sizeof(*block
)) {
364 wil_err_fw(wil
, "gateway4 record too short: %zu\n", size
);
368 if ((size
- sizeof(*d
)) % sizeof(*block
)) {
369 wil_err_fw(wil
, "gateway4 record data size"
370 " not aligned on %zu: %zu\n",
371 sizeof(*block
), size
- sizeof(*d
));
374 n
= (size
- sizeof(*d
)) / sizeof(*block
);
376 gw_cmd
= le32_to_cpu(d
->command
);
378 wil_dbg_fw(wil
, "gw4 write record [%3d] blocks, cmd 0x%08x\n",
381 FW_ADDR_CHECK(gwa_addr
, d
->gateway_addr_addr
, "gateway_addr_addr");
382 for (k
= 0; k
< ARRAY_SIZE(block
->value
); k
++)
383 FW_ADDR_CHECK(gwa_val
[k
], d
->gateway_value_addr
[k
],
384 "gateway_value_addr");
385 FW_ADDR_CHECK(gwa_cmd
, d
->gateway_cmd_addr
, "gateway_cmd_addr");
386 FW_ADDR_CHECK(gwa_ctl
, d
->gateway_ctrl_address
, "gateway_ctrl_address");
388 wil_dbg_fw(wil
, "gw4 addresses: addr 0x%08x cmd 0x%08x ctl 0x%08x\n",
389 le32_to_cpu(d
->gateway_addr_addr
),
390 le32_to_cpu(d
->gateway_cmd_addr
),
391 le32_to_cpu(d
->gateway_ctrl_address
));
392 wil_hex_dump_fw("val addresses: ", DUMP_PREFIX_NONE
, 16, 4,
393 d
->gateway_value_addr
, sizeof(d
->gateway_value_addr
),
396 for (i
= 0; i
< n
; i
++) {
398 u32 a
= le32_to_cpu(block
[i
].addr
);
399 u32 v
[ARRAY_SIZE(block
->value
)];
401 for (k
= 0; k
< ARRAY_SIZE(block
->value
); k
++)
402 v
[k
] = le32_to_cpu(block
[i
].value
[k
]);
404 wil_dbg_fw(wil
, " gw4 write[%3d] [0x%08x] <==\n", i
, a
);
405 wil_hex_dump_fw(" val ", DUMP_PREFIX_NONE
, 16, 4, v
,
408 for (k
= 0; k
< ARRAY_SIZE(block
->value
); k
++)
409 writel(v
[k
], gwa_val
[k
]);
410 rc
= gw_write(wil
, gwa_addr
, gwa_cmd
, gwa_ctl
, gw_cmd
, a
);
418 static const struct {
420 int (*load_handler
)(struct wil6210_priv
*wil
, const void *data
,
422 int (*parse_handler
)(struct wil6210_priv
*wil
, const void *data
,
424 } wil_fw_handlers
[] = {
425 {wil_fw_type_comment
, fw_handle_comment
, fw_handle_capabilities
},
426 {wil_fw_type_data
, fw_handle_data
, fw_ignore_section
},
427 {wil_fw_type_fill
, fw_handle_fill
, fw_ignore_section
},
428 /* wil_fw_type_action */
429 /* wil_fw_type_verify */
430 {wil_fw_type_file_header
, fw_handle_file_header
,
431 fw_handle_file_header
},
432 {wil_fw_type_direct_write
, fw_handle_direct_write
, fw_ignore_section
},
433 {wil_fw_type_gateway_data
, fw_handle_gateway_data
, fw_ignore_section
},
434 {wil_fw_type_gateway_data4
, fw_handle_gateway_data4
,
438 static int wil_fw_handle_record(struct wil6210_priv
*wil
, int type
,
439 const void *data
, size_t size
, bool load
)
443 for (i
= 0; i
< ARRAY_SIZE(wil_fw_handlers
); i
++)
444 if (wil_fw_handlers
[i
].type
== type
)
446 wil_fw_handlers
[i
].load_handler(
448 wil_fw_handlers
[i
].parse_handler(
451 wil_err_fw(wil
, "unknown record type: %d\n", type
);
456 * wil_fw_process - process section from FW file
457 * if load is true: Load the FW and uCode code and data to the
458 * corresponding device memory regions,
459 * otherwise only parse and look for capabilities
463 static int wil_fw_process(struct wil6210_priv
*wil
, const void *data
,
464 size_t size
, bool load
)
467 const struct wil_fw_record_head
*hdr
;
470 for (hdr
= data
;; hdr
= (const void *)hdr
+ s
, size
-= s
) {
471 if (size
< sizeof(*hdr
))
473 hdr_sz
= le32_to_cpu(hdr
->size
);
474 s
= sizeof(*hdr
) + hdr_sz
;
478 wil_err_fw(wil
, "unaligned record size: %zu\n",
482 rc
= wil_fw_handle_record(wil
, le16_to_cpu(hdr
->type
),
483 &hdr
[1], hdr_sz
, load
);
488 wil_err_fw(wil
, "unprocessed bytes: %zu\n", size
);
489 if (size
>= sizeof(*hdr
)) {
490 wil_err_fw(wil
, "Stop at offset %ld"
491 " record type %d [%zd bytes]\n",
492 (long)((const void *)hdr
- data
),
493 le16_to_cpu(hdr
->type
), hdr_sz
);
502 * wil_request_firmware - Request firmware
504 * Request firmware image from the file
505 * If load is true, load firmware to device, otherwise
506 * only parse and extract capabilities
510 int wil_request_firmware(struct wil6210_priv
*wil
, const char *name
,
514 const struct firmware
*fw
;
518 rc
= request_firmware(&fw
, name
, wil_to_dev(wil
));
520 wil_err_fw(wil
, "Failed to load firmware %s\n", name
);
523 wil_dbg_fw(wil
, "Loading <%s>, %zu bytes\n", name
, fw
->size
);
525 for (sz
= fw
->size
, d
= fw
->data
; sz
; sz
-= rc1
, d
+= rc1
) {
526 rc1
= wil_fw_verify(wil
, d
, sz
);
531 rc
= wil_fw_process(wil
, d
, rc1
, load
);
537 release_firmware(fw
);