172 lines
3.6 KiB
C
172 lines
3.6 KiB
C
// SPDX-License-Identifier: BSD-3-Clause-Clear
|
|
/*
|
|
* Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
|
*/
|
|
|
|
#include "core.h"
|
|
|
|
#include "debug.h"
|
|
|
|
static int ath12k_fw_request_firmware_api_n(struct ath12k_base *ab,
|
|
const char *name)
|
|
{
|
|
size_t magic_len, len, ie_len;
|
|
int ie_id, i, index, bit, ret;
|
|
struct ath12k_fw_ie *hdr;
|
|
const u8 *data;
|
|
__le32 *timestamp;
|
|
|
|
ab->fw.fw = ath12k_core_firmware_request(ab, name);
|
|
if (IS_ERR(ab->fw.fw)) {
|
|
ret = PTR_ERR(ab->fw.fw);
|
|
ath12k_dbg(ab, ATH12K_DBG_BOOT, "failed to load %s: %d\n", name, ret);
|
|
ab->fw.fw = NULL;
|
|
return ret;
|
|
}
|
|
|
|
data = ab->fw.fw->data;
|
|
len = ab->fw.fw->size;
|
|
|
|
/* magic also includes the null byte, check that as well */
|
|
magic_len = strlen(ATH12K_FIRMWARE_MAGIC) + 1;
|
|
|
|
if (len < magic_len) {
|
|
ath12k_err(ab, "firmware image too small to contain magic: %zu\n",
|
|
len);
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
if (memcmp(data, ATH12K_FIRMWARE_MAGIC, magic_len) != 0) {
|
|
ath12k_err(ab, "Invalid firmware magic\n");
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
/* jump over the padding */
|
|
magic_len = ALIGN(magic_len, 4);
|
|
|
|
/* make sure there's space for padding */
|
|
if (magic_len > len) {
|
|
ath12k_err(ab, "No space for padding after magic\n");
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
len -= magic_len;
|
|
data += magic_len;
|
|
|
|
/* loop elements */
|
|
while (len > sizeof(struct ath12k_fw_ie)) {
|
|
hdr = (struct ath12k_fw_ie *)data;
|
|
|
|
ie_id = le32_to_cpu(hdr->id);
|
|
ie_len = le32_to_cpu(hdr->len);
|
|
|
|
len -= sizeof(*hdr);
|
|
data += sizeof(*hdr);
|
|
|
|
if (len < ie_len) {
|
|
ath12k_err(ab, "Invalid length for FW IE %d (%zu < %zu)\n",
|
|
ie_id, len, ie_len);
|
|
ret = -EINVAL;
|
|
goto err;
|
|
}
|
|
|
|
switch (ie_id) {
|
|
case ATH12K_FW_IE_TIMESTAMP:
|
|
if (ie_len != sizeof(u32))
|
|
break;
|
|
|
|
timestamp = (__le32 *)data;
|
|
|
|
ath12k_dbg(ab, ATH12K_DBG_BOOT, "found fw timestamp %d\n",
|
|
le32_to_cpup(timestamp));
|
|
break;
|
|
case ATH12K_FW_IE_FEATURES:
|
|
ath12k_dbg(ab, ATH12K_DBG_BOOT,
|
|
"found firmware features ie (%zd B)\n",
|
|
ie_len);
|
|
|
|
for (i = 0; i < ATH12K_FW_FEATURE_COUNT; i++) {
|
|
index = i / 8;
|
|
bit = i % 8;
|
|
|
|
if (index == ie_len)
|
|
break;
|
|
|
|
if (data[index] & (1 << bit))
|
|
__set_bit(i, ab->fw.fw_features);
|
|
}
|
|
|
|
ath12k_dbg_dump(ab, ATH12K_DBG_BOOT, "features", "",
|
|
ab->fw.fw_features,
|
|
sizeof(ab->fw.fw_features));
|
|
break;
|
|
case ATH12K_FW_IE_AMSS_IMAGE:
|
|
ath12k_dbg(ab, ATH12K_DBG_BOOT,
|
|
"found fw image ie (%zd B)\n",
|
|
ie_len);
|
|
|
|
ab->fw.amss_data = data;
|
|
ab->fw.amss_len = ie_len;
|
|
break;
|
|
case ATH12K_FW_IE_M3_IMAGE:
|
|
ath12k_dbg(ab, ATH12K_DBG_BOOT,
|
|
"found m3 image ie (%zd B)\n",
|
|
ie_len);
|
|
|
|
ab->fw.m3_data = data;
|
|
ab->fw.m3_len = ie_len;
|
|
break;
|
|
case ATH12K_FW_IE_AMSS_DUALMAC_IMAGE:
|
|
ath12k_dbg(ab, ATH12K_DBG_BOOT,
|
|
"found dualmac fw image ie (%zd B)\n",
|
|
ie_len);
|
|
ab->fw.amss_dualmac_data = data;
|
|
ab->fw.amss_dualmac_len = ie_len;
|
|
break;
|
|
default:
|
|
ath12k_warn(ab, "Unknown FW IE: %u\n", ie_id);
|
|
break;
|
|
}
|
|
|
|
/* jump over the padding */
|
|
ie_len = ALIGN(ie_len, 4);
|
|
|
|
/* make sure there's space for padding */
|
|
if (ie_len > len)
|
|
break;
|
|
|
|
len -= ie_len;
|
|
data += ie_len;
|
|
}
|
|
|
|
return 0;
|
|
|
|
err:
|
|
release_firmware(ab->fw.fw);
|
|
ab->fw.fw = NULL;
|
|
return ret;
|
|
}
|
|
|
|
void ath12k_fw_map(struct ath12k_base *ab)
|
|
{
|
|
int ret;
|
|
|
|
ret = ath12k_fw_request_firmware_api_n(ab, ATH12K_FW_API2_FILE);
|
|
if (ret == 0)
|
|
ab->fw.api_version = 2;
|
|
else
|
|
ab->fw.api_version = 1;
|
|
|
|
ath12k_dbg(ab, ATH12K_DBG_BOOT, "using fw api %d\n",
|
|
ab->fw.api_version);
|
|
}
|
|
|
|
void ath12k_fw_unmap(struct ath12k_base *ab)
|
|
{
|
|
release_firmware(ab->fw.fw);
|
|
memset(&ab->fw, 0, sizeof(ab->fw));
|
|
}
|