/******************************************************************************
 *
 * Copyright(c) 2019 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 _PHL_TRX_MIT_C_
#include "phl_headers.h"

#if defined(CONFIG_PCI_HCI) && defined(PCIE_TRX_MIT_EN)
enum rtw_phl_status phl_pcie_trx_mit_start(struct phl_info_t *phl_info,
					   u8 dispr_idx)
{
	struct rtw_pcie_trx_mit_info_t info = {0};

	if (dispr_idx != HW_BAND_0)
		return RTW_PHL_STATUS_SUCCESS;

	PHL_INFO("%s :: pcie trx interrupt mitigation off\n", __func__);

	if (RTW_HAL_STATUS_SUCCESS !=
	    rtw_hal_pcie_trx_mit(phl_info->hal, info.tx_timer, info.tx_counter,
				 info.rx_timer, info.rx_counter))
		return RTW_PHL_STATUS_FAILURE;

	return RTW_PHL_STATUS_SUCCESS;
}

enum rtw_phl_status
phl_evt_pcie_trx_mit_hdlr(struct phl_info_t *phl_info, u8 *mit_info)
{
	struct rtw_pcie_trx_mit_info_t *info = (struct rtw_pcie_trx_mit_info_t *)mit_info;

	PHL_INFO("%s :: tx_timer == %d us, tx_counter = %d, rx_timer == %d us, "
		 "rx_counter = %d, fixed_mitigation=%d\n",
		 __func__, info->tx_timer, info->tx_counter, info->rx_timer,
		 info->rx_counter, info->fixed_mitigation);

	if (RTW_HAL_STATUS_SUCCESS !=
	    rtw_hal_pcie_trx_mit(phl_info->hal, info->tx_timer,
				 info->tx_counter, info->rx_timer,
				 info->rx_counter))
		return RTW_PHL_STATUS_FAILURE;

	phl_info->hci->fixed_mitigation = info->fixed_mitigation;

	return RTW_PHL_STATUS_SUCCESS;
}

static void _phl_pcie_trx_mit_done(void *drv_priv, u8 *cmd, u32 cmd_len, enum rtw_phl_status status)
{
	if (cmd) {
		_os_mem_free(drv_priv, cmd, cmd_len);
		cmd = NULL;
	}
}

static enum rtw_phl_status
phl_pcie_trx_mit(struct phl_info_t *phl_info,
                 u32 tx_timer, u8 tx_counter, u32 rx_timer, u8 rx_counter)
{
#ifdef CONFIG_CMD_DISP
	void *drv_priv = phl_to_drvpriv(phl_info);
	struct rtw_pcie_trx_mit_info_t *info = NULL;

	enum rtw_phl_status psts = RTW_PHL_STATUS_FAILURE;
	u32 info_len = sizeof(struct rtw_pcie_trx_mit_info_t);

	info = _os_mem_alloc(drv_priv, info_len);
	if (info == NULL) {
		PHL_ERR("%s: alloc mit_info failed!\n", __func__);
		goto _exit;
	}

	info->tx_timer = tx_timer;
	info->tx_counter = tx_counter;
	info->rx_timer = rx_timer;
	info->rx_counter = rx_counter;

	psts = phl_cmd_enqueue(phl_info,
	                       HW_BAND_0,
	                       MSG_EVT_PCIE_TRX_MIT,
	                       (u8 *)info,
	                       info_len,
	                       _phl_pcie_trx_mit_done,
	                       PHL_CMD_NO_WAIT,
	                       0);

	if (is_cmd_failure(psts)) {
		/* Send cmd success, but wait cmd fail*/
		psts = RTW_PHL_STATUS_FAILURE;
	} else if (psts != RTW_PHL_STATUS_SUCCESS) {
		/* Send cmd fail */
		psts = RTW_PHL_STATUS_FAILURE;
		_os_mem_free(drv_priv, info, info_len);
	}
_exit:
	return psts;
#else
	PHL_ERR("phl_fsm not support %s\n", __func__);
	return RTW_PHL_STATUS_FAILURE;
#endif /*CONFIG_CMD_DISP*/
}

void phl_pcie_trx_mit_watchdog(struct phl_info_t *phl_info)
{
	static enum rtw_tfc_lvl rx_traffic_lvl = RTW_TFC_IDLE;
	struct rtw_stats *phl_stats = &phl_info->phl_com->phl_stats;
	struct bus_sw_cap_t *bus_sw_cap = &phl_info->phl_com->bus_sw_cap;
	struct rtw_pcie_trx_mit_info_t *mit_ctl = &bus_sw_cap->mit_ctl;

	if (phl_info->hci->fixed_mitigation == 1)
		return;

	if (rx_traffic_lvl == phl_stats->rx_traffic.lvl)
		return;

	rx_traffic_lvl = phl_stats->rx_traffic.lvl;

	if (rx_traffic_lvl == RTW_TFC_HIGH)
		phl_pcie_trx_mit(phl_info, 0, 0, mit_ctl->rx_timer,
		                 mit_ctl->rx_counter);
	else
		phl_pcie_trx_mit(phl_info, 0, 0, 0, 0);
}
#endif /*defined(CONFIG_PCI_HCI) && defined(PCIE_TRX_MIT_EN)*/

