/******************************************************************************
 *
 * 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 _HAL_API_EFUSE_C_
#include "hal_headers.h"
#include "efuse/hal_efuse_export.h"

/*WIFI Efuse*/
enum rtw_hal_status
rtw_hal_efuse_shadow_load(struct hal_info_t *hal_info, bool is_limit)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_shadow_load(hal_info->efuse, is_limit);

	return status;
}

enum rtw_hal_status
rtw_hal_efuse_shadow_update(struct hal_info_t *hal_info, bool is_limit)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_EFUSE_PG_FAIL;

	status = rtw_efuse_shadow_update(hal_info->efuse, is_limit);

	return status;
}

enum rtw_hal_status
rtw_hal_efuse_shadow_read(struct hal_info_t *hal_info, u8 byte_count,
						  u16 offset, u32 *value, bool is_limit)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_shadow_read(hal_info->efuse, byte_count, offset, value,
								   is_limit);

	return status;
}

enum rtw_hal_status
rtw_hal_efuse_shadow_write(struct hal_info_t *hal_info, u8 byte_count,
						   u16 offset, u32 value, bool is_limit)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_shadow_write(hal_info->efuse, byte_count, offset, value,
									is_limit);
	return status;
}

enum rtw_hal_status
rtw_hal_efuse_shadow2buf(struct hal_info_t *hal_info, u8 *pbuf, u16 buflen)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_SUCCESS;

	status = rtw_efuse_shadow2buf(hal_info->efuse, pbuf, buflen);

	return status;
}

enum rtw_hal_status rtw_hal_efuse_file_map_load(
	struct hal_info_t *hal_info, char *file_path, u8 is_limit)
{
	enum rtw_hal_status hal_status = RTW_HAL_STATUS_FAILURE;

	hal_status = rtw_efuse_file_map_load(hal_info->efuse, file_path ,is_limit);

	return hal_status;
}

enum rtw_hal_status rtw_hal_efuse_file_mask_load(
	struct hal_info_t *hal_info, char *file_path, u8 is_limit)
{
	enum rtw_hal_status hal_status = RTW_HAL_STATUS_FAILURE;

	hal_status = rtw_efuse_file_mask_load(hal_info->efuse, file_path, is_limit);

	return hal_status;
}

/* usage = used percentage(1 Byte) + used bytes(2 Bytes) */
enum rtw_hal_status rtw_hal_efuse_get_usage(struct hal_info_t *hal_info,
	u32 *usage)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_get_usage(hal_info->efuse, usage);

	return status;
}

enum rtw_hal_status rtw_hal_efuse_get_logical_size(struct hal_info_t *hal_info,
	u32 *size)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_get_logical_size(hal_info->efuse, size, true);

	return status;
}

enum rtw_hal_status rtw_hal_efuse_get_size(struct hal_info_t *hal_info,
	u32 *size)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_get_size(hal_info->efuse, size);

	return status;
}

enum rtw_hal_status rtw_hal_efuse_get_avl(struct hal_info_t *hal_info,
	u32 *size)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_get_avl(hal_info->efuse, size);

	return status;
}

enum rtw_hal_status rtw_hal_efuse_get_shadowmap_from(struct hal_info_t *hal_info,
	u8 *val)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_get_shadowmap_from(hal_info->efuse, val);

	return status;
}

enum rtw_hal_status rtw_hal_efuse_get_offset_mask(struct hal_info_t *hal_info,
	u16 offset, u8 *mask)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_get_offset_mask(hal_info->efuse, offset, mask);

	return status;
}

enum rtw_hal_status rtw_hal_efuse_get_mask_buf(struct hal_info_t *hal_info, 
							u8 *destbuf, u32 *buflen)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_get_mask_buf(hal_info->efuse, destbuf, buflen);

	return status;
}

enum rtw_hal_status
rtw_hal_efuse_get_info(struct rtw_hal_com_t *hal_com,
		       enum rtw_efuse_info info_type,
		       void *value,
		       u8 size)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;
	struct hal_info_t *hal_info = hal_com->hal_priv;

	status = rtw_efuse_get_info(hal_info->efuse, info_type, value, size);

	return status;
}

/* API export to PHL : rtw_hal_get_efuse_info */
enum rtw_hal_status
rtw_hal_get_efuse_info(void *hal,
		       enum rtw_efuse_info info_type,
		       void *value,
		       u8 size)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;
	struct hal_info_t *hal_info = (struct hal_info_t *)hal;

	status = rtw_efuse_get_info(hal_info->efuse, info_type, value, size);

	return status;
}

void rtw_hal_efuse_process(struct rtw_phl_com_t *phl_com,
                           struct hal_info_t *hal_info,
                           char *ic_name
)
{
#ifdef DBG_MONITOR_TIME
	u32 start_t = 0;
#endif /* DBG_MONITOR_TIME */

	if (rtw_efuse_is_processed(hal_info->efuse) == true) {
		PHL_INFO("%s EFUSE module is already initialized.\n", __FUNCTION__);
		return;
	}

#ifdef DBG_MONITOR_TIME
	PHL_FUN_MON_START(&start_t);
#endif /* DBG_MONITOR_TIME */
#ifdef CONFIG_PHL_FW_DUMP_EFUSE
	rtw_phl_fw_dump_efuse_precfg(phl_com);
#endif

	rtw_efuse_process(hal_info->efuse, ic_name);

#ifdef CONFIG_PHL_FW_DUMP_EFUSE
	rtw_phl_fw_dump_efuse_postcfg(phl_com);
#endif

#ifdef DBG_MONITOR_TIME
	PHL_FUNC_MON_END(hal_info->phl_com, &start_t, TIME_HAL_EFUSE_PROC);
#endif /* DBG_MONITOR_TIME */
}

enum rtw_hal_status rtw_hal_efuse_init(struct rtw_phl_com_t *phl_com,
					struct hal_info_t *hal_info)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;
	struct rtw_hal_com_t *hal_com = hal_info->hal_com;

	status = rtw_efuse_init(phl_com, hal_com, &(hal_info->efuse));

	return status;
}

void rtw_hal_efuse_deinit(struct rtw_phl_com_t *phl_com,
					struct hal_info_t *hal_info)
{
	struct rtw_hal_com_t *hal_com = hal_info->hal_com;

	rtw_efuse_deinit(hal_com, hal_info->efuse);
}


enum rtw_hal_status
rtw_hal_efuse_bt_shadow_load(struct hal_info_t *hal_info)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_bt_shadow_load(hal_info->efuse);

	return status;
}

enum rtw_hal_status
rtw_hal_efuse_bt_shadow_update(struct hal_info_t *hal_info)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_EFUSE_PG_FAIL;

	status = rtw_efuse_bt_shadow_update(hal_info->efuse);

	return status;
}

enum rtw_hal_status
rtw_hal_efuse_bt_shadow_read(struct hal_info_t *hal_info, u8 byte_count,
						  u16 offset, u32 *value)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_bt_shadow_read(hal_info->efuse, byte_count, offset, value);

	return status;
}

enum rtw_hal_status
rtw_hal_efuse_bt_shadow_write(struct hal_info_t *hal_info, u8 byte_count,
						   u16 offset, u32 value)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_bt_shadow_write(hal_info->efuse, byte_count, offset, value);
	return status;
}


enum rtw_hal_status
rtw_hal_efuse_bt_shadow2buf(struct hal_info_t *hal_info, u8 *pbuf, u16 buflen)
{
   enum rtw_hal_status status = RTW_HAL_STATUS_SUCCESS;

   status = rtw_efuse_bt_shadow2buf(hal_info->efuse, pbuf, buflen);

   return status;
}

enum rtw_hal_status rtw_hal_efuse_bt_file_map_load(
	struct hal_info_t *hal_info, char *file_path)
{
	enum rtw_hal_status hal_status = RTW_HAL_STATUS_FAILURE;

	hal_status = rtw_efuse_bt_file_map_load(hal_info->efuse, file_path);

	return hal_status;
}


enum rtw_hal_status rtw_hal_efuse_bt_file_mask_load(
	struct hal_info_t *hal_info, char *file_path)
{
	enum rtw_hal_status hal_status = RTW_HAL_STATUS_FAILURE;

	hal_status = rtw_efuse_bt_file_mask_load(hal_info->efuse, file_path);

	return hal_status;
}

/* usage = used percentage(1 Byte) + used bytes(2 Bytes) */
enum rtw_hal_status rtw_hal_efuse_bt_get_usage(struct hal_info_t *hal_info,
	u32 *usage)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_get_usage(hal_info->efuse, usage);

	return status;
}

enum rtw_hal_status rtw_hal_efuse_bt_get_logical_size(struct hal_info_t *hal_info,
	u32 *size)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_bt_get_logical_size(hal_info->efuse, size);

	return status;
}

enum rtw_hal_status rtw_hal_efuse_bt_get_size(struct hal_info_t *hal_info,
	u32 *size)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_bt_get_size(hal_info->efuse, size);

	return status;
}

enum rtw_hal_status rtw_hal_efuse_bt_get_avl(struct hal_info_t *hal_info,
	u32 *size)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_bt_get_avl(hal_info->efuse, size);

	return status;
}


enum rtw_hal_status rtw_hal_efuse_bt_get_offset_mask(struct hal_info_t *hal_info,
	u16 offset, u8 *mask)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_bt_get_offset_mask(hal_info->efuse, offset, mask);

	return status;
}

enum rtw_hal_status rtw_hal_efuse_bt_get_mask_buf(struct hal_info_t *hal_info,
							u8 *destbuf, u32 *buflen)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_bt_get_mask_buf(hal_info->efuse, destbuf, buflen);

	return status;
}
enum rtw_hal_status rtw_hal_efuse_bt_read_hidden(
	struct hal_info_t *hal_info, u32 addr, u32 size, u8 *val)
{
	enum rtw_hal_status hal_status = RTW_HAL_STATUS_FAILURE;

	hal_status = rtw_efuse_bt_read_hidden(hal_info->efuse, addr, size, val);

	return hal_status;
}

enum rtw_hal_status rtw_hal_efuse_bt_write_hidden(
	struct hal_info_t *hal_info, u32 addr, u8 val)
{
	enum rtw_hal_status hal_status = RTW_HAL_STATUS_FAILURE;

	hal_status = rtw_efuse_bt_write_hidden(hal_info->efuse, addr, val);

	return hal_status;
}

enum rtw_hal_status rtw_hal_efuse_read_phy_efuse(
	struct hal_info_t *hal_info, u32 addr, u32 size, u8 *data, u8 type)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_read_phy_wifi(hal_info->efuse, addr, size, data, type);

	return status;
}

enum rtw_hal_status rtw_hal_efuse_renew(
	struct hal_info_t *hal_info, u8 type)
{
	enum rtw_hal_status status = RTW_HAL_STATUS_FAILURE;

	status = rtw_efuse_renew(hal_info->efuse, type);

	return status;
}

void hal_efuse_dump_wifi_map(
	struct hal_info_t *hal_info,
	u32 *used_len,
	char *output,
	u32 *out_len
	)
{
	void *d = hal_info->hal_com->drv_priv;
	u32 _used_len = *used_len;
	u32 _out_len = *out_len;
	u32 map_len = 0;
	u32 i = 0, j = 0;
	u8 *buf = NULL;

	rtw_efuse_get_logical_size(hal_info->efuse, &map_len, true);

	if(map_len == 0){
		PHL_DBG_OUTBUF(_out_len, _used_len, output + _used_len,
				 _out_len - _used_len, "map length is 0!\n");
		goto exit;
	}

	buf = _os_mem_alloc(d, map_len);

	if(buf == NULL){
		PHL_DBG_OUTBUF(_out_len, _used_len, output + _used_len,
				 _out_len - _used_len, "Allocate buffer fail!\n");
		goto exit;
	}
	rtw_efuse_shadow2buf(hal_info->efuse, buf, (u16)map_len);

	PHL_DBG_OUTBUF(_out_len, _used_len, output + _used_len, _out_len - _used_len,
			 "EFUSE Wifi shadow map from %s\n\n",
			 rtw_efuse_get_shadowmap_from_to_str(hal_info->efuse));

	PHL_DBG_OUTBUF(_out_len, _used_len, output + _used_len, _out_len - _used_len,
			 "%14s%-6s%-6s%-6s%-6s%-6s%-6s%-6s%-6s\n",
			 " ", "00", "02", "04", "06", "08", "0A", "0C", "0E");
	PHL_DBG_OUTBUF(_out_len, _used_len, output + _used_len, _out_len - _used_len,
			 "%14s%s\n",
			 " ", "================================================\n");
	for(i = 0; i < map_len; i+=16){
		PHL_DBG_OUTBUF(_out_len, _used_len, output + _used_len,
				 _out_len - _used_len, "0x%08X%4s", i, "");
		for(j = 0; j<16;j++){
			if((i+j) >= map_len)
				break;
			PHL_DBG_OUTBUF(_out_len, _used_len, output + _used_len,
					 _out_len - _used_len, "%02X ", buf[i+j]);
		}
		PHL_DBG_OUTBUF(_out_len, _used_len, output + _used_len,
				 _out_len - _used_len, "\n");
	}
	if(buf)
		_os_mem_free(d, buf, map_len);
exit:
	*used_len = _used_len;
	*out_len = _out_len;
}

void hal_efuse_dump_wifi_logic_map(
	struct hal_info_t *hal_info,
	u32 *used_len,
	char *output,
	u32 *out_len
	)
{
	void *d = hal_info->hal_com->drv_priv;
	u32 _used_len = *used_len;
	u32 _out_len = *out_len;
	u32 map_len = 0;
	u32 i = 0, j = 0;
	u8 *data = NULL;

	rtw_efuse_get_logical_size(hal_info->efuse, &map_len, true);

	if(map_len == 0){
		PHL_DBG_OUTBUF(_out_len, _used_len, output + _used_len,
				 _out_len - _used_len, "map length is 0!\n");
		goto exit;
	}

	data = _os_mem_alloc(d, map_len);

	if(data == NULL){
		PHL_DBG_OUTBUF(_out_len, _used_len, output + _used_len,
				 _out_len - _used_len, "Allocate buffer fail!\n");
		goto exit;
	}

	rtw_efuse_logicmap_buf_load(hal_info->efuse, data, false);

	PHL_DBG_OUTBUF(_out_len, _used_len, output + _used_len, _out_len - _used_len,
			 "EFUSE Wifi map from HW\n");

	PHL_DBG_OUTBUF(_out_len, _used_len, output + _used_len, _out_len - _used_len,
			 "%14s%-6s%-6s%-6s%-6s%-6s%-6s%-6s%-6s\n",
			 " ", "00", "02", "04", "06", "08", "0A", "0C", "0E");
	PHL_DBG_OUTBUF(_out_len, _used_len, output + _used_len, _out_len - _used_len,
			 "%14s%s\n",
			 " ", "================================================\n");
	for(i = 0; i < map_len; i+=16){
		PHL_DBG_OUTBUF(_out_len, _used_len, output + _used_len,
				 _out_len - _used_len, "0x%08X%4s", i, "");
		for(j = 0; j<16;j++){
			if((i+j) >= map_len)
				break;
			PHL_DBG_OUTBUF(_out_len, _used_len, output + _used_len,
					 _out_len - _used_len, "%02X ", data[i+j]);
		}
		PHL_DBG_OUTBUF(_out_len, _used_len, output + _used_len,
				 _out_len - _used_len, "\n");
	}
	if(data)
		_os_mem_free(d, data, map_len);
exit:
	*used_len = _used_len;
	*out_len = _out_len;
}

void hal_efuse_cmd_parser(
	struct hal_info_t *hal_info,
	char input[][MAX_ARGV],
	u32 input_num,
	char *output,
	u32 out_len
	)
{
	u32 hal_cmd_ary_size = sizeof(hal_efuse_cmd_i) / sizeof(struct hal_cmd_info);
	u32 i = 0;
	u8 id = 0;
	u32 used = 0;

	PHL_DBG_OUTBUF(out_len, used, output + used, out_len - used, "\n");

	if (hal_cmd_ary_size == 0)
		return;

	/* Parsing Cmd ID */
	if (input_num) {
		for (i = 0; i < hal_cmd_ary_size; i++) {
			if (_os_strcmp(hal_efuse_cmd_i[i].name, input[0]) == 0) {
				id = hal_efuse_cmd_i[i].id;
				PHL_INFO("enter EFUSE cmd %s\n", hal_efuse_cmd_i[i].name);
				break;
			}
		}
		if (i == hal_cmd_ary_size) {
			PHL_DBG_OUTBUF(out_len, used, output + used,
					 out_len - used,
					 "EFUSE command not found!\n");
			return;
		}
	}

	switch (id) {
		case HAL_EFUSE_HELP:
			PHL_DBG_OUTBUF(out_len, used, output + used,
					 out_len - used,
					 "EFUSE cmd =>\n");
			for (i = 0; i < hal_cmd_ary_size - 1; i++)
				PHL_DBG_OUTBUF(out_len, used, output + used,
						 out_len - used," %s\n", hal_efuse_cmd_i[i].name);
			break;
		case HAL_EFUSE_WIFI_DUMP_MAP:
			hal_efuse_dump_wifi_map(hal_info, &used, output, &out_len);
			break;
		case HAL_EFUSE_WIFI_LOGIC_DUMP_MAP:
			hal_efuse_dump_wifi_logic_map(hal_info, &used, output, &out_len);
			break;
		default:
			PHL_DBG_OUTBUF(out_len, used, output + used,
					 out_len - used,"Do not support this command\n");
			break;
	}
}

s32 hal_efuse_cmd(
	struct hal_info_t *hal_info,
	char *input,
	char *output,
	u32 out_len
	)
{
	char *token;
	u32 argc = 0;
	char argv[MAX_ARGC][MAX_ARGV];

	do {
		token = _os_strsep(&input, ", ");
		if (token) {
			if (_os_strlen((u8*)token) <= MAX_ARGV)
				_os_strcpy(argv[argc], token);

			argc++;
		} else {
			break;
		}
	} while (argc < MAX_ARGC);

	hal_efuse_cmd_parser(hal_info, argv, argc, output, out_len);

	return 0;
}

bool rtw_hal_efuse_proc_cmd(
	struct hal_info_t *hal_info,
	struct rtw_proc_cmd *incmd,
	char *output,
	u32 out_len
	)
{
	if(incmd->in_type == RTW_ARG_TYPE_BUF)
		hal_efuse_cmd(hal_info, incmd->in.buf, output, out_len);
	else if(incmd->in_type == RTW_ARG_TYPE_ARRAY){
		hal_efuse_cmd_parser(hal_info, incmd->in.vector,
					incmd->in_cnt_len, output, out_len);
	}

	return true;
}