/******************************************************************************
 *
 * 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 "addr_cam.h"

#define MAC_AX_NO_HIT_IDX 0xFF

static u8 get_set_bits_of_msk(u8 msk)
{
	u8 set_bits;

	if (msk == 0)
		return 0;
	set_bits = msk & (msk - 1);
	if (set_bits == 0)
		return 1;
	return get_set_bits_of_msk(set_bits) + 1;
}

u32 find_avail_addr_cam_entry(struct mac_ax_adapter *adapter,
			      struct mac_ax_role_info *info)
{
	u16 i;

	for (i = 0; i < adapter->hw_info->macid_num; i++) {
		if (!mac_role_srch_by_addr_cam(adapter, i))
			break;
	}
	if (i == adapter->hw_info->macid_num)
		return MACADDRCAMFL;
	info->a_info.addr_cam_idx = (u8)i;

	return MACSUCCESS;
}

u32 find_avail_bssid_cam_entry(struct mac_ax_adapter *adapter,
			       struct mac_ax_role_info *info)
{
	u8 i;
	struct mac_role_tbl *role;
	u8 maddr_cmp_len = ETH_ALEN;

	info->a_info.bssid_cam_idx = adapter->hw_info->bssid_num;
	info->b_info.bssid_cam_idx = adapter->hw_info->bssid_num;

	if (info->a_info.mask_sel == MAC_AX_BSSID_MSK)
		maddr_cmp_len = get_set_bits_of_msk(info->a_info.addr_mask);

	for (i = 0; i < adapter->hw_info->bssid_num; i++) {
		role = mac_role_srch_by_bssid(adapter, i);
		if (role &&
		    !PLTFM_MEMCMP(info->bssid,
				  role->info.bssid, maddr_cmp_len) &&
		    info->band == role->info.band) {
			info->a_info.bssid_cam_idx =
				role->info.a_info.bssid_cam_idx;
			if (maddr_cmp_len < ETH_ALEN) {
				PLTFM_MEMCPY(info->b_info.bssid,
					     role->info.b_info.bssid,
					     ETH_ALEN);
			}
			break;
		} else if (!role) {
			if (info->a_info.bssid_cam_idx ==
			    adapter->hw_info->bssid_num) {
				info->a_info.bssid_cam_idx = i;
			}
		}
	}

	if (info->a_info.bssid_cam_idx == adapter->hw_info->bssid_num)
		return MACBSSIDCAMFL;
	info->b_info.bssid_cam_idx = info->a_info.bssid_cam_idx;

	return MACSUCCESS;
}

u32 init_addr_cam_info(struct mac_ax_adapter *adapter,
		       struct mac_ax_role_info *info,
		       struct fwcmd_addrcam_info *fw_addrcam)
{
	u32 ret;

	ret = find_avail_addr_cam_entry(adapter, info);
	if (ret)
		return ret;

	ret = find_avail_bssid_cam_entry(adapter, info);
	if (ret)
		return ret;

	fill_addr_cam_info(adapter, info, fw_addrcam);
	fill_bssid_cam_info(adapter, info, fw_addrcam);

	return MACSUCCESS;
}

u32 change_addr_cam_info(struct mac_ax_adapter *adapter,
			 struct mac_ax_role_info *info,
			 struct fwcmd_addrcam_info *fw_addrcam)
{
	u32 ret;

	ret = find_avail_bssid_cam_entry(adapter, info);
	if (ret)
		return ret;

	fill_addr_cam_info(adapter, info, fw_addrcam);
	fill_bssid_cam_info(adapter, info, fw_addrcam);

	return MACSUCCESS;
}

u32 fill_addr_cam_info(struct mac_ax_adapter *adapter,
		       struct mac_ax_role_info *info,
		       struct fwcmd_addrcam_info *fw_addrcam)
{
	struct mac_ax_addr_cam_info a_info;
	u8 i;
	u8 sma_hash = 0x00;
	u8 tma_hash = 0x00;
	u8 maddr_cmp_len;

	maddr_cmp_len = get_set_bits_of_msk(info->a_info.addr_mask);

	switch (info->a_info.mask_sel) {
	case MAC_AX_SMA_MSK:
		for (i = 0; i < maddr_cmp_len; i++)
			sma_hash ^= info->a_info.sma[i];
		for (i = 0; i < ETH_ALEN; i++)
			tma_hash ^= info->a_info.tma[i];
		break;
	case MAC_AX_TMA_MSK:
		for (i = 0; i < ETH_ALEN; i++)
			sma_hash ^= info->a_info.sma[i];
		for (i = 0; i < maddr_cmp_len; i++)
			tma_hash ^= info->a_info.tma[i];
		break;
	case MAC_AX_NO_MSK:
	case MAC_AX_BSSID_MSK:
	default:
		for (i = 0; i < ETH_ALEN; i++)
			sma_hash ^= info->a_info.sma[i];
		for (i = 0; i < ETH_ALEN; i++)
			tma_hash ^= info->a_info.tma[i];
		break;
	}

	a_info = info->a_info;

	fw_addrcam->dword1 =
	  cpu_to_le32(SET_WORD(a_info.addr_cam_idx,
			       FWCMD_H2C_ADDRCAM_INFO_IDX) |
		      SET_WORD(a_info.offset, FWCMD_H2C_ADDRCAM_INFO_OFFSET) |
		      SET_WORD(a_info.len, FWCMD_H2C_ADDRCAM_INFO_LEN));

	fw_addrcam->dword2 =
	  cpu_to_le32(((a_info.valid) ? FWCMD_H2C_ADDRCAM_INFO_VALID : 0) |
	   SET_WORD(a_info.net_type, FWCMD_H2C_ADDRCAM_INFO_NET_TYPE) |
	   SET_WORD(a_info.bcn_hit_cond, FWCMD_H2C_ADDRCAM_INFO_BCN_HIT_COND) |
	   SET_WORD(a_info.hit_rule, FWCMD_H2C_ADDRCAM_INFO_HIT_RULE) |
	   ((a_info.bb_sel) ? FWCMD_H2C_ADDRCAM_INFO_BB_SEL : 0) |
	   SET_WORD(a_info.addr_mask, FWCMD_H2C_ADDRCAM_INFO_ADDR_MASK) |
	   SET_WORD(a_info.mask_sel, FWCMD_H2C_ADDRCAM_INFO_MASK_SEL) |
	   SET_WORD(sma_hash, FWCMD_H2C_ADDRCAM_INFO_SMA_HASH) |
	   SET_WORD(tma_hash, FWCMD_H2C_ADDRCAM_INFO_TMA_HASH));

	fw_addrcam->dword3 =
	  cpu_to_le32(SET_WORD(a_info.bssid_cam_idx,
			       FWCMD_H2C_ADDRCAM_INFO_BSSID_CAM_IDX) |
	   ((a_info.is_mul_ent) ? FWCMD_H2C_ADDRCAM_INFO_IS_MUL_ENT : 0));

	fw_addrcam->dword4 =
	  cpu_to_le32(SET_WORD(a_info.sma[0], FWCMD_H2C_ADDRCAM_INFO_SMA0) |
	   SET_WORD(a_info.sma[1], FWCMD_H2C_ADDRCAM_INFO_SMA1) |
	   SET_WORD(a_info.sma[2], FWCMD_H2C_ADDRCAM_INFO_SMA2) |
	   SET_WORD(a_info.sma[3], FWCMD_H2C_ADDRCAM_INFO_SMA3));

	fw_addrcam->dword5 =
	  cpu_to_le32(SET_WORD(a_info.sma[4], FWCMD_H2C_ADDRCAM_INFO_SMA4) |
	   SET_WORD(a_info.sma[5], FWCMD_H2C_ADDRCAM_INFO_SMA5) |
	   SET_WORD(a_info.tma[0], FWCMD_H2C_ADDRCAM_INFO_TMA0) |
	   SET_WORD(a_info.tma[1], FWCMD_H2C_ADDRCAM_INFO_TMA1));

	fw_addrcam->dword6 =
	  cpu_to_le32(SET_WORD(a_info.tma[2], FWCMD_H2C_ADDRCAM_INFO_TMA2) |
	   SET_WORD(a_info.tma[3], FWCMD_H2C_ADDRCAM_INFO_TMA3) |
	   SET_WORD(a_info.tma[4], FWCMD_H2C_ADDRCAM_INFO_TMA4) |
	   SET_WORD(a_info.tma[5], FWCMD_H2C_ADDRCAM_INFO_TMA5));

	// dword7 rsvd

	fw_addrcam->dword8 =
	  cpu_to_le32(SET_WORD(a_info.macid, FWCMD_H2C_ADDRCAM_INFO_MACID) |
	   SET_WORD(a_info.port_int, FWCMD_H2C_ADDRCAM_INFO_PORT_INT) |
	   SET_WORD(a_info.tsf_sync, FWCMD_H2C_ADDRCAM_INFO_TSF_SYNC) |
	   ((a_info.tf_trs) ? FWCMD_H2C_ADDRCAM_INFO_TF_TRS : 0) |
	   ((a_info.lsig_txop) ? FWCMD_H2C_ADDRCAM_INFO_LSIG_TXOP : 0) |
	   SET_WORD(a_info.tgt_ind, FWCMD_H2C_ADDRCAM_INFO_TGT_IND) |
	   SET_WORD(a_info.frm_tgt_ind, FWCMD_H2C_ADDRCAM_INFO_FRM_TGT_IND));

	fw_addrcam->dword9 =
	  cpu_to_le32((a_info.aid12 & 0xfff) |
	   ((a_info.wol_pattern) ? FWCMD_H2C_ADDRCAM_INFO_WOL_PATTERN : 0) |
	   ((a_info.wol_uc) ? FWCMD_H2C_ADDRCAM_INFO_WOL_UC : 0) |
	   ((a_info.wol_magic) ? FWCMD_H2C_ADDRCAM_INFO_WOL_MAGIC : 0) |
	   ((a_info.wapi) ? FWCMD_H2C_ADDRCAM_INFO_WAPI : 0) |
	   SET_WORD(a_info.sec_ent_mode, FWCMD_H2C_ADDRCAM_INFO_SEC_ENT_MODE) |
	   SET_WORD(a_info.sec_ent_keyid[0],
		    FWCMD_H2C_ADDRCAM_INFO_SEC_ENT0_KEYID) |
	   SET_WORD(a_info.sec_ent_keyid[1],
		    FWCMD_H2C_ADDRCAM_INFO_SEC_ENT1_KEYID) |
	   SET_WORD(a_info.sec_ent_keyid[2],
		    FWCMD_H2C_ADDRCAM_INFO_SEC_ENT2_KEYID) |
	   SET_WORD(a_info.sec_ent_keyid[3],
		    FWCMD_H2C_ADDRCAM_INFO_SEC_ENT3_KEYID) |
	   SET_WORD(a_info.sec_ent_keyid[4],
		    FWCMD_H2C_ADDRCAM_INFO_SEC_ENT4_KEYID) |
	   SET_WORD(a_info.sec_ent_keyid[5],
		    FWCMD_H2C_ADDRCAM_INFO_SEC_ENT5_KEYID) |
	   SET_WORD(a_info.sec_ent_keyid[6],
		    FWCMD_H2C_ADDRCAM_INFO_SEC_ENT6_KEYID));

	fw_addrcam->dword10 =
	  cpu_to_le32(SET_WORD(a_info.sec_ent_valid,
			       FWCMD_H2C_ADDRCAM_INFO_SEC_ENT_VALID) |
	   SET_WORD(a_info.sec_ent[0], FWCMD_H2C_ADDRCAM_INFO_SEC_ENT0) |
	   SET_WORD(a_info.sec_ent[1], FWCMD_H2C_ADDRCAM_INFO_SEC_ENT1) |
	   SET_WORD(a_info.sec_ent[2], FWCMD_H2C_ADDRCAM_INFO_SEC_ENT2));

	fw_addrcam->dword11 =
	  cpu_to_le32(SET_WORD(a_info.sec_ent[3],
			       FWCMD_H2C_ADDRCAM_INFO_SEC_ENT3) |
	   SET_WORD(a_info.sec_ent[4], FWCMD_H2C_ADDRCAM_INFO_SEC_ENT4) |
	   SET_WORD(a_info.sec_ent[5], FWCMD_H2C_ADDRCAM_INFO_SEC_ENT5) |
	   SET_WORD(a_info.sec_ent[6], FWCMD_H2C_ADDRCAM_INFO_SEC_ENT6));

	return MACSUCCESS;
}

u32 fill_bssid_cam_info(struct mac_ax_adapter *adapter,
			struct mac_ax_role_info *role_info,
			struct fwcmd_addrcam_info *fw_addrcam)
{
	struct mac_ax_bssid_cam_info b_info = role_info->b_info;

	fw_addrcam->dword12 =
	  cpu_to_le32(SET_WORD(b_info.bssid_cam_idx,
			       FWCMD_H2C_ADDRCAM_INFO_B_IDX) |
		      SET_WORD(b_info.offset, FWCMD_H2C_ADDRCAM_INFO_B_OFFSET) |
		      SET_WORD(b_info.len, FWCMD_H2C_ADDRCAM_INFO_B_LEN));

	fw_addrcam->dword13 =
	  cpu_to_le32(((b_info.valid) ? FWCMD_H2C_ADDRCAM_INFO_B_VALID : 0) |
	   ((b_info.bb_sel) ? FWCMD_H2C_ADDRCAM_INFO_B_BB_SEL : 0) |
	   SET_WORD(b_info.bss_color, FWCMD_H2C_ADDRCAM_INFO_BSS_COLOR) |
	   SET_WORD(b_info.bssid[0], FWCMD_H2C_ADDRCAM_INFO_BSSID0) |
	   SET_WORD(b_info.bssid[1], FWCMD_H2C_ADDRCAM_INFO_BSSID1));

	fw_addrcam->dword14 =
	  cpu_to_le32(SET_WORD(b_info.bssid[2], FWCMD_H2C_ADDRCAM_INFO_BSSID2) |
	   SET_WORD(b_info.bssid[3], FWCMD_H2C_ADDRCAM_INFO_BSSID3) |
	   SET_WORD(b_info.bssid[4], FWCMD_H2C_ADDRCAM_INFO_BSSID4) |
	   SET_WORD(b_info.bssid[5], FWCMD_H2C_ADDRCAM_INFO_BSSID5));

	return MACSUCCESS;
}

u32 mac_upd_addr_cam(struct mac_ax_adapter *adapter,
		     struct mac_ax_role_info *info,
		     enum mac_ax_role_opmode op)
{
	u32 tbl[21];
	u32 ret;
	u32 i;
	u8 *buf;
	#if MAC_AX_PHL_H2C
	struct rtw_h2c_pkt *h2cb;
	#else
	struct h2c_buf *h2cb;
	#endif
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);
	struct fwcmd_addrcam_info *fwcmd_tbl;
	u8 ctlinfo_aidx_off;

	if (adapter->sm.fwdl == MAC_AX_FWDL_INIT_RDY) {
		h2cb = h2cb_alloc(adapter, H2CB_CLASS_DATA);
		if (!h2cb)
			return MACNPTR;
		buf = h2cb_put(h2cb, sizeof(struct fwcmd_addrcam_info));
		if (!buf) {
			ret = MACNOBUF;
			goto FWOFLD_END;
		}
		fwcmd_tbl = (struct fwcmd_addrcam_info *)buf;

		if (op == CHG)
			ret = change_addr_cam_info(adapter, info, fwcmd_tbl);
		else
			ret = init_addr_cam_info(adapter, info, fwcmd_tbl);
		if (ret)
			goto FWOFLD_END;

		// dword 0
		ret = h2c_pkt_set_hdr(adapter,
				      h2cb,
				      FWCMD_TYPE_H2C,
				      FWCMD_H2C_CAT_MAC,
				      FWCMD_H2C_CL_ADDR_CAM_UPDATE,
				      FWCMD_H2C_FUNC_ADDRCAM_INFO,
				      0,
				      1);
		if (ret)
			goto FWOFLD_END;

		// return MACSUCCESS if h2c aggregation is enabled and enqueued successfully.
		// H2C shall be sent by mac_h2c_agg_tx.
		ret = h2c_agg_enqueue(adapter, h2cb);
		if (ret == MACSUCCESS)
			return MACSUCCESS;

		ret = h2c_pkt_build_txd(adapter, h2cb);
		if (ret)
			goto FWOFLD_END;
		#if MAC_AX_PHL_H2C
		ret = PLTFM_TX(h2cb);
		#else
		ret = PLTFM_TX(h2cb->data, h2cb->len);
		#endif

FWOFLD_END:
		h2cb_free(adapter, h2cb);
		if (!ret)
			h2c_end_flow(adapter);

		return ret;
	}
	if (op == CHG)
		ret = change_addr_cam_info(adapter,
					   info,
					   (struct fwcmd_addrcam_info *)
					   tbl);
	else
		ret = init_addr_cam_info(adapter, info,
					 (struct fwcmd_addrcam_info *)
					 tbl);
	if (ret)
		return ret;
	// Indirect write addr cam
	for (i = 0; i < (u32)((info->a_info.len)) / 4; i++)
		mac_sram_dbg_write(adapter, (info->a_info.addr_cam_idx *
					     info->a_info.len) + (i * 4),
				   le32_to_cpu(tbl[i + 2]), ADDR_CAM_SEL);

	// Indirect write BSSID cam
	for (i = 0; i < (u32)((info->b_info.len)) / 4; i++)
		mac_sram_dbg_write(adapter, (info->b_info.bssid_cam_idx *
					     info->b_info.len) + (i * 4),
				   le32_to_cpu(tbl[i + 13]), BSSID_CAM_SEL);

	// Indirect write cmac table addr cam idx
	if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852A))
		ctlinfo_aidx_off = 0x18;
	else if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852B))
		ctlinfo_aidx_off = 0x18;
	else if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852C) ||
		 is_chip_id(adapter, MAC_AX_CHIP_ID_8192XB))
		ctlinfo_aidx_off = 0x17;
	else
		ctlinfo_aidx_off = 0xFF;
	PLTFM_MSG_WARN("%s ind access cmac tbl start\n", __func__);
	PLTFM_MUTEX_LOCK(&adapter->hw_info->ind_access_lock);
	adapter->hw_info->ind_aces_cnt++;
	MAC_REG_W8(R_AX_INDIR_ACCESS_ENTRY +
		   info->macid * CCTL_INFO_SIZE + ctlinfo_aidx_off,
		   info->a_info.addr_cam_idx);
	adapter->hw_info->ind_aces_cnt--;
	PLTFM_MUTEX_UNLOCK(&adapter->hw_info->ind_access_lock);
	PLTFM_MSG_WARN("%s ind access cmac tbl end\n", __func__);

	return MACSUCCESS;
}

static void addr_cam_opt_2_uint(struct mac_ax_adapter *adapter,
				struct mac_ax_addrcam_ctrl_t *opt,
				u32 *val32)
{
	*val32 = ((opt->addrcam_en) ? B_AX_ADDR_CAM_EN : 0) |
		 ((opt->srch_per_mpdu) ? B_AX_ADDR_CAM_SRCH_PERPKT : 0) |
		 ((opt->a2_bit0_cmp_en) ? B_AX_ADDR_CAM_A2_B0_CHK : 0) |
		 //opt->clr_all_content) ? B_AX_ADDR_CAM_CLR: 0) |
		 SET_WORD(opt->srch_time_lmt, B_AX_ADDR_CAM_CMPLIMT) |
		 SET_WORD(opt->srch_range_lmt, B_AX_ADDR_CAM_RANGE);
}

static void addr_cam_dis_opt_2_uint(struct mac_ax_adapter *adapter,
				    struct mac_ax_addrcam_dis_ctrl_t *opt,
				    u32 *val32)
{
	*val32 = SET_WORD(opt->def_hit_idx, B_AX_ADDR_CAM_DIS_IDX) |
		 ((opt->def_hit_result) ? B_AX_ADDR_CAM_DIS_CAM_HIT : 0) |
		 ((opt->def_a1_hit_result) ? B_AX_ADDR_CAM_DIS_A1_HIT : 0) |
		 ((opt->def_a2_hit_result) ? B_AX_ADDR_CAM_DIS_A2_HIT : 0) |
		 ((opt->def_a3_hit_result) ? B_AX_ADDR_CAM_DIS_A3_HIT : 0) |
		 SET_WORD(opt->def_port, B_AX_ADDR_CAM_DIS_PORT) |
		 SET_WORD(opt->def_sec_idx, B_AX_ADDR_CAM_DIS_SEC_IDX) |
		 SET_WORD(opt->def_macid, B_AX_ADDR_CAM_DIS_MACID);
}

u32 mac_get_cfg_addr_cam(struct mac_ax_adapter *adapter,
			 struct mac_ax_addrcam_ctrl_t *opt,
			 enum mac_ax_band band)
{
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);
	u32 val32;

	if (!opt) {
		PLTFM_MSG_ERR("[ERR]%s opt is null\n", __func__);
		return MACNPTR;
	}

	val32 = check_mac_en(adapter, band, MAC_AX_CMAC_SEL);
	if (val32 != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]%s CMAC%d not enable\n", __func__, band);
		return val32;
	}

	val32 = MAC_REG_R32((band == MAC_AX_BAND_1) ?
		R_AX_ADDR_CAM_CTRL_C1 : R_AX_ADDR_CAM_CTRL);
	opt->addrcam_en = ((val32 & B_AX_ADDR_CAM_EN) != 0);
	opt->srch_per_mpdu = ((val32 & B_AX_ADDR_CAM_SRCH_PERPKT) != 0);
	opt->a2_bit0_cmp_en = ((val32 & B_AX_ADDR_CAM_A2_B0_CHK) != 0);
	opt->srch_time_lmt = GET_FIELD(val32, B_AX_ADDR_CAM_CMPLIMT);
	opt->srch_range_lmt = GET_FIELD(val32, B_AX_ADDR_CAM_RANGE);

	return MACSUCCESS;
}

u32 mac_get_cfg_addr_cam_dis(struct mac_ax_adapter *adapter,
			     struct mac_ax_addrcam_dis_ctrl_t *opt,
			     enum mac_ax_band band)
{
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);
	u32 val32;

	if (!opt) {
		PLTFM_MSG_ERR("[ERR]%s opt is null\n", __func__);
		return MACNPTR;
	}

	val32 = check_mac_en(adapter, band, MAC_AX_CMAC_SEL);
	if (val32 != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]%s CMAC%d not enable\n", __func__, band);
		return val32;
	}

	val32 = MAC_REG_R32((band == MAC_AX_BAND_1) ?
			     R_AX_ADDR_CAM_DIS_INFO_C1 :
			     R_AX_ADDR_CAM_DIS_INFO);

	opt->def_hit_idx = GET_FIELD(val32, B_AX_ADDR_CAM_DIS_IDX);
	opt->def_hit_result = ((val32 & B_AX_ADDR_CAM_DIS_CAM_HIT) != 0);
	opt->def_a1_hit_result = ((val32 & B_AX_ADDR_CAM_DIS_A1_HIT) != 0);
	opt->def_a2_hit_result = ((val32 & B_AX_ADDR_CAM_DIS_A2_HIT) != 0);
	opt->def_a3_hit_result = ((val32 & B_AX_ADDR_CAM_DIS_A3_HIT) != 0);
	opt->def_port = GET_FIELD(val32, B_AX_ADDR_CAM_DIS_PORT);
	opt->def_sec_idx = GET_FIELD(val32, B_AX_ADDR_CAM_DIS_SEC_IDX);
	opt->def_macid = GET_FIELD(val32, B_AX_ADDR_CAM_DIS_MACID);

	return MACSUCCESS;
}

u32 mac_cfg_addr_cam(struct mac_ax_adapter *adapter,
		     struct mac_ax_addrcam_ctrl_t *ctl_opt,
		     struct mac_ax_addrcam_ctrl_t *ctl_msk,
		     enum mac_ax_band band)
{
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);
	struct mac_ax_addrcam_ctrl_t opt = {0};
	u32 ctl_opt_val;
	u32 ctl_msk_val;
	u32 opt_val;
	u32 reg;
	u32 cnt;
	u32 ret = MACSUCCESS;

	ret = check_mac_en(adapter, band, MAC_AX_CMAC_SEL);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]%s CMAC%d not enable\n", __func__, band);
		return ret;
	}

	reg = (band == MAC_AX_BAND_1) ?
	      R_AX_ADDR_CAM_CTRL_C1 : R_AX_ADDR_CAM_CTRL;

	mac_get_cfg_addr_cam(adapter, &opt, band);

	addr_cam_opt_2_uint(adapter, ctl_opt, &ctl_opt_val);
	addr_cam_opt_2_uint(adapter, ctl_msk, &ctl_msk_val);
	addr_cam_opt_2_uint(adapter, &opt, &opt_val);

	opt_val = (ctl_opt_val & ctl_msk_val) |
		  (~(~ctl_opt_val & ctl_msk_val) & opt_val);

	MAC_REG_W32(reg, opt_val);

	if (ctl_opt->clr_all_content & ctl_msk->clr_all_content) {
		opt_val |= B_AX_ADDR_CAM_CLR;
		MAC_REG_W32(reg, opt_val);
		cnt = TRXCFG_WAIT_CNT;
		while (cnt--) {
			if (!(MAC_REG_R16(reg) & B_AX_ADDR_CAM_CLR))
				break;
			PLTFM_DELAY_US(TRXCFG_WAIT_US);
		}
		if (!++cnt) {
			PLTFM_MSG_ERR("[ERR]ADDR_CAM reset\n");
			return MACPOLLTO;
		}
	}

	return MACSUCCESS;
}

u32 mac_cfg_addr_cam_dis(struct mac_ax_adapter *adapter,
			 struct mac_ax_addrcam_dis_ctrl_t *ctl_opt,
			 struct mac_ax_addrcam_dis_ctrl_t *ctl_msk,
			 enum mac_ax_band band)
{
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);
	struct mac_ax_addrcam_dis_ctrl_t opt = {0};
	u32 ctl_opt_val;
	u32 ctl_msk_val;
	u32 opt_val;
	u32 reg;
	u32 ret = MACSUCCESS;

	ret = check_mac_en(adapter, band, MAC_AX_CMAC_SEL);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]%s CMAC%d not enable\n", __func__, band);
		return ret;
	}

	reg = (band == MAC_AX_BAND_1) ?
	      R_AX_ADDR_CAM_DIS_INFO_C1 : R_AX_ADDR_CAM_DIS_INFO;

	mac_get_cfg_addr_cam_dis(adapter, &opt, band);

	addr_cam_dis_opt_2_uint(adapter, ctl_opt, &ctl_opt_val);
	addr_cam_dis_opt_2_uint(adapter, ctl_msk, &ctl_msk_val);
	addr_cam_dis_opt_2_uint(adapter, &opt, &opt_val);

	opt_val = (ctl_opt_val & ctl_msk_val) |
		  (~(~ctl_opt_val & ctl_msk_val) & opt_val);

	MAC_REG_W32(reg, opt_val);

	return MACSUCCESS;
}

u32 addr_cam_init(struct mac_ax_adapter *adapter,
		  enum mac_ax_band band)
{
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);
	u32 val32, reg;
	u32 cnt = TRXCFG_WAIT_CNT;
	u32 ret;

	ret = check_mac_en(adapter, band, MAC_AX_CMAC_SEL);
	if (ret != MACSUCCESS)
		return ret;

	reg = band == MAC_AX_BAND_1 ?
		R_AX_ADDR_CAM_CTRL_C1 : R_AX_ADDR_CAM_CTRL;
	val32 = MAC_REG_R32(reg);
	val32 |= (SET_WORD(ADDR_CAM_SERCH_RANGE, B_AX_ADDR_CAM_RANGE) |
		  B_AX_ADDR_CAM_EN);
	if (band == MAC_AX_BAND_0)
		val32 |= B_AX_ADDR_CAM_CLR;
	MAC_REG_W32(reg, val32);

	cnt = TRXCFG_WAIT_CNT;
	while (cnt--) {
		if (!(MAC_REG_R16(band == MAC_AX_BAND_1 ?
		    R_AX_ADDR_CAM_CTRL_C1 : R_AX_ADDR_CAM_CTRL)
		    & B_AX_ADDR_CAM_CLR))
			break;
		PLTFM_DELAY_US(TRXCFG_WAIT_US);
	}
	if (!++cnt) {
		PLTFM_MSG_ERR("[ERR]ADDR_CAM reset\n");
		return MACPOLLTO;
	}
	return MACSUCCESS;
}

static u32 _set_mac_resp_ack(struct mac_ax_adapter *adapter, u32 ack, u8 band)
{
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);
	u8 val8;
	u32 val32;
	u32 offset_ctrl = band ? R_AX_ADDR_CAM_CTRL_C1 : R_AX_ADDR_CAM_CTRL;
	u32 offset_hit = band ? R_AX_ADDR_CAM_DIS_INFO_C1 :
		R_AX_ADDR_CAM_DIS_INFO;

	if (ack) {
		val8 = MAC_REG_R8(offset_ctrl);
		MAC_REG_W8(offset_ctrl, val8 | B_AX_ADDR_CAM_EN);
	} else {
		val8 = MAC_REG_R8(offset_ctrl);
		MAC_REG_W8(offset_ctrl, val8 & ~B_AX_ADDR_CAM_EN);
		val32 = MAC_REG_R32(offset_hit);
		val32 = SET_CLR_WORD(val32, MAC_AX_NO_HIT_IDX,
				     B_AX_ADDR_CAM_DIS_IDX);
		val32 &= ~(B_AX_ADDR_CAM_DIS_CAM_HIT |
			   B_AX_ADDR_CAM_DIS_A1_HIT);
		MAC_REG_W32(offset_hit, val32);
	}

	return MACSUCCESS;
}

#if MAC_AX_FW_REG_OFLD
u32 set_mac_do_resp_ack_ofld(struct mac_ax_adapter *adapter)
{
	u32 ret;
	u8 cmac1_en;

	cmac1_en = check_mac_en(adapter, 1, MAC_AX_CMAC_SEL) == MACSUCCESS ?
			1 : 0;

	ret = MAC_REG_W_OFLD(R_AX_ADDR_CAM_CTRL, B_AX_ADDR_CAM_EN,
			     1, cmac1_en ? 0 : 1);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("%s: config fail\n", __func__);
		return ret;
	}
	if (cmac1_en) {
		ret = MAC_REG_W_OFLD(R_AX_ADDR_CAM_CTRL_C1,
				     B_AX_ADDR_CAM_EN,
				     1, 1);
		if (ret != MACSUCCESS) {
			PLTFM_MSG_ERR("%s: config fail\n", __func__);
			return ret;
		}
	}

	return MACSUCCESS;
}

u32 set_mac_not_resp_ack_ofld(struct mac_ax_adapter *adapter)
{
	u32 ret, msk;
	u8 cmac1_en;

	cmac1_en = check_mac_en(adapter, 1, MAC_AX_CMAC_SEL) == MACSUCCESS ?
			1 : 0;

	/* set 0xCE34[0] = 0 */
	ret = MAC_REG_W_OFLD(R_AX_ADDR_CAM_CTRL, B_AX_ADDR_CAM_EN,
			     0, 0);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("%s: config fail\n", __func__);
		return ret;
	}

	/* set 0xCE38[9:0] = 0xFF */
	msk = (B_AX_ADDR_CAM_DIS_IDX_MSK << B_AX_ADDR_CAM_DIS_IDX_SH) |
		B_AX_ADDR_CAM_DIS_A1_HIT |
		B_AX_ADDR_CAM_DIS_CAM_HIT;
	ret = MAC_REG_W_OFLD(R_AX_ADDR_CAM_DIS_INFO, msk,
			     MAC_AX_NO_HIT_IDX, cmac1_en ? 0 : 1);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("%s: config fail\n", __func__);
		return ret;
	}

	if (cmac1_en) {
		ret = MAC_REG_W_OFLD(R_AX_ADDR_CAM_CTRL_C1, B_AX_ADDR_CAM_EN,
				     0, 0);
		if (ret != MACSUCCESS) {
			PLTFM_MSG_ERR("%s: config fail\n", __func__);
			return ret;
		}

		ret = MAC_REG_W_OFLD(R_AX_ADDR_CAM_DIS_INFO_C1, msk,
				     MAC_AX_NO_HIT_IDX, 1);
		if (ret != MACSUCCESS) {
			PLTFM_MSG_ERR("%s: config fail\n", __func__);
			return ret;
		}
	}

	return MACSUCCESS;
}

u32 set_mac_resp_ack_ofld(struct mac_ax_adapter *adapter, u32 ack)
{
	u32 ret;

	if (ack)
		ret = set_mac_do_resp_ack_ofld(adapter);
	else
		ret = set_mac_not_resp_ack_ofld(adapter);

	return ret;
}
#endif

u32 set_mac_resp_ack(struct mac_ax_adapter *adapter, u32 *ack)
{
	u32 ret;
#if MAC_AX_FW_REG_OFLD
	if (adapter->sm.fwdl == MAC_AX_FWDL_INIT_RDY)
		return ret = set_mac_resp_ack_ofld(adapter, *ack);
#endif

	ret = _set_mac_resp_ack(adapter, *ack, 0);
	if (ret == MACSUCCESS &&
	    check_mac_en(adapter, 1, MAC_AX_CMAC_SEL) == MACSUCCESS)
		ret = _set_mac_resp_ack(adapter, *ack, 1);

	return ret;
}

u32 get_mac_resp_ack(struct mac_ax_adapter *adapter, u32 *ack)
{
#define MAC_AX_ACK_CMAC1_SH 1
#define MAC_AX_ACK_CMAC0_SH 0
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);
	u8 cmac0 = 0, cmac1 = 0;

	cmac0 = !!(MAC_REG_R8(R_AX_ADDR_CAM_CTRL) & B_AX_ADDR_CAM_EN);
	if (check_mac_en(adapter, 1, MAC_AX_CMAC_SEL) == MACSUCCESS)
		cmac1 = !!(MAC_REG_R8(R_AX_ADDR_CAM_CTRL_C1) &
			   B_AX_ADDR_CAM_EN);

	*ack = cmac0 << MAC_AX_ACK_CMAC0_SH | cmac1 << MAC_AX_ACK_CMAC1_SH;

	return MACSUCCESS;
}

u8 get_addr_cam_size(struct mac_ax_adapter *adapter)
{
	if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852A) ||
	    is_chip_id(adapter, MAC_AX_CHIP_ID_8852B))
		return ADDR_CAM_ENT_LONG_SIZE;
	else
		return ADDR_CAM_ENT_SHORT_SIZE;
}

