/******************************************************************************
 *
 * Copyright(c) 2016 - 2018 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 "halmac_efuse_88xx.h"
#include "halmac_88xx_cfg.h"
#include "halmac_common_88xx.h"
#include "halmac_init_88xx.h"

#if HALMAC_88XX_SUPPORT

#define RSVD_EFUSE_SIZE		16
#define RSVD_CS_EFUSE_SIZE	24
#define PROTECT_EFUSE_SIZE	96
#define FEATURE_DUMP_PHY_EFUSE	HALMAC_FEATURE_DUMP_PHYSICAL_EFUSE
#define FEATURE_DUMP_LOG_EFUSE	HALMAC_FEATURE_DUMP_LOGICAL_EFUSE

static enum halmac_cmd_construct_state
efuse_cmd_cnstr_state_88xx(struct halmac_adapter *adapter);

static enum halmac_ret_status
proc_dump_efuse_88xx(struct halmac_adapter *adapter,
		     enum halmac_efuse_read_cfg cfg);

static enum halmac_ret_status
read_hw_efuse_88xx(struct halmac_adapter *adapter, u32 offset, u32 size,
		   u8 *map);

static enum halmac_ret_status
eeprom_parser_88xx(struct halmac_adapter *adapter, u8 *phy_map, u8 *log_map);

static enum halmac_ret_status
read_log_efuse_map_88xx(struct halmac_adapter *adapter, u8 *map);

static enum halmac_ret_status
proc_pg_efuse_by_map_88xx(struct halmac_adapter *adapter,
			  struct halmac_pg_efuse_info *info,
			  enum halmac_efuse_read_cfg cfg);

static enum halmac_ret_status
dump_efuse_fw_88xx(struct halmac_adapter *adapter);

static enum halmac_ret_status
dump_efuse_drv_88xx(struct halmac_adapter *adapter);

static enum halmac_ret_status
proc_write_log_efuse_88xx(struct halmac_adapter *adapter, u32 offset, u8 value);

static enum halmac_ret_status
update_eeprom_mask_88xx(struct halmac_adapter *adapter,
			struct halmac_pg_efuse_info *info, u8 *updated_mask);

static enum halmac_ret_status
check_efuse_enough_88xx(struct halmac_adapter *adapter,
			struct halmac_pg_efuse_info *info, u8 *updated_mask);

static enum halmac_ret_status
pg_extend_efuse_88xx(struct halmac_adapter *adapter,
		     struct halmac_pg_efuse_info *info, u8 word_en,
		     u8 pre_word_en, u32 eeprom_offset);

static enum halmac_ret_status
proc_pg_efuse_88xx(struct halmac_adapter *adapter,
		   struct halmac_pg_efuse_info *info, u8 word_en,
		   u8 pre_word_en, u32 eeprom_offset);

static enum halmac_ret_status
program_efuse_88xx(struct halmac_adapter *adapter,
		   struct halmac_pg_efuse_info *info, u8 *updated_mask);

static void
mask_eeprom_88xx(struct halmac_adapter *adapter,
		 struct halmac_pg_efuse_info *info);

/**
 * dump_efuse_map_88xx() - dump "physical" efuse map
 * @adapter : the adapter of halmac
 * @cfg : dump efuse method
 * Author : Ivan Lin/KaiYuan Chang
 * Return : enum halmac_ret_status
 * More details of status code can be found in prototype document
 */
enum halmac_ret_status
dump_efuse_map_88xx(struct halmac_adapter *adapter,
		    enum halmac_efuse_read_cfg cfg)
{
	u8 *map = NULL;
	u8 *efuse_map;
	u32 efuse_size = adapter->hw_cfg_info.efuse_size;
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;
	enum halmac_cmd_process_status *proc_status;

	proc_status = &adapter->halmac_state.efuse_state.proc_status;

	if (cfg == HALMAC_EFUSE_R_FW &&
	    halmac_fw_validate(adapter) != HALMAC_RET_SUCCESS)
		return HALMAC_RET_NO_DLFW;

	PLTFM_MSG_TRACE("[TRACE]%s ===>\n", __func__);
	PLTFM_MSG_TRACE("[TRACE]cfg = %d\n", cfg);

	if (*proc_status == HALMAC_CMD_PROCESS_SENDING) {
		PLTFM_MSG_WARN("[WARN]Wait event(efuse)\n");
		return HALMAC_RET_BUSY_STATE;
	}

	if (efuse_cmd_cnstr_state_88xx(adapter) != HALMAC_CMD_CNSTR_IDLE) {
		PLTFM_MSG_WARN("[WARN]Not idle(efuse)\n");
		return HALMAC_RET_ERROR_STATE;
	}

	if (adapter->halmac_state.mac_pwr == HALMAC_MAC_POWER_OFF)
		PLTFM_MSG_ERR("[ERR]Dump efuse in suspend\n");

	*proc_status = HALMAC_CMD_PROCESS_IDLE;
	adapter->evnt.phy_efuse_map = 1;

	status = switch_efuse_bank_88xx(adapter, HALMAC_EFUSE_BANK_WIFI);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]switch efuse bank!!\n");
		return status;
	}

	status = proc_dump_efuse_88xx(adapter, cfg);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]dump efuse!!\n");
		return status;
	}

	if (adapter->efuse_map_valid == 1) {
		*proc_status = HALMAC_CMD_PROCESS_DONE;
		efuse_map = adapter->efuse_map;

		map = (u8 *)PLTFM_MALLOC(efuse_size);
		if (!map) {
			PLTFM_MSG_ERR("[ERR]malloc!!\n");
			return HALMAC_RET_MALLOC_FAIL;
		}
		PLTFM_MEMSET(map, 0xFF, efuse_size);
		PLTFM_MUTEX_LOCK(&adapter->efuse_mutex);
		PLTFM_MEMCPY(map, efuse_map, efuse_size - PROTECT_EFUSE_SIZE);
		PLTFM_MEMCPY(map + efuse_size - PROTECT_EFUSE_SIZE +
			     RSVD_CS_EFUSE_SIZE,
			     efuse_map + efuse_size - PROTECT_EFUSE_SIZE +
			     RSVD_CS_EFUSE_SIZE,
			     PROTECT_EFUSE_SIZE - RSVD_EFUSE_SIZE -
			     RSVD_CS_EFUSE_SIZE);
		PLTFM_MUTEX_UNLOCK(&adapter->efuse_mutex);

		PLTFM_EVENT_SIG(HALMAC_FEATURE_DUMP_PHYSICAL_EFUSE,
				*proc_status, map, efuse_size);
		adapter->evnt.phy_efuse_map = 0;

		PLTFM_FREE(map, efuse_size);
	}

	if (cnv_efuse_state_88xx(adapter, HALMAC_CMD_CNSTR_IDLE) !=
	    HALMAC_RET_SUCCESS)
		return HALMAC_RET_ERROR_STATE;

	PLTFM_MSG_TRACE("[TRACE]%s <===\n", __func__);

	return HALMAC_RET_SUCCESS;
}

/**
 * dump_efuse_map_bt_88xx() - dump "BT physical" efuse map
 * @adapter : the adapter of halmac
 * @bank : bt efuse bank
 * @size : bt efuse map size. get from halmac_get_efuse_size API
 * @map : bt efuse map
 * Author : Soar / Ivan Lin
 * Return : enum halmac_ret_status
 * More details of status code can be found in prototype document
 */
enum halmac_ret_status
dump_efuse_map_bt_88xx(struct halmac_adapter *adapter,
		       enum halmac_efuse_bank bank, u32 size, u8 *map)
{
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;
	enum halmac_cmd_process_status *proc_status;

	proc_status = &adapter->halmac_state.efuse_state.proc_status;

	PLTFM_MSG_TRACE("[TRACE]%s ===>\n", __func__);

	if (adapter->hw_cfg_info.bt_efuse_size != size)
		return HALMAC_RET_EFUSE_SIZE_INCORRECT;

	if (bank >= HALMAC_EFUSE_BANK_MAX || bank == HALMAC_EFUSE_BANK_WIFI) {
		PLTFM_MSG_ERR("[ERR]Undefined BT bank\n");
		return HALMAC_RET_EFUSE_BANK_INCORRECT;
	}

	if (*proc_status == HALMAC_CMD_PROCESS_SENDING) {
		PLTFM_MSG_WARN("[WARN]Wait event(efuse)\n");
		return HALMAC_RET_BUSY_STATE;
	}

	if (efuse_cmd_cnstr_state_88xx(adapter) != HALMAC_CMD_CNSTR_IDLE) {
		PLTFM_MSG_WARN("[WARN]Not idle(efuse)\n");
		return HALMAC_RET_ERROR_STATE;
	}

	status = switch_efuse_bank_88xx(adapter, bank);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]switch efuse bank!!\n");
		return status;
	}

	status = read_hw_efuse_88xx(adapter, 0, size, map);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]read hw efuse\n");
		return status;
	}

	if (cnv_efuse_state_88xx(adapter, HALMAC_CMD_CNSTR_IDLE) !=
	    HALMAC_RET_SUCCESS)
		return HALMAC_RET_ERROR_STATE;

	PLTFM_MSG_TRACE("[TRACE]%s <===\n", __func__);

	return HALMAC_RET_SUCCESS;
}

/**
 * write_efuse_bt_88xx() - write "BT physical" efuse offset
 * @adapter : the adapter of halmac
 * @offset : offset
 * @value : Write value
 * @map : bt efuse map
 * Author : Soar
 * Return : enum halmac_ret_status
 * More details of status code can be found in prototype document
 */
enum halmac_ret_status
write_efuse_bt_88xx(struct halmac_adapter *adapter, u32 offset, u8 value,
		    enum halmac_efuse_bank bank)
{
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;
	enum halmac_cmd_process_status *proc_status;

	proc_status = &adapter->halmac_state.efuse_state.proc_status;

	PLTFM_MSG_TRACE("[TRACE]%s ===>\n", __func__);

	if (*proc_status == HALMAC_CMD_PROCESS_SENDING) {
		PLTFM_MSG_WARN("[WARN]Wait event(efuse)\n");
		return HALMAC_RET_BUSY_STATE;
	}

	if (efuse_cmd_cnstr_state_88xx(adapter) != HALMAC_CMD_CNSTR_IDLE) {
		PLTFM_MSG_WARN("[WARN]Not idle(efuse)\n");
		return HALMAC_RET_ERROR_STATE;
	}

	if (offset >= adapter->hw_cfg_info.efuse_size) {
		PLTFM_MSG_ERR("[ERR]Offset is too large\n");
		return HALMAC_RET_EFUSE_SIZE_INCORRECT;
	}

	if (bank > HALMAC_EFUSE_BANK_MAX || bank == HALMAC_EFUSE_BANK_WIFI) {
		PLTFM_MSG_ERR("[ERR]Undefined BT bank\n");
		return HALMAC_RET_EFUSE_BANK_INCORRECT;
	}

	status = switch_efuse_bank_88xx(adapter, bank);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]switch efuse bank!!\n");
		return status;
	}

	status = write_hw_efuse_88xx(adapter, offset, value);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]write efuse\n");
		return status;
	}

	if (cnv_efuse_state_88xx(adapter, HALMAC_CMD_CNSTR_IDLE) !=
	    HALMAC_RET_SUCCESS)
		return HALMAC_RET_ERROR_STATE;

	PLTFM_MSG_TRACE("[TRACE]%s <===\n", __func__);

	return HALMAC_RET_SUCCESS;
}

/**
 * read_efuse_bt_88xx() - read "BT physical" efuse offset
 * @adapter : the adapter of halmac
 * @offset : offset
 * @value : 1 byte efuse value
 * @bank : efuse bank
 * Author : Soar
 * Return : enum halmac_ret_status
 * More details of status code can be found in prototype document
 */
enum halmac_ret_status
read_efuse_bt_88xx(struct halmac_adapter *adapter, u32 offset, u8 *value,
		   enum halmac_efuse_bank bank)
{
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;
	enum halmac_cmd_process_status *proc_status;

	proc_status = &adapter->halmac_state.efuse_state.proc_status;

	PLTFM_MSG_TRACE("[TRACE]%s ===>\n", __func__);

	if (*proc_status == HALMAC_CMD_PROCESS_SENDING) {
		PLTFM_MSG_WARN("[WARN]Wait event(efuse)\n");
		return HALMAC_RET_BUSY_STATE;
	}

	if (efuse_cmd_cnstr_state_88xx(adapter) != HALMAC_CMD_CNSTR_IDLE) {
		PLTFM_MSG_WARN("[WARN]Not idle(efuse)\n");
		return HALMAC_RET_ERROR_STATE;
	}

	if (offset >= adapter->hw_cfg_info.efuse_size) {
		PLTFM_MSG_ERR("[ERR]Offset is too large\n");
		return HALMAC_RET_EFUSE_SIZE_INCORRECT;
	}

	if (bank > HALMAC_EFUSE_BANK_MAX || bank == HALMAC_EFUSE_BANK_WIFI) {
		PLTFM_MSG_ERR("[ERR]Undefined BT bank\n");
		return HALMAC_RET_EFUSE_BANK_INCORRECT;
	}

	status = switch_efuse_bank_88xx(adapter, bank);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]switch efuse bank\n");
		return status;
	}

	status = read_efuse_88xx(adapter, offset, 1, value);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]read efuse\n");
		return status;
	}

	if (cnv_efuse_state_88xx(adapter, HALMAC_CMD_CNSTR_IDLE) !=
	    HALMAC_RET_SUCCESS)
		return HALMAC_RET_ERROR_STATE;

	PLTFM_MSG_TRACE("[TRACE]%s <===\n", __func__);

	return HALMAC_RET_SUCCESS;
}

/**
 * cfg_efuse_auto_check_88xx() - check efuse after writing it
 * @adapter : the adapter of halmac
 * @enable : 1, enable efuse auto check. others, disable
 * Author : Soar
 * Return : enum halmac_ret_status
 * More details of status code can be found in prototype document
 */
enum halmac_ret_status
cfg_efuse_auto_check_88xx(struct halmac_adapter *adapter, u8 enable)
{
	PLTFM_MSG_TRACE("[TRACE]%s ===>\n", __func__);

	adapter->efuse_auto_check_en = enable;

	PLTFM_MSG_TRACE("[TRACE]%s <===\n", __func__);

	return HALMAC_RET_SUCCESS;
}

/**
 * get_efuse_available_size_88xx() - get efuse available size
 * @adapter : the adapter of halmac
 * @size : physical efuse available size
 * Author : Soar
 * Return : enum halmac_ret_status
 * More details of status code can be found in prototype document
 */
enum halmac_ret_status
get_efuse_available_size_88xx(struct halmac_adapter *adapter, u32 *size)
{
	enum halmac_ret_status status;

	PLTFM_MSG_TRACE("[TRACE]%s ===>\n", __func__);

	status = dump_log_efuse_map_88xx(adapter, HALMAC_EFUSE_R_DRV);

	if (status != HALMAC_RET_SUCCESS)
		return status;

	*size = adapter->hw_cfg_info.efuse_size - PROTECT_EFUSE_SIZE -
		adapter->efuse_end;

	PLTFM_MSG_TRACE("[TRACE]%s <===\n", __func__);

	return HALMAC_RET_SUCCESS;
}

/**
 * get_efuse_size_88xx() - get "physical" efuse size
 * @adapter : the adapter of halmac
 * @size : physical efuse size
 * Author : Ivan Lin/KaiYuan Chang
 * Return : enum halmac_ret_status
 * More details of status code can be found in prototype document
 */
enum halmac_ret_status
get_efuse_size_88xx(struct halmac_adapter *adapter, u32 *size)
{
	PLTFM_MSG_TRACE("[TRACE]%s ===>\n", __func__);

	*size = adapter->hw_cfg_info.efuse_size;

	PLTFM_MSG_TRACE("[TRACE]%s <===\n", __func__);

	return HALMAC_RET_SUCCESS;
}

/**
 * get_log_efuse_size_88xx() - get "logical" efuse size
 * @adapter : the adapter of halmac
 * @size : logical efuse size
 * Author : Ivan Lin/KaiYuan Chang
 * Return : enum halmac_ret_status
 * More details of status code can be found in prototype document
 */
enum halmac_ret_status
get_log_efuse_size_88xx(struct halmac_adapter *adapter, u32 *size)
{
	PLTFM_MSG_TRACE("[TRACE]%s ===>\n", __func__);

	*size = adapter->hw_cfg_info.eeprom_size;

	PLTFM_MSG_TRACE("[TRACE]%s <===\n", __func__);

	return HALMAC_RET_SUCCESS;
}

/**
 * dump_log_efuse_map_88xx() - dump "logical" efuse map
 * @adapter : the adapter of halmac
 * @cfg : dump efuse method
 * Author : Soar
 * Return : enum halmac_ret_status
 * More details of status code can be found in prototype document
 */
enum halmac_ret_status
dump_log_efuse_map_88xx(struct halmac_adapter *adapter,
			enum halmac_efuse_read_cfg cfg)
{
	u8 *map = NULL;
	u32 size = adapter->hw_cfg_info.eeprom_size;
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;
	enum halmac_cmd_process_status *proc_status;

	proc_status = &adapter->halmac_state.efuse_state.proc_status;

	if (cfg == HALMAC_EFUSE_R_FW &&
	    halmac_fw_validate(adapter) != HALMAC_RET_SUCCESS)
		return HALMAC_RET_NO_DLFW;

	PLTFM_MSG_TRACE("[TRACE]%s ===>\n", __func__);
	PLTFM_MSG_TRACE("[TRACE]cfg = %d\n", cfg);

	if (*proc_status == HALMAC_CMD_PROCESS_SENDING) {
		PLTFM_MSG_WARN("[WARN]Wait event(efuse)\n");
		return HALMAC_RET_BUSY_STATE;
	}

	if (efuse_cmd_cnstr_state_88xx(adapter) != HALMAC_CMD_CNSTR_IDLE) {
		PLTFM_MSG_WARN("[WARN]Not idle(efuse)\n");
		return HALMAC_RET_ERROR_STATE;
	}

	if (adapter->halmac_state.mac_pwr == HALMAC_MAC_POWER_OFF)
		PLTFM_MSG_ERR("[ERR]Dump efuse in suspend\n");

	*proc_status = HALMAC_CMD_PROCESS_IDLE;
	adapter->evnt.log_efuse_map = 1;

	status = switch_efuse_bank_88xx(adapter, HALMAC_EFUSE_BANK_WIFI);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]switch efuse bank\n");
		return status;
	}

	status = proc_dump_efuse_88xx(adapter, cfg);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]dump efuse\n");
		return status;
	}

	if (adapter->efuse_map_valid == 1) {
		*proc_status = HALMAC_CMD_PROCESS_DONE;

		map = (u8 *)PLTFM_MALLOC(size);
		if (!map) {
			PLTFM_MSG_ERR("[ERR]malloc map\n");
			return HALMAC_RET_MALLOC_FAIL;
		}
		PLTFM_MEMSET(map, 0xFF, size);

		if (eeprom_parser_88xx(adapter, adapter->efuse_map, map) !=
		    HALMAC_RET_SUCCESS) {
			PLTFM_FREE(map, size);
			return HALMAC_RET_EEPROM_PARSING_FAIL;
		}

		PLTFM_EVENT_SIG(HALMAC_FEATURE_DUMP_LOGICAL_EFUSE,
				*proc_status, map, size);
		adapter->evnt.log_efuse_map = 0;

		PLTFM_FREE(map, size);
	}

	if (cnv_efuse_state_88xx(adapter, HALMAC_CMD_CNSTR_IDLE) !=
	    HALMAC_RET_SUCCESS)
		return HALMAC_RET_ERROR_STATE;

	PLTFM_MSG_TRACE("[TRACE]%s <===\n", __func__);

	return HALMAC_RET_SUCCESS;
}

/**
 * read_logical_efuse_88xx() - read logical efuse map 1 byte
 * @adapter : the adapter of halmac
 * @offset : offset
 * @value : 1 byte efuse value
 * Author : Soar
 * Return : enum halmac_ret_status
 * More details of status code can be found in prototype document
 */
enum halmac_ret_status
read_logical_efuse_88xx(struct halmac_adapter *adapter, u32 offset, u8 *value)
{
	u8 *map = NULL;
	u32 size = adapter->hw_cfg_info.eeprom_size;
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;
	enum halmac_cmd_process_status *proc_status;

	proc_status = &adapter->halmac_state.efuse_state.proc_status;

	PLTFM_MSG_TRACE("[TRACE]%s ===>\n", __func__);

	if (offset >= size) {
		PLTFM_MSG_ERR("[ERR]Offset is too large\n");
		return HALMAC_RET_EFUSE_SIZE_INCORRECT;
	}

	if (*proc_status == HALMAC_CMD_PROCESS_SENDING) {
		PLTFM_MSG_WARN("[WARN]Wait event(efuse)\n");
		return HALMAC_RET_BUSY_STATE;
	}
	if (efuse_cmd_cnstr_state_88xx(adapter) != HALMAC_CMD_CNSTR_IDLE) {
		PLTFM_MSG_WARN("[WARN]Not idle(efuse)\n");
		return HALMAC_RET_ERROR_STATE;
	}

	status = switch_efuse_bank_88xx(adapter, HALMAC_EFUSE_BANK_WIFI);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]switch efuse bank\n");
		return status;
	}

	map = (u8 *)PLTFM_MALLOC(size);
	if (!map) {
		PLTFM_MSG_ERR("[ERR]malloc map\n");
		return HALMAC_RET_MALLOC_FAIL;
	}
	PLTFM_MEMSET(map, 0xFF, size);

	status = read_log_efuse_map_88xx(adapter, map);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]read logical efuse\n");
		PLTFM_FREE(map, size);
		return status;
	}

	*value = *(map + offset);

	if (cnv_efuse_state_88xx(adapter, HALMAC_CMD_CNSTR_IDLE) !=
	    HALMAC_RET_SUCCESS) {
		PLTFM_FREE(map, size);
		return HALMAC_RET_ERROR_STATE;
	}

	PLTFM_MSG_TRACE("[TRACE]%s <===\n", __func__);

	PLTFM_FREE(map, size);

	return HALMAC_RET_SUCCESS;
}

/**
 * write_log_efuse_88xx() - write "logical" efuse offset
 * @adapter : the adapter of halmac
 * @offset : offset
 * @value : value
 * Author : Soar
 * Return : enum halmac_ret_status
 * More details of status code can be found in prototype document
 */
enum halmac_ret_status
write_log_efuse_88xx(struct halmac_adapter *adapter, u32 offset, u8 value)
{
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;
	enum halmac_cmd_process_status *proc_status;

	proc_status = &adapter->halmac_state.efuse_state.proc_status;

	PLTFM_MSG_TRACE("[TRACE]%s ===>\n", __func__);

	if (offset >= adapter->hw_cfg_info.eeprom_size) {
		PLTFM_MSG_ERR("[ERR]Offset is too large\n");
		return HALMAC_RET_EFUSE_SIZE_INCORRECT;
	}

	if (*proc_status == HALMAC_CMD_PROCESS_SENDING) {
		PLTFM_MSG_WARN("[WARN]Wait event(efuse)\n");
		return HALMAC_RET_BUSY_STATE;
	}

	if (efuse_cmd_cnstr_state_88xx(adapter) != HALMAC_CMD_CNSTR_IDLE) {
		PLTFM_MSG_WARN("[WARN]Not idle(efuse)\n");
		return HALMAC_RET_ERROR_STATE;
	}

	status = switch_efuse_bank_88xx(adapter, HALMAC_EFUSE_BANK_WIFI);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]switch efuse bank\n");
		return status;
	}

	status = proc_write_log_efuse_88xx(adapter, offset, value);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]write logical efuse\n");
		return status;
	}

	if (cnv_efuse_state_88xx(adapter, HALMAC_CMD_CNSTR_IDLE) !=
	    HALMAC_RET_SUCCESS)
		return HALMAC_RET_ERROR_STATE;

	PLTFM_MSG_TRACE("[TRACE]%s <===\n", __func__);

	return HALMAC_RET_SUCCESS;
}

/**
 * pg_efuse_by_map_88xx() - pg logical efuse by map
 * @adapter : the adapter of halmac
 * @info : efuse map information
 * @cfg : dump efuse method
 * Author : Soar
 * Return : enum halmac_ret_status
 * More details of status code can be found in prototype document
 */
enum halmac_ret_status
pg_efuse_by_map_88xx(struct halmac_adapter *adapter,
		     struct halmac_pg_efuse_info *info,
		     enum halmac_efuse_read_cfg cfg)
{
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;
	enum halmac_cmd_process_status *proc_status;

	proc_status = &adapter->halmac_state.efuse_state.proc_status;

	PLTFM_MSG_TRACE("[TRACE]%s ===>\n", __func__);

	if (info->efuse_map_size != adapter->hw_cfg_info.eeprom_size) {
		PLTFM_MSG_ERR("[ERR]map size error\n");
		return HALMAC_RET_EFUSE_SIZE_INCORRECT;
	}

	if ((info->efuse_map_size & 0xF) > 0) {
		PLTFM_MSG_ERR("[ERR]not multiple of 16\n");
		return HALMAC_RET_EFUSE_SIZE_INCORRECT;
	}

	if (info->efuse_mask_size != info->efuse_map_size >> 4) {
		PLTFM_MSG_ERR("[ERR]mask size error\n");
		return HALMAC_RET_EFUSE_SIZE_INCORRECT;
	}

	if (!info->efuse_map) {
		PLTFM_MSG_ERR("[ERR]map is NULL\n");
		return HALMAC_RET_NULL_POINTER;
	}

	if (!info->efuse_mask) {
		PLTFM_MSG_ERR("[ERR]mask is NULL\n");
		return HALMAC_RET_NULL_POINTER;
	}

	if (*proc_status == HALMAC_CMD_PROCESS_SENDING) {
		PLTFM_MSG_WARN("[WARN]Wait event(efuse)\n");
		return HALMAC_RET_BUSY_STATE;
	}

	if (efuse_cmd_cnstr_state_88xx(adapter) != HALMAC_CMD_CNSTR_IDLE) {
		PLTFM_MSG_WARN("[WARN]Not idle(efuse)\n");
		return HALMAC_RET_ERROR_STATE;
	}

	status = switch_efuse_bank_88xx(adapter, HALMAC_EFUSE_BANK_WIFI);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]switch efuse bank\n");
		return status;
	}

	status = proc_pg_efuse_by_map_88xx(adapter, info, cfg);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]pg efuse\n");
		return status;
	}

	if (cnv_efuse_state_88xx(adapter, HALMAC_CMD_CNSTR_IDLE) !=
	    HALMAC_RET_SUCCESS)
		return HALMAC_RET_ERROR_STATE;

	PLTFM_MSG_TRACE("[TRACE]%s <===\n", __func__);

	return HALMAC_RET_SUCCESS;
}

/**
 * mask_log_efuse_88xx() - mask logical efuse
 * @adapter : the adapter of halmac
 * @info : efuse map information
 * Author : Soar
 * Return : enum halmac_ret_status
 * More details of status code can be found in prototype document
 */
enum halmac_ret_status
mask_log_efuse_88xx(struct halmac_adapter *adapter,
		    struct halmac_pg_efuse_info *info)
{
	PLTFM_MSG_TRACE("[TRACE]%s ===>\n", __func__);

	if (info->efuse_map_size != adapter->hw_cfg_info.eeprom_size) {
		PLTFM_MSG_ERR("[ERR]map size error\n");
		return HALMAC_RET_EFUSE_SIZE_INCORRECT;
	}

	if ((info->efuse_map_size & 0xF) > 0) {
		PLTFM_MSG_ERR("[ERR]not multiple of 16\n");
		return HALMAC_RET_EFUSE_SIZE_INCORRECT;
	}

	if (info->efuse_mask_size != info->efuse_map_size >> 4) {
		PLTFM_MSG_ERR("[ERR]mask size error\n");
		return HALMAC_RET_EFUSE_SIZE_INCORRECT;
	}

	if (!info->efuse_map) {
		PLTFM_MSG_ERR("[ERR]map is NULL\n");
		return HALMAC_RET_NULL_POINTER;
	}

	if (!info->efuse_mask) {
		PLTFM_MSG_ERR("[ERR]mask is NULL\n");
		return HALMAC_RET_NULL_POINTER;
	}

	mask_eeprom_88xx(adapter, info);

	PLTFM_MSG_TRACE("[TRACE]%s <===\n", __func__);

	return HALMAC_RET_SUCCESS;
}

static enum halmac_cmd_construct_state
efuse_cmd_cnstr_state_88xx(struct halmac_adapter *adapter)
{
	return adapter->halmac_state.efuse_state.cmd_cnstr_state;
}

enum halmac_ret_status
switch_efuse_bank_88xx(struct halmac_adapter *adapter,
		       enum halmac_efuse_bank bank)
{
	u8 reg_value;
	struct halmac_api *api = (struct halmac_api *)adapter->halmac_api;

	if (cnv_efuse_state_88xx(adapter, HALMAC_CMD_CNSTR_BUSY) !=
	    HALMAC_RET_SUCCESS)
		return HALMAC_RET_ERROR_STATE;

	reg_value = HALMAC_REG_R8(REG_LDO_EFUSE_CTRL + 1);

	if (bank == (reg_value & (BIT(0) | BIT(1))))
		return HALMAC_RET_SUCCESS;

	reg_value &= ~(BIT(0) | BIT(1));
	reg_value |= bank;
	HALMAC_REG_W8(REG_LDO_EFUSE_CTRL + 1, reg_value);

	reg_value = HALMAC_REG_R8(REG_LDO_EFUSE_CTRL + 1);
	if ((reg_value & (BIT(0) | BIT(1))) != bank)
		return HALMAC_RET_SWITCH_EFUSE_BANK_FAIL;

	return HALMAC_RET_SUCCESS;
}

static enum halmac_ret_status
proc_dump_efuse_88xx(struct halmac_adapter *adapter,
		     enum halmac_efuse_read_cfg cfg)
{
	u32 h2c_init;
	struct halmac_api *api = (struct halmac_api *)adapter->halmac_api;
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;
	enum halmac_cmd_process_status *proc_status;

	proc_status = &adapter->halmac_state.efuse_state.proc_status;

	*proc_status = HALMAC_CMD_PROCESS_SENDING;

	if (cnv_efuse_state_88xx(adapter, HALMAC_CMD_CNSTR_H2C_SENT) !=
	    HALMAC_RET_SUCCESS)
		return HALMAC_RET_ERROR_STATE;

	if (cfg == HALMAC_EFUSE_R_AUTO) {
		h2c_init = HALMAC_REG_R32(REG_H2C_PKT_READADDR);
		if (adapter->halmac_state.dlfw_state == HALMAC_DLFW_NONE ||
		    h2c_init == 0)
			status = dump_efuse_drv_88xx(adapter);
		else
			status = dump_efuse_fw_88xx(adapter);
	} else if (cfg == HALMAC_EFUSE_R_FW) {
		status = dump_efuse_fw_88xx(adapter);
	} else {
		status = dump_efuse_drv_88xx(adapter);
	}

	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]dump efsue drv/fw\n");
		return status;
	}

	return status;
}

enum halmac_ret_status
cnv_efuse_state_88xx(struct halmac_adapter *adapter,
		     enum halmac_cmd_construct_state dest_state)
{
	struct halmac_efuse_state *state = &adapter->halmac_state.efuse_state;

	if (state->cmd_cnstr_state != HALMAC_CMD_CNSTR_IDLE &&
	    state->cmd_cnstr_state != HALMAC_CMD_CNSTR_BUSY &&
	    state->cmd_cnstr_state != HALMAC_CMD_CNSTR_H2C_SENT)
		return HALMAC_RET_ERROR_STATE;

	if (state->cmd_cnstr_state == dest_state)
		return HALMAC_RET_ERROR_STATE;

	if (dest_state == HALMAC_CMD_CNSTR_BUSY) {
		if (state->cmd_cnstr_state == HALMAC_CMD_CNSTR_H2C_SENT)
			return HALMAC_RET_ERROR_STATE;
	} else if (dest_state == HALMAC_CMD_CNSTR_H2C_SENT) {
		if (state->cmd_cnstr_state == HALMAC_CMD_CNSTR_IDLE)
			return HALMAC_RET_ERROR_STATE;
	}

	state->cmd_cnstr_state = dest_state;

	return HALMAC_RET_SUCCESS;
}

static enum halmac_ret_status
read_hw_efuse_88xx(struct halmac_adapter *adapter, u32 offset, u32 size,
		   u8 *map)
{
	u8 enable;
	u32 value32;
	u32 addr;
	u32 tmp32;
	u32 cnt;
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;
	struct halmac_api *api = (struct halmac_api *)adapter->halmac_api;

	/* Read efuse no need 2.5V LDO */
	enable = 0;
	status = api->halmac_set_hw_value(adapter, HALMAC_HW_LDO25_EN, &enable);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]dis ldo25\n");
		return status;
	}
	value32 = HALMAC_REG_R32(REG_EFUSE_CTRL);

	for (addr = offset; addr < offset + size; addr++) {
		value32 &= ~(BIT_MASK_EF_DATA | BITS_EF_ADDR);
		value32 |= ((addr & BIT_MASK_EF_ADDR) << BIT_SHIFT_EF_ADDR);
		HALMAC_REG_W32(REG_EFUSE_CTRL, value32 & (~BIT_EF_FLAG));

		cnt = 1000000;
		do {
			PLTFM_DELAY_US(1);
			tmp32 = HALMAC_REG_R32(REG_EFUSE_CTRL);
			cnt--;
			if (cnt == 0) {
				PLTFM_MSG_ERR("[ERR]read\n");
				return HALMAC_RET_EFUSE_R_FAIL;
			}
		} while ((tmp32 & BIT_EF_FLAG) == 0);

		*(map + addr - offset) = (u8)(tmp32 & BIT_MASK_EF_DATA);
	}

	return HALMAC_RET_SUCCESS;
}

enum halmac_ret_status
write_hw_efuse_88xx(struct halmac_adapter *adapter, u32 offset, u8 value)
{
	const u8 unlock_code = 0x69;
	u8 value_read = 0;
	u8 enable;
	u32 value32;
	u32 tmp32;
	u32 cnt;
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;
	struct halmac_api *api = (struct halmac_api *)adapter->halmac_api;

	PLTFM_MUTEX_LOCK(&adapter->efuse_mutex);
	adapter->efuse_map_valid = 0;
	PLTFM_MUTEX_UNLOCK(&adapter->efuse_mutex);

	HALMAC_REG_W8(REG_PMC_DBG_CTRL2 + 3, unlock_code);

	/* Enable 2.5V LDO */
	enable = 1;
	status = api->halmac_set_hw_value(adapter, HALMAC_HW_LDO25_EN, &enable);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]en ldo25\n");
		return status;
	}

	value32 = HALMAC_REG_R32(REG_EFUSE_CTRL);
	value32 &= ~(BIT_MASK_EF_DATA | BITS_EF_ADDR);
	value32 = value32 | ((offset & BIT_MASK_EF_ADDR) << BIT_SHIFT_EF_ADDR) |
			(value & BIT_MASK_EF_DATA);
	HALMAC_REG_W32(REG_EFUSE_CTRL, value32 | BIT_EF_FLAG);

	cnt = 1000000;
	do {
		PLTFM_DELAY_US(1);
		tmp32 = HALMAC_REG_R32(REG_EFUSE_CTRL);
		cnt--;
		if (cnt == 0) {
			PLTFM_MSG_ERR("[ERR]write!!\n");
			return HALMAC_RET_EFUSE_W_FAIL;
		}
	} while (BIT_EF_FLAG == (tmp32 & BIT_EF_FLAG));

	HALMAC_REG_W8(REG_PMC_DBG_CTRL2 + 3, 0x00);

	/* Disable 2.5V LDO */
	enable = 0;
	status = api->halmac_set_hw_value(adapter, HALMAC_HW_LDO25_EN, &enable);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]dis ldo25\n");
		return status;
	}

	if (adapter->efuse_auto_check_en == 1) {
		if (read_hw_efuse_88xx(adapter, offset, 1, &value_read) !=
		    HALMAC_RET_SUCCESS)
			return HALMAC_RET_EFUSE_R_FAIL;
		if (value_read != value) {
			PLTFM_MSG_ERR("[ERR]efuse compare\n");
			return HALMAC_RET_EFUSE_W_FAIL;
		}
	}

	return HALMAC_RET_SUCCESS;
}

static enum halmac_ret_status
eeprom_parser_88xx(struct halmac_adapter *adapter, u8 *phy_map, u8 *log_map)
{
	u8 i;
	u8 value8;
	u8 blk_idx;
	u8 word_en;
	u8 valid;
	u8 hdr;
	u8 hdr2 = 0;
	u32 eeprom_idx;
	u32 efuse_idx = 0;
	struct halmac_hw_cfg_info *hw_info = &adapter->hw_cfg_info;

	PLTFM_MEMSET(log_map, 0xFF, hw_info->eeprom_size);

	do {
		value8 = *(phy_map + efuse_idx);
		hdr = value8;

		if ((hdr & 0x1f) == 0x0f) {
			efuse_idx++;
			value8 = *(phy_map + efuse_idx);
			hdr2 = value8;
			if (hdr2 == 0xff)
				break;
			blk_idx = ((hdr2 & 0xF0) >> 1) | ((hdr >> 5) & 0x07);
			word_en = hdr2 & 0x0F;
		} else {
			blk_idx = (hdr & 0xF0) >> 4;
			word_en = hdr & 0x0F;
		}

		if (hdr == 0xff)
			break;

		efuse_idx++;

		if (efuse_idx >= hw_info->efuse_size - PROTECT_EFUSE_SIZE - 1)
			return HALMAC_RET_EEPROM_PARSING_FAIL;

		for (i = 0; i < 4; i++) {
			valid = (u8)((~(word_en >> i)) & BIT(0));
			if (valid == 1) {
				eeprom_idx = (blk_idx << 3) + (i << 1);

				if ((eeprom_idx + 1) > hw_info->eeprom_size) {
					PLTFM_MSG_ERR("[ERR]efuse idx:0x%X\n",
						      efuse_idx - 1);

					PLTFM_MSG_ERR("[ERR]read hdr:0x%X\n",
						      hdr);

					PLTFM_MSG_ERR("[ERR]rad hdr2:0x%X\n",
						      hdr2);

					return HALMAC_RET_EEPROM_PARSING_FAIL;
				}

				value8 = *(phy_map + efuse_idx);
				*(log_map + eeprom_idx) = value8;

				eeprom_idx++;
				efuse_idx++;

				if (efuse_idx > hw_info->efuse_size -
				    PROTECT_EFUSE_SIZE - 1)
					return HALMAC_RET_EEPROM_PARSING_FAIL;

				value8 = *(phy_map + efuse_idx);
				*(log_map + eeprom_idx) = value8;

				efuse_idx++;

				if (efuse_idx > hw_info->efuse_size -
				    PROTECT_EFUSE_SIZE)
					return HALMAC_RET_EEPROM_PARSING_FAIL;
			}
		}
	} while (1);

	adapter->efuse_end = efuse_idx;

	return HALMAC_RET_SUCCESS;
}

static enum halmac_ret_status
read_log_efuse_map_88xx(struct halmac_adapter *adapter, u8 *map)
{
	u8 *local_map = NULL;
	u32 efuse_size;
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;

	if (adapter->efuse_map_valid == 0) {
		efuse_size = adapter->hw_cfg_info.efuse_size;

		local_map = (u8 *)PLTFM_MALLOC(efuse_size);
		if (!local_map) {
			PLTFM_MSG_ERR("[ERR]local map\n");
			return HALMAC_RET_MALLOC_FAIL;
		}

		status = read_efuse_88xx(adapter, 0, efuse_size, local_map);
		if (status != HALMAC_RET_SUCCESS) {
			PLTFM_MSG_ERR("[ERR]read efuse\n");
			PLTFM_FREE(local_map, efuse_size);
			return status;
		}

		if (!adapter->efuse_map) {
			adapter->efuse_map = (u8 *)PLTFM_MALLOC(efuse_size);
			if (!adapter->efuse_map) {
				PLTFM_MSG_ERR("[ERR]malloc adapter map\n");
				PLTFM_FREE(local_map, efuse_size);
				return HALMAC_RET_MALLOC_FAIL;
			}
		}

		PLTFM_MUTEX_LOCK(&adapter->efuse_mutex);
		PLTFM_MEMCPY(adapter->efuse_map, local_map, efuse_size);
		adapter->efuse_map_valid = 1;
		PLTFM_MUTEX_UNLOCK(&adapter->efuse_mutex);

		PLTFM_FREE(local_map, efuse_size);
	}

	if (eeprom_parser_88xx(adapter, adapter->efuse_map, map) !=
	    HALMAC_RET_SUCCESS)
		return HALMAC_RET_EEPROM_PARSING_FAIL;

	return status;
}

static enum halmac_ret_status
proc_pg_efuse_by_map_88xx(struct halmac_adapter *adapter,
			  struct halmac_pg_efuse_info *info,
			  enum halmac_efuse_read_cfg cfg)
{
	u8 *updated_mask = NULL;
	u32 mask_size = adapter->hw_cfg_info.eeprom_size >> 4;
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;

	updated_mask = (u8 *)PLTFM_MALLOC(mask_size);
	if (!updated_mask) {
		PLTFM_MSG_ERR("[ERR]malloc updated mask\n");
		return HALMAC_RET_MALLOC_FAIL;
	}
	PLTFM_MEMSET(updated_mask, 0x00, mask_size);

	status = update_eeprom_mask_88xx(adapter, info, updated_mask);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]update eeprom mask\n");
		PLTFM_FREE(updated_mask, mask_size);
		return status;
	}

	status = check_efuse_enough_88xx(adapter, info, updated_mask);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]chk efuse enough\n");
		PLTFM_FREE(updated_mask, mask_size);
		return status;
	}

	status = program_efuse_88xx(adapter, info, updated_mask);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]pg efuse\n");
		PLTFM_FREE(updated_mask, mask_size);
		return status;
	}

	PLTFM_FREE(updated_mask, mask_size);

	return HALMAC_RET_SUCCESS;
}

static enum halmac_ret_status
dump_efuse_drv_88xx(struct halmac_adapter *adapter)
{
	u8 *map = NULL;
	u32 efuse_size = adapter->hw_cfg_info.efuse_size;

	if (!adapter->efuse_map) {
		adapter->efuse_map = (u8 *)PLTFM_MALLOC(efuse_size);
		if (!adapter->efuse_map) {
			PLTFM_MSG_ERR("[ERR]malloc adapter map!!\n");
			reset_ofld_feature_88xx(adapter,
						FEATURE_DUMP_PHY_EFUSE);
			return HALMAC_RET_MALLOC_FAIL;
		}
	}

	if (adapter->efuse_map_valid == 0) {
		map = (u8 *)PLTFM_MALLOC(efuse_size);
		if (!map) {
			PLTFM_MSG_ERR("[ERR]malloc map\n");
			return HALMAC_RET_MALLOC_FAIL;
		}

		if (read_hw_efuse_88xx(adapter, 0, efuse_size, map) !=
		    HALMAC_RET_SUCCESS) {
			PLTFM_FREE(map, efuse_size);
			return HALMAC_RET_EFUSE_R_FAIL;
		}

		PLTFM_MUTEX_LOCK(&adapter->efuse_mutex);
		PLTFM_MEMCPY(adapter->efuse_map, map, efuse_size);
		adapter->efuse_map_valid = 1;
		PLTFM_MUTEX_UNLOCK(&adapter->efuse_mutex);

		PLTFM_FREE(map, efuse_size);
	}

	return HALMAC_RET_SUCCESS;
}

static enum halmac_ret_status
dump_efuse_fw_88xx(struct halmac_adapter *adapter)
{
	u8 h2c_buf[H2C_PKT_SIZE_88XX] = { 0 };
	u16 seq_num = 0;
	u32 efuse_size = adapter->hw_cfg_info.efuse_size;
	struct halmac_h2c_header_info hdr_info;
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;

	hdr_info.sub_cmd_id = SUB_CMD_ID_DUMP_PHYSICAL_EFUSE;
	hdr_info.content_size = 0;
	hdr_info.ack = 1;
	set_h2c_pkt_hdr_88xx(adapter, h2c_buf, &hdr_info, &seq_num);

	adapter->halmac_state.efuse_state.seq_num = seq_num;

	if (!adapter->efuse_map) {
		adapter->efuse_map = (u8 *)PLTFM_MALLOC(efuse_size);
		if (!adapter->efuse_map) {
			PLTFM_MSG_ERR("[ERR]malloc adapter map\n");
			reset_ofld_feature_88xx(adapter,
						FEATURE_DUMP_PHY_EFUSE);
			return HALMAC_RET_MALLOC_FAIL;
		}
	}

	if (adapter->efuse_map_valid == 0) {
		status = send_h2c_pkt_88xx(adapter, h2c_buf);
		if (status != HALMAC_RET_SUCCESS) {
			PLTFM_MSG_ERR("[ERR]send h2c pkt\n");
			reset_ofld_feature_88xx(adapter,
						FEATURE_DUMP_PHY_EFUSE);
			return status;
		}
	}

	return HALMAC_RET_SUCCESS;
}

static enum halmac_ret_status
proc_write_log_efuse_88xx(struct halmac_adapter *adapter, u32 offset, u8 value)
{
	u8 byte1;
	u8 byte2;
	u8 blk;
	u8 blk_idx;
	u8 hdr;
	u8 hdr2;
	u8 *map = NULL;
	u32 eeprom_size = adapter->hw_cfg_info.eeprom_size;
	u32 end;
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;

	map = (u8 *)PLTFM_MALLOC(eeprom_size);
	if (!map) {
		PLTFM_MSG_ERR("[ERR]malloc map\n");
		return HALMAC_RET_MALLOC_FAIL;
	}
	PLTFM_MEMSET(map, 0xFF, eeprom_size);

	status = read_log_efuse_map_88xx(adapter, map);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]read logical efuse\n");
		PLTFM_FREE(map, eeprom_size);
		return status;
	}

	if (*(map + offset) != value) {
		end = adapter->efuse_end;
		blk = (u8)(offset >> 3);
		blk_idx = (u8)((offset & (8 - 1)) >> 1);

		if (offset > 0x7f) {
			hdr = (((blk & 0x07) << 5) & 0xE0) | 0x0F;
			hdr2 = (u8)(((blk & 0x78) << 1) +
						((0x1 << blk_idx) ^ 0x0F));
		} else {
			hdr = (u8)((blk << 4) + ((0x01 << blk_idx) ^ 0x0F));
		}

		if ((offset & 1) == 0) {
			byte1 = value;
			byte2 = *(map + offset + 1);
		} else {
			byte1 = *(map + offset - 1);
			byte2 = value;
		}

		if (offset > 0x7f) {
			if (adapter->hw_cfg_info.efuse_size <=
			    4 + PROTECT_EFUSE_SIZE + end) {
				PLTFM_FREE(map, eeprom_size);
				return HALMAC_RET_EFUSE_NOT_ENOUGH;
			}

			status = write_hw_efuse_88xx(adapter, end, hdr);
			if (status != HALMAC_RET_SUCCESS) {
				PLTFM_FREE(map, eeprom_size);
				return status;
			}

			status = write_hw_efuse_88xx(adapter, end + 1, hdr2);
			if (status != HALMAC_RET_SUCCESS) {
				PLTFM_FREE(map, eeprom_size);
				return status;
			}

			status = write_hw_efuse_88xx(adapter, end + 2, byte1);
			if (status != HALMAC_RET_SUCCESS) {
				PLTFM_FREE(map, eeprom_size);
				return status;
			}

			status = write_hw_efuse_88xx(adapter, end + 3, byte2);
			if (status != HALMAC_RET_SUCCESS) {
				PLTFM_FREE(map, eeprom_size);
				return status;
			}
		} else {
			if (adapter->hw_cfg_info.efuse_size <=
			    3 + PROTECT_EFUSE_SIZE + end) {
				PLTFM_FREE(map, eeprom_size);
				return HALMAC_RET_EFUSE_NOT_ENOUGH;
			}

			status = write_hw_efuse_88xx(adapter, end, hdr);
			if (status != HALMAC_RET_SUCCESS) {
				PLTFM_FREE(map, eeprom_size);
				return status;
			}

			status = write_hw_efuse_88xx(adapter, end + 1, byte1);
			if (status != HALMAC_RET_SUCCESS) {
				PLTFM_FREE(map, eeprom_size);
				return status;
			}

			status = write_hw_efuse_88xx(adapter, end + 2, byte2);
			if (status != HALMAC_RET_SUCCESS) {
				PLTFM_FREE(map, eeprom_size);
				return status;
			}
		}
	}

	PLTFM_FREE(map, eeprom_size);

	return HALMAC_RET_SUCCESS;
}

enum halmac_ret_status
read_efuse_88xx(struct halmac_adapter *adapter, u32 offset, u32 size, u8 *map)
{
	if (!map) {
		PLTFM_MSG_ERR("[ERR]malloc map\n");
		return HALMAC_RET_NULL_POINTER;
	}

	if (adapter->efuse_map_valid == 1) {
		PLTFM_MEMCPY(map, adapter->efuse_map + offset, size);
	} else {
		if (read_hw_efuse_88xx(adapter, offset, size, map) !=
		    HALMAC_RET_SUCCESS)
			return HALMAC_RET_EFUSE_R_FAIL;
	}

	return HALMAC_RET_SUCCESS;
}

static enum halmac_ret_status
update_eeprom_mask_88xx(struct halmac_adapter *adapter,
			struct halmac_pg_efuse_info *info, u8 *updated_mask)
{
	u8 *map = NULL;
	u8 clr_bit = 0;
	u32 eeprom_size = adapter->hw_cfg_info.eeprom_size;
	u8 *map_pg;
	u8 *efuse_mask;
	u16 i;
	u16 j;
	u16 map_offset;
	u16 mask_offset;
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;

	map = (u8 *)PLTFM_MALLOC(eeprom_size);
	if (!map) {
		PLTFM_MSG_ERR("[ERR]malloc map\n");
		return HALMAC_RET_MALLOC_FAIL;
	}
	PLTFM_MEMSET(map, 0xFF, eeprom_size);

	PLTFM_MEMSET(updated_mask, 0x00, info->efuse_mask_size);

	status = read_log_efuse_map_88xx(adapter, map);

	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_FREE(map, eeprom_size);
		return status;
	}

	map_pg = info->efuse_map;
	efuse_mask = info->efuse_mask;

	for (i = 0; i < info->efuse_mask_size; i++)
		*(updated_mask + i) = *(efuse_mask + i);

	for (i = 0; i < info->efuse_map_size; i += 16) {
		for (j = 0; j < 16; j += 2) {
			map_offset = i + j;
			mask_offset = i >> 4;
			if (*(u16 *)(map_pg + map_offset) ==
			    *(u16 *)(map + map_offset)) {
				switch (j) {
				case 0:
					clr_bit = BIT(4);
					break;
				case 2:
					clr_bit = BIT(5);
					break;
				case 4:
					clr_bit = BIT(6);
					break;
				case 6:
					clr_bit = BIT(7);
					break;
				case 8:
					clr_bit = BIT(0);
					break;
				case 10:
					clr_bit = BIT(1);
					break;
				case 12:
					clr_bit = BIT(2);
					break;
				case 14:
					clr_bit = BIT(3);
					break;
				default:
					break;
				}
				*(updated_mask + mask_offset) &= ~clr_bit;
			}
		}
	}

	PLTFM_FREE(map, eeprom_size);

	return status;
}

static enum halmac_ret_status
check_efuse_enough_88xx(struct halmac_adapter *adapter,
			struct halmac_pg_efuse_info *info, u8 *updated_mask)
{
	u8 pre_word_en;
	u16 i;
	u16 j;
	u32 eeprom_offset;
	u32 pg_num = 0;

	for (i = 0; i < info->efuse_map_size; i = i + 8) {
		eeprom_offset = i;

		if ((eeprom_offset & 7) > 0)
			pre_word_en = (*(updated_mask + (i >> 4)) & 0x0F);
		else
			pre_word_en = (*(updated_mask + (i >> 4)) >> 4);

		if (pre_word_en > 0) {
			if (eeprom_offset > 0x7f) {
				pg_num += 2;
				for (j = 0; j < 4; j++) {
					if (((pre_word_en >> j) & 0x1) > 0)
						pg_num += 2;
				}
			} else {
				pg_num++;
				for (j = 0; j < 4; j++) {
					if (((pre_word_en >> j) & 0x1) > 0)
						pg_num += 2;
				}
			}
		}
	}

	if (adapter->hw_cfg_info.efuse_size <=
	    (pg_num + PROTECT_EFUSE_SIZE + adapter->efuse_end))
		return HALMAC_RET_EFUSE_NOT_ENOUGH;

	return HALMAC_RET_SUCCESS;
}

static enum halmac_ret_status
pg_extend_efuse_88xx(struct halmac_adapter *adapter,
		     struct halmac_pg_efuse_info *info, u8 word_en,
		     u8 pre_word_en, u32 eeprom_offset)
{
	u8 blk;
	u8 hdr;
	u8 hdr2;
	u16 i;
	u32 efuse_end;
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;

	efuse_end = adapter->efuse_end;

	blk = (u8)(eeprom_offset >> 3);
	hdr = (((blk & 0x07) << 5) & 0xE0) | 0x0F;
	hdr2 = (u8)(((blk & 0x78) << 1) + word_en);

	status = write_hw_efuse_88xx(adapter, efuse_end, hdr);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]write efuse\n");
		return status;
	}

	status = write_hw_efuse_88xx(adapter, efuse_end + 1, hdr2);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]write efuse(+1)\n");
		return status;
	}

	efuse_end = efuse_end + 2;
	for (i = 0; i < 4; i++) {
		if (((pre_word_en >> i) & 0x1) > 0) {
			status = write_hw_efuse_88xx(adapter, efuse_end,
						     *(info->efuse_map +
						     eeprom_offset +
						     (i << 1)));
			if (status != HALMAC_RET_SUCCESS) {
				PLTFM_MSG_ERR("[ERR]write efuse(<<1)\n");
				return status;
			}

			status = write_hw_efuse_88xx(adapter, efuse_end + 1,
						     *(info->efuse_map +
						     eeprom_offset + (i << 1)
						     + 1));
			if (status != HALMAC_RET_SUCCESS) {
				PLTFM_MSG_ERR("[ERR]write efuse(<<1)+1\n");
				return status;
			}
			efuse_end = efuse_end + 2;
		}
	}
	adapter->efuse_end = efuse_end;
	return status;
}

static enum halmac_ret_status
proc_pg_efuse_88xx(struct halmac_adapter *adapter,
		   struct halmac_pg_efuse_info *info, u8 word_en,
		   u8 pre_word_en, u32 eeprom_offset)
{
	u8 blk;
	u8 hdr;
	u16 i;
	u32 efuse_end;
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;

	efuse_end = adapter->efuse_end;

	blk = (u8)(eeprom_offset >> 3);
	hdr = (u8)((blk << 4) + word_en);

	status = write_hw_efuse_88xx(adapter, efuse_end, hdr);
	if (status != HALMAC_RET_SUCCESS) {
		PLTFM_MSG_ERR("[ERR]write efuse\n");
		return status;
	}
	efuse_end = efuse_end + 1;
	for (i = 0; i < 4; i++) {
		if (((pre_word_en >> i) & 0x1) > 0) {
			status = write_hw_efuse_88xx(adapter, efuse_end,
						     *(info->efuse_map +
						     eeprom_offset +
						     (i << 1)));
			if (status != HALMAC_RET_SUCCESS) {
				PLTFM_MSG_ERR("[ERR]write efuse(<<1)\n");
				return status;
			}
			status = write_hw_efuse_88xx(adapter, efuse_end + 1,
						     *(info->efuse_map +
						     eeprom_offset + (i << 1)
						     + 1));
			if (status != HALMAC_RET_SUCCESS) {
				PLTFM_MSG_ERR("[ERR]write efuse(<<1)+1\n");
				return status;
			}
			efuse_end = efuse_end + 2;
		}
	}
	adapter->efuse_end = efuse_end;
	return status;
}

static enum halmac_ret_status
program_efuse_88xx(struct halmac_adapter *adapter,
		   struct halmac_pg_efuse_info *info, u8 *updated_mask)
{
	u8 pre_word_en;
	u8 word_en;
	u16 i;
	u32 eeprom_offset;
	enum halmac_ret_status status = HALMAC_RET_SUCCESS;

	for (i = 0; i < info->efuse_map_size; i = i + 8) {
		eeprom_offset = i;

		if (((eeprom_offset >> 3) & 1) > 0) {
			pre_word_en = (*(updated_mask + (i >> 4)) & 0x0F);
			word_en = pre_word_en ^ 0x0F;
		} else {
			pre_word_en = (*(updated_mask + (i >> 4)) >> 4);
			word_en = pre_word_en ^ 0x0F;
		}

		if (pre_word_en > 0) {
			if (eeprom_offset > 0x7f) {
				status = pg_extend_efuse_88xx(adapter, info,
							      word_en,
							      pre_word_en,
							      eeprom_offset);
				if (status != HALMAC_RET_SUCCESS) {
					PLTFM_MSG_ERR("[ERR]extend efuse\n");
					return status;
				}
			} else {
				status = proc_pg_efuse_88xx(adapter, info,
							    word_en,
							    pre_word_en,
							    eeprom_offset);
				if (status != HALMAC_RET_SUCCESS) {
					PLTFM_MSG_ERR("[ERR]extend efuse");
					return status;
				}
			}
		}
	}

	return status;
}

static void
mask_eeprom_88xx(struct halmac_adapter *adapter,
		 struct halmac_pg_efuse_info *info)
{
	u8 pre_word_en;
	u8 *updated_mask;
	u8 *efuse_map;
	u16 i;
	u16 j;
	u32 offset;

	updated_mask = info->efuse_mask;
	efuse_map = info->efuse_map;

	for (i = 0; i < info->efuse_map_size; i = i + 8) {
		offset = i;

		if (((offset >> 3) & 1) > 0)
			pre_word_en = (*(updated_mask + (i >> 4)) & 0x0F);
		else
			pre_word_en = (*(updated_mask + (i >> 4)) >> 4);

		for (j = 0; j < 4; j++) {
			if (((pre_word_en >> j) & 0x1) == 0) {
				*(efuse_map + offset + (j << 1)) = 0xFF;
				*(efuse_map + offset + (j << 1) + 1) = 0xFF;
			}
		}
	}
}

enum halmac_ret_status
get_efuse_data_88xx(struct halmac_adapter *adapter, u8 *buf, u32 size)
{
	u8 seg_id;
	u8 seg_size;
	u8 seq_num;
	u8 fw_rc;
	u8 *map = NULL;
	u32 eeprom_size = adapter->hw_cfg_info.eeprom_size;
	struct halmac_efuse_state *state = &adapter->halmac_state.efuse_state;
	enum halmac_cmd_process_status proc_status;

	seq_num = (u8)EFUSE_DATA_GET_H2C_SEQ(buf);
	PLTFM_MSG_TRACE("[TRACE]Seq num : h2c->%d c2h->%d\n",
			state->seq_num, seq_num);
	if (seq_num != state->seq_num) {
		PLTFM_MSG_ERR("[ERR]Seq num mismatch : h2c->%d c2h->%d\n",
			      state->seq_num, seq_num);
		return HALMAC_RET_SUCCESS;
	}

	if (state->proc_status != HALMAC_CMD_PROCESS_SENDING) {
		PLTFM_MSG_ERR("[ERR]not cmd sending\n");
		return HALMAC_RET_SUCCESS;
	}

	seg_id = (u8)EFUSE_DATA_GET_SEGMENT_ID(buf);
	seg_size = (u8)EFUSE_DATA_GET_SEGMENT_SIZE(buf);
	if (seg_id == 0)
		adapter->efuse_seg_size = seg_size;

	map = (u8 *)PLTFM_MALLOC(eeprom_size);
	if (!map) {
		PLTFM_MSG_ERR("[ERR]malloc map\n");
		return HALMAC_RET_MALLOC_FAIL;
	}
	PLTFM_MEMSET(map, 0xFF, eeprom_size);

	PLTFM_MUTEX_LOCK(&adapter->efuse_mutex);
	PLTFM_MEMCPY(adapter->efuse_map + seg_id * adapter->efuse_seg_size,
		     buf + C2H_DATA_OFFSET_88XX, seg_size);
	PLTFM_MUTEX_UNLOCK(&adapter->efuse_mutex);

	if (EFUSE_DATA_GET_END_SEGMENT(buf) == 0) {
		PLTFM_FREE(map, eeprom_size);
		return HALMAC_RET_SUCCESS;
	}

	fw_rc = state->fw_rc;

	if ((enum halmac_h2c_return_code)fw_rc == HALMAC_H2C_RETURN_SUCCESS) {
		proc_status = HALMAC_CMD_PROCESS_DONE;
		state->proc_status = proc_status;

		PLTFM_MUTEX_LOCK(&adapter->efuse_mutex);
		adapter->efuse_map_valid = 1;
		PLTFM_MUTEX_UNLOCK(&adapter->efuse_mutex);

		if (adapter->evnt.phy_efuse_map == 1) {
			PLTFM_EVENT_SIG(FEATURE_DUMP_PHY_EFUSE,
					proc_status, adapter->efuse_map,
					adapter->hw_cfg_info.efuse_size);
			adapter->evnt.phy_efuse_map = 0;
		}

		if (adapter->evnt.log_efuse_map == 1) {
			if (eeprom_parser_88xx(adapter, adapter->efuse_map,
					       map) != HALMAC_RET_SUCCESS) {
				PLTFM_FREE(map, eeprom_size);
				return HALMAC_RET_EEPROM_PARSING_FAIL;
			}
			PLTFM_EVENT_SIG(FEATURE_DUMP_LOG_EFUSE, proc_status,
					map, eeprom_size);
			adapter->evnt.log_efuse_map = 0;
		}
	} else {
		proc_status = HALMAC_CMD_PROCESS_ERROR;
		state->proc_status = proc_status;

		if (adapter->evnt.phy_efuse_map == 1) {
			PLTFM_EVENT_SIG(FEATURE_DUMP_PHY_EFUSE, proc_status,
					&state->fw_rc, 1);
			adapter->evnt.phy_efuse_map = 0;
		}

		if (adapter->evnt.log_efuse_map == 1) {
			PLTFM_EVENT_SIG(FEATURE_DUMP_LOG_EFUSE, proc_status,
					&state->fw_rc, 1);
			adapter->evnt.log_efuse_map = 0;
		}
	}

	PLTFM_FREE(map, eeprom_size);

	return HALMAC_RET_SUCCESS;
}

enum halmac_ret_status
get_dump_phy_efuse_status_88xx(struct halmac_adapter *adapter,
			       enum halmac_cmd_process_status *proc_status,
			       u8 *data, u32 *size)
{
	u8 *map = NULL;
	u32 efuse_size = adapter->hw_cfg_info.efuse_size;
	struct halmac_efuse_state *state = &adapter->halmac_state.efuse_state;

	*proc_status = state->proc_status;

	if (!data)
		return HALMAC_RET_NULL_POINTER;

	if (!size)
		return HALMAC_RET_NULL_POINTER;

	if (*proc_status == HALMAC_CMD_PROCESS_DONE) {
		if (*size < efuse_size) {
			*size = efuse_size;
			return HALMAC_RET_BUFFER_TOO_SMALL;
		}

		*size = efuse_size;

		map = (u8 *)PLTFM_MALLOC(efuse_size);
		if (!map) {
			PLTFM_MSG_ERR("[ERR]malloc map\n");
			return HALMAC_RET_MALLOC_FAIL;
		}
		PLTFM_MEMSET(map, 0xFF, efuse_size);
		PLTFM_MUTEX_LOCK(&adapter->efuse_mutex);
		PLTFM_MEMCPY(map, adapter->efuse_map,
			     efuse_size - PROTECT_EFUSE_SIZE);
		PLTFM_MEMCPY(map + efuse_size - PROTECT_EFUSE_SIZE +
			     RSVD_CS_EFUSE_SIZE,
			     adapter->efuse_map + efuse_size -
			     PROTECT_EFUSE_SIZE + RSVD_CS_EFUSE_SIZE,
			     PROTECT_EFUSE_SIZE - RSVD_EFUSE_SIZE -
			     RSVD_CS_EFUSE_SIZE);
		PLTFM_MUTEX_UNLOCK(&adapter->efuse_mutex);

		PLTFM_MEMCPY(data, map, *size);

		PLTFM_FREE(map, efuse_size);
	}

	return HALMAC_RET_SUCCESS;
}

enum halmac_ret_status
get_dump_log_efuse_status_88xx(struct halmac_adapter *adapter,
			       enum halmac_cmd_process_status *proc_status,
			       u8 *data, u32 *size)
{
	u8 *map = NULL;
	u32 eeprom_size = adapter->hw_cfg_info.eeprom_size;
	struct halmac_efuse_state *state = &adapter->halmac_state.efuse_state;

	*proc_status = state->proc_status;

	if (!data)
		return HALMAC_RET_NULL_POINTER;

	if (!size)
		return HALMAC_RET_NULL_POINTER;

	if (*proc_status == HALMAC_CMD_PROCESS_DONE) {
		if (*size < eeprom_size) {
			*size = eeprom_size;
			return HALMAC_RET_BUFFER_TOO_SMALL;
		}

		*size = eeprom_size;

		map = (u8 *)PLTFM_MALLOC(eeprom_size);
		if (!map) {
			PLTFM_MSG_ERR("[ERR]malloc map\n");
			return HALMAC_RET_MALLOC_FAIL;
		}
		PLTFM_MEMSET(map, 0xFF, eeprom_size);

		if (eeprom_parser_88xx(adapter, adapter->efuse_map, map) !=
		    HALMAC_RET_SUCCESS) {
			PLTFM_FREE(map, eeprom_size);
			return HALMAC_RET_EEPROM_PARSING_FAIL;
		}

		PLTFM_MEMCPY(data, map, *size);

		PLTFM_FREE(map, eeprom_size);
	}

	return HALMAC_RET_SUCCESS;
}

enum halmac_ret_status
get_h2c_ack_phy_efuse_88xx(struct halmac_adapter *adapter, u8 *buf, u32 size)
{
	u8 seq_num = 0;
	u8 fw_rc;
	struct halmac_efuse_state *state = &adapter->halmac_state.efuse_state;

	seq_num = (u8)H2C_ACK_HDR_GET_H2C_SEQ(buf);
	PLTFM_MSG_TRACE("[TRACE]Seq num : h2c->%d c2h->%d\n",
			state->seq_num, seq_num);
	if (seq_num != state->seq_num) {
		PLTFM_MSG_ERR("[ERR]Seq num mismatch : h2c->%d c2h->%d\n",
			      state->seq_num, seq_num);
		return HALMAC_RET_SUCCESS;
	}

	if (state->proc_status != HALMAC_CMD_PROCESS_SENDING) {
		PLTFM_MSG_ERR("[ERR]not cmd sending\n");
		return HALMAC_RET_SUCCESS;
	}

	fw_rc = (u8)H2C_ACK_HDR_GET_H2C_RETURN_CODE(buf);
	state->fw_rc = fw_rc;

	return HALMAC_RET_SUCCESS;
}

u32
get_rsvd_efuse_size_88xx(struct halmac_adapter *adapter)
{
	return PROTECT_EFUSE_SIZE;
}

#endif /* HALMAC_88XX_SUPPORT */
