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

static void restore_flr_lps(struct mac_ax_adapter *adapter)
{
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);
	u32 val32 = 0;

	MAC_REG_W32(R_AX_WCPU_FW_CTRL, 0);

#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_W32(R_AX_AFE_CTRL1, MAC_REG_R32(R_AX_AFE_CTRL1) &
			    ~B_AX_CMAC_CLK_SEL);

		val32 = MAC_REG_R32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN);
		MAC_REG_W32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN, val32 &
			    ~(B_AX_GPIO8_PULL_LOW_EN | B_AX_LED1_PULL_LOW_EN));
	}
#endif

#if (MAC_AX_8852C_SUPPORT || MAC_AX_8192XB_SUPPORT || MAC_AX_8851E_SUPPORT || \
MAC_AX_8852D_SUPPORT || MAC_AX_1115E_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) ||
	    is_chip_id(adapter, MAC_BE_CHIP_ID_1115E)) {
		val32 = MAC_REG_R32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN);
		MAC_REG_W32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN, val32 &
			    ~(B_AX_GPIO8_PULL_LOW_EN |
			      B_AX_LED1_PULL_LOW_EN_V1));
	}
#endif
}

static void clr_aon_int(struct mac_ax_adapter *adapter)
{
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);
	u32 val32;

	if (adapter->env_info.intf != MAC_AX_INTF_PCIE)
		return;

	val32 = MAC_REG_R32(R_AX_FWS0IMR);
	val32 &= ~B_AX_FS_GPIOA_INT_EN;
	MAC_REG_W32(R_AX_FWS0IMR, val32);

	val32 = MAC_REG_R32(R_AX_FWS0ISR);
	val32 |= B_AX_FS_GPIOA_INT;
	MAC_REG_W32(R_AX_FWS0ISR, val32);
}

static u32 _patch_flr_lps(struct mac_ax_adapter *adapter)
{
	struct mac_ax_ops *mac_ops = adapter_to_mac_ops(adapter);
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);
	u32 val32, cnt, ret = MACSUCCESS;

	if (chk_patch_flr_lps(adapter) == PATCH_DISABLE)
		return MACSUCCESS;

	restore_flr_lps(adapter);

	val32 = MAC_REG_R32(R_AX_FWS0IMR);
	val32 |= B_AX_FS_GPIOA_INT_EN;
	MAC_REG_W32(R_AX_FWS0IMR, val32);

	ret = mac_ops->set_gpio_func(adapter, RTW_MAC_GPIO_SW_IO,
				     LPS_LEAVE_GPIO);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]set gpio fail %d\n", ret);
		return ret;
	}

	val32 = MAC_REG_R32(R_AX_GPIO_EXT_CTRL);
	val32 &= ~BIT18;
	MAC_REG_W32(R_AX_GPIO_EXT_CTRL, val32);

	val32 = MAC_REG_R32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN);
	val32 &= ~B_AX_GPIO10_PULL_LOW_EN;
	MAC_REG_W32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN, val32);

	val32 = MAC_REG_R32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_HIGH_EN);
	val32 |= B_AX_GPIO10_PULL_HIGH_EN;
	MAC_REG_W32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_HIGH_EN, val32);

	val32 = MAC_REG_R32(R_AX_GPIO_INTM);
	val32 |= B_AX_GPIOA_INT_MD;
	MAC_REG_W32(R_AX_GPIO_INTM, val32);

	val32 = MAC_REG_R32(R_AX_GPIO_EXT_CTRL);
	val32 |= BIT26;
	MAC_REG_W32(R_AX_GPIO_EXT_CTRL, val32);

	val32 = MAC_REG_R32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN);
	val32 |= B_AX_GPIO10_PULL_LOW_EN;
	MAC_REG_W32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN, val32);

	val32 = MAC_REG_R32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_HIGH_EN);
	val32 &= ~B_AX_GPIO10_PULL_HIGH_EN;
	MAC_REG_W32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_HIGH_EN, val32);

	val32 = MAC_REG_R32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN);
	val32 &= ~B_AX_GPIO10_PULL_LOW_EN;
	MAC_REG_W32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_LOW_EN, val32);

	val32 = MAC_REG_R32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_HIGH_EN);
	val32 |= B_AX_GPIO10_PULL_HIGH_EN;
	MAC_REG_W32(R_AX_GPIO0_15_EECS_EESK_LED1_PULL_HIGH_EN, val32);

	cnt = LPS_POLL_CNT;
	while (cnt && (GET_FIELD(MAC_REG_R32(R_AX_IC_PWR_STATE), B_AX_WLMAC_PWR_STE) ==
		       MAC_AX_MAC_LPS)) {
		cnt--;
		PLTFM_DELAY_US(LPS_POLL_DLY_US);
	}

	if (!cnt) {
		PLTFM_MSG_ERR("[ERR]Polling MAC state timeout! 0x3F0 = %X\n",
			      MAC_REG_R32(R_AX_IC_PWR_STATE));
		return MACPOLLTO;
	}

	return MACSUCCESS;
}

static u32 pwr_cmd_poll(struct mac_ax_adapter *adapter, struct mac_pwr_cfg *seq)
{
	u8 val = 0;
	u32 addr;
	u32 cnt;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);

	cnt = PWR_POLL_CNT;
	addr = seq->addr;

	while (cnt--) {
		val = MAC_REG_R8(addr);
		val &= seq->msk;
		if (val == (seq->val & seq->msk))
			return MACSUCCESS;
		PLTFM_DELAY_US(PWR_POLL_DLY_US);
	}

	PLTFM_MSG_ERR("[ERR] Polling timeout\n");
	PLTFM_MSG_ERR("[ERR] addr: %X, %X\n", addr, seq->addr);
	PLTFM_MSG_ERR("[ERR] val: %X, %X\n", val, seq->val);

	return MACPOLLTO;
}

static u32 sub_pwr_seq_start(struct mac_ax_adapter *adapter,
			     u8 cv_msk, u8 intf_msk, struct mac_pwr_cfg *seq)
{
	u8 val;
	u32 addr;
	u32 ret;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);

	while (seq->cmd != PWR_CMD_END) {
		if (!(seq->intf_msk & intf_msk) || !(seq->cut_msk & cv_msk))
			goto next_seq;

		switch (seq->cmd) {
		case PWR_CMD_WRITE:
			addr = seq->addr;
			val = MAC_REG_R8(addr);
			val &= ~(seq->msk);
			val |= (seq->val & seq->msk);

			MAC_REG_W8(addr, val);
			break;
		case PWR_CMD_POLL:
			ret = pwr_cmd_poll(adapter, seq);
			if (ret != MACSUCCESS) {
				PLTFM_MSG_ERR("[ERR]pwr cmd poll %d\n", ret);
				return ret;
			}
			break;
		case PWR_CMD_DELAY:
			if (seq->val == PWR_DELAY_US)
				PLTFM_DELAY_US(seq->addr);
			else
				PLTFM_DELAY_US(seq->addr * 1000);
			break;
		default:
			PLTFM_MSG_ERR("[ERR]unknown pwr seq cmd %d\n",
				      seq->cmd);
			return MACNOITEM;
		}
next_seq:
		seq++;
	}

	return MACSUCCESS;
}

u32 pwr_seq_start(struct mac_ax_adapter *adapter,
		  struct mac_pwr_cfg **seq)
{
	u8 cv;
	u8 intf;
	u32 ret;
	struct mac_ax_hw_info *hw_info = adapter->hw_info;
	struct mac_pwr_cfg *sub_seq = *seq;
#if MAC_AX_USB_SUPPORT
	u32 val = 0;
#endif

	switch (hw_info->cv) {
	case CAV:
		cv = PWR_CAV_MSK;
		break;
	case CBV:
		cv = PWR_CBV_MSK;
		break;
	case CCV:
		cv = PWR_CCV_MSK;
		break;
	case CDV:
		cv = PWR_CDV_MSK;
		break;
	case CEV:
		cv = PWR_CEV_MSK;
		break;
	case CFV:
		cv = PWR_CFV_MSK;
		break;
	case CGV:
		//fall through
	default:
		PLTFM_MSG_ERR("[ERR]cut version\n");
		cv = PWR_CGV_MSK;
		break;
	}

	switch (adapter->env_info.intf) {
	case MAC_AX_INTF_SDIO:
		intf = PWR_INTF_MSK_SDIO;
		break;
	case MAC_AX_INTF_USB:
#if MAC_AX_USB_SUPPORT
		val = get_usb_mode(adapter);
		if (val == MAC_AX_USB3)
			intf = PWR_INTF_MSK_USB3;
		else
			intf = PWR_INTF_MSK_USB2;
		break;
#endif
	case MAC_AX_INTF_PCIE:
		intf = PWR_INTF_MSK_PCIE;
		break;
	default:
		PLTFM_MSG_ERR("[ERR]interface\n");
		return MACNOITEM;
	}

	while (sub_seq) {
		ret = sub_pwr_seq_start(adapter, cv, intf, sub_seq);
		if (ret != MACSUCCESS) {
			PLTFM_MSG_ERR("[ERR]sub pwr seq %d\n", ret);
			return ret;
		}
		seq++;
		sub_seq = *seq;
	}

	return MACSUCCESS;
}

u32 mac_pwr_switch(struct mac_ax_adapter *adapter, u8 on)
{
	u32 ret = MACSUCCESS;
	u32 ret_end;
	struct mac_pwr_cfg **pwr_seq;
	struct mac_ax_intf_ops *ops = adapter_to_intf_ops(adapter);
	struct mac_ax_priv_ops *p_ops = adapter_to_priv_ops(adapter);
	u32 (*pwr_func)(struct mac_ax_adapter *adapter);
	u32 val32;
	u32 cnt = PWR_POLL_CNT;

	val32 = MAC_REG_R32(R_AX_GPIO_MUXCFG) & B_AX_BOOT_MODE;
	if (val32 == B_AX_BOOT_MODE) {
		val32 = MAC_REG_R32(R_AX_SYS_PW_CTRL) & ~B_AX_APFN_ONMAC;
		MAC_REG_W32(R_AX_SYS_PW_CTRL, val32);
		val32 = MAC_REG_R32(R_AX_SYS_STATUS1) & ~B_AX_AUTO_WLPON;
		MAC_REG_W32(R_AX_SYS_STATUS1, val32);
		val32 = MAC_REG_R32(R_AX_GPIO_MUXCFG) & ~B_AX_BOOT_MODE;
		MAC_REG_W32(R_AX_GPIO_MUXCFG, val32);
		val32 = MAC_REG_R32(R_AX_RSV_CTRL) & ~B_AX_R_DIS_PRST;
		MAC_REG_W32(R_AX_RSV_CTRL, val32);
	}

	val32 = MAC_REG_R32(R_AX_IC_PWR_STATE);
	val32 = GET_FIELD(val32, B_AX_WLMAC_PWR_STE);
	if (val32 == MAC_AX_MAC_OFF && on == MAC_AX_MAC_OFF) {
		PLTFM_MSG_WARN("MAC has already powered off\n");
		return MACSUCCESS;
	}

	if (!p_ops->intf_pwr_switch) {
		PLTFM_MSG_ERR("interface power switch func is NULL\n");
		ret = MACNPTR;
		return ret;
	}

	if (on) {
		pwr_seq = adapter->mac_pwr_info.pwr_on_seq;
		pwr_func = p_ops->pwr_on;
	} else {
		pwr_seq = adapter->mac_pwr_info.pwr_off_seq;
		pwr_func = p_ops->pwr_off;
		adapter->sm.pwr = MAC_AX_PWR_PRE_OFF;
		adapter->sm.dmac_func = MAC_AX_FUNC_OFF;
		adapter->sm.cmac0_func = MAC_AX_FUNC_OFF;
		adapter->sm.cmac1_func = MAC_AX_FUNC_OFF;
		adapter->sm.bb0_func = MAC_AX_FUNC_OFF;
		adapter->sm.bb1_func = MAC_AX_FUNC_OFF;
		adapter->sm.plat = MAC_AX_PLAT_OFF;
	}

	ret = p_ops->intf_pwr_switch(adapter, PWR_PRE_SWITCH, on);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("interface pre pwr switch fails %d\n", ret);
		goto END;
	}

	if (on) {
		/* Read 0x3F0 */
		val32 = MAC_REG_R32(R_AX_IC_PWR_STATE);
		val32 = GET_FIELD(val32, B_AX_WLMAC_PWR_STE);
		if (val32 != MAC_AX_MAC_OFF) {
			/* 0x04[16] = 1 */
			val32 = MAC_REG_R32(R_AX_SYS_PW_CTRL);
			MAC_REG_W32(R_AX_SYS_PW_CTRL, val32 | B_AX_EN_WLON);
			/* 0x04[9] = 1 */
			val32 = MAC_REG_R32(R_AX_SYS_PW_CTRL);
			MAC_REG_W32(R_AX_SYS_PW_CTRL, val32 | B_AX_APFM_OFFMAC);
			/* polling 0x04[9] = 0 */
			while (--cnt) {
				val32 = MAC_REG_R32(R_AX_SYS_PW_CTRL);
				val32 = val32 & B_AX_APFM_OFFMAC;
				val32 = val32 >> B_AX_REG_CP_ICP_SEL_FAST_SH;
				//val32 = GET_FIELD(val32, B_AX_APFM_OFFMAC);
				if (val32 == MAC_AX_MAC_OFF)
					break;
				PLTFM_DELAY_US(PWR_POLL_DLY_US);
			}
		}
		/* 0x04[16] = 0 */
		val32 = MAC_REG_R32(R_AX_SYS_PW_CTRL);
		MAC_REG_W32(R_AX_SYS_PW_CTRL, val32 & ~B_AX_EN_WLON);
		/* 0x04[10] = 0 */
		val32 = MAC_REG_R32(R_AX_SYS_PW_CTRL);
		MAC_REG_W32(R_AX_SYS_PW_CTRL, val32 & ~B_AX_APFM_SWLPS);

		PLTFM_DELAY_MS(1);

#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_W32(R_AX_AFE_CTRL1, MAC_REG_R32(R_AX_AFE_CTRL1) &
			    ~B_AX_CMAC_CLK_SEL);
	}
#endif
		#if 0
		while (--cnt) {
			val32 = MAC_REG_R32(R_AX_IC_PWR_STATE);
			val32 = GET_FIELD(val32, B_AX_WLMAC_PWR_STE);
			if (val32 != MAC_AX_MAC_LPS)
				break;
			PLTFM_DELAY_US(PWR_POLL_DLY_US);
		}

		if (val32 == MAC_AX_MAC_ON) {
			PLTFM_MSG_WARN("MAC has already powered on %d\n", val32);
			ret = MACALRDYON;
			goto END;
		} else if (val32 == MAC_AX_MAC_LPS) {
			PLTFM_MSG_ERR("MAC leave LPS fail %d\n", val32);
			ret = MACPWRSTAT;
			goto END;
		}
		#endif
	}

	if (!pwr_func) {
		ret = pwr_seq_start(adapter, pwr_seq);
		if (ret != MACSUCCESS) {
			PLTFM_MSG_ERR("[ERR]pwr seq start %d\n", ret);
			adapter->sm.pwr = MAC_AX_PWR_ERR;
			goto END;
		}
	} else {
		ret = pwr_func(adapter);
		if (ret != MACSUCCESS) {
			PLTFM_MSG_ERR("[ERR]pwr func %d\n", ret);
			adapter->sm.pwr = MAC_AX_PWR_ERR;
			goto END;
		}
	}

	ret = p_ops->intf_pwr_switch(adapter, PWR_POST_SWITCH, on);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("interface post pwr switch fails %d\n", ret);
		adapter->sm.pwr = MAC_AX_PWR_ERR;
		goto END;
	}

	mac_ax_init_state(adapter);

	if (on) {
		adapter->sm.pwr = MAC_AX_PWR_ON;
		adapter->sm.plat = MAC_AX_PLAT_ON;
		adapter->sm.io_st = MAC_AX_IO_ST_NORM;
		adapter->sm.fw_rst = MAC_AX_FW_RESET_IDLE;
		adapter->sm.l2_st = MAC_AX_L2_DIS;
		adapter->mac_pwr_info.pwr_in_lps = 0;
		/* patch form BT BG/LDO issue */
		MAC_REG_W8(R_AX_SCOREBOARD + 3, MAC_AX_NOTIFY_TP_MAJOR);
	} else {
		adapter->sm.pwr = MAC_AX_PWR_OFF;
		adapter->sm.l2_st = MAC_AX_L2_DIS;
		/* patch form BT BG/LDO issue */
		MAC_REG_W8(R_AX_SCOREBOARD + 3, MAC_AX_NOTIFY_PWR_MAJOR);
	}
END:
	if (on)
		clr_aon_int(adapter);
	ret_end = p_ops->intf_pwr_switch(adapter, PWR_END_SWITCH, on);
	if (ret_end != MACSUCCESS) {
		PLTFM_MSG_ERR("interface end pwr switch fails %d\n", ret_end);
		adapter->sm.pwr = MAC_AX_PWR_ERR;
		return ret_end;
	}
	return ret;
}
