/******************************************************************************
 *
 * Copyright(c) 2007 - 2020  Realtek Corporation.
 *
 * 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.
 *
 * The full GNU General Public License is included in this distribution in the
 * file called LICENSE.
 *
 * Contact Information:
 * wlanfae <wlanfae@realtek.com>
 * Realtek Corporation, No. 2, Innovation Road II, Hsinchu Science Park,
 * Hsinchu 300, Taiwan.
 *
 * Larry Finger <Larry.Finger@lwfinger.net>
 *
 *****************************************************************************/
#include "halbb_precomp.h"

#ifdef HALBB_FW_OFLD_SUPPORT
bool halbb_check_fw_ofld(struct bb_info *bb)
{
	bool ret = bb->phl_com->dev_cap.fw_cap.offload_cap & BIT0;

	BB_DBG(bb, DBG_FW_INFO, "FW ofld ret = %d\n", (u8)ret);
	return ret;
}

bool halbb_fw_delay(struct bb_info *bb, u32 val)
{
/* halbb_set_reg */
	struct rtw_mac_cmd cmd;
	u32 ret;

	cmd.type = RTW_MAC_DELAY_OFLD;
	cmd.lc = 0;
	cmd.value = val; /*delay us*/
	ret = rtw_hal_mac_add_cmd_ofld(bb->hal_com, &cmd);
	BB_DBG(bb, DBG_FW_INFO, "FW ofld delay:%x\n", val);
	if (ret) {
		BB_WARNING("IO offload fail: %d\n", ret);
		return false;
	}
	else {
		return true;
	}
}

bool halbb_fw_set_reg(struct bb_info *bb, u32 addr, u32 mask, u32 val, u8 lc)
{
/* halbb_set_reg */
	struct rtw_mac_cmd cmd;
	u32 ret;

	cmd.src = RTW_MAC_BB_CMD_OFLD;
	cmd.type = RTW_MAC_WRITE_OFLD;
	cmd.lc = lc;
	cmd.offset = (u16)addr;
	cmd.value = val;
	cmd.mask = mask;
	ret = rtw_hal_mac_add_cmd_ofld(bb->hal_com, &cmd);
	BB_DBG(bb, DBG_FW_INFO, "FW ofld addr:%x, val:%x, msk:%x\n", addr, val, mask);
	if (ret) {
		BB_WARNING("IO offload fail: %d\n", ret);
		return false;
	}
	else {
		return true;
	}
	
}

bool halbb_fw_set_reg_cmn(struct bb_info *bb, u32 addr, 
			            u32 mask, u32 val, enum phl_phy_idx phy_idx, u8 lc)
{
	bool ret = true;
	u32 val_mod = val;

	#ifdef HALBB_DBCC_SUPPORT
	if (bb->hal_com->dbcc_en && phy_idx == HW_PHY_1)
		addr += halbb_phy0_to_phy1_ofst(bb, addr);
	#endif

	ret = halbb_fw_set_reg(bb, addr, mask, val_mod, lc);
	return ret;
}

bool halbb_fwcfg_bb_phy_8852a_2(struct bb_info *bb, u32 addr, u32 data,
			    enum phl_phy_idx phy_idx)
{
#ifdef HALBB_DBCC_SUPPORT
	u32 ofst = 0;
#endif
	bool ret = true;

	if (addr == 0xfe) {
		halbb_delay_ms(bb, 50);
		BB_DBG(bb, DBG_INIT, "Delay 50 ms\n");
	} else if (addr == 0xfd) {
		halbb_delay_ms(bb, 5);
		BB_DBG(bb, DBG_INIT, "Delay 5 ms\n");
	} else if (addr == 0xfc) {
		halbb_delay_ms(bb, 1);
		BB_DBG(bb, DBG_INIT, "Delay 1 ms\n");
	} else if (addr == 0xfb) {
		halbb_delay_us(bb, 50);
		BB_DBG(bb, DBG_INIT, "Delay 50 us\n");
	} else if (addr == 0xfa) {
		halbb_delay_us(bb, 5);
		BB_DBG(bb, DBG_INIT, "Delay 5 us\n");
	} else if (addr == 0xf9) {
		halbb_delay_us(bb, 1);
		BB_DBG(bb, DBG_INIT, "Delay 1 us\n");
	} else {
		#ifdef HALBB_DBCC_SUPPORT
		if ((bb->hal_com->dbcc_en || bb->bb_dbg_i.cr_dbg_mode_en) &&
		    phy_idx == HW_PHY_1) {
			ofst = halbb_phy0_to_phy1_ofst(bb, addr);
			if (ofst == 0)
				return true;
			addr += ofst;
		} else {
			phy_idx = HW_PHY_0;
		}
		#endif

		/*FWOFLD in init BB reg flow */
		if (halbb_check_fw_ofld(bb)) {
			ret &= halbb_fw_set_reg(bb, addr, MASKDWORD, data, 0);
			BB_DBG(bb, DBG_INIT, "[REG FWOFLD]0x%04X = 0x%08X, ret = %d\n", addr, data, (u8)ret);
		}
		else {
			halbb_set_reg(bb, addr, MASKDWORD, data);
			#ifdef HALBB_DBCC_SUPPORT
			BB_DBG(bb, DBG_INIT, "[REG][%d]0x%04X = 0x%08X\n", phy_idx, addr, data);
			#else
			BB_DBG(bb, DBG_INIT, "[REG]0x%04X = 0x%08X\n", addr, data);
			#endif
		}

	}
	return ret;
}

bool halbb_fwofld_cck_en_8852a_2(struct bb_info *bb, bool cck_en,
			     enum phl_phy_idx phy_idx)
{
	bool ret = true;

	if (cck_en) {
		ret &= halbb_fw_set_reg(bb, 0x2344, BIT(31), 0, 0);
	} else {
		ret &= halbb_fw_set_reg(bb, 0x2344, BIT(31), 1, 0);
	}
	BB_DBG(bb, DBG_PHY_CONFIG, "[CCK Enable for PHY%d]\n", phy_idx);
	return ret;
}

bool halbb_fwofld_btg_8852a_2(struct bb_info *bb, bool btg)
{
	struct rtw_phl_com_t *phl = bb->phl_com;
	struct dev_cap_t *dev = &phl->dev_cap;
	bool ret = true;

	if (dev->rfe_type >= 50)
		return true;
	BB_DBG(bb, DBG_PHY_CONFIG, "<====== %s ======>\n", __func__);
	if (btg) {
		// Path A
		ret &= halbb_fw_set_reg(bb, 0x466c, BIT(18) | BIT(17), 0x1, 0);
		// Path B
		ret &= halbb_fw_set_reg(bb, 0x4740, BIT(18) | BIT(17), 0x3, 0);
		BB_DBG(bb, DBG_PHY_CONFIG, "[BT] Apply BTG Setting\n");
		// Apply Grant BT by TMAC Setting
		ret &= halbb_fw_set_reg(bb, 0x980, 0x1e0000, 0x0, 0);
		BB_DBG(bb, DBG_PHY_CONFIG, "[BT] Apply Grant BT by TMAC Setting\n");
		// Add BT share
		ret &= halbb_fw_set_reg(bb, 0x4978, BIT(14), 0x1, 0);
		ret &= halbb_fw_set_reg(bb, 0x4974, 0x3c00000, 0x2, 0);
		ret &= halbb_fw_set_reg(bb, 0x441c, BIT(31), 0x1, 0);
	} else {
		// Path A
		ret &= halbb_fw_set_reg(bb, 0x466c, BIT(18) | BIT(17), 0x0, 0);
		// Path B
		ret &= halbb_fw_set_reg(bb, 0x4740, BIT(18) | BIT(17), 0x0, 0);
		BB_DBG(bb, DBG_PHY_CONFIG, "[BT] Disable BTG Setting\n");
		// Ignore Grant BT by PMAC Setting
		ret &= halbb_fw_set_reg(bb, 0x980, 0x1e0000, 0xf, 0);
		ret &= halbb_fw_set_reg(bb, 0x980, 0x3c000000, 0x4, 0);
		BB_DBG(bb, DBG_PHY_CONFIG, "[BT] Ignore Grant BT by PMAC Setting\n");
		// Reset BT share
		ret &= halbb_fw_set_reg(bb, 0x4978, BIT(14), 0x0, 0);
		ret &= halbb_fw_set_reg(bb, 0x4974, 0x3c00000, 0x0, 0);
		ret &= halbb_fw_set_reg(bb, 0x441c, BIT(31), 0x0, 0);
	}
	return ret;
}

bool halbb_fw_5m_mask_8852a_2(struct bb_info *bb, u8 pri_ch, enum channel_width bw)
{
	bool mask_5m_low = false;
	bool mask_5m_en = false;
	bool ret = true;

	switch (bw) {
		case CHANNEL_WIDTH_40:
			/* Prich=1 : Mask 5M High
			   Prich=2 : Mask 5M Low */
			mask_5m_en = true;
			mask_5m_low = pri_ch == 2 ? true : false;
			break;
		case CHANNEL_WIDTH_80:
			/* Prich=3 : Mask 5M High
			   Prich=4 : Mask 5M Low 
			   Else    : Mask 5M Disable */
			mask_5m_en = ((pri_ch == 3) || (pri_ch == 4)) ? true : false;
			mask_5m_low = pri_ch == 4 ? true : false;
			break;
		default:
			mask_5m_en = false;
			break;
	}

	BB_DBG(bb, DBG_PHY_CONFIG, "[5M Mask] pri_ch = %d, bw = %d", pri_ch, bw);

	if (!mask_5m_en) {
		ret &= halbb_fw_set_reg(bb, 0x46b0, BIT(12), 0x0, 0);
		ret &= halbb_fw_set_reg(bb, 0x4784, BIT(12), 0x0, 0);
	} else {
		if (mask_5m_low) {
			ret &= halbb_fw_set_reg(bb, 0x46b0, 0x3f, 0x4, 0);
			ret &= halbb_fw_set_reg(bb, 0x46b0, BIT(12) | BIT(8) | BIT(6), 0x5, 0);
			ret &= halbb_fw_set_reg(bb, 0x4784, 0x3f, 0x4, 0);
			ret &= halbb_fw_set_reg(bb, 0x4784, BIT(12) | BIT(8) | BIT(6), 0x5, 0);
		} else {
			ret &= halbb_fw_set_reg(bb, 0x46b0, 0x3f, 0x4, 0);
			ret &= halbb_fw_set_reg(bb, 0x46b0, BIT(12) | BIT(8) | BIT(6), 0x6, 0);
			ret &= halbb_fw_set_reg(bb, 0x4784, 0x3f, 0x4, 0);
			ret &= halbb_fw_set_reg(bb, 0x4784, BIT(12) | BIT(8) | BIT(6), 0x6, 0);
		}
	}
	return ret;
}

bool halbb_fw_set_rf_reg_8852a_2(struct bb_info *bb, enum rf_path path,
			      u32 reg_addr, u32 bit_mask, u32 data, u8 lc)
{
/* halbb_write_rf_reg_8852a_2 */
	bool ret = true;
	u32 direct_addr = 0;
	u32 offset_write_rf[2] = {0xc000, 0xd000};

	/*==== Error handling ====*/
	if (path > RF_PATH_B) {
		BB_WARNING("[%s] Unsupported path (%d)\n", __func__, path);
		return false;
	}

	/*==== Calculate offset ====*/
	reg_addr &= 0xff;
	direct_addr = offset_write_rf[path] + (reg_addr << 2);

	/*==== RF register only has 20bits ====*/
	bit_mask &= RFREGOFFSETMASK;
	halbb_fw_delay(bb, 1);
	/*==== Write RF register directly ====*/
	ret = halbb_fw_set_reg(bb, direct_addr, bit_mask, data, lc);

	/*halbb_delay_us(bb, 1);*/
	halbb_fw_delay(bb, 1);

	BB_DBG(bb, DBG_FW_INFO, "FW OFLD RF-%d 0x%x = 0x%x , bit mask = 0x%x, ret = %d\n",
	       path, reg_addr, data, bit_mask, (u8)ret);

	return ret;
}
bool halbb_fw_set_efuse_8852a_2(struct bb_info *bb, u8 central_ch, enum rf_path path, enum phl_phy_idx phy_idx)
{
	u8 band;
	bool ret = true;
	u8 upper_bound = 60; // S(7,4): 3.75
	u8 lower_bound = 64; // S(7,4): -4
	s32 hidden_efuse = 0, normal_efuse = 0, normal_efuse_cck = 0;
	s32 tmp = 0;
	u32 efuse_5g[BB_PATH_MAX_8852A] = {0x4624, 0x46f8};
	u32 efuse_5g_mask = 0x07e00000;
	u32 efuse_2g[BB_PATH_MAX_8852A] = {0x4628, 0x46fc};
	u32 efuse_2g_mask = 0x0000003f;

	BB_DBG(bb, DBG_PHY_CONFIG, "<====== %s ======>\n", __func__);
	
	// 2G Band: (0)
	// 5G Band: (1):Low, (2): Mid, (3):High
	if (central_ch >= 0 && central_ch <= 14)
		band = 0;
	else if (central_ch >= 36 && central_ch <= 64)
		band = 1;
	else if (central_ch >= 100 && central_ch <= 144)
		band = 2;
	else if (central_ch >= 149 && central_ch <= 177)
		band = 3;
	else
		band = 0;

	// === [Set hidden efuse] === //
	if (bb->bb_efuse_i.hidden_efuse_check) {
		for (path = RF_PATH_A; path < BB_PATH_MAX_8852A; path++) {
			if (central_ch >= 0 && central_ch <= 14) {
				hidden_efuse = (bb->bb_efuse_i.gain_cg[path][band] << 2);
				ret &= halbb_fw_set_reg(bb, efuse_2g[path], efuse_2g_mask, (hidden_efuse & 0x3f), 0);
			} else {
				hidden_efuse = (bb->bb_efuse_i.gain_cg[path][band] << 2);
				ret &= halbb_fw_set_reg(bb, efuse_5g[path], efuse_5g_mask, (hidden_efuse & 0x3f), 0);
			}
		}
		BB_DBG(bb, DBG_INIT, "[Efuse][FWOFLD] Hidden efuse dynamic setting!!\n");
	} else {
		BB_DBG(bb, DBG_INIT, "[Efuse][FWOFLD] Values of hidden efuse are all 0xff, bypass dynamic setting!!\n");
	}	

	// === [Set normal efuse] === //
	if (bb->bb_efuse_i.normal_efuse_check) {
		if ((bb->rx_path == RF_PATH_A) || (bb->rx_path == RF_PATH_AB)) {
			normal_efuse = bb->bb_efuse_i.gain_offset[RF_PATH_A][band + 1];
			normal_efuse_cck = bb->bb_efuse_i.gain_offset[RF_PATH_A][0];
		} else if (bb->rx_path == RF_PATH_B) {
			normal_efuse = bb->bb_efuse_i.gain_offset[RF_PATH_B][band + 1];
			normal_efuse_cck = bb->bb_efuse_i.gain_offset[RF_PATH_B][0];
		}
		normal_efuse *= (-1);
		normal_efuse_cck *= (-1);

		// OFDM normal efuse
		if (normal_efuse > 3) {
			tmp = (normal_efuse << 4) + (bb->bb_efuse_i.efuse_ofst << 2) - upper_bound;
			ret &= halbb_fw_set_reg_cmn(bb, 0x494c, 0xf8000000, ((tmp >> 2) & 0x1f), phy_idx, 0);
			ret &= halbb_fw_set_reg_cmn(bb, 0x4964, 0xfe00000, (tmp & 0x7f), phy_idx, 0);
			// Set efuse
			ret &= halbb_fw_set_reg_cmn(bb, 0x4960, 0xfe00000, (upper_bound & 0x7f), phy_idx, 0);
			ret &= halbb_fw_set_reg_cmn(bb, 0x4964, 0x7f, (upper_bound & 0x7f), phy_idx, 0);
			ret &= halbb_fw_set_reg_cmn(bb, 0x4964, 0x3f80, (upper_bound & 0x7f), phy_idx, 0);
		} else if (normal_efuse < -4) {
			tmp = (normal_efuse << 4) + (bb->bb_efuse_i.efuse_ofst << 2) + lower_bound;
			// r_1_rpl_bias_comp
			ret &= halbb_fw_set_reg_cmn(bb, 0x494c, 0xf8000000, ((tmp >> 2) & 0x1f), phy_idx, 0);
			// r_tb_rssi_bias_comp
			ret &= halbb_fw_set_reg_cmn(bb, 0x4964, 0xfe00000, (tmp & 0x7f), phy_idx, 0);
			// Set efuse
			ret &= halbb_fw_set_reg_cmn(bb, 0x4960, 0xfe00000, (lower_bound & 0x7f), phy_idx, 0);
			ret &= halbb_fw_set_reg_cmn(bb, 0x4964, 0x7f, (lower_bound & 0x7f), phy_idx, 0);
			ret &= halbb_fw_set_reg_cmn(bb, 0x4964, 0x3f80, (lower_bound & 0x7f), phy_idx, 0);
		} else {
			ret &= halbb_fw_set_reg_cmn(bb, 0x494c, 0xf8000000, (bb->bb_efuse_i.efuse_ofst & 0x1f), phy_idx, 0);
			ret &= halbb_fw_set_reg_cmn(bb, 0x4964, 0xfe00000, (bb->bb_efuse_i.efuse_ofst_tb & 0x7f), phy_idx, 0);
			// Set efuse
			ret &= halbb_fw_set_reg_cmn(bb, 0x4960, 0xfe00000, ((normal_efuse << 4) & 0x7f), phy_idx, 0);
			ret &= halbb_fw_set_reg_cmn(bb, 0x4964, 0x7f, ((normal_efuse << 4) & 0x7f), phy_idx, 0);
			ret &= halbb_fw_set_reg_cmn(bb, 0x4964, 0x3f80, ((normal_efuse << 4) & 0x7f), phy_idx, 0);
		}

		// CCK normal efuse
		if (band == 0) {
			tmp = normal_efuse_cck << 3;
			ret &= halbb_fw_set_reg(bb, 0x23ac, 0x7f, (tmp & 0x7f), 0);
		}

		BB_DBG(bb, DBG_INIT, "[Efuse][FWOFLD] Normal efuse dynamic setting!!\n");
	} else {
		BB_DBG(bb, DBG_INIT, "[Efuse][FWOFLD] Values of normal efuse are all 0xff, bypass dynamic setting!!\n");
	}
	return ret;
}
bool halbb_fw_set_gain_error_8852a_2(struct bb_info *bb, u8 central_ch)
{
	bool ret = true;
	u8 band;
	u8 path = 0, lna_idx = 0, tia_idx = 0;
	s32 tmp = 0;
	u32 lna_err_a_5g[BB_PATH_MAX_8852A][7] = {{0x462c, 0x462c, 0x4630,
						   0x4634, 0x4634, 0x4638,
						   0x4638}, {0x4700, 0x4700,
						   0x4704, 0x4708, 0x4708,
						   0x470c, 0x470c}};
	u32 lna_err_a_5g_mask[7] = {0x00000fc0, 0x3f000000, 0x0003f000,
				    0x0000003f, 0x00fc0000, 0x00000fc0,
				    0x3f000000};
	u32 lna_err_a_2g[BB_PATH_MAX_8852A][7] = {{0x462c, 0x4630, 0x4630,
						   0x4634, 0x4634, 0x4638,
						   0x463c}, {0x4700, 0x4704,
						   0x4704, 0x4708, 0x4708,
						   0x470c, 0x4710}};
	u32 lna_err_a_2g_mask[7] = {0x0003f000, 0x0000003f, 0x00fc0000,
				    0x00000fc0, 0x3f000000, 0x0003f000,
				    0x0000003f};
	u32 tia_err_a_5g[BB_PATH_MAX_8852A][2] = {{0x4640, 0x4644}, {0x4714,
						   0x4718}};
	u32 tia_err_a_5g_mask[2] = {0x0003f000, 0x0000003f};
	u32 tia_err_a_2g[BB_PATH_MAX_8852A][2] = {{0x4640, 0x4644}, {0x4714,
						   0x4718}};
	u32 tia_err_a_2g_mask[2] = {0x00fc0000, 0x00000fc0};

	BB_DBG(bb, DBG_PHY_CONFIG, "<====== %s ======>\n", __func__);

	// 2G Band: (0)
	// 5G Band: (1):Low, (2): Mid, (3):High
	if (central_ch >= 0 && central_ch <= 14)
		band = 0;
	else if (central_ch >= 36 && central_ch <= 64)
		band = 1;
	else if (central_ch >= 100 && central_ch <= 144)
		band = 2;
	else if (central_ch >= 149 && central_ch <= 177)
		band = 3;
	else
		band = 0;

	if (central_ch >= 0 && central_ch <= 14) {
		for (path = RF_PATH_A; path < BB_PATH_MAX_8852A; path++) {
			// Set 2G LNA Gain Err
			for (lna_idx = 0; lna_idx < 7; lna_idx++) {
				tmp = bb->bb_gain_i.lna_gain[band][path][lna_idx];

				ret &= halbb_fw_set_reg(bb, lna_err_a_2g[path][lna_idx],
					      lna_err_a_2g_mask[lna_idx], (tmp & 0x3f), 0);
				
			}
			// Set 2G TIA Gain Err
			for (tia_idx = 0; tia_idx < 2; tia_idx++) {
				tmp = bb->bb_gain_i.tia_gain[band][path][tia_idx];
				ret &= halbb_fw_set_reg(bb, tia_err_a_2g[path][tia_idx],
					      tia_err_a_2g_mask[tia_idx], (tmp & 0x3f), 0);
			}
		}
	} else {
		for (path = RF_PATH_A; path < BB_PATH_MAX_8852A; path++) {
			// Set 5G LNA Gain Err
			for (lna_idx = 0; lna_idx < 7; lna_idx++) {
				tmp = bb->bb_gain_i.lna_gain[band][path][lna_idx];

				ret &= halbb_fw_set_reg(bb, lna_err_a_5g[path][lna_idx],
					      lna_err_a_5g_mask[lna_idx], (tmp & 0x3f), 0);
			}
			// Set 5G TIA Gain Err
			for (tia_idx = 0; tia_idx < 2; tia_idx++) {
				tmp = bb->bb_gain_i.tia_gain[band][path][tia_idx];
				ret &= halbb_fw_set_reg(bb, tia_err_a_5g[path][tia_idx],
					      tia_err_a_5g_mask[tia_idx], (tmp & 0x3f), 0);
			}
		}
	}
	return ret;
}
bool halbb_fwofld_sco_cck_8852a_2(struct bb_info *bb, u8 pri_ch)
{
	u32 sco_barker_threshold[14] = {0x1cfea, 0x1d0e1, 0x1d1d7, 0x1d2cd,
					0x1d3c3, 0x1d4b9, 0x1d5b0, 0x1d6a6,
					0x1d79c, 0x1d892, 0x1d988, 0x1da7f,
					0x1db75, 0x1ddc4};
	u32 sco_cck_threshold[14] = {0x27de3, 0x27f35, 0x28088, 0x281da,
				     0x2832d, 0x2847f, 0x285d2, 0x28724,
				     0x28877, 0x289c9, 0x28b1c, 0x28c6e,
				     0x28dc1, 0x290ed};
	bool rpt = true;

	if (pri_ch > 14) {
		BB_DBG(bb, DBG_PHY_CONFIG, "[CCK SCO Fail]");
		/*Return true because its not FW offload fail*/
		return true;
	}

	rpt &= halbb_fw_set_reg(bb, 0x23b0, 0x7ffff, sco_barker_threshold[pri_ch - 1], 0);
	rpt &= halbb_fw_set_reg(bb, 0x23b4, 0x7ffff, sco_cck_threshold[pri_ch - 1], 0);
	BB_DBG(bb, DBG_PHY_CONFIG, "[CCK SCO Success]");
	return rpt;
}

bool halbb_fwofld_rf_ch_8852a_2(struct bb_info *bb, u8 central_ch, enum rf_path path,
			    bool *is_2g_ch, u32 *rf_reg18)
{
	//u32 rf_reg18 = 0;
	bool ret = true;

	/*rf_reg18 = halbb_read_rf_reg_8852a_2(bb, path, 0x18, RFREGOFFSETMASK);*/
	/*==== [Error handling] ====*/
	if (*rf_reg18 == INVALID_RF_DATA) {
		BB_WARNING("Invalid RF_0x18 for Path-%d\n", path);
		return false;
	}
	*is_2g_ch = (central_ch <= 14) ? true : false;
	/*==== [Set RF Reg 0x18] ====*/
	*rf_reg18 &= ~0x303ff; /*[17:16],[9:8],[7:0]*/
	*rf_reg18 |= central_ch; /* Channel*/
	/*==== [5G Setting] ====*/
	if (!*is_2g_ch)
		*rf_reg18 |= (BIT(16) | BIT(8));
	/*ret &= halbb_fw_set_rf_reg_8852a_2(bb, path, 0x18, RFREGOFFSETMASK, *rf_reg18);*/

	BB_DBG(bb, DBG_PHY_CONFIG, "[Success][ch_setting] CH: %d for Path-%d\n",
	       central_ch, path);
	return ret;
}

bool halbb_fwofld_ch_8852a_2(struct bb_info *bb, u8 central_ch,
			 enum phl_phy_idx phy_idx, u32 *path0_rf18, u32 *path1_rf18)
{
	u8 sco_comp;
	bool is_2g_ch = false;
	bool ret = true;

	if (bb->is_disable_phy_api) {
		BB_DBG(bb, DBG_PHY_CONFIG, "[%s] Disable PHY API\n", __func__);
		return true;
	}
	/*==== Error handling ====*/
	if ((central_ch > 14 && central_ch < 36) ||
	    (central_ch > 64 && central_ch < 100) ||
	    (central_ch > 144 && central_ch < 149) ||
	    central_ch > 177 ) {
		BB_WARNING("Invalid CH:%d for PHY%d\n", central_ch,
			   phy_idx);
		return false;
	}

	if (phy_idx == HW_PHY_0) {
		/*============== [Path A] ==============*/
		ret &= halbb_fwofld_rf_ch_8852a_2(bb, central_ch, RF_PATH_A, &is_2g_ch, path0_rf18);
		//------------- [Mode Sel - Path A] ------------//
		if (is_2g_ch)
			ret &= halbb_fw_set_reg_cmn(bb, 0x4644, BIT(31) | BIT(30), 1,
					  phy_idx, 0);
		else
			ret &= halbb_fw_set_reg_cmn(bb, 0x4644, BIT(31) | BIT(30), 0,
					  phy_idx, 0);

		/*============== [Path B] ==============*/
		if (!bb->hal_com->dbcc_en) {
			ret &= halbb_fwofld_rf_ch_8852a_2(bb, central_ch, RF_PATH_B,
					       &is_2g_ch, path1_rf18);
			//------------- [Mode Sel - Path B] ------------//
			if (is_2g_ch)
				ret &= halbb_fw_set_reg_cmn(bb, 0x4718, BIT(31) | BIT(30),
						  1, phy_idx, 0);
			else
				ret &= halbb_fw_set_reg_cmn(bb, 0x4718, BIT(31) | BIT(30),
						  0, phy_idx, 0);
		} else { /*==== [Phy0 config at 2/5G] ====*/
			if (is_2g_ch)
				ret &= halbb_fw_set_reg(bb, 0x4970, BIT(1), 0, 0);
			else
				ret &= halbb_fw_set_reg(bb, 0x4970, BIT(1), 1, 0);
		}
		/*==== [SCO compensate fc setting] ====*/
		sco_comp = halbb_sco_mapping_8852a_2(bb, central_ch);
		ret &= halbb_fw_set_reg_cmn(bb, 0x4974, 0x7f, sco_comp, phy_idx, 0);
	} else {
		/*============== [Path B] ==============*/
		ret &= halbb_fwofld_rf_ch_8852a_2(bb, central_ch, RF_PATH_B, &is_2g_ch, path0_rf18);

		//------------- [Mode Sel - Path B] ------------//
		if (is_2g_ch)
			ret &= halbb_fw_set_reg_cmn(bb, 0x4718, BIT(31) | BIT(30), 1,
					  phy_idx, 0);
		else
			ret &= halbb_fw_set_reg_cmn(bb, 0x4718, BIT(31) | BIT(30), 0,
					  phy_idx, 0);

		/*==== [SCO compensate fc setting] ====*/
		sco_comp = halbb_sco_mapping_8852a_2(bb, central_ch);
		ret &= halbb_fw_set_reg_cmn(bb, 0x4974, 0x7f, sco_comp, phy_idx, 0);
	}

	/* === Bandedge ===*/
	if (is_2g_ch)
		ret &= halbb_fw_set_reg_cmn(bb, 0x4498, BIT(30), 1, phy_idx, 0);
	else
		ret &= halbb_fw_set_reg_cmn(bb, 0x4498, BIT(30), 0, phy_idx, 0);
	/* === CCK Parameters === */
	if (central_ch == 14) {
		ret &= halbb_fw_set_reg(bb, 0x2300, 0xffffff, 0x3b13ff, 0);
		ret &= halbb_fw_set_reg(bb, 0x2304, 0xffffff, 0x1c42de, 0);
		ret &= halbb_fw_set_reg(bb, 0x2308, 0xffffff, 0xfdb0ad, 0);
		ret &= halbb_fw_set_reg(bb, 0x230c, 0xffffff, 0xf60f6e, 0);
		ret &= halbb_fw_set_reg(bb, 0x2310, 0xffffff, 0xfd8f92, 0);
		ret &= halbb_fw_set_reg(bb, 0x2314, 0xffffff, 0x2d011, 0);
		ret &= halbb_fw_set_reg(bb, 0x2318, 0xffffff, 0x1c02c, 0);
		ret &= halbb_fw_set_reg(bb, 0x231c, 0xffffff, 0xfff00a, 0);
	} else {	
		ret &= halbb_fw_set_reg(bb, 0x2300, 0xffffff, 0x3d23ff, 0);
		ret &= halbb_fw_set_reg(bb, 0x2304, 0xffffff, 0x29b354, 0);
		ret &= halbb_fw_set_reg(bb, 0x2308, 0xffffff, 0xfc1c8, 0);
		ret &= halbb_fw_set_reg(bb, 0x230c, 0xffffff, 0xfdb053, 0);
		ret &= halbb_fw_set_reg(bb, 0x2310, 0xffffff, 0xf86f9a, 0);
		ret &= halbb_fw_set_reg(bb, 0x2314, 0xffffff, 0xfaef92, 0);
		ret &= halbb_fw_set_reg(bb, 0x2318, 0xffffff, 0xfe5fcc, 0);
		ret &= halbb_fw_set_reg(bb, 0x231c, 0xffffff, 0xffdff5, 0);
	}
	/* === Set Gain Error === */
	ret &= halbb_fw_set_gain_error_8852a_2(bb, central_ch);
	/* === Set Efuse === */
	ret &= halbb_fw_set_efuse_8852a_2(bb, central_ch, bb->rx_path, phy_idx);

	/* === Set Ch idx report in phy-sts === */
	/* write for last cmd*/
	ret &= halbb_fw_set_reg_cmn(bb, 0x0734, 0x0ff0000, central_ch, phy_idx, 0);

	BB_DBG(bb, DBG_PHY_CONFIG, "[Switch CH] CH: %d for PHY%d, ret = %d\n",
	       central_ch, phy_idx, (u8)ret);
	return ret;
}

bool halbb_fw_bw_setting_8852a_2(struct bb_info *bb, enum channel_width bw,
			    enum rf_path path, u32 *rf_reg18)
{
	bool ret = true;
	/*u32 rf_reg18 = 0;*/
	u32 adc_sel[2] = {0x12d0, 0x32d0};
	u32 wbadc_sel[2] = {0x12ec, 0x32ec};

	/*rf_reg18 = halbb_read_rf_reg_8852a_2(bb, path, 0x18, RFREGOFFSETMASK);*/
	/*==== [Error handling] ====*/
	if (*rf_reg18 == INVALID_RF_DATA) {
		BB_WARNING("Invalid RF_0x18 for Path-%d\n", path);
		return false;
	}
	*rf_reg18 &= ~(BIT(11) | BIT(10));
	/*==== [Switch bandwidth] ====*/
	switch (bw) {
	case CHANNEL_WIDTH_5:
	case CHANNEL_WIDTH_10:
	case CHANNEL_WIDTH_20:
		if (bw == CHANNEL_WIDTH_5) {
			/*ADC clock = 20M & WB ADC clock = 40M for BW5 */
			ret &= halbb_fw_set_reg(bb, adc_sel[path], 0x6000, 0x1, 0);
			ret &= halbb_fw_set_reg(bb, wbadc_sel[path], 0x30, 0x0, 0);
		} else if (bw == CHANNEL_WIDTH_10) {
			/*ADC clock = 40M & WB ADC clock = 80M for BW10 */
			ret &= halbb_fw_set_reg(bb, adc_sel[path], 0x6000, 0x2, 0);
			ret &= halbb_fw_set_reg(bb, wbadc_sel[path], 0x30, 0x1, 0);
		} else if (bw == CHANNEL_WIDTH_20) {
			/*ADC clock = 80M & WB ADC clock = 160M for BW20 */
			ret &= halbb_fw_set_reg(bb, adc_sel[path], 0x6000, 0x0, 0);
			ret &= halbb_fw_set_reg(bb, wbadc_sel[path], 0x30, 0x2, 0);
		}

		/*RF bandwidth */
		*rf_reg18 |= (BIT(11) | BIT(10));

		break;
	case CHANNEL_WIDTH_40:
		/*ADC clock = 80M & WB ADC clock = 160M for BW40 */
		ret &= halbb_fw_set_reg(bb, adc_sel[path], 0x6000, 0x0, 0);
		ret &= halbb_fw_set_reg(bb, wbadc_sel[path], 0x30, 0x2, 0);

		/*RF bandwidth */
		*rf_reg18 |= BIT(11);

		break;
	case CHANNEL_WIDTH_80:
		/*ADC clock = 160M & WB ADC clock = 160M for BW40 */
		ret &= halbb_fw_set_reg(bb, adc_sel[path], 0x6000, 0x0, 0);
		ret &= halbb_fw_set_reg(bb, wbadc_sel[path], 0x30, 0x2, 0);

		/*RF bandwidth */
		*rf_reg18 |= BIT(10);

		break;
	default:
		BB_WARNING("Fail to set ADC\n");
	}

	/*==== [Write RF register] ====*/
	/*ret &= halbb_fw_set_rf_reg_8852a_2(bb, path, 0x18, RFREGOFFSETMASK, rf_reg18);*/
	BB_DBG(bb, DBG_PHY_CONFIG,
	       "[bw_setting] ADC setting for Path-%d\n, ret = %d", path, (u8)ret);
	return ret;
}

bool halbb_fwofld_bw_8852a_2(struct bb_info *bb, u8 pri_ch, enum channel_width bw,
			 enum phl_phy_idx phy_idx, u32 *path0_rf18, u32 *path1_rf18)
{
	bool ret = true;

	if (bb->is_disable_phy_api) {
		BB_DBG(bb, DBG_PHY_CONFIG, "[%s] Disable PHY API\n", __func__);
		return true;
	}

	/*==== Error handling ====*/
	if (bw >= CHANNEL_WIDTH_MAX || (bw == CHANNEL_WIDTH_40 && pri_ch > 2) ||
	    (bw == CHANNEL_WIDTH_80 && pri_ch > 4)) {
		BB_WARNING("Fail to switch bw(bw:%d, pri ch:%d)\n", bw,
			   pri_ch);
		return true;
	}

	/*==== Switch bandwidth ====*/
	switch (bw) {
	case CHANNEL_WIDTH_5:
	case CHANNEL_WIDTH_10:
	case CHANNEL_WIDTH_20:
		if (bw == CHANNEL_WIDTH_5) {
			/*RF_BW:[31:30]=0x0 */
			ret &= halbb_fw_set_reg_cmn(bb, 0x4974, 0xC0000000, 0x0,
					  phy_idx, 0);
			/*small BW:[13:12]=0x1 */
			ret &= halbb_fw_set_reg_cmn(bb, 0x4978, 0x3000, 0x1, phy_idx, 0);
			/*Pri ch:[11:8]=0x0 */
			ret &= halbb_fw_set_reg_cmn(bb, 0x4978, 0xf00, 0x0, phy_idx, 0);
		} else if (bw == CHANNEL_WIDTH_10) {
			/*RF_BW:[31:30]=0x0 */
			ret &= halbb_fw_set_reg_cmn(bb, 0x4974, 0xC0000000, 0x0,
					  phy_idx, 0);
			/*small BW:[13:12]=0x2 */
			ret &= halbb_fw_set_reg_cmn(bb, 0x4978, 0x3000, 0x2, phy_idx, 0);
			/*Pri ch:[11:8]=0x0 */
			ret &= halbb_fw_set_reg_cmn(bb, 0x4978, 0xf00, 0x0, phy_idx, 0);
		} else if (bw == CHANNEL_WIDTH_20) {
			/*RF_BW:[31:30]=0x0 */
			ret &= halbb_fw_set_reg_cmn(bb, 0x4974, 0xC0000000, 0x0,
					  phy_idx, 0);
			/*small BW:[13:12]=0x0 */
			ret &= halbb_fw_set_reg_cmn(bb, 0x4978, 0x3000, 0x0, phy_idx, 0);
			/*Pri ch:[11:8]=0x0 */
			ret &= halbb_fw_set_reg_cmn(bb, 0x4978, 0xf00, 0x0, phy_idx, 0);
		}

		break;
	case CHANNEL_WIDTH_40:
		/*RF_BW:[31:30]=0x1 */
		ret &= halbb_fw_set_reg_cmn(bb, 0x4974, 0xC0000000, 0x1, phy_idx, 0);
		/*small BW:[13:12]=0x0 */
		ret &= halbb_fw_set_reg_cmn(bb, 0x4978, 0x3000, 0x0, phy_idx, 0);
		/*Pri ch:[11:8] */
		ret &= halbb_fw_set_reg_cmn(bb, 0x4978, 0xf00, pri_ch, phy_idx, 0);
		/*CCK primary channel */
		if (pri_ch == 1)
			ret &= halbb_fw_set_reg(bb, 0x237c, BIT(0), 1, 0);
		else
			ret &= halbb_fw_set_reg(bb, 0x237c, BIT(0), 0, 0);

		break;
	case CHANNEL_WIDTH_80:
		/*RF_BW:[31:30]=0x2 */
		ret &= halbb_fw_set_reg_cmn(bb, 0x4974, 0xC0000000, 0x2, phy_idx, 0);
		/*small BW:[13:12]=0x0 */
		ret &= halbb_fw_set_reg_cmn(bb, 0x4978, 0x3000, 0x0, phy_idx, 0);
		/*Pri ch:[11:8] */
		ret &= halbb_fw_set_reg_cmn(bb, 0x4978, 0xf00, pri_ch, phy_idx, 0);

		break;
	default:
		BB_WARNING("Fail to switch bw (bw:%d, pri ch:%d)\n", bw,
			   pri_ch);
	}

	if (phy_idx == HW_PHY_0) {
		/*============== [Path A] ==============*/
		ret &= halbb_fw_bw_setting_8852a_2(bb, bw, RF_PATH_A, path0_rf18);
		/*============== [Path B] ==============*/
		if (!bb->hal_com->dbcc_en)
			ret &= halbb_fw_bw_setting_8852a_2(bb, bw, RF_PATH_B, path1_rf18);
	} else {
		/*============== [Path B] ==============*/
		ret &= halbb_fw_bw_setting_8852a_2(bb, bw, RF_PATH_B, path1_rf18);
	}

	BB_DBG(bb, DBG_PHY_CONFIG,
		  "[Switch BW] BW: %d for PHY%d\n, ret = %d", bw, phy_idx, (u8)ret);

	return ret;
}

bool halbb_fwofld_bw_ch_8852a_2(struct bb_info *bb, u8 pri_ch, u8 central_ch,
			    enum channel_width bw, enum phl_phy_idx phy_idx)
{
	bool rpt = true;
	bool cck_en = false;
	bool is_2g_ch = false;
	u8 pri_ch_idx = 0;
	u32 path0_rf18 = 0;
	u32 path1_rf18 = 0;

	/*==== [Set pri_ch idx] ====*/
	if (central_ch <= 14) {
		// === 2G === //
		switch (bw) {
		case CHANNEL_WIDTH_20:
			break;

		case CHANNEL_WIDTH_40:
			pri_ch_idx = pri_ch > central_ch ? 1 : 2;
			break;

		default:
			break;
		}

		/*==== [CCK SCO Compesate] ====*/
		rpt &= halbb_fwofld_sco_cck_8852a_2(bb, pri_ch);

		cck_en = true;
		is_2g_ch = true;
	} else {
		// === 5G === //
		switch (bw) {
		case CHANNEL_WIDTH_20:
			break;

		case CHANNEL_WIDTH_40:
		case CHANNEL_WIDTH_80:
			if (pri_ch > central_ch)
				pri_ch_idx = (pri_ch - central_ch) >> 1;
			else
				pri_ch_idx = ((central_ch - pri_ch) >> 1) + 1;
			break;

		default:
			break;
		}
		cck_en = false;
		is_2g_ch = false;
	}
	if (!bb->hal_com->dbcc_en) {
		/*============== [Path A] ==============*/
		path0_rf18 = halbb_read_rf_reg_8852a_2(bb, RF_PATH_A, 0x18, RFREGOFFSETMASK);
		/*============== [Path B] ==============*/
		path1_rf18 = halbb_read_rf_reg_8852a_2(bb, RF_PATH_B, 0x18, RFREGOFFSETMASK);
	} else {
		if (phy_idx == HW_PHY_0) {
			/*============== [Path A] ==============*/
			path0_rf18 = halbb_read_rf_reg_8852a_2(bb, RF_PATH_A, 0x18, RFREGOFFSETMASK);
		}
		else {
			/*============== [Path B] ==============*/
			path1_rf18 = halbb_read_rf_reg_8852a_2(bb, RF_PATH_B, 0x18, RFREGOFFSETMASK);
		}
	}
	/*BB_WARNING("RF a/b = %x , %x", path0_rf18, path1_rf18);*/
	/*==== [Switch BW] ====*/
	rpt &= halbb_fwofld_bw_8852a_2(bb, pri_ch_idx, bw, phy_idx, &path0_rf18, &path1_rf18);
	/*BB_WARNING("SwBW : RF a/b = %x , %x", path0_rf18, path1_rf18);*/
	/*==== [Switch CH] ====*/
	rpt &= halbb_fwofld_ch_8852a_2(bb, central_ch, phy_idx, &path0_rf18, &path1_rf18);
	/*BB_WARNING("SwCH : RF a/b = %x , %x", path0_rf18, path1_rf18);*/
	if (!bb->hal_com->dbcc_en) {
		/*============== [Path A] ==============*/
		rpt &= halbb_fw_set_rf_reg_8852a_2(bb, RF_PATH_A, 0x18, RFREGOFFSETMASK, path0_rf18, 0);
		/*============== [Path B] ==============*/
		rpt &= halbb_fw_set_rf_reg_8852a_2(bb, RF_PATH_B, 0x18, RFREGOFFSETMASK, path1_rf18, 0);
	} else {
		if (phy_idx == HW_PHY_0) {
			/*============== [Path A] ==============*/
			rpt &= halbb_fw_set_rf_reg_8852a_2(bb, RF_PATH_A, 0x18, RFREGOFFSETMASK, path0_rf18, 0);
		} else {
			/*============== [Path B] ==============*/
			rpt &= halbb_fw_set_rf_reg_8852a_2(bb, RF_PATH_B, 0x18, RFREGOFFSETMASK, path1_rf18, 0);
		}
	}
	/*==== [CCK Enable / Disable] ====*/
	rpt &= halbb_fwofld_cck_en_8852a_2(bb, cck_en, phy_idx);
	/*==== [Spur elimination] ====*/
	if (central_ch == 153) {
		rpt &= halbb_fw_set_reg(bb, 0x469c, 0xfff, 0x210, 0);
		rpt &= halbb_fw_set_reg(bb, 0x4770, 0xfff, 0x210, 0);
		rpt &= halbb_fw_set_reg(bb, 0x42ac, 0xfff, 0x7c0, 0);
		rpt &= halbb_fw_set_reg(bb, 0x469c, BIT(12), 0x1, 0);
		rpt &= halbb_fw_set_reg(bb, 0x4770, BIT(12), 0x1, 0);
		rpt &= halbb_fw_set_reg(bb, 0x42c4, BIT(23), 0x1, 0);
	} else if (central_ch == 151) {
		rpt &= halbb_fw_set_reg(bb, 0x469c, 0xfff, 0x210, 0);
		rpt &= halbb_fw_set_reg(bb, 0x4770, 0xfff, 0x210, 0);
		rpt &= halbb_fw_set_reg(bb, 0x42ac, 0xfff, 0x40, 0);
		rpt &= halbb_fw_set_reg(bb, 0x469c, BIT(12), 0x1, 0);
		rpt &= halbb_fw_set_reg(bb, 0x4770, BIT(12), 0x1, 0);
		rpt &= halbb_fw_set_reg(bb, 0x42c4, BIT(23), 0x1, 0);
	} else if (central_ch == 155) {
		rpt &= halbb_fw_set_reg(bb, 0x469c, 0xfff, 0x2d0, 0);
		rpt &= halbb_fw_set_reg(bb, 0x4770, 0xfff, 0x2d0, 0);
		rpt &= halbb_fw_set_reg(bb, 0x42ac, 0xfff, 0x740, 0);
		rpt &= halbb_fw_set_reg(bb, 0x469c, BIT(12), 0x1, 0);
		rpt &= halbb_fw_set_reg(bb, 0x4770, BIT(12), 0x1, 0);
		rpt &= halbb_fw_set_reg(bb, 0x42c4, BIT(23), 0x1, 0);
	} else {
		rpt &= halbb_fw_set_reg(bb, 0x469c, BIT(12), 0x0, 0);
		rpt &= halbb_fw_set_reg(bb, 0x4770, BIT(12), 0x0, 0);
		rpt &= halbb_fw_set_reg(bb, 0x42c4, BIT(23), 0x0, 0);
	}

	if (is_2g_ch && ((bb->rx_path == RF_PATH_B) || (bb->rx_path == RF_PATH_AB)))
		rpt &=halbb_fwofld_btg_8852a_2(bb, true);
	else
		rpt &=halbb_fwofld_btg_8852a_2(bb, false);

	/* Dynamic 5M Mask Setting */
	rpt &=halbb_fw_5m_mask_8852a_2(bb, pri_ch, bw);

	/*==== [TSSI reset] ====*/
	if (!bb->hal_com->dbcc_en) {
		// Path A
		rpt &= halbb_fw_set_reg(bb, 0x58dc, BIT(31) | BIT(30), 0x1, 0);
		rpt &= halbb_fw_set_reg(bb, 0x58dc, BIT(31) | BIT(30), 0x3, 0);
		// Path B
		rpt &= halbb_fw_set_reg(bb, 0x78dc, BIT(31) | BIT(30), 0x1, 0);
		rpt &= halbb_fw_set_reg(bb, 0x78dc, BIT(31) | BIT(30), 0x3, 1);
	} else {
		if (phy_idx == HW_PHY_0) {
			// Path A
			rpt &= halbb_fw_set_reg(bb, 0x58dc, BIT(31) | BIT(30), 0x1, 0);
			rpt &= halbb_fw_set_reg(bb, 0x58dc, BIT(31) | BIT(30), 0x3, 1);
		} else {
			// Path B
			rpt &= halbb_fw_set_reg(bb, 0x78dc, BIT(31) | BIT(30), 0x1, 0);
			rpt &= halbb_fw_set_reg(bb, 0x78dc, BIT(31) | BIT(30), 0x3, 1);
		}
	}

	return rpt;
}
#endif