/******************************************************************************
 *
 * 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 "power_saving.h"
#include "coex.h"
#include "mac_priv.h"

static u32 lps_status[4] = {0};
static u32 ips_status[4] = {0};
static u8 rpwm_seq_num = RPWM_SEQ_NUM_MAX;
static u8 cpwm_seq_num = CPWM_SEQ_NUM_MAX;

static u32 send_h2c_lps_parm(struct mac_ax_adapter *adapter,
			     struct lps_parm *parm)
{
	u32 ret = MACSUCCESS;
	struct h2c_info h2c_info = {0};
	struct fwcmd_lps_parm *fwcmd_lps;

	h2c_info.agg_en = 0;
	h2c_info.content_len = sizeof(struct fwcmd_lps_parm);
	h2c_info.h2c_cat = FWCMD_H2C_CAT_MAC;
	h2c_info.h2c_class = FWCMD_H2C_CL_PS;
	h2c_info.h2c_func = FWCMD_H2C_FUNC_LPS_PARM;
	h2c_info.rec_ack = 0;
	h2c_info.done_ack = 0;

	fwcmd_lps = (struct fwcmd_lps_parm *)PLTFM_MALLOC(h2c_info.content_len);
	if (!fwcmd_lps)
		return MACNPTR;

	fwcmd_lps->dword0 =
	cpu_to_le32(SET_WORD(parm->macid, FWCMD_H2C_LPS_PARM_MACID) |
		SET_WORD(parm->psmode, FWCMD_H2C_LPS_PARM_PSMODE) |
		SET_WORD(parm->rlbm, FWCMD_H2C_LPS_PARM_RLBM) |
		SET_WORD(parm->smartps, FWCMD_H2C_LPS_PARM_SMARTPS) |
		SET_WORD(parm->awakeinterval,
			 FWCMD_H2C_LPS_PARM_AWAKEINTERVAL));

	fwcmd_lps->dword1 =
	cpu_to_le32((parm->vouapsd ? FWCMD_H2C_LPS_PARM_VOUAPSD : 0) |
		(parm->viuapsd ? FWCMD_H2C_LPS_PARM_VIUAPSD : 0) |
		(parm->beuapsd ? FWCMD_H2C_LPS_PARM_BEUAPSD : 0) |
		(parm->bkuapsd ? FWCMD_H2C_LPS_PARM_BKUAPSD : 0) |
		SET_WORD(parm->lastrpwm, FWCMD_H2C_LPS_PARM_LASTRPWM) |
		(parm->bcnnohit_en ? FWCMD_H2C_LPS_PARM_BCNNOHIT_EN : 0) |
		(parm->nulltype ? FWCMD_H2C_LPS_PARM_NULLTYPE : 0) |
		(parm->dyntxantnum_en ? FWCMD_H2C_LPS_PARM_DYNTXANTNUM_EN : 0) |
		(parm->maxtxantnum ? FWCMD_H2C_LPS_PARM_MAXTXANTNUM : 0) |
		(parm->lpstxantnum ? FWCMD_H2C_LPS_PARM_LPSTXANTNUM : 0));

	ret = mac_h2c_common(adapter, &h2c_info, (u32 *)fwcmd_lps);

	PLTFM_FREE(fwcmd_lps, h2c_info.content_len);

	return ret;
}

static void send_rpwm(struct mac_ax_adapter *adapter,
		      struct ps_rpwm_parm *parm)
{
	u16 rpwm_value = 0;
	u8 toggle = 0;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);

	rpwm_value = MAC_REG_R16(R_AX_RPWM);
	if (0 == (rpwm_value & PS_RPWM_TOGGLE))
		toggle = 1;

	if (parm->notify_wake) {
		rpwm_value |= PS_RPWM_NOTIFY_WAKE;
	} else {
		if (rpwm_seq_num == RPWM_SEQ_NUM_MAX)
			rpwm_seq_num = 0;
		else
			rpwm_seq_num += 1;

		rpwm_value = (SET_WORD(parm->req_pwr_state, PS_RPWM_STATE) |
			SET_WORD(rpwm_seq_num, PS_RPWM_SEQ_NUM));

		if (parm->req_pwr_state == MAC_AX_RPWM_REQ_PWR_STATE_ACTIVE ||
		    parm->req_pwr_state == MAC_AX_RPWM_REQ_PWR_STATE_BAND0_RFON ||
		    parm->req_pwr_state == MAC_AX_RPWM_REQ_PWR_STATE_BAND1_RFON ||
		    parm->req_pwr_state == MAC_AX_RPWM_REQ_PWR_STATE_BAND0_RFOFF ||
		    parm->req_pwr_state == MAC_AX_RPWM_REQ_PWR_STATE_BAND1_RFOFF)
			rpwm_value |= PS_RPWM_ACK;

		if (parm->req_pwr_state == MAC_AX_RPWM_REQ_PWR_STATE_CLK_GATED ||
		    parm->req_pwr_state == MAC_AX_RPWM_REQ_PWR_STATE_PWR_GATED ||
		    parm->req_pwr_state == MAC_AX_RPWM_REQ_PWR_STATE_HIOE_PWR_GATED) {
			adapter->mac_pwr_info.pwr_in_lps = 1;
		}
	}

	if (toggle == 1)
		rpwm_value |= PS_RPWM_TOGGLE;
	else
		rpwm_value &= ~PS_RPWM_TOGGLE;

	switch (adapter->env_info.intf) {
#if MAC_AX_USB_SUPPORT
	case MAC_AX_INTF_USB:
#if MAC_AX_8852A_SUPPORT || MAC_AX_8852B_SUPPORT || MAC_AX_8851B_SUPPORT || MAC_AX_8852BT_SUPPORT
		if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852A) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8852B) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8851B) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8852BT))
			MAC_REG_W16(R_AX_USB_D2F_F2D_INFO + 2, rpwm_value);
#endif

#if MAC_AX_8852C_SUPPORT || MAC_AX_8192XB_SUPPORT || MAC_AX_8851E_SUPPORT || MAC_AX_8852D_SUPPORT
		if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852C) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8192XB) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8851E) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8852D))
			MAC_REG_W16(R_AX_USB_D2F_F2D_INFO_V1 + 2, rpwm_value);
#endif
		break;
#endif //MAC_AX_USB_SUPPORT

#if MAC_AX_SDIO_SUPPORT
	case MAC_AX_INTF_SDIO:
#if MAC_AX_8852A_SUPPORT || MAC_AX_8852B_SUPPORT || MAC_AX_8851B_SUPPORT || MAC_AX_8852BT_SUPPORT
		if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852A) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8852B) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8851B) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8852BT))
			MAC_REG_W16(R_AX_SDIO_HRPWM1 + 2, rpwm_value);
#endif

#if MAC_AX_8852C_SUPPORT || MAC_AX_8192XB_SUPPORT || MAC_AX_8851E_SUPPORT || MAC_AX_8852D_SUPPORT
		if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852C) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8192XB) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8851E) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8852D))
			MAC_REG_W16(R_AX_SDIO_HRPWM1_V1 + 2, rpwm_value);
#endif

		if (parm->req_pwr_state == MAC_AX_RPWM_REQ_PWR_STATE_CLK_GATED ||
		    parm->req_pwr_state == MAC_AX_RPWM_REQ_PWR_STATE_PWR_GATED ||
		    parm->req_pwr_state == MAC_AX_RPWM_REQ_PWR_STATE_HIOE_PWR_GATED)
			adapter->sdio_info.tx_seq = 1;
		break;
#endif //MAC_AX_SDIO_SUPPORT

#if MAC_AX_PCIE_SUPPORT
	case MAC_AX_INTF_PCIE:
#if MAC_AX_8852A_SUPPORT || MAC_AX_8852B_SUPPORT || MAC_AX_8851B_SUPPORT || MAC_AX_8852BT_SUPPORT
		if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852A) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8852B) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8851B) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8852BT))
			MAC_REG_W16(R_AX_PCIE_HRPWM, rpwm_value);
#endif

#if MAC_AX_8852C_SUPPORT || MAC_AX_8192XB_SUPPORT || MAC_AX_8851E_SUPPORT || MAC_AX_8852D_SUPPORT
		if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852C) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8192XB) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8851E) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8852D))
			MAC_REG_W16(R_AX_PCIE_HRPWM_V1, rpwm_value);
#endif

		break;
#endif //MAC_AX_PCIE_SUPPORT
	default:
		PLTFM_MSG_ERR("%s: invalid interface = %d!!\n",
			      __func__, adapter->env_info.intf);

		break;
	}

	PLTFM_DELAY_US(RPWM_DELAY_FOR_32K_TICK);

	PLTFM_MSG_TRACE("Send RPWM. rpwm_val=0x%x\n", rpwm_value);
}

static u32 leave_lps(struct mac_ax_adapter *adapter, u8 macid)
{
	struct lps_parm h2c_lps_parm;
	u32 ret;

	PLTFM_MEMSET(&h2c_lps_parm, 0, sizeof(struct lps_parm));

	h2c_lps_parm.macid = macid;
	h2c_lps_parm.psmode = MAC_AX_PS_MODE_ACTIVE;
	h2c_lps_parm.lastrpwm = LAST_RPWM_ACTIVE;

	ret = send_h2c_lps_parm(adapter, &h2c_lps_parm);

	if (ret)
		return ret;

	lps_status[(macid >> 5)] &= ~BIT(macid & 0x1F);

	return MACSUCCESS;
}

static u32 enter_lps(struct mac_ax_adapter *adapter,
		     u8 macid,
		     struct mac_ax_lps_info *lps_info)
{
	struct lps_parm h2c_lps_parm;
	u32 ret = 0;

	if (!lps_info) {
		PLTFM_MSG_ERR("[ERR]:LPS info is null\n");
		return MACNOITEM;
	}

	if (lps_status[(macid >> 5)] & BIT(macid & 0x1F))
		ret = leave_lps(adapter, macid);

	if (ret)
		return ret;

	PLTFM_MEMSET(&h2c_lps_parm, 0, sizeof(struct lps_parm));

	h2c_lps_parm.macid = macid;
	h2c_lps_parm.psmode = MAC_AX_PS_MODE_LEGACY;

	if (lps_info->listen_bcn_mode > MAC_AX_RLBM_USERDEFINE)
		lps_info->listen_bcn_mode = MAC_AX_RLBM_MIN;

	if (lps_info->listen_bcn_mode == MAC_AX_RLBM_USERDEFINE) {
		h2c_lps_parm.rlbm = MAC_AX_RLBM_USERDEFINE;
		h2c_lps_parm.awakeinterval = lps_info->awake_interval;
		if (h2c_lps_parm.awakeinterval == 0)
			h2c_lps_parm.awakeinterval = 1;
	} else if (lps_info->listen_bcn_mode == MAC_AX_RLBM_MAX) {
		h2c_lps_parm.rlbm = MAC_AX_RLBM_MAX;
		h2c_lps_parm.awakeinterval = 1;
	} else {
		h2c_lps_parm.rlbm = MAC_AX_RLBM_MIN;
		h2c_lps_parm.awakeinterval = 1;
	}

	h2c_lps_parm.smartps = lps_info->smart_ps_mode;
	h2c_lps_parm.lastrpwm = LAST_RPWM_PS;
	h2c_lps_parm.bcnnohit_en = lps_info->bcnnohit_en;
	h2c_lps_parm.nulltype = lps_info->nulltype;

	h2c_lps_parm.dyntxantnum_en = lps_info->dyn_tx_ant_num_en;

	if (h2c_lps_parm.dyntxantnum_en) {
		h2c_lps_parm.maxtxantnum = lps_info->max_tx_ant_num;
		h2c_lps_parm.lpstxantnum = lps_info->lps_tx_ant_num;
	}

	ret = send_h2c_lps_parm(adapter, &h2c_lps_parm);

	if (ret)
		return ret;

	lps_status[(macid >> 5)] |= BIT(macid & 0x1F);

	return MACSUCCESS;
}

static u32 enter_wmmlps(struct mac_ax_adapter *adapter,
			u8 macid,
			struct mac_ax_lps_info *lps_info)
{
	struct lps_parm h2c_lps_parm;
	u32 ret = 0;

	if (!lps_info) {
		PLTFM_MSG_ERR("[ERR]:LPS info is null\n");
		return MACNOITEM;
	}

	if (lps_status[(macid >> 5)] & BIT(macid & 0x1F))
		ret = leave_lps(adapter, macid);

	if (ret)
		return ret;

	PLTFM_MEMSET(&h2c_lps_parm, 0, sizeof(struct lps_parm));

	h2c_lps_parm.macid = macid;
	h2c_lps_parm.psmode = MAC_AX_PS_MODE_WMMPS;

	if (lps_info->listen_bcn_mode > MAC_AX_RLBM_USERDEFINE)
		lps_info->listen_bcn_mode = MAC_AX_RLBM_MIN;

	if (lps_info->listen_bcn_mode == MAC_AX_RLBM_USERDEFINE) {
		h2c_lps_parm.rlbm = MAC_AX_RLBM_USERDEFINE;
		h2c_lps_parm.awakeinterval = lps_info->awake_interval;
		if (h2c_lps_parm.awakeinterval == 0)
			h2c_lps_parm.awakeinterval = 1;
	} else if (lps_info->listen_bcn_mode == MAC_AX_RLBM_MAX) {
		h2c_lps_parm.rlbm = MAC_AX_RLBM_MAX;
		h2c_lps_parm.awakeinterval = 1;
	} else {
		h2c_lps_parm.rlbm = MAC_AX_RLBM_MIN;
		h2c_lps_parm.awakeinterval = 1;
	}

	h2c_lps_parm.lastrpwm = LAST_RPWM_PS;
	h2c_lps_parm.bcnnohit_en = lps_info->bcnnohit_en;
	h2c_lps_parm.vouapsd = lps_info->vouapsd_en;
	h2c_lps_parm.viuapsd = lps_info->viuapsd_en;
	h2c_lps_parm.beuapsd = lps_info->beuapsd_en;
	h2c_lps_parm.bkuapsd = lps_info->bkuapsd_en;

	ret = send_h2c_lps_parm(adapter, &h2c_lps_parm);

	if (ret)
		return ret;

	lps_status[(macid >> 5)] |= BIT(macid & 0x1F);

	return MACSUCCESS;
}

static u32 set_req_pwr_state(struct mac_ax_adapter *adapter,
			     enum mac_ax_rpwm_req_pwr_state req_pwr_state)
{
	struct ps_rpwm_parm parm;

	PLTFM_MEMSET(&parm, 0, sizeof(struct ps_rpwm_parm));

	if (req_pwr_state >= MAC_AX_RPWM_REQ_PWR_STATE_MAX) {
		PLTFM_MSG_ERR("%s: invalid pwr state:%d\n",
			      __func__, req_pwr_state);
		return MACNOITEM;
	}

	parm.req_pwr_state = req_pwr_state;
	parm.notify_wake = 0;
	send_rpwm(adapter, &parm);

	return MACSUCCESS;
}

static u32 _chk_cpwm_seq_num(u8 seq_num)
{
	u32 ret;

	if (cpwm_seq_num == CPWM_SEQ_NUM_MAX) {
		if (seq_num == 0) {
			cpwm_seq_num = seq_num;
			ret = MACSUCCESS;
		} else {
			ret = MACCPWMSEQERR;
		}
	} else {
		if (seq_num == (cpwm_seq_num + 1)) {
			cpwm_seq_num = seq_num;
			ret = MACSUCCESS;
		} else {
			ret = MACCPWMSEQERR;
		}
	}

	return ret;
}

static u32 chk_cur_pwr_state(struct mac_ax_adapter *adapter,
			     enum mac_ax_rpwm_req_pwr_state req_pwr_state)
{
	u16 cpwm = 0;
	u32 rpwm_32k;
	u32 req_32k;
#if MAC_AX_PCIE_SUPPORT
	struct mac_ax_priv_ops *p_ops = adapter_to_priv_ops(adapter);
	u32 ret = MACSUCCESS;
#endif
#if MAC_AX_USB_SUPPORT
#define USB_ON_IOH_SW_RST_RETRY_CNT 100
#define USB_ON_IOH_SW_RST_WAIT_US 50
	u32 val32, cnt;
#endif
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);

	if (req_pwr_state >= MAC_AX_RPWM_REQ_PWR_STATE_CLK_GATED)
		req_32k = 1;
	else
		req_32k = 0;

	//(workaround) CPWM register is in OFF area
	//Use LDM to check if FW receives RPWM
	rpwm_32k = (MAC_REG_R32(R_AX_LDM) & B_PS_LDM_32K_EN) >> B_PS_LDM_32K_EN_SH;
	if (req_32k != rpwm_32k)
		return MACCPWMPWRSTATERR;

	//There is no CPWM if 32K state
	if (req_32k)
		return MACSUCCESS;

#if MAC_AX_8852A_SUPPORT || MAC_AX_8852B_SUPPORT || MAC_AX_8851B_SUPPORT || MAC_AX_8852BT_SUPPORT
	if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852A) ||
	    is_chip_id(adapter, MAC_AX_CHIP_ID_8852B) ||
	    is_chip_id(adapter, MAC_AX_CHIP_ID_8851B) ||
	    is_chip_id(adapter, MAC_AX_CHIP_ID_8852BT))
		cpwm = MAC_REG_R16(R_AX_CPWM);
#endif

#if MAC_AX_8852C_SUPPORT || MAC_AX_8192XB_SUPPORT || MAC_AX_8851E_SUPPORT || MAC_AX_8852D_SUPPORT
	if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852C) ||
	    is_chip_id(adapter, MAC_AX_CHIP_ID_8192XB) ||
	    is_chip_id(adapter, MAC_AX_CHIP_ID_8851E) ||
	    is_chip_id(adapter, MAC_AX_CHIP_ID_8852D)) {
		switch (adapter->env_info.intf) {
#if MAC_AX_USB_SUPPORT
		case MAC_AX_INTF_USB:
			if (chk_patch_usb_on_ioh_sw_rst(adapter) == PATCH_ENABLE) {
				val32 = MAC_REG_R32(R_AX_USB_IO_OFFREG_WDT_V1) |
						    B_AX_ON_IOH_SW_RST_V1;
				MAC_REG_W32(R_AX_USB_IO_OFFREG_WDT_V1, val32);
				cnt = USB_ON_IOH_SW_RST_RETRY_CNT;
				while (--cnt) {
					val32 = MAC_REG_R32(R_AX_USB_IO_OFFREG_WDT_V1);
					if (!(val32 & B_AX_OFF_IOH_RST_STS_V1))
						break;
					PLTFM_DELAY_US(USB_ON_IOH_SW_RST_WAIT_US);
				}
				if (!cnt)
					PLTFM_MSG_ERR("%s: reset off wrapper polling timeout!!\n",
						      __func__);
			}
			cpwm = MAC_REG_R16(R_AX_USB_D2F_F2D_INFO_V1);
			break;
#endif // MAC_AX_USB_SUPPORT

#if MAC_AX_SDIO_SUPPORT
		case MAC_AX_INTF_SDIO:
			return MACCPWMINTFERR;
			break;
#endif // MAC_AX_SDIO_SUPPORT

#if MAC_AX_PCIE_SUPPORT
		case MAC_AX_INTF_PCIE:
			cpwm = MAC_REG_R16(R_AX_PCIE_CRPWM);
			break;
#endif // MAC_AX_PCIE_SUPPORT
		default:
			PLTFM_MSG_ERR("%s: invalid interface = %d!!\n",
				      __func__, adapter->env_info.intf);
			return MACCPWMINTFERR;
			break;
		}
	}
#endif

	PLTFM_MSG_TRACE("Read CPWM=0x%x\n", cpwm);
	if (rpwm_seq_num != GET_FIELD(cpwm, PS_CPWM_RSP_SEQ_NUM)) {
		PLTFM_MSG_TRACE("RPWM seq mismatch!!: expect val:%d, Rx val:%d\n",
				rpwm_seq_num, GET_FIELD(cpwm, PS_CPWM_RSP_SEQ_NUM));
#if MAC_AX_8852A_SUPPORT || MAC_AX_8852B_SUPPORT || MAC_AX_8851B_SUPPORT || MAC_AX_8852BT_SUPPORT
		if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852A) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8852B) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8851B) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8852BT)) {
			return MACCPWMSEQERR;
		}
#endif
	}

	if (_chk_cpwm_seq_num(GET_FIELD(cpwm, PS_CPWM_SEQ_NUM)) == MACCPWMSEQERR) {
		PLTFM_MSG_TRACE("CPWM seq mismatch!!: expect val:%d, Rx val:%d\n",
				cpwm_seq_num, GET_FIELD(cpwm, PS_CPWM_SEQ_NUM));
#if MAC_AX_8852A_SUPPORT || MAC_AX_8852B_SUPPORT || MAC_AX_8851B_SUPPORT || MAC_AX_8852BT_SUPPORT
		if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852A) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8852B) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8851B) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8852BT)) {
			return MACCPWMSEQERR;
		}
#endif
	}

	if (req_pwr_state != GET_FIELD(cpwm, PS_CPWM_STATE))
		return MACCPWMSTATERR;

	if (adapter->mac_pwr_info.pwr_in_lps) {
		adapter->mac_pwr_info.pwr_in_lps = 0;
#if MAC_AX_8852C_SUPPORT || MAC_AX_8192XB_SUPPORT || MAC_AX_8851E_SUPPORT || MAC_AX_8852D_SUPPORT
		if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852C) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8192XB) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8851E) ||
		    is_chip_id(adapter, MAC_AX_CHIP_ID_8852D)) {
#if MAC_AX_PCIE_SUPPORT
			if (adapter->env_info.intf == MAC_AX_INTF_PCIE) {
				ret = p_ops->sync_trx_bd_idx(adapter);
				if (ret != MACSUCCESS) {
					PLTFM_MSG_ERR("sync trx bd fail: %d\n",
						      ret);
					return ret;
				}
			}
#endif
		}
#endif
	}

	return MACSUCCESS;
}

u32 mac_cfg_lps(struct mac_ax_adapter *adapter, u8 macid,
		enum mac_ax_ps_mode ps_mode, struct mac_ax_lps_info *lps_info)
{
	u32 ret = 0;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);

	switch (ps_mode) {
	case MAC_AX_PS_MODE_ACTIVE:
		ret = leave_lps(adapter, macid);
		/* patch form BT BG/LDO issue */
		MAC_REG_W8(R_AX_SCOREBOARD + 3, MAC_AX_NOTIFY_TP_MAJOR);
		break;
	case MAC_AX_PS_MODE_LEGACY:
		ret = enter_lps(adapter, macid, lps_info);
		/* patch form BT BG/LDO issue */
		MAC_REG_W8(R_AX_SCOREBOARD + 3, MAC_AX_NOTIFY_PWR_MAJOR);
		break;
	case MAC_AX_PS_MODE_WMMPS:
		ret = enter_wmmlps(adapter, macid, lps_info);
		/* patch form BT BG/LDO issue */
		MAC_REG_W8(R_AX_SCOREBOARD + 3, MAC_AX_NOTIFY_PWR_MAJOR);
		break;
	default:
		return MACNOITEM;
	}

	if (ret)
		return ret;

	return MACSUCCESS;
}

u32 mac_ps_pwr_state(struct mac_ax_adapter *adapter,
		     enum mac_ax_pwr_state_action action,
		     enum mac_ax_rpwm_req_pwr_state req_pwr_state)
{
	u32 ret = MACSUCCESS;

	switch (action) {
	case MAC_AX_PWR_STATE_ACT_REQ:
		ret = set_req_pwr_state(adapter, req_pwr_state);
		break;
	case MAC_AX_PWR_STATE_ACT_CHK:
		ret = chk_cur_pwr_state(adapter, req_pwr_state);
		break;
	default:
		ret = MACNOITEM;
	}

	return ret;
}

void show_rx_bcn_info(struct mac_ax_adapter *adapter)
{
#if MAC_AX_8852C_SUPPORT || MAC_AX_8192XB_SUPPORT || MAC_AX_8851E_SUPPORT || MAC_AX_8852D_SUPPORT
	u8 role_idx, bcn_rate, hit_rate, no_hit_rate;
	u32 bcn_rate_info;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);

	if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852C) ||
	    is_chip_id(adapter, MAC_AX_CHIP_ID_8192XB) ||
	    is_chip_id(adapter, MAC_AX_CHIP_ID_8851E) ||
	    is_chip_id(adapter, MAC_AX_CHIP_ID_8852D)) {
		bcn_rate_info = MAC_REG_R32(R_RXBCNHIT_RATE);
		role_idx = (u8)GET_FIELD(bcn_rate_info, B_AX_ROLE_IDX);
		bcn_rate = (u8)GET_FIELD(bcn_rate_info, B_AX_BCN_RATE);
		hit_rate = (u8)GET_FIELD(bcn_rate_info, B_AX_BCN_HIT_RATE);
		no_hit_rate = (u8)GET_FIELD(bcn_rate_info, B_AX_BCN_NO_HIT_RATE);
		PLTFM_MSG_ALWAYS("role_idx: %d, bcn_rx_rate = %d%%, hit_rate = %d%%, no_hit_rate = %d%%\n",
				 role_idx, bcn_rate, hit_rate, no_hit_rate);
	}
#endif
}

u32 mac_chk_leave_lps(struct mac_ax_adapter *adapter, u8 macid)
{
	u8 band = 0;
	u8 port = 0;
	u32 chk_msk = 0;
	struct mac_role_tbl *role;
	u16 pwrbit_set_reg[2] = {R_AX_PPWRBIT_SETTING, R_AX_PPWRBIT_SETTING_C1};
	u32 pwr_mgt_en_bit = 0xE;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);
	u32 reg_pause = 0;
	u32 reg_sleep = 0;
	u8 macid_grp = macid >> MACID_GRP_SH;
	u8 macid_sh = macid & MACID_GRP_MASK;

	role = mac_role_srch(adapter, macid);

	if (!role) {
		PLTFM_MSG_ERR("[ERR]cannot find macid: %d\n", macid);
		return MACNOITEM;
	}

	band = role->info.a_info.bb_sel;
	port = role->info.a_info.port_int;

	chk_msk = pwr_mgt_en_bit << (PORT_SH * port);
	switch (macid_grp) {
	case MACID_GRP_0:
		reg_sleep = R_AX_MACID_SLEEP_0;
		reg_pause = R_AX_SS_MACID_PAUSE_0;
		break;
	case MACID_GRP_1:
		reg_sleep = R_AX_MACID_SLEEP_1;
		reg_pause = R_AX_SS_MACID_PAUSE_1;
		break;
	case MACID_GRP_2:
		reg_sleep = R_AX_MACID_SLEEP_2;
		reg_pause = R_AX_SS_MACID_PAUSE_2;
		break;
	case MACID_GRP_3:
		reg_sleep = R_AX_MACID_SLEEP_3;
		reg_pause = R_AX_SS_MACID_PAUSE_3;
		break;
	default:
		return MACPSSTATFAIL;
	}

	// Bypass Tx pause check during STOP SER period
	if (adapter->sm.ser_ctrl_st != MAC_AX_SER_CTRL_STOP)
		if (MAC_REG_R32(reg_pause) & BIT(macid_sh))
			return MACPSSTATFAIL;

	if ((MAC_REG_R32(reg_sleep) & BIT(macid_sh)))
		return MACPSSTATFAIL;

	if ((MAC_REG_R32(pwrbit_set_reg[band]) & chk_msk))
		return MACPSSTATPWRBITFAIL;

	show_rx_bcn_info(adapter);

#if MAC_AX_PCIE_SUPPORT
	if (adapter->env_info.intf == MAC_AX_INTF_PCIE) {
		struct mac_ax_priv_ops *p_ops = adapter_to_priv_ops(adapter);
		u32 ret;

		ret = p_ops->ltr_dyn_ctrl(adapter, LTR_DYN_CTRL_LEAVE_LPS, 0);
		if (ret != MACSUCCESS) {
			PLTFM_MSG_ERR("[ERR]%s pcie ltr dyn ctrl fail %d\n", __func__, ret);
			return ret;
		}
	}
#endif

	return MACSUCCESS;
}

u8 _is_in_lps(struct mac_ax_adapter *adapter)
{
	u8 i;

	for (i = 0; i < 4; i++) {
		if (lps_status[i] != 0)
			return 1;
	}

	return 0;
}

void reset_lps_seq_num(struct mac_ax_adapter *adapter)
{
	rpwm_seq_num = RPWM_SEQ_NUM_MAX;
	cpwm_seq_num = CPWM_SEQ_NUM_MAX;
}

static u32 send_h2c_ips_cfg(struct mac_ax_adapter *adapter,
			    struct ips_cfg *cfg)
{
	u32 ret = MACSUCCESS;
	struct h2c_info h2c_info = {0};
	struct fwcmd_ips_cfg *fwcmd_ips;

	h2c_info.agg_en = 0;
	h2c_info.content_len = sizeof(struct fwcmd_ips_cfg);
	h2c_info.h2c_cat = FWCMD_H2C_CAT_MAC;
	h2c_info.h2c_class = FWCMD_H2C_CL_PS;
	h2c_info.h2c_func = FWCMD_H2C_FUNC_IPS_CFG;
	h2c_info.rec_ack = 0;
	h2c_info.done_ack = 1;

	fwcmd_ips = (struct fwcmd_ips_cfg *)PLTFM_MALLOC(h2c_info.content_len);
	if (!fwcmd_ips)
		return MACNPTR;

	fwcmd_ips->dword0 =
	cpu_to_le32(SET_WORD(cfg->macid, FWCMD_H2C_IPS_CFG_MACID) |
		(cfg->enable ? FWCMD_H2C_IPS_CFG_ENABLE : 0));

	ret = mac_h2c_common(adapter, &h2c_info, (u32 *)fwcmd_ips);

	PLTFM_FREE(fwcmd_ips, h2c_info.content_len);

	return ret;
}

static u32 leave_ips(struct mac_ax_adapter *adapter, u8 macid)
{
	struct ips_cfg h2c_ips_cfg;
	u32 ret;

	PLTFM_MEMSET(&h2c_ips_cfg, 0, sizeof(struct ips_cfg));

	h2c_ips_cfg.macid = macid;
	h2c_ips_cfg.enable = 0;

	ret = send_h2c_ips_cfg(adapter, &h2c_ips_cfg);
	if (ret != MACSUCCESS)
		return ret;

	ips_status[(macid >> 5)] &= ~BIT(macid & 0x1F);

	return MACSUCCESS;
}

static u32 enter_ips(struct mac_ax_adapter *adapter,
		     u8 macid)
{
	struct ips_cfg h2c_ips_cfg;
	u32 ret;

	if (ips_status[(macid >> 5)] & BIT(macid & 0x1F)) {
		PLTFM_MSG_ERR("[ERR]:IPS info is null\n");
		ret = leave_ips(adapter, macid);
		if (ret != MACSUCCESS)
			return ret;
	}

	PLTFM_MEMSET(&h2c_ips_cfg, 0, sizeof(struct ips_cfg));

	h2c_ips_cfg.macid = macid;
	h2c_ips_cfg.enable = 1;

	ret = send_h2c_ips_cfg(adapter, &h2c_ips_cfg);
	if (ret != MACSUCCESS)
		return ret;

	ips_status[(macid >> 5)] |= BIT(macid & 0x1F);

	return MACSUCCESS;
}

u32 mac_cfg_ips(struct mac_ax_adapter *adapter, u8 macid,
		u8 enable)
{
	u32 ret;

	if (enable == 1)
		ret = enter_ips(adapter, macid);
	else
		ret = leave_ips(adapter, macid);

	return ret;
}

u32 mac_chk_leave_ips(struct mac_ax_adapter *adapter, u8 macid)
{
	u8 band = 0;
	u8 port = 0;
	u32 chk_msk = 0;
	struct mac_role_tbl *role;
	u32 pwr_mgt_en_bit = 0xE;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);
	u32 reg_pause = 0;
	u32 reg_sleep = 0;
	u8 macid_grp = macid >> MACID_GRP_SH;
	u8 macid_sh = macid & MACID_GRP_MASK;

	role = mac_role_srch(adapter, macid);

	if (!role) {
		PLTFM_MSG_ERR("[ERR]cannot find macid: %d\n", macid);
		return MACNOITEM;
	}

	band = role->info.a_info.bb_sel;
	port = role->info.a_info.port_int;

	chk_msk = pwr_mgt_en_bit << (PORT_SH * port);
	switch (macid_grp) {
	case MACID_GRP_0:
		reg_sleep = R_AX_MACID_SLEEP_0;
		reg_pause = R_AX_SS_MACID_PAUSE_0;
		break;
	case MACID_GRP_1:
		reg_sleep = R_AX_MACID_SLEEP_1;
		reg_pause = R_AX_SS_MACID_PAUSE_1;
		break;
	case MACID_GRP_2:
		reg_sleep = R_AX_MACID_SLEEP_2;
		reg_pause = R_AX_SS_MACID_PAUSE_2;
		break;
	case MACID_GRP_3:
		reg_sleep = R_AX_MACID_SLEEP_3;
		reg_pause = R_AX_SS_MACID_PAUSE_3;
		break;
	default:
		return MACPSSTATFAIL;
	}

	// Bypass Tx pause check during STOP SER period
	if (adapter->sm.ser_ctrl_st != MAC_AX_SER_CTRL_STOP)
		if (MAC_REG_R32(reg_pause) & BIT(macid_sh))
			return MACPSSTATFAIL;

	if (MAC_REG_R32(reg_sleep) & BIT(macid_sh))
		return MACPSSTATFAIL;

#if MAC_AX_PCIE_SUPPORT
	if (adapter->env_info.intf == MAC_AX_INTF_PCIE) {
		struct mac_ax_priv_ops *p_ops = adapter_to_priv_ops(adapter);
		u32 ret;

		ret = p_ops->ltr_dyn_ctrl(adapter, LTR_DYN_CTRL_LEAVE_IPS, 0);
		if (ret != MACSUCCESS) {
			PLTFM_MSG_ERR("[ERR]%s pcie ltr dyn ctrl fail %d\n", __func__, ret);
			return ret;
		}
	}
#endif

	return MACSUCCESS;
}

u8 _is_in_ips(struct mac_ax_adapter *adapter)
{
	u8 i;

	for (i = 0; i < 4; i++) {
		if (ips_status[i] != 0)
			return 1;
	}

	return 0;
}

u32 mac_ps_notify_wake(struct mac_ax_adapter *adapter)
{
	struct ps_rpwm_parm parm;

	PLTFM_MEMSET(&parm, 0, sizeof(struct ps_rpwm_parm));

	if (adapter->mac_pwr_info.pwr_in_lps == 0) {
		PLTFM_MSG_ERR("%s: Not in power saving!\n", __func__);
		return MACPWRSTAT;
	}

	parm.notify_wake = 1;
	send_rpwm(adapter, &parm);

	return MACSUCCESS;
}

u32 mac_cfg_ps_advance_parm(struct mac_ax_adapter *adapter,
			    struct mac_ax_ps_adv_parm *parm)
{
	u32 ret = MACSUCCESS;
	struct h2c_info h2c_info = {0};
	struct fwcmd_ps_advance_parm *fwcmd_parm;

	if (!parm)
		return MACBADDR;

	PLTFM_MSG_ALWAYS("%s: MACID(%d), TRXTimeOutTimeSet(%d), TRXTimeOutTimeVal(%d)!\n",
			 __func__, parm->macid, parm->trxtimeouttimeset, parm->trxtimeouttimeval);
	PLTFM_MSG_ALWAYS("%s: EnSmartPsDtimRx(%d)!\n", __func__, parm->ensmartpsdtimrx);

	h2c_info.agg_en = 0;
	h2c_info.content_len = sizeof(struct fwcmd_ps_advance_parm);
	h2c_info.h2c_cat = FWCMD_H2C_CAT_MAC;
	h2c_info.h2c_class = FWCMD_H2C_CL_PS;
	h2c_info.h2c_func = FWCMD_H2C_FUNC_PS_ADVANCE_PARM;
	h2c_info.rec_ack = 0;
	h2c_info.done_ack = 1;

	fwcmd_parm = (struct fwcmd_ps_advance_parm *)PLTFM_MALLOC(h2c_info.content_len);
	if (!fwcmd_parm) {
		PLTFM_MSG_ERR("%s: fwcmd_parm malloc fail!\n", __func__);
		return MACNPTR;
	}

	fwcmd_parm->dword0 =
	cpu_to_le32(SET_WORD(parm->macid, FWCMD_H2C_PS_ADVANCE_PARM_MACID) |
		    SET_WORD(parm->trxtimeouttimeset, FWCMD_H2C_PS_ADVANCE_PARM_TRXTIMEOUTTIMESET) |
		    (parm->ensmartpsdtimrx ? FWCMD_H2C_PS_ADVANCE_PARM_ENSMARTPSDTIMRX : 0) |
		    (parm->entrxextmode ? FWCMD_H2C_PS_ADVANCE_PARM_TRXEXTMODE : 0));
	fwcmd_parm->dword1 =
	cpu_to_le32(SET_WORD(parm->trxtimeouttimeval, FWCMD_H2C_PS_ADVANCE_PARM_TRXTIMEOUTTIMEVAL) |
		    SET_WORD(parm->extnum, FWCMD_H2C_PS_ADVANCE_PARM_EXTNUM));

	ret = mac_h2c_common(adapter, &h2c_info, (u32 *)fwcmd_parm);
	if (ret != MACSUCCESS)
		PLTFM_MSG_ERR("%s: H2C sent fail (%d)!\n", __func__, ret);

	PLTFM_FREE(fwcmd_parm, h2c_info.content_len);

	return ret;
}

static u32 send_h2c_pw_cfg(struct mac_ax_adapter *adapter,
			   struct periodic_wake_cfg *parm)
{
	u32 ret = MACSUCCESS;
	struct h2c_info h2c_info = {0};
	struct fwcmd_periodic_wake *fwcmd_pw;

	h2c_info.agg_en = 0;
	h2c_info.content_len = sizeof(struct fwcmd_periodic_wake);
	h2c_info.h2c_cat = FWCMD_H2C_CAT_MAC;
	h2c_info.h2c_class = FWCMD_H2C_CL_PS;
	h2c_info.h2c_func = FWCMD_H2C_FUNC_PERIODIC_WAKE;
	h2c_info.rec_ack = 0;
	h2c_info.done_ack = 1;

	fwcmd_pw = (struct fwcmd_periodic_wake *)PLTFM_MALLOC(h2c_info.content_len);
	if (!fwcmd_pw)
		return MACNPTR;

	fwcmd_pw->dword0 =
	cpu_to_le32(SET_WORD(parm->macid, FWCMD_H2C_PERIODIC_WAKE_MACID) |
		(parm->enable ? FWCMD_H2C_PERIODIC_WAKE_ENABLE : 0) |
		(parm->band ? FWCMD_H2C_PERIODIC_WAKE_BAND : 0) |
		SET_WORD(parm->port, FWCMD_H2C_PERIODIC_WAKE_PORT));
	fwcmd_pw->dword1 = cpu_to_le32(parm->wake_period);
	fwcmd_pw->dword2 = cpu_to_le32(parm->wake_duration);

	ret = mac_h2c_common(adapter, &h2c_info, (u32 *)fwcmd_pw);

	PLTFM_FREE(fwcmd_pw, h2c_info.content_len);

	return ret;
}

u32 mac_periodic_wake_cfg(struct mac_ax_adapter *adapter,
			  struct mac_ax_periodic_wake_info pw_info)
{
	u32 ret = MACSUCCESS;
	struct periodic_wake_cfg parm;

	if (adapter->sm.fwdl != MAC_AX_FWDL_INIT_RDY)
		return MACNOFW;

	PLTFM_MEMSET(&parm, 0, sizeof(struct periodic_wake_cfg));
	parm.macid = pw_info.macid;
	parm.enable = pw_info.enable;
	parm.band = pw_info.band;
	parm.port = pw_info.port;
	parm.wake_period = pw_info.wake_period;
	parm.wake_duration = pw_info.wake_duration;

	PLTFM_MSG_ALWAYS("%s: macid(%d), enable(%d), band(%d), port(%d)\n",
			 __func__, parm.macid, parm.enable, parm.band, parm.port);
	PLTFM_MSG_ALWAYS("%s: wake_period(%d), wake_duration(%d)\n",
			 __func__, parm.wake_period, parm.wake_duration);

	ret = send_h2c_pw_cfg(adapter, &parm);
	if (ret)
		return ret;

	return ret;
}

u32 send_h2c_req_pwr_state(struct mac_ax_adapter *adapter,
			   struct req_pwr_state_cfg *parm)
{
	u32 ret = MACSUCCESS;
	struct h2c_info h2c_info = {0};
	struct fwcmd_bb_rf_pwr_st *fwcmd_req_pwr_st;

	h2c_info.agg_en = 0;
	h2c_info.content_len = sizeof(struct fwcmd_bb_rf_pwr_st);
	h2c_info.h2c_cat = FWCMD_H2C_CAT_MAC;
	h2c_info.h2c_class = FWCMD_H2C_CL_PS;
	h2c_info.h2c_func = FWCMD_H2C_FUNC_BB_RF_PWR_ST;
	h2c_info.rec_ack = 0;
	h2c_info.done_ack = 0;

	fwcmd_req_pwr_st = (struct fwcmd_bb_rf_pwr_st *)PLTFM_MALLOC(h2c_info.content_len);
	if (!fwcmd_req_pwr_st)
		return MACNPTR;

	fwcmd_req_pwr_st->dword0 =
	cpu_to_le32(SET_WORD(parm->req_pwr_state, FWCMD_H2C_BB_RF_PWR_ST_BB_RF_PWR_REQ_ST));

	ret = mac_h2c_common(adapter, &h2c_info, (u32 *)fwcmd_req_pwr_st);

	PLTFM_FREE(fwcmd_req_pwr_st, h2c_info.content_len);

	return ret;
}

u32 set_pwr_st_cfg(struct mac_ax_adapter *adapter,
		   struct req_pwr_state_cfg *parm)
{
	u32 ret = MACSUCCESS;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);

	switch (parm->req_pwr_state) {
	case REQ_PWR_ST_ADC_OFF:
		MAC_REG_W8(R_AX_ANAPAR_POW_MAC, (MAC_REG_R8(R_AX_ANAPAR_POW_MAC) &
			   ~(B_AX_POW_PC_LDO_PORT0 | B_AX_POW_PC_LDO_PORT1)));
		break;
	case REQ_PWR_ST_BB_OFF:
		MAC_REG_W8(R_AX_SYS_FUNC_EN, (MAC_REG_R8(R_AX_SYS_FUNC_EN) &
			   ~(B_AX_FEN_BBRSTB | B_AX_FEN_BB_GLB_RSTN)));
		MAC_REG_W8(R_AX_SYS_ISO_CTRL_EXTEND, (MAC_REG_R8(R_AX_SYS_ISO_CTRL_EXTEND) |
			   B_AX_R_SYM_ISO_BB2PP));
		MAC_REG_W16(R_AX_AFE_LDO_CTRL, (MAC_REG_R16(R_AX_AFE_LDO_CTRL) &
			    ~(B_AX_R_SYM_WLBBOFF_PC_EN | B_AX_R_SYM_WLBBOFF_P1_PC_EN |
			    B_AX_R_SYM_WLBBOFF_P2_PC_EN | B_AX_R_SYM_WLBBOFF_P3_PC_EN |
			    B_AX_R_SYM_WLBBOFF_P4_PC_EN | B_AX_R_SYM_WLBBOFF1_P1_PC_EN |
			    B_AX_R_SYM_WLBBOFF1_P2_PC_EN | B_AX_R_SYM_WLBBOFF1_P3_PC_EN |
			    B_AX_R_SYM_WLBBOFF1_P4_PC_EN)));
		break;
	case REQ_PWR_ST_CPU_OFF:
		MAC_REG_W8(R_AX_PLATFORM_ENABLE, (MAC_REG_R8(R_AX_PLATFORM_ENABLE) &
			   ~B_AX_WCPU_EN));
		MAC_REG_W16(R_AX_SYS_CLK_CTRL, (MAC_REG_R16(R_AX_SYS_CLK_CTRL) &
			    ~(B_AX_CPU_CLK_EN | B_AX_CPU_IDMEM_CLK_EN)));
		MAC_REG_W32(R_AX_SYS_ISO_CTRL_EXTEND, (MAC_REG_R32(R_AX_SYS_ISO_CTRL_EXTEND) |
			    B_AX_R_SYM_ISO_IMEM02PP | B_AX_R_SYM_ISO_IMEM12PP |
			    B_AX_R_SYM_ISO_IMEM22PP | B_AX_R_SYM_ISO_IMEM32PP |
			    B_AX_R_SYM_ISO_IMEM42PP | B_AX_R_SYM_ISO_DMEM12PP_V1 |
			    B_AX_R_SYM_ISO_DMEM22PP_V1 | B_AX_R_SYM_ISO_DMEM32PP_V1 |
			    B_AX_R_SYM_ISO_DMEM42PP | B_AX_R_SYM_ISO_DMEM52PP |
			    B_AX_R_SYM_ISO_DMEM62PP));
		MAC_REG_W32(R_AX_AFE_CTRL1, (MAC_REG_R32(R_AX_AFE_CTRL1) &
			    ~(B_AX_IMEM0_PC_EN | B_AX_IMEM1_PC_EN | B_AX_IMEM2_PC_EN |
			    B_AX_IMEM3_PC_EN | B_AX_IMEM4_PC_EN | B_AX_DMEM1_PC_EN |
			    B_AX_DMEM2_PC_EN | B_AX_DMEM3_PC_EN | B_AX_DMEM4_PC_EN |
			    B_AX_DMEM5_PC_EN | B_AX_DMEM6_PC_EN)));
		break;
	case REQ_PWR_ST_MAC_OFF:
		MAC_REG_W32(R_AX_SYS_ISO_CTRL_EXTEND, (MAC_REG_R32(R_AX_SYS_ISO_CTRL_EXTEND) &
			    ~B_AX_R_SYM_FEN_WLMACOFF));
		MAC_REG_W16(R_AX_SYS_CLK_CTRL, (MAC_REG_R16(R_AX_SYS_CLK_CTRL) & ~B_AX_MAC_CLK_EN));
		MAC_REG_W8(R_AX_PMC_DBG_CTRL2, (MAC_REG_R8(R_AX_PMC_DBG_CTRL2) |
			   B_AX_SYSON_DIS_PMCR_AX_WRMSK));
		MAC_REG_W8(R_AX_SYS_ISO_CTRL, (MAC_REG_R8(R_AX_SYS_ISO_CTRL) | B_AX_ISO_WLPON2PP));
		MAC_REG_W8(R_AX_SYS_ISO_CTRL, (MAC_REG_R8(R_AX_SYS_ISO_CTRL) | B_AX_ISO_WD2PP));
		MAC_REG_W32(R_AX_AFE_LDO_CTRL, (MAC_REG_R32(R_AX_AFE_LDO_CTRL) &
			    ~(B_AX_R_SYM_WLPOFF_PC_EN | B_AX_R_SYM_WLPOFF_P1_PC_EN |
			    B_AX_R_SYM_WLPOFF_P2_PC_EN | B_AX_R_SYM_WLPOFF_P3_PC_EN |
			    B_AX_R_SYM_WLPOFF_P4_PC_EN)));
		MAC_REG_W32(R_AX_AFE_LDO_CTRL, (MAC_REG_R32(R_AX_AFE_LDO_CTRL) &
			    ~(B_AX_R_SYM_WLPON_PC_EN | B_AX_R_SYM_WLPON_P1_PC_EN |
			    B_AX_R_SYM_WLPON_P2_PC_EN | B_AX_R_SYM_WLPON_P3_PC_EN)));
		break;
	case REQ_PWR_ST_PLL_OFF:
		MAC_REG_W8(R_AX_ANAPAR_POW_MAC, (MAC_REG_R8(R_AX_ANAPAR_POW_MAC) &
			   ~(B_AX_POW_POWER_CUT_POW_LDO | B_AX_POW_PLL_V1)));
		break;
	case REQ_PWR_ST_SWRD_OFF:
		MAC_REG_W8(R_AX_ANAPARLDO_POW_MAC, (MAC_REG_R8(R_AX_ANAPARLDO_POW_MAC) &
			   ~B_AX_POW_SW_SPSDIG));
		break;
	case REQ_PWR_ST_XTAL_OFF:
		MAC_REG_W32(R_AX_WLAN_XTAL_SI_CTRL, REQ_PWR_ST_XTAL_OFF_VAL);
		break;
	case REQ_PWR_ST_ADIE_OFF:
		MAC_REG_W32(R_AX_SYM_ANAPAR_XTAL_MODE_DECODER,
			    (MAC_REG_R32(R_AX_SYM_ANAPAR_XTAL_MODE_DECODER) |
			    B_AX_ADIE_CTRL_BY_SW));
		MAC_REG_W32(R_AX_SYM_ANAPAR_XTAL_MODE_DECODER,
			    (MAC_REG_R32(R_AX_SYM_ANAPAR_XTAL_MODE_DECODER) & ~B_AX_ADIE_EN));
		break;
	case REQ_PWR_ST_BYPASS_DATA_ON:
		MAC_REG_W32(R_AX_RCR, (MAC_REG_R32(R_AX_RCR) | B_AX_STOP_RX_IN));
		if ((MAC_REG_R32(R_AX_SYS_ISO_CTRL_EXTEND) & B_AX_CMAC1_FEN) == B_AX_CMAC1_FEN)
			MAC_REG_W32(R_AX_RCR_C1, (MAC_REG_R32(R_AX_RCR_C1) | B_AX_STOP_RX_IN));
		break;
	case REQ_PWR_ST_MP_INTER_BCN:
		// set LPS option
#if MAC_AX_PCIE_SUPPORT
		if (adapter->env_info.intf == MAC_AX_INTF_PCIE)
			MAC_REG_W32(R_AX_WLLPS_CTRL, MP_INTER_BCN_LPS_OP_PCIE);
#endif
#if MAC_AX_USB_SUPPORT
		if (adapter->env_info.intf == MAC_AX_INTF_USB)
			MAC_REG_W32(R_AX_WLLPS_CTRL, MP_INTER_BCN_LPS_OP_USB);
#endif
#if MAC_AX_SDIO_SUPPORT
		if (adapter->env_info.intf == MAC_AX_INTF_USB)
			MAC_REG_W32(R_AX_WLLPS_CTRL, MP_INTER_BCN_LPS_OP_SDIO);
#endif
		// enter LPS
		MAC_REG_W32(R_AX_WLLPS_CTRL, (MAC_REG_R32(R_AX_WLLPS_CTRL) | B_AX_WL_LPS_EN));
		break;
	default:
		return MACNOITEM;
	}

	return ret;
}

u32 mac_req_pwr_state_cfg(struct mac_ax_adapter *adapter,
			  enum mac_req_pwr_st req_pwr_st)
{
	u32 ret = MACSUCCESS;
	struct req_pwr_state_cfg parm;

	PLTFM_MEMSET(&parm, 0, sizeof(struct req_pwr_state_cfg));
	parm.req_pwr_state = req_pwr_st;

	switch (req_pwr_st) {
	case REQ_PWR_ST_OPEN_RF:
		ret = send_h2c_req_pwr_state(adapter, &parm);
		break;
	case REQ_PWR_ST_CLOSE_RF:
		ret = send_h2c_req_pwr_state(adapter, &parm);
		break;
	case REQ_PWR_ST_ADC_OFF:
		ret = set_pwr_st_cfg(adapter, &parm);
		break;
	case REQ_PWR_ST_BB_OFF:
		ret = set_pwr_st_cfg(adapter, &parm);
		break;
	case REQ_PWR_ST_CPU_OFF:
		ret = set_pwr_st_cfg(adapter, &parm);
		break;
	case REQ_PWR_ST_MAC_OFF:
		ret = set_pwr_st_cfg(adapter, &parm);
		break;
	case REQ_PWR_ST_PLL_OFF:
		ret = set_pwr_st_cfg(adapter, &parm);
		break;
	case REQ_PWR_ST_SWRD_OFF:
		ret = set_pwr_st_cfg(adapter, &parm);
		break;
	case REQ_PWR_ST_XTAL_OFF:
		ret = set_pwr_st_cfg(adapter, &parm);
		break;
	case REQ_PWR_ST_ADIE_OFF:
		ret = set_pwr_st_cfg(adapter, &parm);
		break;
	case REQ_PWR_ST_BYPASS_DATA_ON:
		ret = set_pwr_st_cfg(adapter, &parm);
		break;
	case REQ_PWR_ST_MP_INTER_BCN:
		ret = set_pwr_st_cfg(adapter, &parm);
		break;
	default:
		return MACNOITEM;
	}

	return ret;
}

u32 send_h2c_req_pwr_lvl(struct mac_ax_adapter *adapter,
			 struct req_pwr_lvl_cfg *parm)
{
	u32 ret = MACSUCCESS;
	struct h2c_info h2c_info = {0};
	struct fwcmd_ps_power_level *fwcmd_req_pwr_lvl;

	h2c_info.agg_en = 0;
	h2c_info.content_len = sizeof(struct fwcmd_ps_power_level);
	h2c_info.h2c_cat = FWCMD_H2C_CAT_MAC;
	h2c_info.h2c_class = FWCMD_H2C_CL_PS;
	h2c_info.h2c_func = FWCMD_H2C_FUNC_PS_POWER_LEVEL;
	h2c_info.rec_ack = 0;
	h2c_info.done_ack = 0;

	fwcmd_req_pwr_lvl = (struct fwcmd_ps_power_level *)PLTFM_MALLOC(h2c_info.content_len);
	if (!fwcmd_req_pwr_lvl)
		return MACNPTR;

	fwcmd_req_pwr_lvl->dword0 =
	cpu_to_le32(SET_WORD(parm->macid, FWCMD_H2C_PS_POWER_LEVEL_MACID) |
		    SET_WORD(parm->bcn_to_val, FWCMD_H2C_PS_POWER_LEVEL_BCN_TO_VAL) |
		    SET_WORD(parm->ps_lvl, FWCMD_H2C_PS_POWER_LEVEL_PS_LVL) |
		    SET_WORD(parm->trx_lvl, FWCMD_H2C_PS_POWER_LEVEL_TRX_LVL));

	ret = mac_h2c_common(adapter, &h2c_info, (u32 *)fwcmd_req_pwr_lvl);

	PLTFM_FREE(fwcmd_req_pwr_lvl, h2c_info.content_len);

	return ret;
}

u32 mac_req_pwr_lvl_cfg(struct mac_ax_adapter *adapter,
			struct mac_ax_req_pwr_lvl_info *pwr_lvl_info)
{
	u32 ret = MACSUCCESS;
	struct req_pwr_lvl_cfg parm;

	PLTFM_MEMSET(&parm, 0, sizeof(struct req_pwr_lvl_cfg));

	parm.macid = pwr_lvl_info->macid;

	if (pwr_lvl_info->ps_lvl >= REQ_PS_LVL_MAX ||
	    pwr_lvl_info->trx_lvl >= REQ_TRX_LVL_MAX ||
	    pwr_lvl_info->bcn_to_lvl >= REQ_BCN_TO_LVL_MAX)
		return MACNOITEM;

	if (pwr_lvl_info->bcn_to_val == REQ_BCN_TO_VAL_NONVALID)
		pwr_lvl_info->bcn_to_val = REQ_BCN_TO_VAL_MIN << pwr_lvl_info->bcn_to_lvl;
	else if (pwr_lvl_info->bcn_to_val < REQ_BCN_TO_VAL_MIN)
		pwr_lvl_info->bcn_to_val = REQ_BCN_TO_VAL_MIN;
	else if (pwr_lvl_info->bcn_to_val > REQ_BCN_TO_VAL_MAX)
		pwr_lvl_info->bcn_to_val = REQ_BCN_TO_VAL_MAX;

	parm.bcn_to_val = pwr_lvl_info->bcn_to_val;
	parm.ps_lvl = pwr_lvl_info->ps_lvl;
	parm.trx_lvl = pwr_lvl_info->trx_lvl;

	ret = send_h2c_req_pwr_lvl(adapter, &parm);

	return ret;
}

u32 send_h2c_lps_option_cfg(struct mac_ax_adapter *adapter,
			    struct lps_option_cfg *parm)
{
	u32 ret = MACSUCCESS;
	struct h2c_info h2c_info = {0};
	struct fwcmd_lps_option_cfg *fwcmd_lps_option;

	h2c_info.agg_en = 0;
	h2c_info.content_len = sizeof(struct fwcmd_lps_option_cfg);
	h2c_info.h2c_cat = FWCMD_H2C_CAT_MAC;
	h2c_info.h2c_class = FWCMD_H2C_CL_PS;
	h2c_info.h2c_func = FWCMD_H2C_FUNC_LPS_OPTION_CFG;
	h2c_info.rec_ack = 0;
	h2c_info.done_ack = 0;

	fwcmd_lps_option = (struct fwcmd_lps_option_cfg *)PLTFM_MALLOC(h2c_info.content_len);
	if (!fwcmd_lps_option)
		return MACNPTR;

	fwcmd_lps_option->dword0 =
	cpu_to_le32(parm->req_lps_option ? FWCMD_H2C_LPS_OPTION_CFG_REQ_XTAL_OPTION : 0);

	ret = mac_h2c_common(adapter, &h2c_info, (u32 *)fwcmd_lps_option);

	PLTFM_FREE(fwcmd_lps_option, h2c_info.content_len);

	return ret;
}

u32 mac_lps_option_cfg(struct mac_ax_adapter *adapter,
		       struct rtw_mac_lps_option *lps_opt)
{
	u32 ret = MACSUCCESS;
	struct lps_option_cfg parm;

	PLTFM_MEMSET(&parm, 0, sizeof(struct lps_option_cfg));

#if MAC_AX_USB_SUPPORT
	if (adapter->env_info.intf == MAC_AX_INTF_USB) {
		parm.req_lps_option = lps_opt->req_xtal_option;
		ret = send_h2c_lps_option_cfg(adapter, &parm);
	}
#endif //MAC_AX_USB_SUPPORT
	return ret;
}

u32 send_h2c_tbtt_tuning(struct mac_ax_adapter *adapter,
			 struct tbtt_tuning_cfg *parm)
{
	u32 ret = MACSUCCESS;
	struct h2c_info h2c_info = {0};
	struct fwcmd_tbtt_tuning *fwcmd_tbtt_tuning_info;

	h2c_info.agg_en = 0;
	h2c_info.content_len = sizeof(struct fwcmd_tbtt_tuning);
	h2c_info.h2c_cat = FWCMD_H2C_CAT_MAC;
	h2c_info.h2c_class = FWCMD_H2C_CL_PS;
	h2c_info.h2c_func = FWCMD_H2C_FUNC_TBTT_TUNING;
	h2c_info.rec_ack = 0;
	h2c_info.done_ack = 0;

	fwcmd_tbtt_tuning_info = (struct fwcmd_tbtt_tuning *)PLTFM_MALLOC(h2c_info.content_len);
	if (!fwcmd_tbtt_tuning_info)
		return MACNPTR;

	fwcmd_tbtt_tuning_info->dword0 =
	cpu_to_le32(SET_WORD(parm->band, FWCMD_H2C_TBTT_TUNING_BAND) |
		    SET_WORD(parm->port, FWCMD_H2C_TBTT_TUNING_PORT));

	fwcmd_tbtt_tuning_info->dword1 =
	cpu_to_le32(SET_WORD(parm->shift_val, FWCMD_H2C_TBTT_TUNING_SHIFT_VAL));

	ret = mac_h2c_common(adapter, &h2c_info, (u32 *)fwcmd_tbtt_tuning_info);

	PLTFM_FREE(fwcmd_tbtt_tuning_info, h2c_info.content_len);

	return ret;
}

u32 mac_tbtt_tuning_cfg(struct mac_ax_adapter *adapter,
			struct mac_ax_tbtt_tuning_info *tbtt_tuning_info)
{
	u32 ret = MACSUCCESS;
	struct tbtt_tuning_cfg parm;
#if MAC_AX_8852A_SUPPORT || MAC_AX_8852B_SUPPORT || MAC_AX_8851B_SUPPORT || \
MAC_AX_8852BT_SUPPORT
	struct mac_ax_port_cfg_para cfg_para;

	if (is_chip_id(adapter, MAC_AX_CHIP_ID_8852A) ||
		is_chip_id(adapter, MAC_AX_CHIP_ID_8852B) ||
		is_chip_id(adapter, MAC_AX_CHIP_ID_8852BT) ||
		is_chip_id(adapter, MAC_AX_CHIP_ID_8851B)) {

		cfg_para.mbssid_idx = 0;
		cfg_para.val = tbtt_tuning_info->shift_val/TU_UNIT;
		cfg_para.port = (u8)tbtt_tuning_info->port;
		cfg_para.band = (u8)tbtt_tuning_info->band;
		ret = mac_port_cfg(adapter, MAC_AX_PCFG_TBTT_SHIFT, &cfg_para);

		return ret;
	}
#endif
	PLTFM_MEMSET(&parm, 0, sizeof(struct tbtt_tuning_cfg));

	if (tbtt_tuning_info->band < MAC_AX_BAND_NUM)
		parm.band = tbtt_tuning_info->band;
	else
		return MACNOITEM;

	if (tbtt_tuning_info->port < MAC_AX_PORT_NUM)
		parm.port = tbtt_tuning_info->port;
	else
		return MACNOITEM;

	parm.shift_val = tbtt_tuning_info->shift_val;

	ret = send_h2c_tbtt_tuning(adapter, &parm);

	return ret;
}
