/**
 * @file exo_ahw_al_ext_flash_common.c
 *
 * @brief This file contains abstracted driver functions for external flash memories
 *
 * @copyright Copyright 2023 Antaris, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "exo_ahw_al_ext_flash_common.h"
#include "exo_ahw_al_ext_flash_wrapper.h"
#include "exo_ahw_al_common.h"
#include "exo_ah_iob_csn.h"
#include "exo_ahdlsd.h"
#include <stdlib.h>
#include "logger.h"
#include "exo_common.h"
static hal_ret_sts ahw_al_ext_mem_init(ahw_al_ext_mem_hdl* mem_arg, ahw_inst_id_t ahw_id);

/* External memory handle for MX25L512 nor flash */
ahw_al_ext_mem_hdl ext_mem_mx25;

/* External memory handle for MT25QL512 nor flash */
ahw_al_ext_mem_hdl ext_mem_mt25;

/* External memory handle for MT25QL512 nor flash */
ahw_al_ext_mem_hdl ext_fmc_mem_mt28;

/* External memory handle for M24C16 EEPROM_1  */
ahw_al_ext_mem_hdl ext_mem_eeprom_1;

/* External memory handle for M24C16 EEPROM_2  */
ahw_al_ext_mem_hdl ext_mem_eeprom_2;

hal_ret_sts ahw_al_ext_mem_driver_init(ahw_inst_id_t ext_mem_id)
{
	hal_ret_sts ret = HAL_AH_DRIVER_ERR;
	switch(ext_mem_id)
	{
	case EXT_MEM_MX25L512_QSPI:
		ret= ahw_al_ext_mem_init(&ext_mem_mx25, ext_mem_id);
		break;
	case EXT_MEM_MT25QL512_QSPI:
		ret= ahw_al_ext_mem_init(&ext_mem_mt25, ext_mem_id);
		break;
	case EXT_MEM_MT28EW128_FMC:
		ret= ahw_al_ext_mem_init(&ext_fmc_mem_mt28, ext_mem_id);
		break;
	case EXT_MEM_M24C16_EEPROM_1:
		ret = ahw_al_ext_mem_init(&ext_mem_eeprom_1, ext_mem_id);
		break;
	case EXT_MEM_M24C16_EEPROM_2:
		ret= ahw_al_ext_mem_init(&ext_mem_eeprom_2, ext_mem_id);
		break;
	case EXT_MEM_S25HL512T_QSPI:
		break;
	default:
		break;
	}
	return ret;
}

/**
 * @brief  API to initialize the external flash memory
 */
static hal_ret_sts ahw_al_ext_mem_init(ahw_al_ext_mem_hdl* mem_arg, ahw_inst_id_t ahw_id)
{
#ifdef LINUX_TEMP_PORT
	DEBUG_CPRINT(("\n EXO AH-AL EXT MEMORY Initialize"));
#endif
	hal_ret_sts sts;
	mem_arg->ahw_gen_info.ahw_inst_id =ahw_id;
	if(HAL_SCS ==ahw_al_wrpr_ext_flash_init(mem_arg))
	{
		os_sem_create_bin(&mem_arg->semaphore_hdle, "HAL_EXT_MEM_SEM");
		ahal_set_hdle(ahw_id,mem_arg);
		sts = HAL_SCS;
		log_id(LOG_INFO_L1,LOG_AHW,AHW_AL_EXT_MEM_INIT_SCS);
		if(HAL_SCS != ahdlsd_updt_ah_state(ahw_id, AH_ACTIVATED))
		{
			sts = HAL_AHDLSD_FW_ERR;
		}
		if(HAL_SCS != ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_FREE_STATE))
		{
			sts = HAL_AHIOBCSN_FW_ERR;
		}
	}
	else
	{
		sts = HAL_AH_INIT_ERR;
		log_id(LOG_ERROR,LOG_AHW,AHW_AL_EXT_MEM_INIT_ERR);
		if(HAL_SCS != ahdlsd_updt_ah_state(ahw_id, AH_ERROR))
		{
			sts = HAL_AHDLSD_FW_ERR;
		}
	}
#ifdef LINUX_TEMP_PORT
	DEBUG_CPRINT(("\n EXO AH-AL EXT MEMORY Initialization completed successfully"));
#endif
	return sts;
}

/**
 * @brief  API to write data in external flash memory
 */
hal_ret_sts ahw_al_ext_mem_write(ahw_inst_id_t ahw_id, uint32_t address, uint8_t *data, uint32_t size)
{
	hal_ret_sts sts;
	ahw_al_ext_mem_hdl* mem_arg=ahal_get_hdle(ahw_id);
	os_sem_take(mem_arg->semaphore_hdle,AHAL_FLASH_MAX_SEM_TIMEOUT);
	sts = ahiobcsn_check_ste(&mem_arg->ahw_gen_info);
	if(HAL_SCS == sts)
	{
		ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_BUSY_STATE);
		if(HAL_SCS == (hal_ret_sts)ahw_al_wrpr_ext_flash_write(mem_arg, address, data, size))
		{
			ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_FREE_STATE);
			sts = HAL_SCS;
		}
		else
		{
			ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_FREE_STATE);
			sts = HAL_AH_DRIVER_ERR;
            log_id_with_one_arg(LOG_ERROR,LOG_AHW,AHW_AL_EXT_MEM_WRITE_ERR_INST_ID__eq__u8_,ahw_id);
		}
	}
	else
	{
	}
	os_sem_give(mem_arg->semaphore_hdle);
	return sts;
}

/**
 * @brief  API to write data in external flash memory
 */
hal_ret_sts ahw_al_ext_mem_erase_and_write(ahw_inst_id_t ahw_id, uint32_t address, uint8_t *data, uint32_t size)
{
	hal_ret_sts sts;
	ahw_al_ext_mem_hdl* mem_arg=ahal_get_hdle(ahw_id);
	os_sem_take(mem_arg->semaphore_hdle,AHAL_FLASH_MAX_SEM_TIMEOUT);
	sts = ahiobcsn_check_ste(&mem_arg->ahw_gen_info);
	if(HAL_SCS == sts)
	{
		ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_BUSY_STATE);
		if(HAL_SCS == (hal_ret_sts)ahw_al_wrpr_ext_flash_erase_and_write(mem_arg, address, data, size))
		{
			ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_FREE_STATE);
			sts = HAL_SCS;
		}
		else
		{
			ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_FREE_STATE);
			sts = HAL_AH_DRIVER_ERR;
            log_id_with_one_arg(LOG_ERROR,LOG_AHW,AHW_AL_EXT_MEM_ERASE_AND_WRITE_ERR_INST_ID__eq__u8_,ahw_id);
		}
	}
	else
	{
	}
	os_sem_give(mem_arg->semaphore_hdle);
	return sts;
}

/**
 * @brief API to read from external flash memory
 *
 */
hal_ret_sts ahw_al_ext_mem_read(ahw_inst_id_t ahw_id, uint32_t address, uint8_t *data, uint32_t size)
{
	hal_ret_sts sts;
	ahw_al_ext_mem_hdl* mem_arg=ahal_get_hdle(ahw_id);
	sts = ahiobcsn_check_ste(&mem_arg->ahw_gen_info);
	os_sem_take(mem_arg->semaphore_hdle,AHAL_FLASH_MAX_SEM_TIMEOUT);
	if(HAL_SCS == sts)
	{
		ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_BUSY_STATE);
		if(HAL_SCS == (hal_ret_sts)ahw_al_wrpr_ext_flash_read(mem_arg, address, data, size))
		{
			ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_FREE_STATE);
			sts = HAL_SCS;
		}
		else
		{
			ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_FREE_STATE);
			sts = HAL_AH_DRIVER_ERR;
            log_id_with_one_arg(LOG_ERROR,LOG_AHW,AHW_AL_EXT_MEM_READ_ERR_INST_ID__eq__u8_,ahw_id);
		}
	}
	else
	{
	}
	os_sem_give(mem_arg->semaphore_hdle);
	return sts;
}

/**
 * @brief API to erase external flash memory
 *
 */
hal_ret_sts ahw_al_ext_mem_erase(ahw_inst_id_t ahw_id, uint32_t start_address, uint32_t end_address)
{
	hal_ret_sts sts;
	ahw_al_ext_mem_hdl* mem_arg=ahal_get_hdle(ahw_id);
	os_sem_take(mem_arg->semaphore_hdle,AHAL_FLASH_MAX_SEM_TIMEOUT);
	sts = ahiobcsn_check_ste(&mem_arg->ahw_gen_info);
	if(HAL_SCS == sts)
	{
		ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_BUSY_STATE);
		if(HAL_SCS == (hal_ret_sts)ahw_al_wrpr_ext_flash_erase(mem_arg, start_address, end_address))
		{
			ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_FREE_STATE);
			sts = HAL_SCS;
		}
		else
		{
			ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_FREE_STATE);
			sts = HAL_AH_DRIVER_ERR;
            log_id_with_one_arg(LOG_ERROR,LOG_AHW,AHW_AL_EXT_MEM_ERASE_ERR_INST_ID__eq__u8_,ahw_id);
		}
	}
	else
	{
	}
	os_sem_give(mem_arg->semaphore_hdle);
	return sts;
}


/**
 * @brief API to enable memory mapped mode
 *
 * This should not be enabled if write operation would be performed in run time
 */
hal_ret_sts ahw_al_ext_mem_enable_memory_map(ahw_inst_id_t ahw_id)
{
	hal_ret_sts sts;
	ahw_al_ext_mem_hdl* mem_arg=ahal_get_hdle(ahw_id);
	os_sem_take(mem_arg->semaphore_hdle,AHAL_FLASH_MAX_SEM_TIMEOUT);
	sts = ahiobcsn_check_ste(&mem_arg->ahw_gen_info);
	if(HAL_SCS == sts)
	{
		ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_BUSY_STATE);
		if(HAL_SCS == (hal_ret_sts)ahw_al_wrpr_ext_flash_memmap_enable(mem_arg))
		{
			ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_FREE_STATE);
			sts = HAL_SCS;
		}
		else
		{
			ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_FREE_STATE);
			sts = HAL_AH_DRIVER_ERR;
            log_id_with_one_arg(LOG_ERROR,LOG_AHW,AHW_AL_EXT_MEM_ENABLE_MEMORY_MAP_ERR_INST_ID__eq__u8_,ahw_id);
		}
	}
	else
	{
	}
	os_sem_give(mem_arg->semaphore_hdle);
	return sts;
}

/**
 * @brief  API to reinit the external flash memory
 */
hal_ret_sts ahw_al_ext_mem_reinit(ahw_inst_id_t ahw_id)
{
	hal_ret_sts sts = 0;
	ahw_al_ext_mem_hdl* mem_arg = ahal_get_hdle(ahw_id);
	os_sem_take(mem_arg->semaphore_hdle,AHAL_FLASH_MAX_SEM_TIMEOUT);
	sts = ahiobcsn_check_ste(&mem_arg->ahw_gen_info);
	if(HAL_SCS == sts)
	{
		ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_BUSY_STATE);
		if(HAL_SCS == (hal_ret_sts)ahw_al_wrpr_ext_flash_reinit(mem_arg))
		{
			ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_FREE_STATE);
			sts = HAL_SCS;
		}
		else
		{
			ahiobcsn_updt_ahste(&mem_arg->ahw_gen_info, AH_FREE_STATE);
			sts = HAL_AH_DRIVER_ERR;
		}
	}
	else
	{
	}
	os_sem_give(mem_arg->semaphore_hdle);
	return sts;
}

