/******************************************************************************
 *
 * 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 "mac_ax.h"

#define CHIP_ID_HW_DEF_8852A	0x50
#define CHIP_ID_HW_DEF_8852B	0x51
#define CHIP_ID_HW_DEF_8852C	0x52
#define CHIP_ID_HW_DEF_8192XB	0x53

#define SDIO_WAIT_CNT		50
#if MAC_AX_SDIO_SUPPORT
static u8 r8_indir_cmd52_sdio(void *drv_adapter,
			      struct mac_ax_pltfm_cb *pltfm_cb, u32 adr);
#endif

#ifndef CONFIG_NEW_HALMAC_INTERFACE
static u8 chk_pltfm_cb(void *drv_adapter, enum mac_ax_intf intf,
		       struct mac_ax_pltfm_cb *pltfm_cb)
{
	if (!pltfm_cb)
		return MACSUCCESS;

	if (!pltfm_cb->msg_print)
		return MACSUCCESS;

#if MAC_AX_SDIO_SUPPORT
	if (!pltfm_cb->sdio_cmd52_r8 || !pltfm_cb->sdio_cmd53_r8 ||
	    !pltfm_cb->sdio_cmd53_r16 || !pltfm_cb->sdio_cmd53_r32 ||
	    !pltfm_cb->sdio_cmd53_rn || !pltfm_cb->sdio_cmd52_w8 ||
	    !pltfm_cb->sdio_cmd53_w8 || !pltfm_cb->sdio_cmd53_w16 ||
	    !pltfm_cb->sdio_cmd53_w32 || !pltfm_cb->sdio_cmd53_wn ||
	    !pltfm_cb->sdio_cmd52_cia_r8) {
		pltfm_cb->msg_print(drv_adapter, _PHL_ERR_, "[ERR]CB-SDIO\n");
		return MACSUCCESS;
	}
#endif

#if (MAC_AX_USB_SUPPORT || MAC_AX_PCIE_SUPPORT)
	if (!pltfm_cb->reg_r8 || !pltfm_cb->reg_r16 ||
	    !pltfm_cb->reg_r32 || !pltfm_cb->reg_w8 ||
	    !pltfm_cb->reg_w16 || !pltfm_cb->reg_w32) {
		pltfm_cb->msg_print(drv_adapter, _PHL_ERR_, "[ERR]CB-USB or PCIE\n");
		return MACSUCCESS;
	}
#endif
	if (!pltfm_cb->rtl_free || !pltfm_cb->rtl_malloc ||
	    !pltfm_cb->rtl_memcpy || !pltfm_cb->rtl_memset ||
	    !pltfm_cb->rtl_delay_us || !pltfm_cb->rtl_delay_ms ||
	    !pltfm_cb->rtl_mutex_init || !pltfm_cb->rtl_mutex_deinit ||
	    !pltfm_cb->rtl_mutex_lock || !pltfm_cb->rtl_mutex_unlock ||
	    !pltfm_cb->event_notify) {
		pltfm_cb->msg_print(drv_adapter, _PHL_ERR_, "[ERR]CB-OS\n");
		return MACSUCCESS;
	}

	return MACPFCB;
}
#endif /*CONFIG_NEW_HALMAC_INTERFACE*/

static u8 chk_pltfm_endian(void)
{
	u32 num = 1;
	u8 *num_ptr = (u8 *)&num;

	if (*num_ptr != PLATFOM_IS_LITTLE_ENDIAN)
		return MACSUCCESS;

	return MACPFCB;
}

#ifdef CONFIG_NEW_HALMAC_INTERFACE
#if MAC_AX_SDIO_SUPPORT
static u8 r8_indir_cmd52_sdio(void *drv_adapter,
			      struct mac_ax_pltfm_cb *pltfm_cb, u32 adr)
{
	u8 tmp;
	u32 cnt;

	PLTFM_SDIO_CMD52_W8(R_AX_SDIO_INDIRECT_ADDR, (u8)adr);
	PLTFM_SDIO_CMD52_W8(R_AX_SDIO_INDIRECT_ADDR + 1, (u8)(adr >> 8));
	PLTFM_SDIO_CMD52_W8(R_AX_SDIO_INDIRECT_ADDR + 2, (u8)(adr >> 16));
	PLTFM_SDIO_CMD52_W8(R_AX_SDIO_INDIRECT_ADDR + 3,
			    (u8)((adr | B_AX_INDIRECT_RDY) >> 24));
	PLTFM_SDIO_CMD52_W8(R_AX_SDIO_INDIRECT_CTRL, (u8)B_AX_INDIRECT_REG_R);

	cnt = SDIO_WAIT_CNT;
	do {
		tmp = PLTFM_SDIO_CMD52_R8(R_AX_SDIO_INDIRECT_ADDR + 3);
		cnt--;
	} while (((tmp & BIT(7)) == 0) && (cnt > 0));

	if (((tmp & BIT(7)) == 0) && cnt == 0)
		PLTFM_MSG_ERR("[ERR]sdio indirect CMD52 read\n");

	return PLTFM_SDIO_CMD52_R8(R_AX_SDIO_INDIRECT_DATA);
}
#endif
static u32 get_chip_info(struct mac_ax_adapter *adapter,
			 struct mac_ax_pltfm_cb *pltfm_cb,
			 enum mac_ax_intf intf, u8 *id, u8 *cv)
{
	u32 cv_temp;
	u8 cur_id;

	if (!cv || !id)
		return MACNPTR;

	switch (intf) {
#if MAC_AX_SDIO_SUPPORT
	case MAC_AX_INTF_SDIO:
		cur_id = r8_indir_cmd52_sdio(adapter, R_AX_SYS_CHIPINFO);
		*cv = r8_indir_cmd52_sdio(adapter, R_AX_SYS_CFG1 + 1) >> 4;
		break;
#endif
#if (MAC_AX_USB_SUPPORT || MAC_AX_PCIE_SUPPORT)
	case MAC_AX_INTF_USB:
	case MAC_AX_INTF_PCIE:
		cur_id = PLTFM_REG_R8(R_AX_SYS_CHIPINFO);
		*cv = PLTFM_REG_R8(R_AX_SYS_CFG1 + 1) >> 4;

		if (cur_id == CHIP_ID_HW_DEF_8852A) {
			if (*cv <= CBV) {
				cv_temp = PLTFM_REG_R32(R_AX_GPIO0_7_FUNC_SEL);
				if (cv_temp == 0xdeadbeef)
					*cv = CAV;
				else
					*cv = CBV;
			}
		}
		break;
#endif
	default:
		return MACINTF;
	}

	switch (cur_id) {
	case CHIP_ID_HW_DEF_8852A:
		*id = MAC_AX_CHIP_ID_8852A;
		break;
	case CHIP_ID_HW_DEF_8852B:
		*id = MAC_AX_CHIP_ID_8852B;
		break;
	case CHIP_ID_HW_DEF_8852C:
		*id = MAC_AX_CHIP_ID_8852C;
		break;
	default:
		return MACCHIPID;
	}

	return MACSUCCESS;
}

u32 mac_ax_ops_init_v1(void *phl_adapter, void *drv_adapter,
		       enum rtw_chip_id chip_id,
		       enum rtw_hci_type hci,
		       struct mac_ax_adapter **mac_adapter,
		       struct mac_ax_ops **mac_ops)
{
	u32 ret;
	u8 cv;
	struct mac_ax_adapter *adapter;
	enum mac_ax_intf intf = MAC_AX_INTF_INVALID;

	if (!chk_pltfm_endian())
		return MACPFED;

	ret = 0;

	if (hci == RTW_HCI_PCIE)
		intf = MAC_AX_INTF_PCIE;
	else if (hci == RTW_HCI_USB)
		intf = MAC_AX_INTF_USB;
	else if (hci == RTW_HCI_SDIO)
		intf = MAC_AX_INTF_SDIO;

	ret = get_chip_info(drv_adapter, NULL, intf, &chip_id, &cv);
	if (ret)
		return ret;

	adapter = get_mac_ax_adapter(intf, chip_id, cv, phl_adapter,
				     drv_adapter, NULL);
	if (!adapter) {
		PLTFM_MSG_PRINT("[ERR]Get MAC adapter\n");
		return MACADAPTER;
	}
	PLTFM_MSG_ALWAYS("MAC_AX_MAJOR_VER = %d\n"
			    "MAC_AX_PROTOTYPE_VER = %d\n"
			    "MAC_AX_SUB_VER = %d\n"
			    "MAC_AX_SUB_INDEX = %d\n",
			    MAC_AX_MAJOR_VER, MAC_AX_PROTOTYPE_VER,
			    MAC_AX_SUB_VER, MAC_AX_SUB_INDEX);

	*mac_adapter = adapter;
	*mac_ops = adapter->ops;

	return MACSUCCESS;
}

#else

#if MAC_AX_SDIO_SUPPORT
static u8 r8_indir_cmd52_sdio(void *drv_adapter,
			      struct mac_ax_pltfm_cb *pltfm_cb, u32 adr)
{
	u8 tmp;
	u32 cnt;

	pltfm_cb->sdio_cmd52_w8(drv_adapter, R_AX_SDIO_INDIRECT_ADDR,
				(u8)adr);
	pltfm_cb->sdio_cmd52_w8(drv_adapter, R_AX_SDIO_INDIRECT_ADDR + 1,
				(u8)(adr >> 8));
	pltfm_cb->sdio_cmd52_w8(drv_adapter, R_AX_SDIO_INDIRECT_ADDR + 2,
				(u8)(adr >> 16));
	pltfm_cb->sdio_cmd52_w8(drv_adapter, R_AX_SDIO_INDIRECT_ADDR + 3,
				(u8)((adr | B_AX_INDIRECT_RDY) >> 24));
	pltfm_cb->sdio_cmd52_w8(drv_adapter, R_AX_SDIO_INDIRECT_CTRL,
				(u8)B_AX_INDIRECT_REG_R);

	cnt = SDIO_WAIT_CNT;
	do {
		tmp = pltfm_cb->sdio_cmd52_r8(drv_adapter,
					      R_AX_SDIO_INDIRECT_ADDR + 3);
		cnt--;
	} while (((tmp & BIT(7)) == 0) && (cnt > 0));

	if (((tmp & BIT(7)) == 0) && cnt == 0)
		pltfm_cb->msg_print(drv_adapter, _PHL_ERR_,
				    "[ERR]sdio indirect CMD52 read\n");

	return pltfm_cb->sdio_cmd52_r8(drv_adapter, R_AX_SDIO_INDIRECT_DATA);
}
#endif
static u32 get_chip_info(void *drv_adapter, struct mac_ax_pltfm_cb *pltfm_cb,
			 enum mac_ax_intf intf, u8 *id, u8 *cv)
{
	u8 cur_id;

	if (!cv || !id)
		return MACNPTR;

	switch (intf) {
#if MAC_AX_SDIO_SUPPORT
	case MAC_AX_INTF_SDIO:
		cur_id = r8_indir_cmd52_sdio(drv_adapter, pltfm_cb,
					     R_AX_SYS_CHIPINFO);
		*cv = r8_indir_cmd52_sdio(drv_adapter, pltfm_cb,
					  R_AX_SYS_CFG1 + 1) >> 4;
		if (cur_id == CHIP_ID_HW_DEF_8852A)
			if (*cv <= CBV)
				*cv = CBV;

		break;
#endif
#if (MAC_AX_USB_SUPPORT || MAC_AX_PCIE_SUPPORT)
	case MAC_AX_INTF_USB:
	case MAC_AX_INTF_PCIE:
		cur_id = pltfm_cb->reg_r8(drv_adapter, R_AX_SYS_CHIPINFO);
		*cv = pltfm_cb->reg_r8(drv_adapter, R_AX_SYS_CFG1 + 1) >> 4;
		if (cur_id == CHIP_ID_HW_DEF_8852A)
			if (*cv <= CBV)
				*cv = CBV;

		break;
#endif
	default:
		return MACINTF;
	}

	switch (cur_id) {
	case CHIP_ID_HW_DEF_8852A:
		*id = MAC_AX_CHIP_ID_8852A;
		break;
	case CHIP_ID_HW_DEF_8852B:
		*id = MAC_AX_CHIP_ID_8852B;
		break;
	case CHIP_ID_HW_DEF_8852C:
		*id = MAC_AX_CHIP_ID_8852C;
		break;
	case CHIP_ID_HW_DEF_8192XB:
		*id = MAC_AX_CHIP_ID_8192XB;
		break;
	default:
		return MACCHIPID;
	}

	return MACSUCCESS;
}

u32 mac_ax_ops_init(void *drv_adapter, struct mac_ax_pltfm_cb *pltfm_cb,
		    enum mac_ax_intf intf,
		    struct mac_ax_adapter **mac_adapter,
		    struct mac_ax_ops **mac_ops)
{
	u32 ret;
	u8 chip_id = 0;
	u8 cv = 0;
	struct mac_ax_adapter *adapter;

	if (!chk_pltfm_cb(drv_adapter, intf, pltfm_cb))
		return MACPFCB;

	if (!chk_pltfm_endian())
		return MACPFED;

	pltfm_cb->msg_print(drv_adapter, _PHL_ERR_,
			    "MAC_AX_MAJOR_VER = %d\n"
			    "MAC_AX_PROTOTYPE_VER = %d\n"
			    "MAC_AX_SUB_VER = %d\n"
			    "MAC_AX_SUB_INDEX = %d\n",
			    MAC_AX_MAJOR_VER, MAC_AX_PROTOTYPE_VER,
			    MAC_AX_SUB_VER, MAC_AX_SUB_INDEX);

	ret = get_chip_info(drv_adapter, pltfm_cb, intf, &chip_id, &cv);
	if (ret)
		return ret;

	adapter = get_mac_ax_adapter(intf, chip_id, cv, drv_adapter,
				     pltfm_cb);
	if (!adapter) {
		pltfm_cb->msg_print(drv_adapter, _PHL_ERR_, "[ERR]Get MAC adapter\n");
		return MACADAPTER;
	}

	*mac_adapter = adapter;
	*mac_ops = adapter->ops;

#if MAC_AX_FEATURE_HV
	adapter->hv_ops = get_hv_ax_ops(adapter);
#endif

#if MAC_AX_PHL_H2C

#else
	ret = h2cb_init(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]h2cb init %d\n", ret);
		return ret;
	}
#endif

	ret = role_tbl_init(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]role tbl init %d\n", ret);
		return ret;
	}

	ret = sec_info_tbl_init(adapter, SEC_CAM_NORMAL);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]sec info tbl init %d\n", ret);
		return ret;
	}

	ret = efuse_tbl_init(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]efuse tbl init %d\n", ret);
		return ret;
	}

	ret = p2p_info_init(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]p2p info init %d\n", ret);
		return ret;
	}

	ret = mport_info_init(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]mpinfo info init %d\n", ret);
		return ret;
	}

	ret = mix_info_init(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]mix info init %d\n", ret);
		return ret;
	}

#if MAC_AX_SDIO_SUPPORT
	ret = sdio_tbl_init(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]sdio tbl init %d\n", ret);
		return ret;
	}
#endif

	ret = hfc_info_init(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]hfc info init %d\n", ret);
		return ret;
	}

	ret = dbcc_info_init(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]dbcc info init %d\n", ret);
		return ret;
	}

	return MACSUCCESS;
}
#endif /*CONFIG_NEW_HALMAC_INTERFACE*/

#if MAC_AX_PHL_H2C
u32 mac_ax_phl_init(void *phl_adapter, struct mac_ax_adapter *mac_adapter)
{
	struct mac_ax_adapter *adapter = mac_adapter;

	adapter->phl_adapter = phl_adapter;

	return MACSUCCESS;
}
#endif
u32 mac_ax_ops_exit(struct mac_ax_adapter *adapter)
{
	u32 ret;
	struct mac_ax_efuse_param *efuse_param = &adapter->efuse_param;
	struct scan_chinfo_list *scan_list;

	ret = h2cb_exit(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]h2c buffer exit %d\n", ret);
		return ret;
	}

	ret = free_sec_info_tbl(adapter, SEC_CAM_NORMAL);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]sec table exit %d\n", ret);
		return ret;
	}

	ret = role_tbl_exit(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]role table exit %d\n", ret);
		return ret;
	}

	ret = efuse_tbl_exit(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]efuse table exit %d\n", ret);
		return ret;
	}

	ret = p2p_info_exit(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]p2p info exit %d\n", ret);
		return ret;
	}

	ret = mport_info_exit(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]mpinfo info exit %d\n", ret);
		return ret;
	}

	ret = mix_info_exit(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]mix info exit %d\n", ret);
		return ret;
	}

#if MAC_AX_SDIO_SUPPORT
	ret = sdio_tbl_exit(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]efuse table exit %d\n", ret);
		return ret;
	}
#endif

	ret = hfc_info_exit(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]hfc info exit %d\n", ret);
		return ret;
	}

	ret = free_aoac_report(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]free aoac report %d\n", ret);
		return ret;
	}

	ret = dbcc_info_exit(adapter);
	if (ret != MACSUCCESS) {
		PLTFM_MSG_ERR("[ERR]dbcc info exit %d\n", ret);
		return ret;
	}

	if (efuse_param->efuse_map) {
		PLTFM_FREE(efuse_param->efuse_map,
			   adapter->hw_info->efuse_size);
		efuse_param->efuse_map = (u8 *)NULL;
	}

	if (efuse_param->bt_efuse_map) {
		PLTFM_FREE(efuse_param->bt_efuse_map,
			   adapter->hw_info->bt_efuse_size);
		efuse_param->bt_efuse_map = (u8 *)NULL;
	}

	if (efuse_param->log_efuse_map) {
		PLTFM_FREE(efuse_param->log_efuse_map,
			   adapter->hw_info->log_efuse_size);
		efuse_param->log_efuse_map = (u8 *)NULL;
	}

	if (efuse_param->bt_log_efuse_map) {
		PLTFM_FREE(efuse_param->bt_log_efuse_map,
			   adapter->hw_info->bt_log_efuse_size);
		efuse_param->bt_log_efuse_map = (u8 *)NULL;
	}

	if (efuse_param->dav_efuse_map) {
		PLTFM_FREE(efuse_param->dav_efuse_map,
			   adapter->hw_info->dav_efuse_size);
		efuse_param->dav_efuse_map = (u8 *)NULL;
	}

	if (efuse_param->dav_log_efuse_map) {
		PLTFM_FREE(efuse_param->dav_log_efuse_map,
			   adapter->hw_info->dav_log_efuse_size);
		efuse_param->dav_log_efuse_map = (u8 *)NULL;
	}

	scan_list = adapter->scanofld_info.list;
	if (scan_list) {
		mac_scanofld_ch_list_clear(adapter, scan_list);
		PLTFM_FREE((u8 *)scan_list, sizeof(struct scan_chinfo_list));
		adapter->scanofld_info.list = NULL;
	}

	PLTFM_FREE(adapter->hw_info, sizeof(struct mac_ax_hw_info));

	PLTFM_FREE(adapter, sizeof(struct mac_ax_adapter));
	return MACSUCCESS;
}

u32 is_chip_id(struct mac_ax_adapter *adapter, enum mac_ax_chip_id id)
{
	return (id == adapter->hw_info->chip_id ? 1 : 0);
}

u32 is_cv(struct mac_ax_adapter *adapter, enum rtw_cv cv)
{
	return (cv == adapter->hw_info->cv ? 1 : 0);
}

