Commit a7feb56f authored by Maya Erez's avatar Maya Erez Committed by Kalle Valo

wil6210: add support for multiple sections in brd file

Current board file loading procedure assumes that the board file
includes only one section.
New board files can include multiple sections.
Add the ability to read multiple addresses and max size from FW
file and load multiple sections from the board file into those
addresses.
Signed-off-by: default avatarMaya Erez <merez@codeaurora.org>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent 0131d185
/* /*
* Copyright (c) 2014,2016 Qualcomm Atheros, Inc. * Copyright (c) 2014,2016 Qualcomm Atheros, Inc.
* Copyright (c) 2018, The Linux Foundation. All rights reserved. * Copyright (c) 2018-2019, The Linux Foundation. All rights reserved.
* *
* Permission to use, copy, modify, and/or distribute this software for any * Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above * purpose with or without fee is hereby granted, provided that the above
...@@ -109,12 +109,17 @@ struct wil_fw_record_concurrency { /* type == wil_fw_type_comment */ ...@@ -109,12 +109,17 @@ struct wil_fw_record_concurrency { /* type == wil_fw_type_comment */
/* brd file info encoded inside a comment record */ /* brd file info encoded inside a comment record */
#define WIL_BRD_FILE_MAGIC (0xabcddcbb) #define WIL_BRD_FILE_MAGIC (0xabcddcbb)
struct brd_info {
__le32 base_addr;
__le32 max_size_bytes;
} __packed;
struct wil_fw_record_brd_file { /* type == wil_fw_type_comment */ struct wil_fw_record_brd_file { /* type == wil_fw_type_comment */
/* identifies brd file record */ /* identifies brd file record */
struct wil_fw_record_comment_hdr hdr; struct wil_fw_record_comment_hdr hdr;
__le32 version; __le32 version;
__le32 base_addr; struct brd_info brd_info[0];
__le32 max_size_bytes;
} __packed; } __packed;
/* perform action /* perform action
......
...@@ -156,17 +156,52 @@ fw_handle_brd_file(struct wil6210_priv *wil, const void *data, ...@@ -156,17 +156,52 @@ fw_handle_brd_file(struct wil6210_priv *wil, const void *data,
size_t size) size_t size)
{ {
const struct wil_fw_record_brd_file *rec = data; const struct wil_fw_record_brd_file *rec = data;
u32 max_num_ent, i, ent_size;
if (size < sizeof(*rec)) { if (size <= offsetof(struct wil_fw_record_brd_file, brd_info)) {
wil_err_fw(wil, "brd_file record too short: %zu\n", size); wil_err(wil, "board record too short, size %zu\n", size);
return 0; return -EINVAL;
}
ent_size = size - offsetof(struct wil_fw_record_brd_file, brd_info);
max_num_ent = ent_size / sizeof(struct brd_info);
if (!max_num_ent) {
wil_err(wil, "brd info entries are missing\n");
return -EINVAL;
} }
wil->brd_file_addr = le32_to_cpu(rec->base_addr); wil->brd_info = kcalloc(max_num_ent, sizeof(struct wil_brd_info),
wil->brd_file_max_size = le32_to_cpu(rec->max_size_bytes); GFP_KERNEL);
if (!wil->brd_info)
return -ENOMEM;
wil_dbg_fw(wil, "brd_file_addr 0x%x, brd_file_max_size %d\n", for (i = 0; i < max_num_ent; i++) {
wil->brd_file_addr, wil->brd_file_max_size); wil->brd_info[i].file_addr =
le32_to_cpu(rec->brd_info[i].base_addr);
wil->brd_info[i].file_max_size =
le32_to_cpu(rec->brd_info[i].max_size_bytes);
if (!wil->brd_info[i].file_addr)
break;
wil_dbg_fw(wil,
"brd info %d: file_addr 0x%x, file_max_size %d\n",
i, wil->brd_info[i].file_addr,
wil->brd_info[i].file_max_size);
}
wil->num_of_brd_entries = i;
if (wil->num_of_brd_entries == 0) {
kfree(wil->brd_info);
wil->brd_info = NULL;
wil_dbg_fw(wil,
"no valid brd info entries, using brd file addr\n");
} else {
wil_dbg_fw(wil, "num of brd info entries %d\n",
wil->num_of_brd_entries);
}
return 0; return 0;
} }
...@@ -634,6 +669,11 @@ int wil_request_firmware(struct wil6210_priv *wil, const char *name, ...@@ -634,6 +669,11 @@ int wil_request_firmware(struct wil6210_priv *wil, const char *name,
} }
wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, fw->size); wil_dbg_fw(wil, "Loading <%s>, %zu bytes\n", name, fw->size);
/* re-initialize board info params */
wil->num_of_brd_entries = 0;
kfree(wil->brd_info);
wil->brd_info = NULL;
for (sz = fw->size, d = fw->data; sz; sz -= rc1, d += rc1) { for (sz = fw->size, d = fw->data; sz; sz -= rc1, d += rc1) {
rc1 = wil_fw_verify(wil, d, sz); rc1 = wil_fw_verify(wil, d, sz);
if (rc1 < 0) { if (rc1 < 0) {
...@@ -662,11 +702,13 @@ static int wil_brd_process(struct wil6210_priv *wil, const void *data, ...@@ -662,11 +702,13 @@ static int wil_brd_process(struct wil6210_priv *wil, const void *data,
{ {
int rc = 0; int rc = 0;
const struct wil_fw_record_head *hdr = data; const struct wil_fw_record_head *hdr = data;
size_t s, hdr_sz; size_t s, hdr_sz = 0;
u16 type; u16 type;
int i = 0;
/* Assuming the board file includes only one header record and one data /* Assuming the board file includes only one file header
* record. Each record starts with wil_fw_record_head. * and one or several data records.
* Each record starts with wil_fw_record_head.
*/ */
if (size < sizeof(*hdr)) if (size < sizeof(*hdr))
return -EINVAL; return -EINVAL;
...@@ -674,40 +716,67 @@ static int wil_brd_process(struct wil6210_priv *wil, const void *data, ...@@ -674,40 +716,67 @@ static int wil_brd_process(struct wil6210_priv *wil, const void *data,
if (s > size) if (s > size)
return -EINVAL; return -EINVAL;
/* Skip the header record and handle the data record */ /* Skip the header record and handle the data records */
hdr = (const void *)hdr + s;
size -= s; size -= s;
if (size < sizeof(*hdr))
return -EINVAL;
hdr_sz = le32_to_cpu(hdr->size);
if (wil->brd_file_max_size && hdr_sz > wil->brd_file_max_size) for (hdr = data + s;; hdr = (const void *)hdr + s, size -= s, i++) {
return -EINVAL; if (size < sizeof(*hdr))
if (sizeof(*hdr) + hdr_sz > size) break;
return -EINVAL;
if (hdr_sz % 4) { if (i >= wil->num_of_brd_entries) {
wil_err_fw(wil, "unaligned record size: %zu\n", wil_err_fw(wil,
hdr_sz); "Too many brd records: %d, num of expected entries %d\n",
return -EINVAL; i, wil->num_of_brd_entries);
} break;
type = le16_to_cpu(hdr->type); }
if (type != wil_fw_type_data) {
wil_err_fw(wil, "invalid record type for board file: %d\n", hdr_sz = le32_to_cpu(hdr->size);
type); s = sizeof(*hdr) + hdr_sz;
return -EINVAL; if (wil->brd_info[i].file_max_size &&
hdr_sz > wil->brd_info[i].file_max_size)
return -EINVAL;
if (sizeof(*hdr) + hdr_sz > size)
return -EINVAL;
if (hdr_sz % 4) {
wil_err_fw(wil, "unaligned record size: %zu\n",
hdr_sz);
return -EINVAL;
}
type = le16_to_cpu(hdr->type);
if (type != wil_fw_type_data) {
wil_err_fw(wil,
"invalid record type for board file: %d\n",
type);
return -EINVAL;
}
if (hdr_sz < sizeof(struct wil_fw_record_data)) {
wil_err_fw(wil, "data record too short: %zu\n", hdr_sz);
return -EINVAL;
}
wil_dbg_fw(wil,
"using info from fw file for record %d: addr[0x%08x], max size %d\n",
i, wil->brd_info[i].file_addr,
wil->brd_info[i].file_max_size);
rc = __fw_handle_data(wil, &hdr[1], hdr_sz,
cpu_to_le32(wil->brd_info[i].file_addr));
if (rc)
return rc;
} }
if (hdr_sz < sizeof(struct wil_fw_record_data)) {
wil_err_fw(wil, "data record too short: %zu\n", hdr_sz); if (size) {
wil_err_fw(wil, "unprocessed bytes: %zu\n", size);
if (size >= sizeof(*hdr)) {
wil_err_fw(wil,
"Stop at offset %ld record type %d [%zd bytes]\n",
(long)((const void *)hdr - data),
le16_to_cpu(hdr->type), hdr_sz);
}
return -EINVAL; return -EINVAL;
} }
wil_dbg_fw(wil, "using addr from fw file: [0x%08x]\n", return 0;
wil->brd_file_addr);
rc = __fw_handle_data(wil, &hdr[1], hdr_sz,
cpu_to_le32(wil->brd_file_addr));
return rc;
} }
/** /**
...@@ -738,7 +807,8 @@ int wil_request_board(struct wil6210_priv *wil, const char *name) ...@@ -738,7 +807,8 @@ int wil_request_board(struct wil6210_priv *wil, const char *name)
rc = dlen; rc = dlen;
goto out; goto out;
} }
/* Process the data record */
/* Process the data records */
rc = wil_brd_process(wil, brd->data, dlen); rc = wil_brd_process(wil, brd->data, dlen);
out: out:
......
...@@ -838,6 +838,7 @@ void wil_priv_deinit(struct wil6210_priv *wil) ...@@ -838,6 +838,7 @@ void wil_priv_deinit(struct wil6210_priv *wil)
wmi_event_flush(wil); wmi_event_flush(wil);
destroy_workqueue(wil->wq_service); destroy_workqueue(wil->wq_service);
destroy_workqueue(wil->wmi_wq); destroy_workqueue(wil->wmi_wq);
kfree(wil->brd_info);
} }
static void wil_shutdown_bl(struct wil6210_priv *wil) static void wil_shutdown_bl(struct wil6210_priv *wil)
...@@ -1709,7 +1710,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw) ...@@ -1709,7 +1710,7 @@ int wil_reset(struct wil6210_priv *wil, bool load_fw)
rc = wil_request_firmware(wil, wil->wil_fw_name, true); rc = wil_request_firmware(wil, wil->wil_fw_name, true);
if (rc) if (rc)
goto out; goto out;
if (wil->brd_file_addr) if (wil->num_of_brd_entries)
rc = wil_request_board(wil, board_file); rc = wil_request_board(wil, board_file);
else else
rc = wil_request_firmware(wil, board_file, true); rc = wil_request_firmware(wil, board_file, true);
......
...@@ -913,6 +913,11 @@ struct wil_fw_stats_global { ...@@ -913,6 +913,11 @@ struct wil_fw_stats_global {
struct wmi_link_stats_global stats; struct wmi_link_stats_global stats;
}; };
struct wil_brd_info {
u32 file_addr;
u32 file_max_size;
};
struct wil6210_priv { struct wil6210_priv {
struct pci_dev *pdev; struct pci_dev *pdev;
u32 bar_size; u32 bar_size;
...@@ -927,8 +932,8 @@ struct wil6210_priv { ...@@ -927,8 +932,8 @@ struct wil6210_priv {
const char *hw_name; const char *hw_name;
const char *wil_fw_name; const char *wil_fw_name;
char *board_file; char *board_file;
u32 brd_file_addr; u32 num_of_brd_entries;
u32 brd_file_max_size; struct wil_brd_info *brd_info;
DECLARE_BITMAP(hw_capa, hw_capa_last); DECLARE_BITMAP(hw_capa, hw_capa_last);
DECLARE_BITMAP(fw_capabilities, WMI_FW_CAPABILITY_MAX); DECLARE_BITMAP(fw_capabilities, WMI_FW_CAPABILITY_MAX);
DECLARE_BITMAP(platform_capa, WIL_PLATFORM_CAPA_MAX); DECLARE_BITMAP(platform_capa, WIL_PLATFORM_CAPA_MAX);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment