// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
/* Copyright(c) 2019-2020 Realtek Corporation
*/
#include "cam.h"
#include "coex.h"
#include "debug.h"
#include "fw.h"
#include "mac.h"
#include "phy.h"
#include "reg.h"
static struct sk_buff *rtw89_fw_h2c_alloc_skb(u32 len, bool header)
{
struct sk_buff *skb;
u32 header_len = 0;
if (header)
header_len = H2C_HEADER_LEN;
skb = dev_alloc_skb(len + header_len + 24);
if (!skb)
return NULL;
skb_reserve(skb, header_len + 24);
memset(skb->data, 0, len);
return skb;
}
struct sk_buff *rtw89_fw_h2c_alloc_skb_with_hdr(u32 len)
{
return rtw89_fw_h2c_alloc_skb(len, true);
}
struct sk_buff *rtw89_fw_h2c_alloc_skb_no_hdr(u32 len)
{
return rtw89_fw_h2c_alloc_skb(len, false);
}
static u8 _fw_get_rdy(struct rtw89_dev *rtwdev)
{
u8 val = rtw89_read8(rtwdev, R_AX_WCPU_FW_CTRL);
return FIELD_GET(B_AX_WCPU_FWDL_STS_MASK, val);
}
#define FWDL_WAIT_CNT 400000
int rtw89_fw_check_rdy(struct rtw89_dev *rtwdev)
{
u8 val;
int ret;
ret = read_poll_timeout_atomic(_fw_get_rdy, val,
val == RTW89_FWDL_WCPU_FW_INIT_RDY,
1, FWDL_WAIT_CNT, false, rtwdev);
if (ret) {
switch (val) {
case RTW89_FWDL_CHECKSUM_FAIL:
rtw89_err(rtwdev, "fw checksum fail\n");
return -EINVAL;
case RTW89_FWDL_SECURITY_FAIL:
rtw89_err(rtwdev, "fw security fail\n");
return -EINVAL;
case RTW89_FWDL_CV_NOT_MATCH:
rtw89_err(rtwdev, "fw cv not match\n");
return -EINVAL;
default:
return -EBUSY;
}
}
set_bit(RTW89_FLAG_FW_RDY, rtwdev->flags);
return 0;
}
static int rtw89_fw_hdr_parser(struct rtw89_dev *rtwdev, const u8 *fw, u32 len,
struct rtw89_fw_bin_info *info)
{
struct rtw89_fw_hdr_section_info *section_info;
const u8 *fw_end = fw + len;
const u8 *bin;
u32 i;
if (!info)
return -EINVAL;
info->section_num = GET_FW_HDR_SEC_NUM(fw);
info->hdr_len = RTW89_FW_HDR_SIZE +
info->section_num * RTW89_FW_SECTION_HDR_SIZE;
bin = fw + info->hdr_len;
/* jump to section header */
fw += RTW89_FW_HDR_SIZE;
section_info = info->section_info;
for (i = 0; i < info->section_num; i++) {
section_info->len = GET_FWSECTION_HDR_SEC_SIZE(fw);
if (GET_FWSECTION_HDR_CHECKSUM(fw))
section_info->len += FWDL_SECTION_CHKSUM_LEN;
section_info->redl = GET_FWSECTION_HDR_REDL(fw);
section_info->dladdr =
GET_FWSECTION_HDR_DL_ADDR(fw) & 0x1fffffff;
section_info->addr = bin;
bin += section_info->len;
fw += RTW89_FW_SECTION_HDR_SIZE;
section_info++;
}
if (fw_end != bin) {
rtw89_err(rtwdev, "[ERR]fw bin size\n");
return -EINVAL;
}
return 0;
}
static
int rtw89_mfw_recognize(struct rtw89_dev *rtwdev, enum rtw89_fw_type type,
struct rtw89_fw_suit *fw_suit)
{
struct rtw89_fw_info *fw_info = &rtwdev->fw;
const u8 *mfw = fw_info->firmware->data;
u32 mfw_len = fw_info->firmware->size;
const struct rtw89_mfw_hdr *mfw_hdr = (const struct rtw89_mfw_hdr *)mfw;
const struct r
|