// SPDX-License-Identifier: GPL-2.0
/* Copyright(c) 2007 - 2017 Realtek Corporation */

#include "mp_precomp.h"
#include "phydm_precomp.h"

#ifdef FAHM_SUPPORT

u16
phydm_hw_divider(
	void	*p_dm_void,
	u16	numerator,
	u16	denumerator
)
{
	struct PHY_DM_STRUCT	*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	u16	result = DEVIDER_ERROR;
	u32	tmp_u32 = ((numerator << 16) | denumerator);
	u32	reg_devider_input;
	u32	reg_devider_rpt;
	u8	i;

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("[%s]===>\n", __func__));

	if (p_dm->support_ic_type & ODM_IC_11AC_SERIES) {
		reg_devider_input =  0x1cbc;
		reg_devider_rpt = 0x1f98;
	} else {
		reg_devider_input =  0x980;
		reg_devider_rpt = 0x9f0;
	}

	odm_set_bb_reg(p_dm, reg_devider_input, MASKDWORD, tmp_u32);

	for (i = 0; i < 10; i++) {
		ODM_delay_ms(1);
		if (odm_get_bb_reg(p_dm, reg_devider_rpt, BIT(24))) { /*Chk HW rpt is ready*/
			
			result = (u16)odm_get_bb_reg(p_dm, reg_devider_rpt, MASKBYTE2);
			break;
		}
	}
	return	result;
}

void
phydm_fahm_trigger(
	void		*p_dm_void,
	u16		trigger_period	/*unit (4us)*/
)
{
	struct PHY_DM_STRUCT	*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO			*ccx_info = &p_dm->dm_ccx_info;
	u32		fahm_reg1;
	u32		fahm_reg2;

	if (p_dm->support_ic_type & ODM_IC_11AC_SERIES) {

		odm_set_bb_reg(p_dm, 0x1cf8, 0xffff00, trigger_period);
		
		fahm_reg1 =  0x994;
	} else {
	
		odm_set_bb_reg(p_dm, 0x978, 0xff000000, (trigger_period & 0xff));		
		odm_set_bb_reg(p_dm, 0x97c, 0xff, (trigger_period & 0xff00)>>8);
		
		fahm_reg1 =  0x890;
	}

	odm_set_bb_reg(p_dm, fahm_reg1, BIT(2), 0);
	odm_set_bb_reg(p_dm, fahm_reg1, BIT(2), 1);
}

void
phydm_fahm_set_valid_cnt(
	void		*p_dm_void,
	u8		numerator_sel,
	u8		denumerator_sel
)
{
	struct PHY_DM_STRUCT	*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO			*ccx_info = &p_dm->dm_ccx_info;
	u32		fahm_reg1;

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("[%s]===>\n", __func__));

	if ((ccx_info->fahm_nume_sel == numerator_sel) && 
		(ccx_info->fahm_denum_sel == denumerator_sel)) {

		PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("no need to update\n", __func__));
		return;
	}

	ccx_info->fahm_nume_sel = numerator_sel;
	ccx_info->fahm_denum_sel = denumerator_sel;
	
	if (p_dm->support_ic_type & ODM_IC_11AC_SERIES) {
		fahm_reg1 =  0x994;
	} else {
		fahm_reg1 =  0x890;
	}

	odm_set_bb_reg(p_dm, fahm_reg1, 0xe0, numerator_sel);
	odm_set_bb_reg(p_dm, fahm_reg1, 0x7000, denumerator_sel);
}

void
phydm_get_fahm_result(
	void		*p_dm_void
)
{
	struct PHY_DM_STRUCT	*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO			*ccx_info = &p_dm->dm_ccx_info;
	u16		fahm_rpt_cnt[12];	/*packet count*/
	u16		fahm_rpt[12];		/*percentage*/
	u16		fahm_denumerator;	/*packet count*/
	u32		reg_rpt, reg_rpt_2;
	u32		reg_val_tmp;
	bool	is_ready = false;
	u8		i;

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("[%s]===>\n", __func__));
	
	if (p_dm->support_ic_type & ODM_IC_11AC_SERIES) {
		reg_rpt =  0x1f80;
		reg_rpt_2 = 0x1f98;
	} else {
		reg_rpt =  0x9d8;
		reg_rpt_2 = 0x9f0;
	}

	for (i = 0; i < 10; i++) {
		
		if (odm_get_bb_reg(p_dm, reg_rpt_2, BIT(31))) { /*Chk HW rpt is ready*/
			
			is_ready = true;
			break;
		}
		ODM_delay_ms(1);
	}

	if (!is_ready)
		return;

	/*Get Denumerator*/
	fahm_denumerator = (u16)odm_get_bb_reg(p_dm, reg_rpt_2, MASKLWORD);

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("Reg[0x%x] fahm_denmrtr = %d\n", reg_rpt_2, fahm_denumerator));
	

	/*Get nemerator*/
	for (i = 0; i<6; i++) {
		reg_val_tmp = odm_get_bb_reg(p_dm, reg_rpt + (i<<2), MASKDWORD);
		
		PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("Reg[0x%x] fahm_denmrtr = %d\n", (p_dm, reg_rpt + (i*4), reg_val_tmp)));
		
		fahm_rpt_cnt[i*2] = (u16)(reg_val_tmp & MASKLWORD);
		fahm_rpt_cnt[i*2 +1] = (u16)((reg_val_tmp & MASKHWORD)>>16);
	}

	for (i = 0; i<12; i++) {
		fahm_rpt[i] = phydm_hw_divider(p_dm, fahm_rpt_cnt[i], fahm_denumerator);
	}

	PHYDM_DBG(p_dm, DBG_ENV_MNTR,("FAHM_RPT_cnt[10:0]=[%d, %d, %d, %d, %d(IGI), %d, %d, %d, %d, %d, %d, %d]\n",
		fahm_rpt_cnt[11], fahm_rpt_cnt[10], fahm_rpt_cnt[9], fahm_rpt_cnt[8], fahm_rpt_cnt[7], fahm_rpt_cnt[6], 
		fahm_rpt_cnt[5], fahm_rpt_cnt[4], fahm_rpt_cnt[3], fahm_rpt_cnt[2], fahm_rpt_cnt[1], fahm_rpt_cnt[0]));

	PHYDM_DBG(p_dm, DBG_ENV_MNTR,("FAHM_RPT_%[10:0]=[%d, %d, %d, %d, %d(IGI), %d, %d, %d, %d, %d, %d, %d]\n",
		fahm_rpt[11], fahm_rpt[10], fahm_rpt[9], fahm_rpt[8], fahm_rpt[7], fahm_rpt[6], 
		fahm_rpt[5], fahm_rpt[4], fahm_rpt[3], fahm_rpt[2], fahm_rpt[1], fahm_rpt[0]));
	
}

void
phydm_set_fahm_th_by_igi(
	void		*p_dm_void,
	u8		igi
)
{
	struct PHY_DM_STRUCT	*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO			*ccx_info = &p_dm->dm_ccx_info;
	u8	fahm_th[11];
	u8	rssi_th[11];	/*in RSSI scale*/
	u8	th_gap = 2 * IGI_TO_NHM_TH_MULTIPLIER;	/*beacuse unit is 0.5dB for FAHM*/
	u8	i;

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("[%s]===>\n", __func__));

	if (ccx_info->env_mntr_igi == igi) {
		PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("No need to update FAHM_th, IGI=0x%x\n", ccx_info->env_mntr_igi));
		return;
	}

	ccx_info->env_mntr_igi = igi;	/*bkp IGI*/

	if (igi >= CCA_CAP) 
		fahm_th[0] = (igi - CCA_CAP) * IGI_TO_NHM_TH_MULTIPLIER;
	else
		fahm_th[0] = 0;
	
	rssi_th[0] = igi -10 - CCA_CAP;
	
	for (i = 1; i <= 10; i++) {
		fahm_th[i] = fahm_th[0] + th_gap * i;
		rssi_th[i] = rssi_th[0] +  (i<<1);
	}

	PHYDM_DBG(p_dm, DBG_ENV_MNTR,("FAHM_RSSI_th[10:0]=[%d, %d, %d, (IGI)%d, %d, %d, %d, %d, %d, %d, %d]\n",
		rssi_th[10], rssi_th[9], rssi_th[8], rssi_th[7], rssi_th[6], rssi_th[5], rssi_th[4], rssi_th[3], rssi_th[2], rssi_th[1], rssi_th[0]));

	if (p_dm->support_ic_type & ODM_IC_11AC_SERIES) {
		
		odm_set_bb_reg(p_dm, 0x1c38, 0xffffff00, ((fahm_th[2]<<24) |(fahm_th[1]<<16) | (fahm_th[0]<<8)));
		odm_set_bb_reg(p_dm, 0x1c78, 0xffffff00, ((fahm_th[5]<<24) |(fahm_th[4]<<16) | (fahm_th[3]<<8)));
		odm_set_bb_reg(p_dm, 0x1c7c, 0xffffff00, ((fahm_th[7]<<24) |(fahm_th[6]<<16)));
		odm_set_bb_reg(p_dm, 0x1cb8, 0xffffff00, ((fahm_th[10]<<24) |(fahm_th[9]<<16) | (fahm_th[8]<<8)));
	} else {

		odm_set_bb_reg(p_dm, 0x970, MASKDWORD, ((fahm_th[3]<<24) |(fahm_th[2]<<16) | (fahm_th[1]<<8) | fahm_th[0]));
		odm_set_bb_reg(p_dm, 0x974, MASKDWORD, ((fahm_th[7]<<24) |(fahm_th[6]<<16) | (fahm_th[5]<<8) | fahm_th[4]));
		odm_set_bb_reg(p_dm, 0x978, MASKDWORD, ((fahm_th[10]<<16) | (fahm_th[9]<<8) | fahm_th[8]));
	}	
}

void
phydm_fahm_init(
	void			*p_dm_void
)
{
	struct PHY_DM_STRUCT	*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO			*ccx_info = &p_dm->dm_ccx_info;
	u32	fahm_reg1;

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("[%s]===>\n", __func__));
	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("IGI=0x%x\n", p_dm->dm_dig_table.cur_ig_value));

	if (p_dm->support_ic_type & ODM_IC_11AC_SERIES) {
		fahm_reg1 =  0x994;
	} else {
		fahm_reg1 =  0x890;
	}

	ccx_info->fahm_period = 65535;
	
	odm_set_bb_reg(p_dm, fahm_reg1, 0x6, 3);	/*FAHM HW block enable*/
	
	phydm_fahm_set_valid_cnt(p_dm, FAHM_INCLD_FA, (FAHM_INCLD_FA| FAHM_INCLD_CRC_OK |FAHM_INCLD_CRC_ER));
	phydm_set_fahm_th_by_igi(p_dm, p_dm->dm_dig_table.cur_ig_value);
}

void
phydm_fahm_dbg(
	void		*p_dm_void,
	char		input[][16],
	u32		*_used,
	char		*output,
	u32		*_out_len,
	u32		input_num
)
{
	struct PHY_DM_STRUCT	*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO	*ccx_info = &p_dm->dm_ccx_info;
	char		help[] = "-h";
	u32		var1[10] = {0};
	u32		used = *_used;
	u32		out_len = *_out_len;
	u32		i;

	for (i = 0; i < 2; i++) {
		if (input[i + 1]) {
			PHYDM_SSCANF(input[i + 1], DCMD_DECIMAL, &var1[i]);
		}
	}

	if ((strcmp(input[1], help) == 0)) {
		PHYDM_SNPRINTF((output + used, out_len - used, "{1: trigger, 2:get result}\n"));
		PHYDM_SNPRINTF((output + used, out_len - used, "{3: MNTR mode sel} {1: driver, 2. FW}\n"));
		return;
	} else if (var1[0] == 1) { /* Set & trigger CLM */
		
		phydm_set_fahm_th_by_igi(p_dm, p_dm->dm_dig_table.cur_ig_value);
		phydm_fahm_trigger(p_dm, ccx_info->fahm_period);
		PHYDM_SNPRINTF((output + used, out_len - used, "Monitor FAHM for %d * 4us\n", ccx_info->fahm_period));
		
	} else if (var1[0] == 2) { /* Get CLM results */

		phydm_get_fahm_result(p_dm);
		PHYDM_SNPRINTF((output + used, out_len - used, "FAHM_result=%d us\n", (ccx_info->clm_result<<2)));

	} /*else if (var1[0] == 3) {

		if (var1[1] == 1)
			ccx_info->clm_mntr_mode = CLM_DRIVER_MNTR;
		else if (var1[1] == 2)
			ccx_info->clm_mntr_mode = CLM_FW_MNTR;

	}*/ else {
		
		PHYDM_SNPRINTF((output + used, out_len - used, "Error\n"));
	}
	
	*_used = used;
	*_out_len = out_len;
}


#endif

void
phydm_c2h_clm_report_handler(
	void	*p_dm_void,
	u8	*cmd_buf,
	u8	cmd_len
)
{
	struct PHY_DM_STRUCT	*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO			*ccx_info = &p_dm->dm_ccx_info;
	u8	clm_report = cmd_buf[0];

	if (cmd_len >=12)
		return;
	
	ccx_info->clm_fw_result_acc += clm_report;
	ccx_info->clm_fw_result_cnt++;

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("[%d] clm_report= %d\n", ccx_info->clm_fw_result_cnt, clm_report));
	
}

static void
phydm_clm_h2c(
	void	*p_dm_void,
	u16	obs_time,
	u8	fw_clm_en
)
{
	struct PHY_DM_STRUCT		*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	u8		h2c_val[H2C_MAX_LENGTH] = {0};
	u8		i = 0;
	u8		obs_time_idx = 0;
	
	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("%s ======>\n", __func__));
	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("obs_time_index=%d *4 ms\n", obs_time));

	for (i =1; i<=16; i++) {
		if (obs_time & BIT(16 -i)) {
			obs_time_idx = 16-i;
			break;
		}
	}
	
	/*
	obs_time =(2^16 -1) ~ (2^15)  => obs_time_idx = 15  (65535 ~ 32768)
	obs_time =(2^15 -1) ~ (2^14)  => obs_time_idx = 14
	...
	...
	...
	obs_time =(2^1 -1) ~ (2^0)  => obs_time_idx = 0

	*/

	h2c_val[0] = obs_time_idx | (((fw_clm_en) ? 1 : 0)<< 7);
	h2c_val[1] = CLM_MAX_REPORT_TIME;

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("PHYDM h2c[0x4d]=0x%x %x %x %x %x %x %x\n",
		h2c_val[6], h2c_val[5], h2c_val[4], h2c_val[3], h2c_val[2], h2c_val[1], h2c_val[0]));

	odm_fill_h2c_cmd(p_dm, PHYDM_H2C_FW_CLM_MNTR, H2C_MAX_LENGTH, h2c_val);

}

bool
phydm_cal_nhm_cnt(
	void		*p_dm_void
)
{
	struct PHY_DM_STRUCT		*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO		*ccx_info = &p_dm->dm_ccx_info;
	u8						noisy_nhm_th_index, low_pwr_cnt = 0, high_pwr_cnt = 0;
	u8						noisy_nhm_th = 0x52;
	u8						i;
	bool					noisy = false, clean = true;

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("[%s]===>\n", __func__));

	if (!(p_dm->support_ability & ODM_BB_ENV_MONITOR))
		return noisy;

	/*nhm_th = 0x52 means 0x52/2-110 = -69dbm*/
	/* IGI < 0x14 */
	if (ccx_info->nhm_th[10] < noisy_nhm_th)
		return	clean;
	else if (ccx_info->nhm_th[0] > noisy_nhm_th)
		return	(p_dm->noisy_decision) ? noisy : clean;
	/* 0x14 <= IGI <= 0x37*/
	else {
		/* search index */
		noisy_nhm_th_index = (noisy_nhm_th - ccx_info->nhm_th[0]) << 2;

		for (i = 0; i <= 11; i++) {
			if (i <= noisy_nhm_th_index)
				low_pwr_cnt += ccx_info->nhm_result[i];
			else
				high_pwr_cnt += ccx_info->nhm_result[i];
		}

		if (low_pwr_cnt + high_pwr_cnt == 0)
			return noisy;		/* noisy environment */
		else if (low_pwr_cnt - high_pwr_cnt >= 100)
			return clean;		/* clean environment */
		else
			return noisy;		/* noisy environment */
	}
}

void
phydm_nhm_setting(
	void		*p_dm_void,
	u8	nhm_setting
)
{
	struct PHY_DM_STRUCT	*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO	*ccx_info = &p_dm->dm_ccx_info;

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("[%s]===>\n", __func__));

	
	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("IGI=0x%x\n", ccx_info->echo_igi));
	
	if (nhm_setting == SET_NHM_SETTING) {
		PHYDM_DBG(p_dm, DBG_ENV_MNTR,
		("NHM_th[H->L]=[0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x]\n",
		ccx_info->nhm_th[10], ccx_info->nhm_th[9], ccx_info->nhm_th[8],
		ccx_info->nhm_th[7], ccx_info->nhm_th[6], ccx_info->nhm_th[5],
		ccx_info->nhm_th[4], ccx_info->nhm_th[3], ccx_info->nhm_th[2],
		ccx_info->nhm_th[1], ccx_info->nhm_th[0]));
	}

	if (p_dm->support_ic_type & ODM_IC_11AC_SERIES) {

		if (nhm_setting == SET_NHM_SETTING) {

			/*Set inexclude_cca, inexclude_txon*/
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11AC, BIT(9), ccx_info->nhm_inexclude_cca);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11AC, BIT(10), ccx_info->nhm_inexclude_txon);

			/*Set NHM period*/
			odm_set_bb_reg(p_dm, ODM_REG_CCX_PERIOD_11AC, MASKHWORD, ccx_info->nhm_period);

			/*Set NHM threshold*/ /*Unit: PWdB U(8,1)*/
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11AC, MASKBYTE0, ccx_info->nhm_th[0]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11AC, MASKBYTE1, ccx_info->nhm_th[1]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11AC, MASKBYTE2, ccx_info->nhm_th[2]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11AC, MASKBYTE3, ccx_info->nhm_th[3]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11AC, MASKBYTE0, ccx_info->nhm_th[4]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11AC, MASKBYTE1, ccx_info->nhm_th[5]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11AC, MASKBYTE2, ccx_info->nhm_th[6]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11AC, MASKBYTE3, ccx_info->nhm_th[7]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH8_11AC, MASKBYTE0, ccx_info->nhm_th[8]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11AC, MASKBYTE2, ccx_info->nhm_th[9]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11AC, MASKBYTE3, ccx_info->nhm_th[10]);

			/*CCX EN*/
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11AC, BIT(8), CCX_EN);

		} else if (nhm_setting == STORE_NHM_SETTING) {

			/*Store pervious disable_ignore_cca, disable_ignore_txon*/
			ccx_info->nhm_inexclude_cca_restore = (enum nhm_inexclude_cca)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11AC, BIT(9));
			ccx_info->nhm_inexclude_txon_restore = (enum nhm_inexclude_txon)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11AC, BIT(10));

			/*Store pervious NHM period*/
			ccx_info->nhm_period_restore = (u16)odm_get_bb_reg(p_dm, ODM_REG_CCX_PERIOD_11AC, MASKHWORD);

			/*Store NHM threshold*/
			ccx_info->nhm_th_restore[0] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11AC, MASKBYTE0);
			ccx_info->nhm_th_restore[1] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11AC, MASKBYTE1);
			ccx_info->nhm_th_restore[2] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11AC, MASKBYTE2);
			ccx_info->nhm_th_restore[3] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11AC, MASKBYTE3);
			ccx_info->nhm_th_restore[4] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11AC, MASKBYTE0);
			ccx_info->nhm_th_restore[5] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11AC, MASKBYTE1);
			ccx_info->nhm_th_restore[6] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11AC, MASKBYTE2);
			ccx_info->nhm_th_restore[7] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11AC, MASKBYTE3);
			ccx_info->nhm_th_restore[8] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH8_11AC, MASKBYTE0);
			ccx_info->nhm_th_restore[9] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11AC, MASKBYTE2);
			ccx_info->nhm_th_restore[10] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11AC, MASKBYTE3);
		} else if (nhm_setting == RESTORE_NHM_SETTING) {

			/*Set disable_ignore_cca, disable_ignore_txon*/
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11AC, BIT(9), ccx_info->nhm_inexclude_cca_restore);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11AC, BIT(10), ccx_info->nhm_inexclude_txon_restore);

			/*Set NHM period*/
			odm_set_bb_reg(p_dm, ODM_REG_CCX_PERIOD_11AC, MASKHWORD, ccx_info->nhm_period);

			/*Set NHM threshold*/
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11AC, MASKBYTE0, ccx_info->nhm_th_restore[0]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11AC, MASKBYTE1, ccx_info->nhm_th_restore[1]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11AC, MASKBYTE2, ccx_info->nhm_th_restore[2]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11AC, MASKBYTE3, ccx_info->nhm_th_restore[3]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11AC, MASKBYTE0, ccx_info->nhm_th_restore[4]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11AC, MASKBYTE1, ccx_info->nhm_th_restore[5]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11AC, MASKBYTE2, ccx_info->nhm_th_restore[6]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11AC, MASKBYTE3, ccx_info->nhm_th_restore[7]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH8_11AC, MASKBYTE0, ccx_info->nhm_th_restore[8]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11AC, MASKBYTE2, ccx_info->nhm_th_restore[9]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11AC, MASKBYTE3, ccx_info->nhm_th_restore[10]);
		} else
			return;
	}

	else if (p_dm->support_ic_type & ODM_IC_11N_SERIES) {

		if (nhm_setting == SET_NHM_SETTING) {

			/*Set disable_ignore_cca, disable_ignore_txon*/
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11N, BIT(9), ccx_info->nhm_inexclude_cca);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11N, BIT(10), ccx_info->nhm_inexclude_txon);

			/*Set NHM period*/
			odm_set_bb_reg(p_dm, ODM_REG_CCX_PERIOD_11N, MASKHWORD, ccx_info->nhm_period);

			/*Set NHM threshold*/
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11N, MASKBYTE0, ccx_info->nhm_th[0]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11N, MASKBYTE1, ccx_info->nhm_th[1]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11N, MASKBYTE2, ccx_info->nhm_th[2]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11N, MASKBYTE3, ccx_info->nhm_th[3]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11N, MASKBYTE0, ccx_info->nhm_th[4]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11N, MASKBYTE1, ccx_info->nhm_th[5]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11N, MASKBYTE2, ccx_info->nhm_th[6]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11N, MASKBYTE3, ccx_info->nhm_th[7]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH8_11N, MASKBYTE0, ccx_info->nhm_th[8]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11N, MASKBYTE2, ccx_info->nhm_th[9]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11N, MASKBYTE3, ccx_info->nhm_th[10]);

			/*CCX EN*/
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11N, BIT(8), CCX_EN);
		} else if (nhm_setting == STORE_NHM_SETTING) {

			/*Store pervious disable_ignore_cca, disable_ignore_txon*/
			ccx_info->nhm_inexclude_cca_restore = (enum nhm_inexclude_cca)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11N, BIT(9));
			ccx_info->nhm_inexclude_txon_restore = (enum nhm_inexclude_txon)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11N, BIT(10));

			/*Store pervious NHM period*/
			ccx_info->nhm_period_restore = (u16)odm_get_bb_reg(p_dm, ODM_REG_CCX_PERIOD_11N, MASKHWORD);

			/*Store NHM threshold*/
			ccx_info->nhm_th_restore[0] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11N, MASKBYTE0);
			ccx_info->nhm_th_restore[1] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11N, MASKBYTE1);
			ccx_info->nhm_th_restore[2] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11N, MASKBYTE2);
			ccx_info->nhm_th_restore[3] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11N, MASKBYTE3);
			ccx_info->nhm_th_restore[4] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11N, MASKBYTE0);
			ccx_info->nhm_th_restore[5] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11N, MASKBYTE1);
			ccx_info->nhm_th_restore[6] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11N, MASKBYTE2);
			ccx_info->nhm_th_restore[7] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11N, MASKBYTE3);
			ccx_info->nhm_th_restore[8] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH8_11N, MASKBYTE0);
			ccx_info->nhm_th_restore[9] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11N, MASKBYTE2);
			ccx_info->nhm_th_restore[10] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11N, MASKBYTE3);

		} else if (nhm_setting == RESTORE_NHM_SETTING) {

			/*Set disable_ignore_cca, disable_ignore_txon*/
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11N, BIT(9), ccx_info->nhm_inexclude_cca_restore);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11N, BIT(10), ccx_info->nhm_inexclude_txon_restore);

			/*Set NHM period*/
			odm_set_bb_reg(p_dm, ODM_REG_CCX_PERIOD_11N, MASKHWORD, ccx_info->nhm_period_restore);

			/*Set NHM threshold*/
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11N, MASKBYTE0, ccx_info->nhm_th_restore[0]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11N, MASKBYTE1, ccx_info->nhm_th_restore[1]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11N, MASKBYTE2, ccx_info->nhm_th_restore[2]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11N, MASKBYTE3, ccx_info->nhm_th_restore[3]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11N, MASKBYTE0, ccx_info->nhm_th_restore[4]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11N, MASKBYTE1, ccx_info->nhm_th_restore[5]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11N, MASKBYTE2, ccx_info->nhm_th_restore[6]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11N, MASKBYTE3, ccx_info->nhm_th_restore[7]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH8_11N, MASKBYTE0, ccx_info->nhm_th_restore[8]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11N, MASKBYTE2, ccx_info->nhm_th_restore[9]);
			odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11N, MASKBYTE3, ccx_info->nhm_th_restore[10]);
		} else
			return;

	}
}

void
phydm_nhm_trigger(
	void		*p_dm_void
)
{
	struct PHY_DM_STRUCT		*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("[%s]===>\n", __func__));

	if (p_dm->support_ic_type & ODM_IC_11AC_SERIES) {

		/*Trigger NHM*/
		odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11AC, BIT(1), 0);
		odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11AC, BIT(1), 1);
	} else if (p_dm->support_ic_type & ODM_IC_11N_SERIES) {

		/*Trigger NHM*/
		odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11N, BIT(1), 0);
		odm_set_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11N, BIT(1), 1);
	}
}

void
phydm_get_nhm_result(
	void		*p_dm_void
)
{
	struct PHY_DM_STRUCT		*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO		*ccx_info = &p_dm->dm_ccx_info;
	u32			value32;
	u8			i;

	if (p_dm->support_ic_type & ODM_IC_11AC_SERIES) {

		value32 = odm_read_4byte(p_dm, ODM_REG_NHM_CNT_11AC);
		ccx_info->nhm_result[0] = (u8)(value32 & MASKBYTE0);
		ccx_info->nhm_result[1] = (u8)((value32 & MASKBYTE1) >> 8);
		ccx_info->nhm_result[2] = (u8)((value32 & MASKBYTE2) >> 16);
		ccx_info->nhm_result[3] = (u8)((value32 & MASKBYTE3) >> 24);

		value32 = odm_read_4byte(p_dm, ODM_REG_NHM_CNT7_TO_CNT4_11AC);
		ccx_info->nhm_result[4] = (u8)(value32 & MASKBYTE0);
		ccx_info->nhm_result[5] = (u8)((value32 & MASKBYTE1) >> 8);
		ccx_info->nhm_result[6] = (u8)((value32 & MASKBYTE2) >> 16);
		ccx_info->nhm_result[7] = (u8)((value32 & MASKBYTE3) >> 24);

		value32 = odm_read_4byte(p_dm, ODM_REG_NHM_CNT11_TO_CNT8_11AC);
		ccx_info->nhm_result[8] = (u8)(value32 & MASKBYTE0);
		ccx_info->nhm_result[9] = (u8)((value32 & MASKBYTE1) >> 8);
		ccx_info->nhm_result[10] = (u8)((value32 & MASKBYTE2) >> 16);
		ccx_info->nhm_result[11] = (u8)((value32 & MASKBYTE3) >> 24);

		/*Get NHM duration*/
		value32 = odm_read_4byte(p_dm, ODM_REG_NHM_DUR_READY_11AC);
		ccx_info->nhm_duration = (u16)(value32 & MASKLWORD);

	}

	else if (p_dm->support_ic_type & ODM_IC_11N_SERIES) {

		value32 = odm_read_4byte(p_dm, ODM_REG_NHM_CNT_11N);
		ccx_info->nhm_result[0] = (u8)(value32 & MASKBYTE0);
		ccx_info->nhm_result[1] = (u8)((value32 & MASKBYTE1) >> 8);
		ccx_info->nhm_result[2] = (u8)((value32 & MASKBYTE2) >> 16);
		ccx_info->nhm_result[3] = (u8)((value32 & MASKBYTE3) >> 24);

		value32 = odm_read_4byte(p_dm, ODM_REG_NHM_CNT7_TO_CNT4_11N);
		ccx_info->nhm_result[4] = (u8)(value32 & MASKBYTE0);
		ccx_info->nhm_result[5] = (u8)((value32 & MASKBYTE1) >> 8);
		ccx_info->nhm_result[6] = (u8)((value32 & MASKBYTE2) >> 16);
		ccx_info->nhm_result[7] = (u8)((value32 & MASKBYTE3) >> 24);

		value32 = odm_read_4byte(p_dm, ODM_REG_NHM_CNT9_TO_CNT8_11N);
		ccx_info->nhm_result[8] = (u8)((value32 & MASKBYTE2) >> 16);
		ccx_info->nhm_result[9] = (u8)((value32 & MASKBYTE3) >> 24);

		value32 = odm_read_4byte(p_dm, ODM_REG_NHM_CNT10_TO_CNT11_11N);
		ccx_info->nhm_result[10] = (u8)((value32 & MASKBYTE2) >> 16);
		ccx_info->nhm_result[11] = (u8)((value32 & MASKBYTE3) >> 24);

		/*Get NHM duration*/
		value32 = odm_read_4byte(p_dm, ODM_REG_NHM_CNT10_TO_CNT11_11N);
		ccx_info->nhm_duration = (u16)(value32 & MASKLWORD);

	}

	/* sum all nhm_result */
	ccx_info->nhm_result_total = 0;
	for (i = 0; i <= 11; i++)
		ccx_info->nhm_result_total += ccx_info->nhm_result[i];

		PHYDM_DBG(p_dm, DBG_ENV_MNTR,
		("NHM_result=(H->L)[%d %d %d %d (igi) %d %d %d %d %d %d %d %d]\n",
			ccx_info->nhm_result[11], ccx_info->nhm_result[10], ccx_info->nhm_result[9],
			ccx_info->nhm_result[8], ccx_info->nhm_result[7], ccx_info->nhm_result[6],
			ccx_info->nhm_result[5], ccx_info->nhm_result[4], ccx_info->nhm_result[3],
			ccx_info->nhm_result[2], ccx_info->nhm_result[1], ccx_info->nhm_result[0]));
}

static bool
phydm_check_nhm_rdy(
	void		*p_dm_void
)
{
	struct PHY_DM_STRUCT		*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	u8			i;
	bool			is_ready = false;

	if (p_dm->support_ic_type & ODM_IC_11AC_SERIES) {

		for (i = 0; i < 200; i++) {
			ODM_delay_ms(1);
			if (odm_get_bb_reg(p_dm, ODM_REG_NHM_DUR_READY_11AC, BIT(16))) {
				is_ready = 1;
				break;
			}
		}
	}

	else if (p_dm->support_ic_type & ODM_IC_11N_SERIES) {

		for (i = 0; i < 200; i++) {
			ODM_delay_ms(1);
			if (odm_get_bb_reg(p_dm, 0x8b4, BIT(17))) {
				is_ready = 1;
				break;
			}
		}
	}
	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("NHM rdy=%d\n", is_ready));
	return is_ready;
}

void
phydm_clm_setting(
	void			*p_dm_void
)
{
	struct PHY_DM_STRUCT		*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO	*ccx_info = &p_dm->dm_ccx_info;

	if (p_dm->support_ic_type & ODM_IC_11AC_SERIES) {

		odm_set_bb_reg(p_dm, ODM_REG_CCX_PERIOD_11AC, MASKLWORD, ccx_info->clm_period);	/*4us sample 1 time*/
		/**/
	} else if (p_dm->support_ic_type & ODM_IC_11N_SERIES) {

		odm_set_bb_reg(p_dm, ODM_REG_CCX_PERIOD_11N, MASKLWORD, ccx_info->clm_period);
		/**/
	}

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("Set CLM period=%d * 4us\n", ccx_info->clm_period));

}

static void
phydm_clm_hw_restart(
	void			*p_dm_void
)
{
	struct PHY_DM_STRUCT		*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO	*ccx_info = &p_dm->dm_ccx_info;

	if (p_dm->support_ic_type & ODM_IC_11AC_SERIES) {

		odm_set_bb_reg(p_dm, ODM_REG_CLM_11AC, BIT(8), 0x0); /*Enable CCX for CLM*/
		odm_set_bb_reg(p_dm, ODM_REG_CLM_11AC, BIT(8), 0x1); /*Enable CCX for CLM*/

	} else if (p_dm->support_ic_type & ODM_IC_11N_SERIES) {

		odm_set_bb_reg(p_dm, ODM_REG_CLM_11AC, BIT(8), 0x0); /*Enable CCX for CLM*/
		odm_set_bb_reg(p_dm, ODM_REG_CLM_11N, BIT(8), 0x1);	/*Enable CCX for CLM*/
	}

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("Set CLM period=%d * 4us\n", ccx_info->clm_period));

}

void
phydm_clm_trigger(
	void			*p_dm_void
)
{
	struct PHY_DM_STRUCT		*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("[%s]===>\n", __func__));

	if (p_dm->support_ic_type & ODM_IC_11AC_SERIES) {
		odm_set_bb_reg(p_dm, ODM_REG_CLM_11AC, BIT(0), 0x0);	/*Trigger CLM*/
		odm_set_bb_reg(p_dm, ODM_REG_CLM_11AC, BIT(0), 0x1);
	} else if (p_dm->support_ic_type & ODM_IC_11N_SERIES) {
		odm_set_bb_reg(p_dm, ODM_REG_CLM_11N, BIT(0), 0x0);	/*Trigger CLM*/
		odm_set_bb_reg(p_dm, ODM_REG_CLM_11N, BIT(0), 0x1);
	}
}

static bool
phydm_check_clm_rdy(
	void			*p_dm_void
)
{
	struct PHY_DM_STRUCT		*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	bool			is_ready = false;
	u8				i;

	if (p_dm->support_ic_type & ODM_IC_11AC_SERIES) {
		for (i = 0; i < 200; i++) {
			ODM_delay_ms(1);
			if (odm_get_bb_reg(p_dm, ODM_REG_CLM_RESULT_11AC, BIT(16))) {
				is_ready = 1;
				break;
			}
		}
	} else if (p_dm->support_ic_type & ODM_IC_11N_SERIES) {
		for (i = 0; i < 200; i++) {
			ODM_delay_ms(1);
			if (odm_get_bb_reg(p_dm, ODM_REG_CLM_READY_11N, BIT(16))) {
				is_ready = 1;
				break;
			}
		}
	}
	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("CLM rdy=%d\n", is_ready));
	return is_ready;
}

void
phydm_get_clm_result(
	void			*p_dm_void
)
{
	struct PHY_DM_STRUCT		*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO	*ccx_info = &p_dm->dm_ccx_info;

	if (p_dm->support_ic_type & ODM_IC_11AC_SERIES)
		ccx_info->clm_result = (u16)odm_get_bb_reg(p_dm, ODM_REG_CLM_RESULT_11AC, MASKDWORD);
	else if (p_dm->support_ic_type & ODM_IC_11N_SERIES)
		ccx_info->clm_result = (u16)odm_get_bb_reg(p_dm, ODM_REG_CLM_RESULT_11N, MASKDWORD);

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("CLM result = %d *4 us\n", ccx_info->clm_result));
}

static void
phydm_set_nhm_th_by_igi(
	void			*p_dm_void,
	u8				igi
)
{
	struct PHY_DM_STRUCT	*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO		*ccx_info = &p_dm->dm_ccx_info;
	u8	th_gap = 2 * IGI_TO_NHM_TH_MULTIPLIER;
	u8	i;

	ccx_info->echo_igi = igi;
	ccx_info->nhm_th[0] = (ccx_info->echo_igi - CCA_CAP) * IGI_TO_NHM_TH_MULTIPLIER;
	for (i = 1; i <= 10; i++)
		ccx_info->nhm_th[i] = ccx_info->nhm_th[0] + th_gap * i;
}

void
phydm_set_clm_mntr_mode(
	void			*p_dm_void,
	enum clm_monitor_mode_e mode
)
{
	struct PHY_DM_STRUCT	*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO		*ccx_info = &p_dm->dm_ccx_info;

	if (ccx_info->clm_mntr_mode != mode) {
		ccx_info->clm_mntr_mode = mode;
		phydm_clm_hw_restart(p_dm);

		if (mode == CLM_DRIVER_MNTR) {
			phydm_clm_h2c(p_dm,0, 0);
		}
	}
}

void
phydm_ccx_monitor_trigger(
	void			*p_dm_void,
	u16			monitor_time		/*unit ms*/
)
{
	u8			nhm_th[11], i, igi;
	struct PHY_DM_STRUCT	*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO		*ccx_info = &p_dm->dm_ccx_info;
	u16 	monitor_time_4us = 0;

	if (!(p_dm->support_ability & ODM_BB_ENV_MONITOR))
		return;

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("[%s]===>\n", __func__));

	if (monitor_time == 0)
		return;

	if (monitor_time >= 262)
		monitor_time_4us = 65534;
	else
		monitor_time_4us = monitor_time * MS_TO_4US_RATIO;

	/* check if NHM threshold is changed */
	if (p_dm->support_ic_type & ODM_IC_11AC_SERIES) {
		
		nhm_th[0] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11AC, MASKBYTE0);
		nhm_th[1] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11AC, MASKBYTE1);
		nhm_th[2] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11AC, MASKBYTE2);
		nhm_th[3] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11AC, MASKBYTE3);
		nhm_th[4] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11AC, MASKBYTE0);
		nhm_th[5] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11AC, MASKBYTE1);
		nhm_th[6] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11AC, MASKBYTE2);
		nhm_th[7] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11AC, MASKBYTE3);
		nhm_th[8] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH8_11AC, MASKBYTE0);
		nhm_th[9] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11AC, MASKBYTE2);
		nhm_th[10] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11AC, MASKBYTE3);
	} else if (p_dm->support_ic_type & ODM_IC_11N_SERIES) {
		
		nhm_th[0] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11N, MASKBYTE0);
		nhm_th[1] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11N, MASKBYTE1);
		nhm_th[2] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11N, MASKBYTE2);
		nhm_th[3] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH3_TO_TH0_11N, MASKBYTE3);
		nhm_th[4] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11N, MASKBYTE0);
		nhm_th[5] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11N, MASKBYTE1);
		nhm_th[6] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11N, MASKBYTE2);
		nhm_th[7] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH7_TO_TH4_11N, MASKBYTE3);
		nhm_th[8] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH8_11N, MASKBYTE0);
		nhm_th[9] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11N, MASKBYTE2);
		nhm_th[10] = (u8)odm_get_bb_reg(p_dm, ODM_REG_NHM_TH9_TH10_11N, MASKBYTE3);
	}

	for (i = 0; i <= 10; i++) {
		
		if (nhm_th[i] != ccx_info->nhm_th[i]) {	
			PHYDM_DBG(p_dm, DBG_ENV_MNTR,
				("nhm_th[%d] != ccx_info->nhm_th[%d]!!\n", i, i));
		}
	}
	/*[NHM]*/
	igi = (u8)odm_get_bb_reg(p_dm, 0xC50, MASKBYTE0);
	phydm_set_nhm_th_by_igi(p_dm, igi);

	ccx_info->nhm_period = monitor_time_4us;
	ccx_info->nhm_inexclude_cca = NHM_EXCLUDE_CCA;
	ccx_info->nhm_inexclude_txon = NHM_EXCLUDE_TXON;

	phydm_nhm_setting(p_dm, SET_NHM_SETTING);
	phydm_nhm_trigger(p_dm);

	/*[CLM]*/
	ccx_info->clm_period = monitor_time_4us;
	
	if (ccx_info->clm_mntr_mode == CLM_DRIVER_MNTR) {
		phydm_clm_setting(p_dm);
		phydm_clm_trigger(p_dm);
	} else if (ccx_info->clm_mntr_mode == CLM_FW_MNTR){
		phydm_clm_h2c(p_dm, monitor_time_4us, true);
	} else {
		PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("CLM_ECHO_DBG_MODE\n"));
	}

}

void
phydm_ccx_monitor_result(
	void			*p_dm_void
)
{
	struct PHY_DM_STRUCT	*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO		*ccx_info = &p_dm->dm_ccx_info;
	u32					clm_result_tmp = 0;

	if (!(p_dm->support_ability & ODM_BB_ENV_MONITOR))
		return;

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("%s ======>\n", __func__));

	if (phydm_check_nhm_rdy(p_dm)) {
		phydm_get_nhm_result(p_dm);

		if (ccx_info->nhm_result_total != 0)
			ccx_info->nhm_ratio  = (u8)(((ccx_info->nhm_result_total - ccx_info->nhm_result[0])*100) >> 8);
	}

	if (ccx_info->clm_mntr_mode == CLM_DRIVER_MNTR) {
		
		if (phydm_check_clm_rdy(p_dm)) {
			phydm_get_clm_result(p_dm);

			if (ccx_info->clm_period != 0) {

				if (ccx_info->clm_period == 64000)
					ccx_info->clm_ratio = (u8)(((ccx_info->clm_result >> 6) + 5) /10);
				else if (ccx_info->clm_period == 65535) {
					
					clm_result_tmp = (u32)(ccx_info->clm_result * 100);
					ccx_info->clm_ratio = (u8)((clm_result_tmp + (1<<15)) >> 16);
				} else
					ccx_info->clm_ratio = (u8)((ccx_info->clm_result*100) / ccx_info->clm_period);
			}
		}
		
	} else {
		if (ccx_info->clm_fw_result_cnt != 0)
			ccx_info->clm_ratio = (u8)(ccx_info->clm_fw_result_acc /ccx_info->clm_fw_result_cnt);
		else
			ccx_info->clm_ratio = 0;

		PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("clm_fw_result_acc=%d, clm_fw_result_cnt=%d\n",
			ccx_info->clm_fw_result_acc, ccx_info->clm_fw_result_cnt));
		
		ccx_info->clm_fw_result_acc = 0;
		ccx_info->clm_fw_result_cnt = 0;
	}

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("IGI=0x%x, nhm_ratio=%d, clm_ratio=%d\n\n",
		ccx_info->echo_igi, ccx_info->nhm_ratio, ccx_info->clm_ratio));
		
}

void
phydm_ccx_monitor(
	void			*p_dm_void
)
{
	struct PHY_DM_STRUCT	*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;

	if (!(p_dm->support_ability & ODM_BB_ENV_MONITOR))
		return;

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("[%s]===>\n", __func__));

	phydm_ccx_monitor_result(p_dm);
	phydm_ccx_monitor_trigger(p_dm, 262);	/*monitor 262ms*/
}

static void
phydm_nhm_init(
	void			*p_dm_void
)
{
	struct PHY_DM_STRUCT	*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO		*ccx_info = &p_dm->dm_ccx_info;

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("[%s]===>\n", __func__));
	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("cur_ig_value=0x%x\n", p_dm->dm_dig_table.cur_ig_value));

	phydm_set_nhm_th_by_igi(p_dm, p_dm->dm_dig_table.cur_ig_value);

	ccx_info->nhm_period = 64000;
	ccx_info->nhm_inexclude_cca = NHM_EXCLUDE_CCA;
	ccx_info->nhm_inexclude_txon = NHM_EXCLUDE_TXON;

	phydm_nhm_setting(p_dm, SET_NHM_SETTING);
}

static void
phydm_clm_init(
	void			*p_dm_void
)
{
	struct PHY_DM_STRUCT	*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO		*ccx_info = &p_dm->dm_ccx_info;

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("[%s]===>\n", __func__));

	ccx_info->clm_mntr_mode = CLM_DRIVER_MNTR;
	ccx_info->clm_period = 65535;
	phydm_clm_setting(p_dm);
	phydm_clm_hw_restart(p_dm);
}

void
phydm_env_monitor_init(
	void			*p_dm_void
)
{
	struct PHY_DM_STRUCT	*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;

	if (!(p_dm->support_ability & ODM_BB_ENV_MONITOR))
		return;

	PHYDM_DBG(p_dm, DBG_ENV_MNTR, ("[%s]===>\n", __func__));

	phydm_nhm_init(p_dm);
	phydm_clm_init(p_dm);
}

void
phydm_clm_dbg(
	void		*p_dm_void,
	char		input[][16],
	u32		*_used,
	char		*output,
	u32		*_out_len,
	u32		input_num
)
{
	struct PHY_DM_STRUCT	*p_dm = (struct PHY_DM_STRUCT *)p_dm_void;
	struct _CCX_INFO	*ccx_info = &p_dm->dm_ccx_info;
	char		help[] = "-h";
	u32		var1[10] = {0};
	u32		used = *_used;
	u32		out_len = *_out_len;
	u32		i;

	for (i = 0; i < 2; i++) {
		if (input[i + 1]) {
			PHYDM_SSCANF(input[i + 1], DCMD_DECIMAL, &var1[i]);
		}
	}

	if ((strcmp(input[1], help) == 0)) {
		PHYDM_SNPRINTF((output + used, out_len - used, "{1: trigger, 2:get result}\n"));
		PHYDM_SNPRINTF((output + used, out_len - used, "{3: MNTR mode sel} {1: driver, 2. FW}\n"));
		return;
	} else if (var1[0] == 1) { /* Set & trigger CLM */
		
		ccx_info->clm_period = 65535;		/* 65535*4us = 262.14ms*/
		phydm_clm_setting(p_dm);
		phydm_clm_hw_restart(p_dm);
		phydm_clm_trigger(p_dm);
		PHYDM_SNPRINTF((output + used, out_len - used, "Monitor CLM for 262ms\n"));
		
	} else if (var1[0] == 2) { /* Get CLM results */

		phydm_get_clm_result(p_dm);
		PHYDM_SNPRINTF((output + used, out_len - used, "CLM_result=%d us\n", (ccx_info->clm_result<<2)));

	} else if (var1[0] == 3) {

		if (var1[1] == 1)
			ccx_info->clm_mntr_mode = CLM_DRIVER_MNTR;
		else if (var1[1] == 2)
			ccx_info->clm_mntr_mode = CLM_FW_MNTR;

	} else {
		
		PHYDM_SNPRINTF((output + used, out_len - used, "Error\n"));
	}
	
	*_used = used;
	*_out_len = out_len;
}
