// SPDX-License-Identifier: BSD-3-Clause-Clear
/*
* Copyright (c) 2018-2019 The Linux Foundation. All rights reserved.
*/
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/remoteproc.h>
#include <linux/firmware.h>
#include "core.h"
#include "dp_tx.h"
#include "dp_rx.h"
#include "debug.h"
#include "hif.h"
unsigned int ath11k_debug_mask;
EXPORT_SYMBOL(ath11k_debug_mask);
module_param_named(debug_mask, ath11k_debug_mask, uint, 0644);
MODULE_PARM_DESC(debug_mask, "Debugging mask");
static const struct ath11k_hw_params ath11k_hw_params[] = {
{
.hw_rev = ATH11K_HW_IPQ8074,
.name = "ipq8074 hw2.0",
.fw = {
.dir = "IPQ8074/hw2.0",
.board_size = 256 * 1024,
.cal_size = 256 * 1024,
},
.max_radios = 3,
.bdf_addr = 0x4B0C0000,
.hw_ops = &ipq8074_ops,
.ring_mask = &ath11k_hw_ring_mask_ipq8074,
},
{
.name = "qca6390 hw2.0",
.hw_rev = ATH11K_HW_QCA6390_HW20,
.fw = {
.dir = "QCA6390/hw2.0",
.board_size = 256 * 1024,
.cal_size = 256 * 1024,
},
.max_radios = 3,
.bdf_addr = 0x4B0C0000,
.hw_ops = &qca6390_ops,
.ring_mask = &ath11k_hw_ring_mask_ipq8074,
},
};
static int ath11k_core_create_board_name(struct ath11k_base *ab, char *name,
size_t name_len)
{
scnprintf(name, name_len,
"bus=%s,qmi-chip-id=%d,qmi-board-id=%d",
ath11k_bus_str(ab->hif.bus),
ab->qmi.target.chip_id,
ab->qmi.target.board_id);
ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot using board name '%s'\n", name);
return 0;
}
const struct firmware *ath11k_core_firmware_request(struct ath11k_base *ab,
const char *file)
{
const struct firmware *fw;
char path[100];
int ret;
if (file == NULL)
return ERR_PTR(-ENOENT);
ath11k_core_create_firmware_path(ab, file, path, sizeof(path));
ret = firmware_request_nowarn(&fw, path, ab->dev);
if (ret)
return ERR_PTR(ret);
ath11k_dbg(ab, ATH11K_DBG_BOOT, "boot firmware request %s size %zu\n",
path, fw->size);
return fw;
}
void ath11k_core_free_bdf(struct ath11k_base *ab, struct ath11k_board_data *bd)
{
if (!IS_ERR(bd->fw))
release_firmware(bd->fw);
memset(bd, 0, sizeof(*bd));
}
static int ath11k_core_parse_bd_ie_board(struct ath11k_base *ab,
struct ath11k_board_data *bd,
const void *buf, size_t buf_len,
const char *boardname,
int bd_ie_type)
{
const struct ath11k_fw_ie *hdr;
bool name_match_found;
int ret, board_ie_id;
size_t board_ie_len;
const void *board_ie_data;
name_match_found = false;
/* go through ATH11K_BD_IE_BOARD_ elements */
while (buf_len > sizeof(struct ath11k_fw_ie)) {
hdr = buf;
board_ie_id = le32_to_cpu(hdr->id);
board_ie_len = le32_to_cpu(hdr->len);
board_ie_data = hdr->data;
buf_len -= sizeof(*hdr);
buf += sizeof(*hdr);
if (buf_len < ALIGN(board_ie_len, 4)) {
ath11k_err(ab, "invalid ATH11K_BD_IE_BOARD length: %zu < %zu\n",
buf_len, ALIGN(board_ie_len, 4));
ret = -EINVAL;
goto out;
}
switch (board_ie_id) {
case ATH11K_BD_IE_BOARD_NAME:
ath11k_dbg_dump(ab, ATH11K_DBG_BOOT, "board name", "",
board_ie_data, board_ie_len);
if (board_ie_len != strlen(boardname))
break;
ret = memcmp(board_ie_data, boardname, strlen(boardname));
if (ret)
break;
name_match_found = true;
ath11k_dbg(ab, ATH11K_DBG_BOOT,
"boot found match for name '%s'",
boardname);
break;
case ATH11K_BD_IE_BOARD_DATA:
if (!name_match_found)
/* no match found */
break;
ath11k_dbg(ab, ATH11K_DBG_BOOT,
"boot found board data for '%s'", boardname);
bd->data = board_ie_data;
bd->len = board_ie_len;
ret = 0;
goto out;
default:
ath11k_warn(ab, "unknown ATH11K_BD_IE_BOARD found: %d\n",
board_ie_id);
break;
}
/* jump over the padding */
board_ie_len = ALIGN(board_ie_len, 4);
buf_len -= board_ie_len;
buf += board_ie_len;
}
/* no match found */
ret = -ENOENT;
out:
return ret;
}
static int