/******************************************************************************
 *
 * Copyright(c) 2019 Realtek Corporation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 ******************************************************************************/

#include "fwdl.h"

#define FWDL_WAIT_CNT 400000
#define FWDL_SECTION_MAX_NUM 10
#define FWDL_SECTION_CHKSUM_LEN	8
#define FWDL_SECTION_PER_PKT_LEN 2020
#define FWDL_TRY_CNT 3
#define FWDL_DIGEST_SIZE 0x20

struct fwhdr_section_info {
	u8 redl;
	u8 *addr;
	u32 len;
	u32 dladdr;
};

struct fw_bin_info {
	u8 section_num;
	u32 hdr_len;
	u32 git_idx;
	struct fwhdr_section_info section_info[FWDL_SECTION_MAX_NUM];
};

struct hw_info {
	u8 chip;
	u8 cut;
	u8 category;
};

struct fwld_info {
	u32 len;
	u8 *fw;
};

static inline void fwhdr_section_parser(struct fwhdr_section_t *section,
					struct fwhdr_section_info *info)
{
	u32 hdr_val;
	u32 section_len;

	hdr_val = le32_to_cpu(section->dword1);
	section_len = GET_FIELD(hdr_val, SECTION_INFO_SEC_SIZE);
	if (hdr_val & SECTION_INFO_CHECKSUM)
		section_len += FWDL_SECTION_CHKSUM_LEN;
	info->len = section_len;
	info->redl = (hdr_val & SECTION_INFO_REDL) ? 1 : 0;

	info->dladdr = (GET_FIELD(le32_to_cpu(section->dword0),
		SECTION_INFO_SEC_DL_ADDR)) & 0x1FFFFFFF;
}

static inline void fwhdr_hdr_parser(struct fwhdr_hdr_t *hdr,
				    struct fw_bin_info *info)
{
	u32 hdr_val;

	hdr_val = le32_to_cpu(hdr->dword6);
	info->section_num = GET_FIELD(hdr_val, FWHDR_SEC_NUM);
	info->hdr_len = FWHDR_HDR_LEN + info->section_num * FWHDR_SECTION_LEN;

	/* fill HALMAC information */
	hdr_val = le32_to_cpu(hdr->dword7);
	hdr_val = SET_CLR_WORD(hdr_val, FWDL_SECTION_PER_PKT_LEN,
			       FWHDR_FW_PART_SZ);
	hdr->dword7 = cpu_to_le32(hdr_val);

	hdr_val = le32_to_cpu(hdr->dword2);
	info->git_idx = GET_FIELD(hdr_val, FWHDR_COMMITID);
}

static u32 fwhdr_parser(struct mac_ax_adapter *adapter, u8 *fw, u32 len,
			struct fw_bin_info *info)
{
	u32 i;
	u8 *fw_end = fw + len;
	u8 *bin_ptr;
	struct fwhdr_section_info *cur_section_info;

	if (!info) {
		PLTFM_MSG_ERR("[ERR]%s: *info = NULL\n", __func__);
		return MACNPTR;
	} else if (!fw) {
		PLTFM_MSG_ERR("[ERR]%s: *fw = NULL\n", __func__);
		return MACNOITEM;
	} else if (!len) {
		PLTFM_MSG_ERR("[ERR]%s: len = 0\n", __func__);
		return MACBUFSZ;
	}

	fwhdr_hdr_parser((struct fwhdr_hdr_t *)fw, info);
	bin_ptr = fw + info->hdr_len;

	/* jump to section header */
	fw += FWHDR_HDR_LEN;
	cur_section_info = info->section_info;
	for (i = 0; i < info->section_num; i++) {
		fwhdr_section_parser((struct fwhdr_section_t *)fw,
				     cur_section_info);
		cur_section_info->addr = bin_ptr;
		bin_ptr += cur_section_info->len;
		fw += FWHDR_SECTION_LEN;
		cur_section_info++;
	}

	if (fw_end != bin_ptr) {
		PLTFM_MSG_ERR("[ERR]%s: ", __func__);
		PLTFM_MSG_ERR("fw bin size != fw size in fwhdr\n");
		return MACFWBIN;
	}

	return MACSUCCESS;
}

static inline u32 update_fw_ver(struct mac_ax_adapter *adapter,
				struct fwhdr_hdr_t *hdr)
{
	u32 hdr_val;
	struct mac_ax_fw_info *info = &adapter->fw_info;

	hdr_val = le32_to_cpu(hdr->dword1);
	info->major_ver = GET_FIELD(hdr_val, FWHDR_MAJORVER);
	info->minor_ver = GET_FIELD(hdr_val, FWHDR_MINORVER);
	info->sub_ver = GET_FIELD(hdr_val, FWHDR_SUBVERSION);
	info->sub_idx = GET_FIELD(hdr_val, FWHDR_SUBINDEX);

	hdr_val = le32_to_cpu(hdr->dword5);
	info->build_year = GET_FIELD(hdr_val, FWHDR_YEAR);

	hdr_val = le32_to_cpu(hdr->dword4);
	info->build_mon = GET_FIELD(hdr_val, FWHDR_MONTH);
	info->build_date = GET_FIELD(hdr_val, FWHDR_DATE);
	info->build_hour = GET_FIELD(hdr_val, FWHDR_HOUR);
	info->build_min = GET_FIELD(hdr_val, FWHDR_MIN);

	info->h2c_seq = 0;
	info->rec_seq = 0;

	return MACSUCCESS;
}

static u32 __fwhdr_download(struct mac_ax_adapter *adapter,
			    u8 *fw, u32 hdr_len, u8 redl)
{
	u8 *buf;
	u32 ret = 0;
	#if MAC_AX_PHL_H2C
	struct rtw_h2c_pkt *h2cb;
	#else
	struct h2c_buf *h2cb;
	#endif

	h2cb = h2cb_alloc(adapter, H2CB_CLASS_DATA);
	if (!h2cb) {
		PLTFM_MSG_ERR("[ERR]%s: h2cb_alloc fail\n", __func__);
		return MACNPTR;
	}

	buf = h2cb_put(h2cb, hdr_len);
	if (!buf) {
		PLTFM_MSG_ERR("[ERR]%s: h2cb_put fail\n", __func__);
		ret = MACNOBUF;
		goto fail;
	}

	PLTFM_MEMCPY(buf, fw, hdr_len);

	if (redl) {
		ret = h2c_pkt_set_hdr_fwdl(adapter, h2cb,
					   FWCMD_TYPE_H2C, FWCMD_H2C_CAT_MAC,
					   FWCMD_H2C_CL_FWDL,
					   FWCMD_H2C_FUNC_FWHDR_REDL, 0, 0);
	} else {
		ret = h2c_pkt_set_hdr_fwdl(adapter, h2cb,
					   FWCMD_TYPE_H2C, FWCMD_H2C_CAT_MAC,
					   FWCMD_H2C_CL_FWDL,
					   FWCMD_H2C_FUNC_FWHDR_DL, 0, 0);
	}

	if (ret) {
		PLTFM_MSG_ERR("[ERR]%s: set h2c hdr fail\n", __func__);
		goto fail;
	}

	ret = h2c_pkt_build_txd(adapter, h2cb);
	if (ret) {
		PLTFM_MSG_ERR("[ERR]%s: build h2c txd fail\n", __func__);
		goto fail;
	}

	#if MAC_AX_PHL_H2C
	ret = PLTFM_TX(h2cb);
	#else
	ret = PLTFM_TX(h2cb->data, h2cb->len);
	#endif
	if (ret) {
		PLTFM_MSG_ERR("[ERR]%s: PLTFM_TX fail\n", __func__);
		goto fail;
	}

	h2cb_free(adapter, h2cb);

	return MACSUCCESS;
fail:
	h2cb_free(adapter, h2cb);

	PLTFM_MSG_ERR("[ERR]%s ret: %d\n", __func__, ret);

	return ret;
}

#if MAC_AX_PHL_H2C
static u32 __sections_build_txd(struct mac_ax_adapter *adapter,
				struct rtw_h2c_pkt *h2cb)
{
	u8 *buf;
	u32 ret;
	u32 txd_len;
	struct rtw_t_meta_data info = {0};
	struct mac_ax_ops *ops = adapter_to_mac_ops(adapter);

	info.type = RTW_PHL_PKT_TYPE_FWDL;
	info.pktlen = (u16)h2cb->data_len;
	txd_len = ops->txdesc_len(adapter, &info);

	buf = h2cb_push(h2cb, txd_len);
	if (!buf) {
		PLTFM_MSG_ERR("[ERR]%s: h2cb_push fail\n", __func__);
		return MACNPTR;
	}

	ret = ops->build_txdesc(adapter, &info, buf, txd_len);
	if (ret) {
		PLTFM_MSG_ERR("[ERR]%s: ", __func__);
		PLTFM_MSG_ERR("build_txdesc fail\n");
		return ret;
	}

	return MACSUCCESS;
}

static u32 __sections_push(struct rtw_h2c_pkt *h2cb)
{
#define section_push_len 8
	h2cb->vir_data -= section_push_len;
	h2cb->vir_tail -= section_push_len;

	return MACSUCCESS;
}

#else
static u32 __sections_build_txd(struct mac_ax_adapter *adapter,
				struct h2c_buf *h2cb)
{
	u8 *buf;
	u32 ret;
	u32 txd_len;
	struct rtw_t_meta_data info;
	struct mac_ax_ops *ops = adapter_to_mac_ops(adapter);

	info.type = RTW_PHL_PKT_TYPE_FWDL;
	info.pktlen = (u16)h2cb->len;
	txd_len = ops->txdesc_len(adapter, &info);

	buf = h2cb_push(h2cb, txd_len);
	if (!buf) {
		PLTFM_MSG_ERR("[ERR]%s: h2cb_push fail\n", __func__);
		return MACNPTR;
	}

	ret = ops->build_txdesc(adapter, &info, buf, txd_len);
	if (ret) {
		PLTFM_MSG_ERR("[ERR]%s: ", __func__);
		PLTFM_MSG_ERR("mac_build_txdesc fail\n");
		return ret;
	}

	return MACSUCCESS;
}
#endif
static u32 __sections_download(struct mac_ax_adapter *adapter,
			       struct fwhdr_section_info *info)
{
	u8 *section = info->addr;
	u32 residue_len = info->len;
	u32 pkt_len;
	u8 *buf;
	u32 ret = 0;
	#if MAC_AX_PHL_H2C
	struct rtw_h2c_pkt *h2cb;
	#else
	struct h2c_buf *h2cb;
	#endif

	while (residue_len) {
		if (residue_len >= FWDL_SECTION_PER_PKT_LEN)
			pkt_len = FWDL_SECTION_PER_PKT_LEN;
		else
			pkt_len = residue_len;

		h2cb = h2cb_alloc(adapter, H2CB_CLASS_LONG_DATA);
		if (!h2cb) {
			PLTFM_MSG_ERR("[ERR]%s: ", __func__);
			PLTFM_MSG_ERR("h2cb_alloc fail\n");
			return MACNPTR;
		}
		#if MAC_AX_PHL_H2C
		__sections_push(h2cb);
		#endif
		buf = h2cb_put(h2cb, pkt_len);
		if (!buf) {
			PLTFM_MSG_ERR("[ERR]%s: ", __func__);
			PLTFM_MSG_ERR("h2cb_put fail\n");
			ret = MACNOBUF;
			goto fail;
		}

		PLTFM_MEMCPY(buf, section, pkt_len);

		ret = __sections_build_txd(adapter, h2cb);
		if (ret) {
			PLTFM_MSG_ERR("[ERR]%s: ", __func__);
			PLTFM_MSG_ERR("__sections_build_txd fail\n");
			goto fail;
		}
		#if MAC_AX_PHL_H2C
		ret = PLTFM_TX(h2cb);
		#else
		ret = PLTFM_TX(h2cb->data, h2cb->len);
		#endif
		if (ret) {
			PLTFM_MSG_ERR("[ERR]%s: PLTFM_TX fail\n", __func__);
			goto fail;
		}

		h2cb_free(adapter, h2cb);

		section += pkt_len;
		residue_len -= pkt_len;
	}

	return MACSUCCESS;
fail:
	h2cb_free(adapter, h2cb);

	PLTFM_MSG_ERR("[ERR]%s ret: %d\n", __func__, ret);

	return ret;
}

static u32 __write_memory(struct mac_ax_adapter *adapter,
			  u8 *buffer, u32 addr, u32 len)
{
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);
	u8 *content = NULL;
	u32 dl_size;
	u32 target_addr, write_addr;
	u32 seg_size, seg_bytes;
	u32 val32;
	u32 index = 0;
	u32 ret = MACSUCCESS;

	PLTFM_MSG_WARN("%s ind access start\n", __func__);
	PLTFM_MUTEX_LOCK(&adapter->hw_info->ind_access_lock);
	adapter->hw_info->ind_aces_cnt++;

	MAC_REG_W32(R_AX_FILTER_MODEL_ADDR, addr);
	MAC_REG_W32(R_AX_INDIR_ACCESS_ENTRY, 0xAAAAAAAA);
	MAC_REG_W32(R_AX_INDIR_ACCESS_ENTRY + 4, 0xBBBBBBBB);

	val32 = MAC_REG_R32(R_AX_INDIR_ACCESS_ENTRY);
	if (val32 != 0xAAAAAAAA) {
		ret = MACMEMRO;
		goto ind_aces_end;
	}

	val32 = MAC_REG_R32(R_AX_INDIR_ACCESS_ENTRY + 4);
	if (val32 != 0xBBBBBBBB) {
		ret = MACMEMRO;
		goto ind_aces_end;
	}

ind_aces_end:
	adapter->hw_info->ind_aces_cnt--;
	PLTFM_MUTEX_UNLOCK(&adapter->hw_info->ind_access_lock);
	PLTFM_MSG_WARN("%s ind access end\n", __func__);
	if (ret != MACSUCCESS)
		return ret;

	content = (u8 *)PLTFM_MALLOC(len);
	if (!content) {
		PLTFM_MSG_ERR("[ERR]%s: malloc fail\n", __func__);
		return MACNOBUF;
	}

	PLTFM_MEMCPY(content, buffer, len);

	dl_size = len;
	target_addr = addr;

	PLTFM_MSG_WARN("%s ind access trg 0x%X start\n", __func__, target_addr);
	PLTFM_MUTEX_LOCK(&adapter->hw_info->ind_access_lock);
	adapter->hw_info->ind_aces_cnt++;
	while (dl_size != 0) {
		MAC_REG_W32(R_AX_FILTER_MODEL_ADDR, target_addr);
		write_addr = R_AX_INDIR_ACCESS_ENTRY;

		if (dl_size >= ROMDL_SEG_LEN)
			seg_size = ROMDL_SEG_LEN;
		else
			seg_size = dl_size;

		seg_bytes = seg_size;

		while (seg_bytes != 0) {
			val32 = *((u32 *)(content + index));
			MAC_REG_W32(write_addr,
				    cpu_to_le32(val32));

			seg_bytes -= 4;
			write_addr += 4;
			index += 4;
		}

		target_addr += seg_size;
		dl_size -= seg_size;
	}
	adapter->hw_info->ind_aces_cnt--;
	PLTFM_MUTEX_UNLOCK(&adapter->hw_info->ind_access_lock);
	PLTFM_MSG_WARN("%s ind access trg 0x%X end\n", __func__, target_addr);

	PLTFM_FREE(content, len);

	return MACSUCCESS;
}

static u32 fwdl_phase0(struct mac_ax_adapter *adapter)
{
	u32 cnt = FWDL_WAIT_CNT;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);

	if (adapter->sm.fwdl != MAC_AX_FWDL_CPU_ON) {
		PLTFM_MSG_ERR("[ERR]%s: state != CPU_ON\n", __func__);
		return MACPROCERR;
	}

	while (--cnt) {
		if (MAC_REG_R8(R_AX_WCPU_FW_CTRL) & B_AX_H2C_PATH_RDY)
			break;
		PLTFM_DELAY_US(1);
	}

	if (!cnt) {
		PLTFM_MSG_ERR("[ERR]%s: poll 0x1E0[1] = 1 fail\n", __func__);
		return MACPOLLTO;
	}

	adapter->sm.fwdl = MAC_AX_FWDL_H2C_PATH_RDY;

	return MACSUCCESS;
}

static u32 fwdl_phase1(struct mac_ax_adapter *adapter,
		       u8 *fw, u32 hdr_len, u8 redl)
{
	u32 ret;
	u32 cnt = FWDL_WAIT_CNT;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);

	if (adapter->sm.fwdl != MAC_AX_FWDL_H2C_PATH_RDY) {
		PLTFM_MSG_ERR("[ERR]%s: state != H2C_PATH_RDY\n", __func__);
		return MACPROCERR;
	}

	ret = __fwhdr_download(adapter, fw, hdr_len, redl);
	if (ret) {
		PLTFM_MSG_ERR("[ERR]%s: __fwhdr_download fail\n", __func__);
		return ret;
	}

	while (--cnt) {
		if (MAC_REG_R8(R_AX_WCPU_FW_CTRL) & B_AX_FWDL_PATH_RDY)
			break;
		PLTFM_DELAY_US(1);
	}

	if (!cnt) {
		PLTFM_MSG_ERR("[ERR]%s: poll 0x1E0[2] = 1 fail\n", __func__);
		return MACPOLLTO;
	}

	MAC_REG_W32(R_AX_HALT_H2C_CTRL, 0);
	MAC_REG_W32(R_AX_HALT_C2H_CTRL, 0);

	adapter->sm.fwdl = MAC_AX_FWDL_PATH_RDY;

	return MACSUCCESS;
}

static u32 check_fw_rdy(struct mac_ax_adapter *adapter)
{
	u32 val8 = FWDL_INITIAL_STATE;
	u32 cnt = FWDL_WAIT_CNT;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);

	while (--cnt) {
		val8 = GET_FIELD(MAC_REG_R8(R_AX_WCPU_FW_CTRL),
				 B_AX_WCPU_FWDL_STS);
		if (val8 == FWDL_WCPU_FW_INIT_RDY)
			break;
		PLTFM_DELAY_US(1);
	}

	if (!cnt) {
		PLTFM_MSG_ERR("[ERR]%s: poll 0x1E0[7:5] = 7 fail\n", __func__);

		switch (val8) {
		case FWDL_CHECKSUM_FAIL:
			return MACFWCHKSUM;

		case FWDL_SECURITY_FAIL:
			return MACFWSECBOOT;

		case FWDL_CUT_NOT_MATCH:
			return MACFWCUT;

		default:
			return MACPOLLTO;
		}
	}

	adapter->sm.fwdl = MAC_AX_FWDL_INIT_RDY;

	return MACSUCCESS;
}

static u32 fwdl_phase2(struct mac_ax_adapter *adapter, u8 *fw,
		       struct fw_bin_info *info, u8 redl)
{
	u32 ret;
	u32 section_num = info->section_num;
	struct fwhdr_section_info *section_info = info->section_info;

	if (adapter->sm.fwdl != MAC_AX_FWDL_PATH_RDY) {
		PLTFM_MSG_ERR("[ERR]%s: state != FWDL_PATH_RDY\n", __func__);
		return MACPROCERR;
	}

	while (section_num > 0) {
		if (!redl) {
			ret = __sections_download(adapter, section_info);
			if (ret) {
				PLTFM_MSG_ERR("[ERR]%s: ", __func__);
				PLTFM_MSG_ERR("__sections_download fail\n");
				return ret;
			}
		} else {
			if (section_info->redl) {
				ret = __sections_download(adapter,
							  section_info);
				if (ret) {
					PLTFM_MSG_ERR("[ERR]%s: ", __func__);
					PLTFM_MSG_ERR("__sections_download ");
					PLTFM_MSG_ERR("fail\n");
					return ret;
				}
			}
		}
		section_info++;
		section_num--;
	}

	PLTFM_DELAY_MS(5);

	ret = check_fw_rdy(adapter);
	if (ret) {
		PLTFM_MSG_ERR("%s: check_fw_rdy fail\n", __func__);
		return ret;
	}

	return MACSUCCESS;
}

static void fwdl_fail_dump(struct mac_ax_adapter *adapter,
			   struct fw_bin_info *info, u32 ret)
{
	u32 val32, digest_addr, digest;
	u16 index;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);
#if MAC_AX_FEATURE_DBGPKG
	struct mac_ax_ops *mac_ops = adapter_to_mac_ops(adapter);
	struct mac_ax_dbgpkg dbg_val = {0};
	struct mac_ax_dbgpkg_en dbg_en = {0};
#endif

	PLTFM_MSG_ERR("[ERR]fwdl ret = %d\n", ret);
	val32 = MAC_REG_R32(R_AX_WCPU_FW_CTRL);
	PLTFM_MSG_ERR("[ERR]fwdl 0x1E0 = 0x%x\n", val32);

	val32 = MAC_REG_R32(R_AX_BOOT_DBG);
	PLTFM_MSG_ERR("[ERR]fwdl 0x83F0 = 0x%x\n", val32);

	val32 = MAC_REG_R32(R_AX_UDM3);
	PLTFM_MSG_ERR("[ERR]fwdl 0x1FC = 0x%x\n", val32);

	val32 = info->git_idx;
	PLTFM_MSG_ERR("[ERR]fw git idx = 0x%x\n", val32);

	PLTFM_MUTEX_LOCK(&adapter->hw_info->dbg_port_lock);
	adapter->hw_info->dbg_port_cnt++;
	if (adapter->hw_info->dbg_port_cnt != 1) {
		PLTFM_MSG_ERR("[ERR]fwdl fail dump lock cnt %d\n",
			      adapter->hw_info->dbg_port_cnt);
		goto end;
	}

	MAC_REG_W32(R_AX_DBG_CTRL, 0xf200f2);
	val32 = MAC_REG_R32(R_AX_SYS_STATUS1);
	val32 = SET_CLR_WORD(val32, 0x1, B_AX_SEL_0XC0);
	MAC_REG_W32(R_AX_SYS_STATUS1, val32);

	for (index = 0; index < 15; index++) {
		val32 = MAC_REG_R32(R_AX_DBG_PORT_SEL);
		PLTFM_MSG_ERR("[ERR]fw PC = 0x%x\n", val32);
		PLTFM_DELAY_US(10);
	}

	PLTFM_MSG_WARN("%s ind access FW digest start\n", __func__);
	PLTFM_MUTEX_LOCK(&adapter->hw_info->ind_access_lock);
	adapter->hw_info->ind_aces_cnt++;
	digest_addr = (0xB8E17C00 + 64) & ~0xA0000000;

	MAC_REG_W32(R_AX_FILTER_MODEL_ADDR, digest_addr);
	PLTFM_MSG_ERR("IRAM digest:");
	for (index = 0; index < FWDL_DIGEST_SIZE; index += 4) {
		digest = MAC_REG_R32(R_AX_INDIR_ACCESS_ENTRY + index);
		PLTFM_MSG_ERR("0x%x\t", digest);
	}

	PLTFM_MSG_ERR("DMEM digest:");
	for (index = 0; index < FWDL_DIGEST_SIZE; index += 4) {
		digest = MAC_REG_R32(R_AX_INDIR_ACCESS_ENTRY +
				     FWDL_DIGEST_SIZE + index);
		PLTFM_MSG_ERR("0x%x\t", digest);
	}
	adapter->hw_info->ind_aces_cnt--;
	PLTFM_MUTEX_UNLOCK(&adapter->hw_info->ind_access_lock);
	PLTFM_MSG_WARN("%s ind access FW digest end\n", __func__);

	pltfm_dbg_dump(adapter);

	mac_ops->dump_ple_dbg_page(adapter, 0);

end:
#if MAC_AX_FEATURE_DBGPKG
	dbg_en.ss_dbg = 0;
	dbg_en.dle_dbg = 1;
	dbg_en.dmac_dbg = 1;
	dbg_en.cmac_dbg = 1;
	dbg_en.mac_dbg_port = 1;
	dbg_en.plersvd_dbg = 1;
	mac_ops->dbg_status_dump(adapter, &dbg_val, &dbg_en);
#endif
	adapter->hw_info->dbg_port_cnt--;
	PLTFM_MUTEX_UNLOCK(&adapter->hw_info->dbg_port_lock);
}

u32 disable_fw_watchdog(struct mac_ax_adapter *adapter)
{
	u32 val32, ret;

	ret = mac_sram_dbg_write(adapter, R_AX_WDT_CTRL,
				 B_AX_WDT_CTRL_ALL_DIS, CPU_LOCAL_SEL);
	if (ret)
		return ret;

	val32 = mac_sram_dbg_read(adapter, R_AX_WDT_STATUS, CPU_LOCAL_SEL);
	val32 = val32 | B_AX_FS_WDT_INT;
	val32 = val32 & (~B_AX_FS_WDT_INT_MSK);
	ret = mac_sram_dbg_write(adapter, R_AX_WDT_STATUS, val32, CPU_LOCAL_SEL);
	if (ret)
		return ret;

	return MACSUCCESS;
}

u32 mac_fwredl(struct mac_ax_adapter *adapter, u8 *fw, u32 len)
{
	u32 val32;
	u32 ret;
	struct fw_bin_info info;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);

	val32 = MAC_REG_R32(R_AX_WCPU_FW_CTRL);
	val32 &= ~(B_AX_WCPU_FWDL_EN | B_AX_H2C_PATH_RDY | B_AX_FWDL_PATH_RDY);
	val32 = SET_CLR_WORD(val32, FWDL_INITIAL_STATE,
			     B_AX_WCPU_FWDL_STS);

	MAC_REG_W32(R_AX_WCPU_FW_CTRL, val32);

	ret = fwhdr_parser(adapter, fw, len, &info);
	if (ret) {
		PLTFM_MSG_ERR("[ERR]%s: fwhdr_parser fail\n", __func__);
		goto fwdl_err;
	}

	ret = update_fw_ver(adapter, (struct fwhdr_hdr_t *)fw);
	if (ret) {
		PLTFM_MSG_ERR("[ERR]%s: update_fw_ver fail\n", __func__);
		goto fwdl_err;
	}

	adapter->sm.fwdl = MAC_AX_FWDL_H2C_PATH_RDY;

	ret = fwdl_phase1(adapter, fw, info.hdr_len, 1);
	if (ret) {
		PLTFM_MSG_ERR("[ERR]%s: fwdl_phase1 fail\n", __func__);
		goto fwdl_err;
	}

	ret = fwdl_phase2(adapter, fw, &info, 1);
	if (ret) {
		PLTFM_MSG_ERR("[ERR]%s: fwdl_phase2 fail\n", __func__);
		goto fwdl_err;
	}

	mac_scanofld_reset_state(adapter);
	return MACSUCCESS;

fwdl_err:
	fwdl_fail_dump(adapter, &info, ret);

	return ret;
}

u32 mac_fwdl(struct mac_ax_adapter *adapter, u8 *fw, u32 len)
{
	u8 retry_cnt;
	u32 ret;
	struct fw_bin_info info;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);

	ret = 0;
	retry_cnt = 0;
	MAC_REG_W32(R_AX_UDM1, 0);
	// FWDL retry, for 025 temp workaround
	while (retry_cnt < FWDL_TRY_CNT) {
		if (!fw) {
			PLTFM_MSG_ERR("[ERR]%s: no fw\n", __func__);
			ret = MACNOFW;
			PLTFM_MEMSET(&info, 0, sizeof(struct fw_bin_info));
			goto fwdl_err;
		}

		ret = fwhdr_parser(adapter, fw, len, &info);
		if (ret) {
			PLTFM_MSG_ERR("[ERR]%s: fwhdr_parser fail\n", __func__);
			goto fwdl_err;
		}

		ret = update_fw_ver(adapter, (struct fwhdr_hdr_t *)fw);
		if (ret)
			goto fwdl_err;

		ret = fwdl_phase0(adapter);
		if (ret) {
			PLTFM_MSG_ERR("[ERR]%s: fwdl_phase0 fail\n", __func__);
			goto fwdl_err;
		}

		ret = fwdl_phase1(adapter, fw, info.hdr_len, 0);
		if (ret) {
			PLTFM_MSG_ERR("[ERR]%s: fwdl_phase1 fail\n", __func__);
			goto fwdl_err;
		}

		ret = fwdl_phase2(adapter, fw, &info, 0);
		if (ret) {
			PLTFM_MSG_ERR("[ERR]%s: fwdl_phase2 fail\n", __func__);
			goto fwdl_err;
		}

		return MACSUCCESS;

fwdl_err:
		retry_cnt++;
		PLTFM_MSG_ERR("[ERR]%s: Retry FWDL count %d\n", __func__, retry_cnt);
		// At most retry 2 times
		if (retry_cnt < FWDL_TRY_CNT) {
			ret = mac_disable_cpu(adapter);
			if (ret != MACSUCCESS) {
				PLTFM_MSG_ERR("[ERR]%s: mac_disable_cpu fail\n", __func__);
				fwdl_fail_dump(adapter, &info, ret);
				return ret;
			}

			ret = mac_enable_cpu(adapter, AX_BOOT_REASON_PWR_ON, 1);
			if (ret != MACSUCCESS) {
				PLTFM_MSG_ERR("[ERR]%s: mac_enable_cpu fail\n", __func__);
				fwdl_fail_dump(adapter, &info, ret);
				return ret;
			}
			MAC_REG_W32(R_AX_UDM1, retry_cnt);
		} else {
			break;
		}
	}

	fwdl_fail_dump(adapter, &info, ret);

	return ret;
}

u32 mac_enable_cpu(struct mac_ax_adapter *adapter, u8 boot_reason, u8 dlfw)
{
	u32 val32, ret;
	u16 val16;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);

	if (MAC_REG_R32(R_AX_PLATFORM_ENABLE) & B_AX_WCPU_EN)
		return MACCPUSTATE;

	if (adapter->sm.fwdl != MAC_AX_FWDL_IDLE) {
		PLTFM_MSG_ERR("[ERR]%s: state != FWDL_IDLE\n", __func__);
		return MACPROCERR;
	}

	//FW cannot support too much log. Reset R_AX_LDM for FW debug config
	MAC_REG_W32(R_AX_LDM, 0);

	MAC_REG_W32(R_AX_HALT_H2C_CTRL, 0);
	MAC_REG_W32(R_AX_HALT_C2H_CTRL, 0);

	val32 = MAC_REG_R32(R_AX_UDM0);
	val32 &= ~(B_AX_UDM0_DBG_MODE_CTRL | B_AX_UDM0_TRAP_LOOP_CTRL);
	MAC_REG_W32(R_AX_UDM0, val32);

	MAC_REG_W32(R_AX_SYS_CLK_CTRL,
		    MAC_REG_R32(R_AX_SYS_CLK_CTRL) | B_AX_CPU_CLK_EN);

	val32 = MAC_REG_R32(R_AX_WCPU_FW_CTRL);
	val32 &= ~(B_AX_WCPU_FWDL_EN | B_AX_H2C_PATH_RDY | B_AX_FWDL_PATH_RDY);
	val32 = SET_CLR_WORD(val32, FWDL_INITIAL_STATE,
			     B_AX_WCPU_FWDL_STS);

	if (dlfw)
		val32 |= B_AX_WCPU_FWDL_EN;

	MAC_REG_W32(R_AX_WCPU_FW_CTRL, val32);

	val16 = MAC_REG_R16(R_AX_BOOT_REASON);
	val16 = SET_CLR_WORD(val16, boot_reason, B_AX_BOOT_REASON);
	MAC_REG_W16(R_AX_BOOT_REASON, val16);

	val32 = MAC_REG_R32(R_AX_PLATFORM_ENABLE);
	MAC_REG_W32(R_AX_PLATFORM_ENABLE, val32 | B_AX_WCPU_EN);

	adapter->sm.fwdl = MAC_AX_FWDL_CPU_ON;

	if (!dlfw) {
		PLTFM_DELAY_MS(5);

		ret = check_fw_rdy(adapter);
		if (ret) {
			PLTFM_MSG_ERR("[ERR]%s: ", __func__);
			PLTFM_MSG_ERR("check_fw_rdy fail\n");
			return ret;
		}
	}

    // Prevent sequence number in HALMAC and FW mismatching
	reset_lps_seq_num(adapter);

	return MACSUCCESS;
}

u32 mac_disable_cpu(struct mac_ax_adapter *adapter)
{
	u32 val32, ret;

	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);

	adapter->sm.fwdl = MAC_AX_FWDL_IDLE;
	//todo: need to check cpu in safe state before reset CPU

	val32 = MAC_REG_R32(R_AX_PLATFORM_ENABLE);
	MAC_REG_W32(R_AX_PLATFORM_ENABLE, val32 & ~B_AX_WCPU_EN);

	val32 = MAC_REG_R32(R_AX_UDM0);
	val32 &= ~(B_AX_UDM0_DBG_MODE_CTRL | B_AX_UDM0_TRAP_LOOP_CTRL);
	MAC_REG_W32(R_AX_UDM0, val32);

	val32 = MAC_REG_R32(R_AX_WCPU_FW_CTRL);
	val32 &= ~(B_AX_WCPU_FWDL_EN | B_AX_H2C_PATH_RDY | B_AX_FWDL_PATH_RDY);
	MAC_REG_W32(R_AX_WCPU_FW_CTRL, val32);

	val32 = MAC_REG_R32(R_AX_SYS_CLK_CTRL);
	MAC_REG_W32(R_AX_SYS_CLK_CTRL, val32 & ~B_AX_CPU_CLK_EN);

	ret = disable_fw_watchdog(adapter);
	if (ret) {
		PLTFM_MSG_ERR("[ERR]%s: disable_fw_watchdog fail\n", __func__);
		return ret;
	}

	adapter->sm.plat = MAC_AX_PLAT_OFF;

	val32 = MAC_REG_R32(R_AX_PLATFORM_ENABLE);
	MAC_REG_W32(R_AX_PLATFORM_ENABLE, val32 & ~B_AX_PLATFORM_EN);

	val32 = MAC_REG_R32(R_AX_PLATFORM_ENABLE);
	MAC_REG_W32(R_AX_PLATFORM_ENABLE, val32 | B_AX_PLATFORM_EN);

	adapter->sm.plat = MAC_AX_PLAT_ON;

	return MACSUCCESS;
}

u32 mac_romdl(struct mac_ax_adapter *adapter, u8 *ROM, u32 ROM_addr, u32 len)
{
	u8 *content = NULL;
	u32 val32, ret;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);

	ret = mac_disable_cpu(adapter);
	if (ret)
		return ret;

	if (!ROM)
		return MACNOITEM;

	val32 = MAC_REG_R32(R_AX_SEC_CTRL);

	if (val32 & BIT(0)) {
		ret = __write_memory(adapter, ROM, ROM_addr, len);
		if (ret)
			return ret;
	} else {
		PLTFM_MSG_ERR("[ERR]%s: __write_memory fail\n", __func__);
		return MACSECUREON;
	}

	PLTFM_FREE(content, len);

	return MACSUCCESS;
}

u32 mac_ram_boot(struct mac_ax_adapter *adapter, u8 *fw, u32 len)
{
	u32 addr;
	u32 ret, section_num;
	struct fw_bin_info info;
	struct fwhdr_section_info *section_info;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);

	ret = mac_disable_cpu(adapter);
	if (ret)
		goto fwdl_err;

	ret = fwhdr_parser(adapter, fw, len, &info);
	if (ret) {
		PLTFM_MSG_ERR("[ERR]%s: fwhdr_parser fail\n", __func__);
		goto fwdl_err;
	}

	ret = update_fw_ver(adapter, (struct fwhdr_hdr_t *)fw);
	if (ret)
		goto fwdl_err;

	section_num = info.section_num;
	section_info = info.section_info;

	while (section_num > 0) {
		ret = __write_memory(adapter, section_info->addr,
				     section_info->dladdr, section_info->len);
		if (ret)
			goto fwdl_err;

		section_info++;
		section_num--;
	}

	addr = (0xb8003000 + R_AX_CPU_BOOT_ADDR) & 0x1FFFFFFF;
	PLTFM_MSG_WARN("%s ind access 0x%X start\n", __func__, addr);
	PLTFM_MUTEX_LOCK(&adapter->hw_info->ind_access_lock);
	adapter->hw_info->ind_aces_cnt++;
	MAC_REG_W32(R_AX_FILTER_MODEL_ADDR, addr);
	MAC_REG_W32(R_AX_INDIR_ACCESS_ENTRY, (info.section_info[0].dladdr) |
					      0xA0000000);
	adapter->hw_info->ind_aces_cnt--;
	PLTFM_MUTEX_UNLOCK(&adapter->hw_info->ind_access_lock);
	PLTFM_MSG_WARN("%s ind access 0x%X end\n", __func__, addr);

	ret = mac_enable_cpu(adapter, AX_BOOT_REASON_PWR_ON, 0);
	if (ret) {
		PLTFM_MSG_ERR("[ERR]%s: mac_enable_cpu fail\n", __func__);
		goto fwdl_err;
	}

	PLTFM_DELAY_MS(10);

	ret = check_fw_rdy(adapter);
	if (ret) {
		PLTFM_MSG_ERR("[ERR]%s: check_fw_rdy fail\n", __func__);
		goto fwdl_err;
	}
	return MACSUCCESS;

fwdl_err:
	fwdl_fail_dump(adapter, &info, ret);

	return ret;
}

u32 mac_enable_fw(struct mac_ax_adapter *adapter, enum rtw_fw_type cat)
{
	u32 ret = MACSUCCESS;
#if defined(PHL_FEATURE_AP) || defined(PHL_FEATURE_NIC)
	u32 chip_id, cv;
	u32 fw_len = 0;
	u8 *fw = NULL;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);

	chip_id = GET_FIELD(MAC_REG_R32(R_AX_SYS_CHIPINFO), B_AX_HW_ID);
	cv = GET_FIELD(MAC_REG_R32(R_AX_SYS_CFG1), B_AX_CHIP_VER);

	switch (chip_id) {
#ifdef CONFIG_RTL8852A
	case RTL8852A_ID:
		switch (cv) {
#ifdef MAC_FW_8852A_U2
		case FWDL_CBV:
			switch (cat) {
#ifdef PHL_FEATURE_AP
			case RTW_FW_AP:
				fw_len = array_length_8852a_u2_ap;
				fw = array_8852a_u2_ap;
				break;
#endif /*PHL_FEATURE_AP*/
#ifdef PHL_FEATURE_NIC
			case RTW_FW_NIC:
				fw_len = array_length_8852a_u2_nic;
				fw = array_8852a_u2_nic;
				break;
#ifdef CONFIG_WOWLAN
			case RTW_FW_WOWLAN:
				fw_len = array_length_8852a_u2_wowlan;
				fw = array_8852a_u2_wowlan;
				break;
#endif /*CONFIG_WOWLAN*/
#endif /*PHL_FEATURE_NIC*/
			default:
				PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__);
				fw_len = 0;
				fw = 0;
				break;
			}
			break;
#endif /*MAC_FW_8852A_U2*/
#ifdef MAC_FW_8852A_U3
		case FWDL_CCV:
			switch (cat) {
#ifdef PHL_FEATURE_AP
			case RTW_FW_AP:
				fw_len = array_length_8852a_u3_ap;
				fw = array_8852a_u3_ap;
				break;
#endif /*PHL_FEATURE_AP*/
#ifdef PHL_FEATURE_NIC
			case RTW_FW_NIC:
				fw_len = array_length_8852a_u3_nic;
				fw = array_8852a_u3_nic;
				break;
#ifdef CONFIG_WOWLAN
			case RTW_FW_WOWLAN:
				fw_len = array_length_8852a_u3_wowlan;
				fw = array_8852a_u3_wowlan;
				break;
#endif /*CONFIG_WOWLAN*/
#endif /*PHL_FEATURE_NIC*/
			default:
				PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__);
				fw_len = 0;
				fw = 0;
				break;
			}
			break;
#endif /*MAC_FW_8852A_U3*/
		default:
			PLTFM_MSG_ERR("[ERR]%s: invalid cut\n", __func__);
			fw_len = 0;
			fw = 0;
			break;
		}
		break;
#endif /*CONFIG_RTL8852A*/
#ifdef CONFIG_RTL8852B
	case RTL8852B_ID:
		switch (cv) {
#ifdef MAC_FW_8852B_U1
		case FWDL_CAV:
			switch (cat) {
#ifdef PHL_FEATURE_AP
			case RTW_FW_AP:
				fw_len = array_length_8852b_u1_ap;
				fw = array_8852b_u1_ap;
				break;
#endif /*PHL_FEATURE_AP*/
#ifdef PHL_FEATURE_NIC
			case RTW_FW_NIC:
				fw_len = array_length_8852b_u1_nic;
				fw = array_8852b_u1_nic;
				break;
#ifdef CONFIG_WOWLAN
			case RTW_FW_WOWLAN:
				fw_len = array_length_8852b_u1_wowlan;
				fw = array_8852b_u1_wowlan;
				break;
#endif /*CONFIG_WOWLAN*/
#endif /*PHL_FEATURE_NIC*/
			default:
				PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__);
				fw_len = 0;
				fw = 0;
				break;
			}
			break;
#endif /*MAC_FW_8852B_U1*/
#ifdef MAC_FW_8852B_U2
		case FWDL_CBV:
			switch (cat) {
#ifdef PHL_FEATURE_NIC
			case RTW_FW_NIC:
				fw_len = array_length_8852b_u2_nic;
				fw = array_8852b_u2_nic;
				break;
#ifdef CONFIG_WOWLAN
			case RTW_FW_WOWLAN:
				fw_len = array_length_8852b_u2_wowlan;
				fw = array_8852b_u2_wowlan;
				break;
#endif /*CONFIG_WOWLAN*/
#endif /*PHL_FEATURE_NIC*/
			default:
				PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__);
				fw_len = 0;
				fw = 0;
				break;
			}
			break;
#endif /*MAC_FW_8852B_U2*/
		default:
			switch (cat) {
#ifdef PHL_FEATURE_NIC
			case RTW_FW_NIC:
				fw_len = array_length_8852b_u2_nic;
				fw = array_8852b_u2_nic;
				break;
#ifdef CONFIG_WOWLAN
			case RTW_FW_WOWLAN:
				fw_len = array_length_8852b_u2_wowlan;
				fw = array_8852b_u2_wowlan;
				break;
#endif /*CONFIG_WOWLAN*/
#endif /*PHL_FEATURE_NIC*/
			default:
				PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__);
				fw_len = 0;
				fw = 0;
				break;
			}
			break;
		}
		break;
#endif /*CONFIG_RTL8852B*/
#ifdef CONFIG_RTL8852C
	case RTL8852C_ID:
		switch (cv) {
#ifdef MAC_FW_8852C_U1
		case FWDL_CAV:
			switch (cat) {
#ifdef PHL_FEATURE_AP
			case RTW_FW_AP:
				fw_len = array_length_8852c_u1_ap;
				fw = array_8852c_u1_ap;
				break;
#endif /*PHL_FEATURE_AP*/
#ifdef PHL_FEATURE_NIC
			case RTW_FW_NIC:
				fw_len = array_length_8852c_u1_nic;
				fw = array_8852c_u1_nic;
				break;
#ifdef CONFIG_WOWLAN
			case RTW_FW_WOWLAN:
				fw_len = array_length_8852c_u1_wowlan;
				fw = array_8852c_u1_wowlan;
				break;
#endif /*CONFIG_WOWLAN*/
#endif /*PHL_FEATURE_NIC*/
			default:
				PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__);
				fw_len = 0;
				fw = 0;
				break;
				}
				break;
#endif /*MAC_FW_8852C_U1*/
		default:
			PLTFM_MSG_ERR("[ERR]%s: invalid cut\n", __func__);
			fw_len = 0;
			fw = 0;
			break;
		}
		break;
#endif /*CONFIG_RTL8852C*/

	default:
		PLTFM_MSG_ERR("[ERR]%s: invalid chip\n", __func__);
		fw_len = 0;
		fw = 0;
		break;
	}

	ret = mac_disable_cpu(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]%s: mac_disable_cpu fail\n", __func__);
		return ret;
	}

	if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852B) ||
	    is_chip_id(adapter, MAC_AX_CHIP_ID_8852C)) {
		ret = mac_check_OTP(adapter, 1);
		if (ret != MACSUCCESS) {
			PLTFM_MSG_ERR("[ERR]%s: mac_check_OTP fail\n",
				      __func__);
			return ret;
		}
	}

	ret = mac_enable_cpu(adapter, AX_BOOT_REASON_PWR_ON, 1);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]%s: mac_enable_cpu fail\n", __func__);
		return ret;
	}

	ret = mac_fwdl(adapter, fw, fw_len);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]%s: mac_enable_cpu fail\n", __func__);
		return ret;
	}

	if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852B) ||
	    is_chip_id(adapter, MAC_AX_CHIP_ID_8852C)) {
		ret = mac_check_OTP(adapter, 0);
		if (ret != MACSUCCESS) {
			PLTFM_MSG_ERR("[ERR]%s: mac_check_OTP fail\n",
				      __func__);
			return ret;
		}
	}

#endif /* #if defined(PHL_FEATURE_AP) || defined(PHL_FEATURE_NIC) */
	return ret;
}

u32 mac_query_fw_buff(struct mac_ax_adapter *adapter, enum rtw_fw_type cat, u8 **fw, u32 *fw_len)
{
	u32 ret = MACSUCCESS;
#if defined(PHL_FEATURE_AP) || defined(PHL_FEATURE_NIC)
	u32 chip_id, cv;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);

	chip_id = GET_FIELD(MAC_REG_R32(R_AX_SYS_CHIPINFO), B_AX_HW_ID);
	cv = GET_FIELD(MAC_REG_R32(R_AX_SYS_CFG1), B_AX_CHIP_VER);

	switch (chip_id) {
#ifdef CONFIG_RTL8852A
	case RTL8852A_ID:
		switch (cv) {
#ifdef MAC_FW_8852A_U2
		case FWDL_CBV:
			switch (cat) {
#ifdef PHL_FEATURE_AP
			case RTW_FW_AP:
				*fw_len = array_length_8852a_u2_ap;
				*fw = array_8852a_u2_ap;
				break;
#endif /*PHL_FEATURE_AP*/
#ifdef PHL_FEATURE_NIC
			case RTW_FW_NIC:
				*fw_len = array_length_8852a_u2_nic;
				*fw = array_8852a_u2_nic;
				break;
#ifdef CONFIG_WOWLAN
			case RTW_FW_WOWLAN:
				*fw_len = array_length_8852a_u2_wowlan;
				*fw = array_8852a_u2_wowlan;
				break;
#endif /*CONFIG_WOWLAN*/
#endif /*PHL_FEATURE_NIC*/
			default:
				PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__);
				*fw_len = 0;
				fw = 0;
				break;
			}
			break;
#endif /*MAC_FW_8852A_U2*/
#ifdef MAC_FW_8852A_U3
		case FWDL_CCV:
			switch (cat) {
#ifdef PHL_FEATURE_AP
			case RTW_FW_AP:
				*fw_len = array_length_8852a_u3_ap;
				*fw = array_8852a_u3_ap;
				break;
#endif /*PHL_FEATURE_AP*/
#ifdef PHL_FEATURE_NIC
			case RTW_FW_NIC:
				*fw_len = array_length_8852a_u3_nic;
				*fw = array_8852a_u3_nic;
				break;
#ifdef CONFIG_WOWLAN
			case RTW_FW_WOWLAN:
				*fw_len = array_length_8852a_u3_wowlan;
				*fw = array_8852a_u3_wowlan;
				break;
#endif /*CONFIG_WOWLAN*/
#endif /*PHL_FEATURE_NIC*/
			default:
				PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__);
				*fw_len = 0;
				fw = 0;
				break;
			}
			break;
#endif /*MAC_FW_8852A_U3*/
		default:
			PLTFM_MSG_ERR("[ERR]%s: invalid cut\n", __func__);
			*fw_len = 0;
			fw = 0;
			break;
		}
		break;
#endif /*MAC_AX_8852A_SUPPORT*/
#ifdef CONFIG_RTL8852B
	case RTL8852B_ID:
		switch (cv) {
#ifdef MAC_FW_8852B_U2
		case FWDL_CBV:
			switch (cat) {
#ifdef PHL_FEATURE_AP
			case RTW_FW_AP:
				PLTFM_MSG_ERR("[ERR]%s: 8852b does not have ap image\n", __func__);
				*fw_len = 0;
				fw = 0;
				break;
#endif /*PHL_FEATURE_AP*/
#ifdef PHL_FEATURE_NIC
			case RTW_FW_NIC:
				*fw_len = array_length_8852b_u2_nic;
				*fw = array_8852b_u2_nic;
				break;
#ifdef CONFIG_WOWLAN
			case RTW_FW_WOWLAN:
				*fw_len = array_length_8852b_u2_wowlan;
				*fw = array_8852b_u2_wowlan;
				break;
#endif /*CONFIG_WOWLAN*/
#endif /*PHL_FEATURE_NIC*/
			default:
				PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__);
				*fw_len = 0;
				fw = 0;
				break;
			}
			break;
#endif /*MAC_FW_8852B_U1*/
		default:
			PLTFM_MSG_ERR("[ERR]%s: invalid cut\n", __func__);
			*fw_len = 0;
			fw = 0;
			break;
		}
		break;
#endif /*MAC_AX_8852B_SUPPORT*/
#ifdef CONFIG_RTL8852C
	case RTL8852C_ID:
		switch (cv) {
#ifdef MAC_FW_8852C_U1
		case FWDL_CAV:
			switch (cat) {
#ifdef PHL_FEATURE_AP
			case RTW_FW_AP:
				*fw_len = array_length_8852c_u1_ap;
				*fw = array_8852c_u1_ap;
				break;
#endif /*PHL_FEATURE_AP*/
#ifdef PHL_FEATURE_NIC
			case RTW_FW_NIC:
				*fw_len = array_length_8852c_u1_nic;
				*fw = array_8852c_u1_nic;
				break;
#ifdef CONFIG_WOWLAN
			case RTW_FW_WOWLAN:
				*fw_len = array_length_8852c_u1_wowlan;
				*fw = array_8852c_u1_wowlan;
				break;
#endif /*CONFIG_WOWLAN*/
#endif /*PHL_FEATURE_NIC*/
			default:
				PLTFM_MSG_ERR("[ERR]%s: no cat\n", __func__);
				*fw_len = 0;
				fw = 0;
				break;
				}
				break;
#endif /*MAC_FW_8852C_U1*/
		default:
			PLTFM_MSG_ERR("[ERR]%s: invalid cut\n", __func__);
			*fw_len = 0;
			fw = 0;
			break;
		}
		break;
#endif /*MAC_AX_8852C_SUPPORT*/

	default:
		PLTFM_MSG_ERR("[ERR]%s: invalid chip\n", __func__);
		*fw_len = 0;
		fw = 0;
		break;
	}

#endif /* #if defined(PHL_FEATURE_AP) || defined(PHL_FEATURE_NIC) */
	return ret;
}