/******************************************************************************
 *
 * Copyright(c) 2015 - 2017 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.
 *
 *****************************************************************************/
#define _RTL8822CS_HALINIT_C_

#include <drv_types.h>		/* PADAPTER, basic_types.h and etc. */
#include <hal_data.h>		/* HAL_DATA_TYPE */
#include "../../hal_halmac.h"	/* rtw_halmac_query_tx_page_num() */
#include "../rtl8822c.h"	/* rtl8822c_hal_init(), rtl8822c_phy_init_haldm() and etc. */

#ifdef CONFIG_FWLPS_IN_IPS
static u8 fw_ips_leave(struct _ADAPTER *a)
{
	struct sreset_priv *psrtpriv = &GET_HAL_DATA(a)->srestpriv;
	struct debug_priv *pdbgpriv = &adapter_to_dvobj(a)->drv_dbg;
	struct pwrctrl_priv *pwrctl = adapter_to_pwrctl(a);
	systime start_time;
	u8 cpwm_orig, cpwm_now, rpwm;
	u8 bMacPwrCtrlOn = _TRUE;


	if ((pwrctl->bips_processing == _FALSE)
	    || (psrtpriv->silent_reset_inprogress == _TRUE)
	    || (GET_HAL_DATA(a)->bFWReady == _FALSE)
	    || (pwrctl->pre_ips_type != 0))
		return _FAIL;

	RTW_INFO("%s: Leaving FW_IPS\n", __func__);

	/* for polling cpwm */
	cpwm_orig = 0;
	rtw_hal_get_hwreg(a, HW_VAR_CPWM, &cpwm_orig);

	/* set rpwm */
#if 1
	rtw_hal_get_hwreg(a, HW_VAR_RPWM_TOG, &rpwm);
	rpwm += 0x80;
#else
	rpwm = pwrctl->tog;
#endif
	rpwm |= PS_ACK;
	rtw_hal_set_hwreg(a, HW_VAR_SET_RPWM, (u8 *)(&rpwm));
	RTW_INFO("%s: write rpwm=%02x\n", __FUNCTION__, rpwm);

	pwrctl->tog = (rpwm + 0x80) & 0x80;

	/* do polling cpwm */
	start_time = rtw_get_current_time();
	do {
		rtw_mdelay_os(1);

		rtw_hal_get_hwreg(a, HW_VAR_CPWM, &cpwm_now);
		if ((cpwm_orig ^ cpwm_now) & 0x80) {
#ifdef DBG_CHECK_FW_PS_STATE
			RTW_INFO("%s: polling cpwm ok when leaving IPS in FWLPS state,"
				 " cost %d ms,"
				 " cpwm_orig=0x%02x, cpwm_now=0x%02x, 0x100=0x%x\n",
				 __FUNCTION__,
				 rtw_get_passing_time_ms(start_time),
				 cpwm_orig, cpwm_now, rtw_read8(a, REG_CR_8822C));
#endif /* DBG_CHECK_FW_PS_STATE */
			break;
		}

		if (rtw_get_passing_time_ms(start_time) > 100) {
			RTW_ERR("%s: polling cpwm timeout when leaving IPS in FWLPS state\n", __FUNCTION__);
			break;
		}
	} while (1);

	rtl8822c_set_FwPwrModeInIPS_cmd(a, 0);

	rtw_hal_set_hwreg(a, HW_VAR_APFM_ON_MAC, &bMacPwrCtrlOn);

#ifdef DBG_CHECK_FW_PS_STATE
	if (rtw_fw_ps_state(a) == _FAIL) {
		RTW_INFO("after hal init, fw ps state in 32k\n");
		pdbgpriv->dbg_ips_drvopen_fail_cnt++;
	}
#endif /* DBG_CHECK_FW_PS_STATE */

	return _SUCCESS;
}

static u8 fw_ips_enter(struct _ADAPTER *a)
{
	struct sreset_priv *psrtpriv = &GET_HAL_DATA(a)->srestpriv;
	struct debug_priv *pdbgpriv = &adapter_to_dvobj(a)->drv_dbg;
	struct pwrctrl_priv *pwrctl = adapter_to_pwrctl(a);
	systime start_time;
	int cnt = 0;
	u8 val8 = 0, rpwm;


	if ((pwrctl->bips_processing == _FALSE)
	    || (psrtpriv->silent_reset_inprogress == _TRUE)
	    || (GET_HAL_DATA(a)->bFWReady == _FALSE)
	    || (a->netif_up == _FALSE)) {
		pdbgpriv->dbg_carddisable_cnt++;
		pwrctl->pre_ips_type = 1;

		return _FAIL;
	}

	RTW_INFO("%s: issue H2C to FW when entering IPS\n", __FUNCTION__);
	rtl8822c_set_FwPwrModeInIPS_cmd(a, 0x1);

	/*
	 * poll 0x1cc to make sure H2C command already finished by FW;
	 * MAC_0x1cc=0 means H2C done by FW.
	 */
	start_time = rtw_get_current_time();
	do {
		rtw_mdelay_os(10);
		val8 = rtw_read8(a, REG_HMETFR_8822C);
		cnt++;
		if (!val8)
			break;

		if (rtw_get_passing_time_ms(start_time) > 100) {
			RTW_ERR("%s: fail to wait H2C, REG_HMETFR=0x%x, cnt=%d\n",
				__FUNCTION__, val8, cnt);
#ifdef DBG_CHECK_FW_PS_STATE
			RTW_WARN("MAC_1C0=0x%08x, MAC_1C4=0x%08x, MAC_1C8=0x%08x, MAC_1CC=0x%08x\n",
				 rtw_read32(a, 0x1c0), rtw_read32(a, 0x1c4),
				 rtw_read32(a, 0x1c8), rtw_read32(a, REG_HMETFR_8822C));
#endif /* DBG_CHECK_FW_PS_STATE */
			goto exit;
		}
	} while (1);

	/* H2C done, enter 32k */
	/* set rpwm to enter 32k */
#if 1
	rtw_hal_get_hwreg(a, HW_VAR_RPWM_TOG, &rpwm);
	rpwm += 0x80;
#else
	rpwm = pwrctl->tog;
#endif
	rpwm |= PS_STATE_S0;
	rtw_hal_set_hwreg(a, HW_VAR_SET_RPWM, &rpwm);
	RTW_INFO("%s: write rpwm=%02x\n", __FUNCTION__, rpwm);
	pwrctl->tog = (rpwm + 0x80) & 0x80;

	cnt = val8 = 0;
	start_time = rtw_get_current_time();
	do {
		val8 = rtw_read8(a, REG_CR_8822C);
		cnt++;
		RTW_INFO("%s: polling 0x100=0x%x, cnt=%d\n",
			 __FUNCTION__, val8, cnt);
		if (val8 == 0xEA) {
			RTW_INFO("%s: polling 0x100=0xEA, cnt=%d, cost %d ms\n",
				 __FUNCTION__, cnt,
				 rtw_get_passing_time_ms(start_time));
			break;
		}

		if (rtw_get_passing_time_ms(start_time) > 100) {
			RTW_ERR("%s: polling polling 0x100=0xEA timeout! cnt=%d\n",
				__FUNCTION__, cnt);
#ifdef DBG_CHECK_FW_PS_STATE
			RTW_WARN("MAC_1C0=0x%08x, MAC_1C4=0x%08x, MAC_1C8=0x%08x, MAC_1CC=0x%08x\n",
				 rtw_read32(a, 0x1c0), rtw_read32(a, 0x1c4),
				 rtw_read32(a, 0x1c8), rtw_read32(a, REG_HMETFR_8822C));
#endif /* DBG_CHECK_FW_PS_STATE */
			break;
		}

		rtw_mdelay_os(10);
	} while (1);

exit:
	RTW_INFO("polling done when entering IPS, check result: 0x100=0x%02x, cnt=%d, MAC_1cc=0x%02x\n",
		 rtw_read8(a, REG_CR_8822C), cnt, rtw_read8(a, REG_HMETFR_8822C));

	pwrctl->pre_ips_type = 0;

	return _SUCCESS;
}
#endif /* CONFIG_FWLPS_IN_IPS */


u32 rtl8822cs_init(PADAPTER adapter)
{
	u8 ok = _TRUE;
	PHAL_DATA_TYPE hal;


	hal = GET_HAL_DATA(adapter);

#ifdef CONFIG_FWLPS_IN_IPS
	if (fw_ips_leave(adapter) == _SUCCESS)
		return _SUCCESS;
#endif
	ok = rtl8822c_hal_init(adapter);
	if (_FALSE == ok)
		return _FAIL;

	rtw_halmac_query_tx_page_num(adapter_to_dvobj(adapter));

	rtl8822c_mac_verify(adapter);

	rtl8822c_phy_init_haldm(adapter);
#ifdef CONFIG_BEAMFORMING
	rtl8822c_phy_bf_init(adapter);
#endif

#ifdef CONFIG_FW_MULTI_PORT_SUPPORT
	/*HW /FW init*/
	rtw_hal_set_default_port_id_cmd(adapter, 0);
#endif

#ifdef CONFIG_BT_COEXIST
	/* Init BT hw config. */
	if (hal->EEPROMBluetoothCoexist == _TRUE) {
		rtw_btcoex_HAL_Initialize(adapter, _FALSE);
		#ifdef CONFIG_FW_MULTI_PORT_SUPPORT
		rtw_hal_set_wifi_btc_port_id_cmd(adapter);
		#endif
	} else
#endif /* CONFIG_BT_COEXIST */
		rtw_btcoex_wifionly_hw_config(adapter);

	rtl8822c_init_misc(adapter);

	return _SUCCESS;
}

u32 rtl8822cs_deinit(PADAPTER adapter)
{
#ifdef CONFIG_FWLPS_IN_IPS
	if (fw_ips_enter(adapter) == _SUCCESS)
		return _SUCCESS;
#endif

	return rtl8822c_deinit(adapter);
}

void rtl8822cs_init_default_value(PADAPTER adapter)
{
	PHAL_DATA_TYPE hal;


	hal = GET_HAL_DATA(adapter);

	rtl8822c_init_default_value(adapter);

	/* interface related variable */
	hal->SdioRxFIFOCnt = 0;
}
