/**
 * @file eps_gpio_exp.c
 *
 * @brief This file contains APIs for GPIO expander control that involves in output channel enabling
 *
 * @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 "eps.h"
#include "exo_osal.h"
#include "exo_common.h"
#include "sensor_driver_intf.h"
#include "eps_config.h"
#include "log_eps.h"
#include "logger.h"
#include "exo_gpio.h"

#define GIO_INTF_ENB

#ifndef LINUX_TEMP_PORT
#include "stm32f7xx_hal_ext.h"
#endif
#include "exo_io_al_i2c_common.h"
#include "exo_ahw_al_gpio_exp_common.h"
#include "mcp23008_gpio_expander.h"
#include "pcal6408a_gpio_expander.h"
#include "pca9673_gpio_expander.h"
#include "nvm.h"
#include "exo_pinmap.h"

pca9673_dev eps_en_hdle;
uint8_t ps_ssd_num;
uint8_t edge_ssd_num;
extern ioal_i2c_hdle ioal_hi2c4;
extern ioal_i2c_hdle ioal_hi2c3;
//static eps_err_sts_e eps_reset_func(pca9673_gpio_pin pin);

/**
* @brief This API used for selecting one of two SSD present in edge board. Data will be written in NVM memory
*
*/
void set_ssd_slot_slec(uint8_t* num)
{
	uint8_t ssd_board_slet =num[0];

	if(ssd_board_slet ==1 )
	{
		 ps_ssd_num =num[1];
		nvm_parameter_write(NVM_DATA,NVM_PS_SSD_SLOT_CFG,(uint8_t*)&ps_ssd_num);
	}
	else if(ssd_board_slet ==2)
	{
		 edge_ssd_num=num[1];
		nvm_parameter_write(NVM_DATA,NVM_EDGE_SSD_SLOT_CFG,(uint8_t*)&edge_ssd_num);
	}

}

uint8_t get_ssd_slot_slec(uint8_t board_id)
{
	uint8_t ssd_slot_selec=0;
	if(board_id ==1 )
	{
		nvm_parameter_read(NVM_DATA,NVM_PS_SSD_SLOT_CFG,(uint8_t*)&ssd_slot_selec);
	}
	else if(board_id==2)
	{
		nvm_parameter_read(NVM_DATA,NVM_EDGE_SSD_SLOT_CFG,(uint8_t*)&ssd_slot_selec);
	}
	return ssd_slot_selec;
}

uint8_t edge_ssd_slot_cfg(uint8_t state)
{
	uint8_t ret;
	ret = nvm_parameter_read(NVM_DATA,NVM_EDGE_SSD_SLOT_CFG,(uint8_t*)&edge_ssd_num);

	if(edge_ssd_num==0 || edge_ssd_num >3)
	{
	   ret = 1;
	}
	else
	{
	if(edge_ssd_num & 0x01)   // j4
	{
#ifdef JANUS2
		io_hal_gpio_writepin(EDGE_SSD_EN1,state);
#endif
	}
	if(edge_ssd_num & 0x02)   //j3
	{
#ifdef JANUS2
		io_hal_gpio_writepin(EDGE_SSD_EN2,state);
#endif
	}
	}
  return ret;
}

uint8_t ps_ssd_slot_cfg(uint8_t state)
{
	uint8_t ret=0;
	ret = nvm_parameter_read(NVM_DATA,NVM_PS_SSD_SLOT_CFG,(uint8_t*)&ps_ssd_num);

	if(ps_ssd_num==0 || ps_ssd_num>3 )
	{
	   ret = 1;
	}
	else
	{
	if(ps_ssd_num & 0x01)   // j4
	{
#ifdef JANUS2
		io_hal_gpio_writepin(PS_SSD_EN1,state);
#endif
	}
	}

	if(ps_ssd_num & 0x02)   //j3
	{
#ifdef JANUS2
		io_hal_gpio_writepin(PS_SSD_EN2,state);
#endif
	}
  return ret;
}

int8_t eps_i2c_write(void* intf_hdl, uint16_t slv_addr,uint8_t *data, uint16_t len)
{
	eps_err_sts_e eps_sts=EPS_COM_FAIL;

	if(io_hal_i2c_transmit(intf_hdl,slv_addr,data,2,I2C_TIMEOUT) == HAL_SCS)
	{
		eps_sts=EPS_SUCCESS;
	}

	if(eps_sts != 0)
		DEBUG_CPRINT(("EPS_I2C_WR_ERR\n"));
	log_id(LOG_ERROR, LOG_EPS,EPS_I2C_WR_ERROR);

	return eps_sts;
}

int8_t eps_i2c_read(void* intf_hdl, uint16_t slv_addr,uint8_t *data, uint16_t len)
{
	eps_err_sts_e eps_sts=EPS_COM_FAIL;
	eps_sts= io_hal_i2c_receive(intf_hdl,slv_addr,data,len,I2C_TIMEOUT);

	if(eps_sts != 0)
		DEBUG_CPRINT(("EPS_I2C_RD_ERR\n"));
	log_id(LOG_ERROR, LOG_EPS,EPS_I2C_RD_ERR);

	return eps_sts;
}

void eps_gpio_exp_init()
{
	eps_en_hdle.io_intf_hdle = &ioal_hi2c3;
	eps_en_hdle.write=(pca9673_write_fptr_t)eps_i2c_write;
	eps_en_hdle.read=(pca9673_read_fptr_t)eps_i2c_read;
	eps_en_hdle.slave_addr=EPS_EN_SLV_ADDR;
}

/**
* @brief This API initialize pin value of GPIO expander present in edge, PL server and GPS board.
*
* @param[in]  chnl : output channel selection
*
*/
void eps_dft_init(uint8_t chnl)
{
#ifndef LINUX_TEMP_PORT
    
	switch(chnl)
	{
	case HW_EDGE_BRD:
		edge_server_gpio_init();
	break;

	case HW_PL_SERVER:
		payload_server_gpio_init();
	break;

	case HW_GPS:
		gps_gpio_init();
	break;
	}
#endif        
}

/**
* @brief This API initialize pin value of GPIO expander present in edge, PL server and GPS board.
*
*/
eps_err_sts_e eps_set_chnl_state_gps(uint8_t pin,ch_state state)
{
	eps_err_sts_e eps_sts=EPS_ERR;
#ifndef LINUX_TEMP_PORT
        switch(pin)
	{
	case 0:
	case 3:
		if(state==ON)
			eps_dft_init(HW_GPS);
#ifdef JANUS2
	
		io_hal_gpio_writepin(GPS_ON,state);
		io_hal_gpio_writepin(GPS_HUB_ON,state);
		io_hal_gpio_writepin(GPS_ON_CORE,state);
#endif        
		eps_sts=0;
		//eps_sts = mcp23008_write_pin(&eps_gps_en_hdle,0x82,state);
		break;
	case 1:
#ifdef JANUS2
		io_hal_gpio_writepin(GPS_FDTI_RESETN,state);
#endif        
		eps_sts=0;
		//eps_sts = mcp23008_write_pin(&eps_gps_en_hdle,0xC2,state);
		break;
	}
#else
    eps_sts=0;        
#endif    
    
	return eps_sts;
}
#if 0
static eps_err_sts_e eps_reset_func(pca9673_gpio_pin pin)
{
	eps_err_sts_e eps_sts=EPS_ERR;
	//uint8_t state;
	//uint8_t tx_data2,tx_data3;
	//pca9673_readpin(&eps_en_hdle,pin, &tx_data2);
	eps_sts = pca9673_writepin(&eps_en_hdle,pin,PCA9673_PIN_RESET);
	os_delay(50);
	//pca9673_readpin(&eps_en_hdle,pin, &tx_data3);
	eps_sts = pca9673_writepin(&eps_en_hdle,pin,PCA9673_PIN_SET);
	//eps_sts = pca9673_writepin(&eps_en_hdle,pin,PCA9673_PIN_RESET);
	//os_delay(50);
	//eps_sts = pca9673_writepin(&eps_en_hdle,pin,PCA9673_PIN_RESET);

	return eps_sts;
}

eps_err_sts_e eps_fw_boot_en(eps_slv_en_type boot_mode_en)
{
	eps_err_sts_e eps_sts=EPS_ERR;
	//eps_sts = pca9673_self_test(&eps_en_hdle);
	//uint8_t tx_data,tx_data1;;
	eps_sts = EPS_SUCCESS;
	if(eps_sts == EPS_SUCCESS)
	{
		switch(boot_mode_en)
		{
			case EPS_CONV_BRD_EN:
			{
				//pca9673_readpin(&eps_en_hdle, PCA9673_GPIO_6, &tx_data);
				//eps_sts = pca9673_writepin(&eps_en_hdle, PCA9673_GPIO_6, PCA9673_PIN_RESET); //EN/DIS chnl 11 pull dwn
				eps_sts = pca9673_writepin(&eps_en_hdle, PCA9673_GPIO_6, PCA9673_PIN_SET); //EN/DIS chnl 11 pull dwn
				//pca9673_readpin(&eps_en_hdle, PCA9673_GPIO_7, &tx_data1);
				//eps_sts = pca9673_writepin(&eps_en_hdle, PCA9673_GPIO_7, PCA9673_PIN_RESET); //BOOT0 pull dwn
				eps_sts = pca9673_writepin(&eps_en_hdle, PCA9673_GPIO_7, PCA9673_PIN_SET); //BOOT0 pull dwn
				eps_sts = eps_reset_func(PCA9673_GPIO_11);
			}
			break;
			case EPS_OP_CHNL_EN:
			{
				eps_sts = pca9673_writepin(&eps_en_hdle, PCA9673_GPIO_6, PCA9673_PIN_RESET); //EN/DIS chnl 11 pull dwn
				eps_sts = pca9673_writepin(&eps_en_hdle, PCA9673_GPIO_10, PCA9673_PIN_RESET); //BOOT1 pull dwn
				eps_sts = eps_reset_func(PCA9673_GPIO_12);
			}
			break;
			case EPS_CONV_BRD_DIS:
			{
				//eps_sts = pca9673_writepin(&eps_en_hdle, PCA9673_GPIO_7, PCA9673_PIN_SET); //BOOT0 pull up
				eps_sts = pca9673_writepin(&eps_en_hdle, PCA9673_GPIO_7, PCA9673_PIN_RESET); //BOOT0 pull up
				eps_sts = eps_reset_func(PCA9673_GPIO_11);
				os_delay(1000);
				//eps_sts = pca9673_writepin(&eps_en_hdle, PCA9673_GPIO_6, PCA9673_PIN_SET); //EN/DIS chnl 11 pull up
				eps_sts = pca9673_writepin(&eps_en_hdle, PCA9673_GPIO_6, PCA9673_PIN_RESET); //EN/DIS chnl 11 pull up
			}
			break;
			case EPS_OP_CHNL_DIS:
			{
				eps_sts = pca9673_writepin(&eps_en_hdle, PCA9673_GPIO_10, PCA9673_PIN_SET); //BOOT1 pull up
				eps_sts = eps_reset_func(PCA9673_GPIO_12);
				os_delay(1000);
				eps_sts = pca9673_writepin(&eps_en_hdle, PCA9673_GPIO_6, PCA9673_PIN_SET); //EN/DIS chnl 11 pull up
			}
			break;
			default:
				break;
		}
	}
	else
	{
		eps_sts = EPS_COM_FAIL;
	}
	return eps_sts;

}
#endif



// boot 1 = low
// eps reset
void eps_fw_boot_en_mcu2()
{
	uint8_t data1[2] = {0xff,0xff};
	pca9673_write(&eps_en_hdle,data1, 2);

	// BOOT1 pin = low
	uint8_t data8[2] = {0xBF, 0xFF};
	pca9673_write(&eps_en_hdle,data8, 2);
	os_delay(50);

	// EPS reset
	uint8_t data2[2] = {0xBF, 0xFA};
	pca9673_write(&eps_en_hdle,data2, 2);
	os_delay(50);
	uint8_t data3[2] = {0xBF, 0xFE};
	pca9673_write(&eps_en_hdle,data3, 2);
	//os_delay(50);
}


void eps_fw_boot_en_mcu1()
{
	uint8_t data1[2] = {0xff,0xff};
	pca9673_write(&eps_en_hdle,data1, 2);

	// chnl 11 en
	uint8_t data8[2] = {0xBF, 0xFF};
	pca9673_write(&eps_en_hdle,data8, 2);
	os_delay(50);

	// BOOT1 pin = low&EPS reset
	uint8_t data2[2] = {0x3F, 0xFD};
	pca9673_write(&eps_en_hdle,data2, 2);
	os_delay(50);
	uint8_t data3[2] = {0x3F, 0xFF};
	pca9673_write(&eps_en_hdle,data3, 2);
	//os_delay(50);
}

// boot 1 = high
// eps reset
void eps_fw_boot_dis_mcu2()
{
	os_delay(50);
	uint8_t data5[2] = {0xBF, 0xFB};
	pca9673_write(&eps_en_hdle,data5, 2);
	os_delay(50);
	uint8_t data9[2] = {0xBF, 0xFF};
	pca9673_write(&eps_en_hdle,data9, 2);
	os_delay(90000);
	uint8_t data6[2] = {0xFF, 0xFF};
	pca9673_write(&eps_en_hdle,data6, 2);
}

// boot 1 = high
// eps reset
void eps_fw_boot_dis_mcu1()
{
	os_delay(50);
	uint8_t data5[2] = {0xBF, 0xFD};
	pca9673_write(&eps_en_hdle,data5, 2);
	os_delay(50);
	uint8_t data9[2] = {0xBF, 0xFF};
	pca9673_write(&eps_en_hdle,data9, 2);
	os_delay(90000);
	uint8_t data6[2] = {0xFF, 0xFF};
	pca9673_write(&eps_en_hdle,data6, 2);
}

void eps_fw_boot_en(eps_slv_en_type boot_mode_en)
{
	switch(boot_mode_en)
	{
		case EPS_CONV_BRD_EN:
		{
			eps_fw_boot_en_mcu1();
		}
		break;
		case EPS_OP_CHNL_EN:
		{
			eps_fw_boot_en_mcu2();
		}
		break;
		case EPS_CONV_BRD_DIS:
		{
			eps_fw_boot_dis_mcu1();
		}
		break;
		case EPS_OP_CHNL_DIS:
		{
			eps_fw_boot_dis_mcu2();
		}
		break;
		default:
			break;
		}
}


/**
* @brief This API control MCU IO pin state of SBAND enable pin
*
*/
eps_err_sts_e eps_set_chnl_state_sband(uint8_t pin,ch_state state)
{
	eps_err_sts_e eps_sts=EPS_SUCCESS;
	switch(pin)
	{
	case 0 :
		eps_sts= eps_en_chnl(2,state); //EPS_S_BAND
		break;
	case 1:
#ifdef JANUS2                
		io_hal_gpio_writepin(SBAND_BB_EN,state);
#endif                
		break;
	}
	//	eps_sts= EPS_SUCCESS;
	return eps_sts;
}


void ps_ethernet_enable()
{
#ifdef JANUS2                
	io_hal_gpio_writepin(PS_ETH_DCDC1_ON,1);
	io_hal_gpio_writepin(PS_ETH_DCDC2_ON,1);
	os_delay(300);
	io_hal_gpio_writepin(PS_ETH_RESET,1);
#endif                
}


/**
* @brief This API control MCU IO & GPIO expander pin state and for PL server board
*
*/
eps_err_sts_e eps_set_chnl_state_pl_srvr(uint8_t pin, ch_state state)
{
    
	eps_err_sts_e eps_sts=EPS_ERR;
#ifndef LINUX_TEMP_PORT
	static uint8_t ps_brd_on_sts;
#ifndef HEAT_SINK_DISABLE
	os_itc_msg_handle_t ssd_heat_ipc;
	ssd_heat_ipc.pld.pld_data=HW_PL_SERVER;
	ssd_heat_ipc.src_entity=EPS_TASK;
#endif
        switch(pin)
	{
	case 0:
		if(state==ON)
		eps_dft_init(HW_PL_SERVER);

		//io_hal_gpio_writepin(PS_ETH_DCDC1_ON,state);
		//io_hal_gpio_writepin(PS_ETH_DCDC2_ON,state);
		eps_sts = ps_ssd_slot_cfg(state);
		if(eps_sts!=EPS_SUCCESS && state == 1)
		{
#ifdef JANUS2                
			io_hal_gpio_writepin(PS_SSD_EN1,state);  //j4
#endif
		}
		break;

	case 1:
			//io_hal_gpio_writepin(PS_ETH_RESET,state);
		break;

	case 2:
#ifdef JANUS2
		io_hal_gpio_writepin(PAYLOAD_SSD_CORE_ENABLE,state);
#endif

#ifdef JANUS1
		HAL_GPIO_WritePin(GPIOJ, GPIO_PIN_10, state);
#endif
		eps_sts = EPS_SUCCESS;
		ps_brd_on_sts = state;
		break;
	case 3:  //ssd heat ctrl
		if(ps_brd_on_sts == 1)
		{
#ifndef HEAT_SINK_DISABLE
			if(state)
			{
#ifdef JANUS2
				io_hal_gpio_writepin(PS_SSD_HEAT_ENABLE,IOAL_GPIO_PIN_SET);
				ssd_heat_ipc.Msg_id=HEAT_COIL_ON_EVNT;
#endif
			}
			else
			{
#ifdef JANUS2
				io_hal_gpio_writepin(PS_SSD_HEAT_ENABLE,IOAL_GPIO_PIN_RESET);
#endif
				ssd_heat_ipc.Msg_id=HEAT_COIL_OFF_EVNT;
			}
			os_itc_msg_send(&ssd_heat_ipc,OBC_SENSOR_HDLR,1000);
#endif
		}
		else
		{
		}
		break;
	}
#endif
	eps_sts= EPS_SUCCESS;
	return eps_sts;
}

/**
* @brief This API control GPIO expander pin state present in edge board
*
*/
eps_err_sts_e eps_set_chnl_state_edge(uint8_t pin, ch_state state)
{

#ifndef LINUX_TEMP_PORT
	static uint8_t brd_on_sts,sts;
	os_itc_msg_handle_t ssd_heat_ipc;
	ssd_heat_ipc.pld.pld_data=HW_EDGE_BRD;
	ssd_heat_ipc.src_entity=EPS_TASK;
#endif
	eps_err_sts_e eps_sts=EPS_ERR;
	switch(pin)
	{
	case 0:
		eps_sts= eps_en_chnl(9,state);
		break;
#ifndef LINUX_TEMP_PORT
	case 1:   //ssd selection
		if(state==ON)
		eps_dft_init(HW_EDGE_BRD);
#ifdef JANUS2
		io_hal_gpio_writepin(EDGE_UART_SW_SEL_1,state);
		io_hal_gpio_writepin(EDGE_UART_SW_SEL_2,state);
#endif
        sts = edge_ssd_slot_cfg(state);
		if(sts==1 && state == 1)
		{
#ifdef JANUS2
			io_hal_gpio_writepin(EDGE_SSD_EN1,state);
#endif
		}
		eps_sts=0;
		break;

	case 2:
#ifdef JANUS2
		io_hal_gpio_writepin(EDGE_EN_5V_EDGE,state);
		io_hal_gpio_writepin(EDGE_EN_1P8V_EDGE,state);
#endif
		eps_sts=0;
		break;

	case 3:
#ifdef JANUS2
		io_hal_gpio_writepin(EDGE_EN_3P3V_EDGE_EN,state);
#endif
		eps_sts=0;
		brd_on_sts = state;
		break;

	case 4:     //SSD heat control
		if(brd_on_sts == 1)
		{
			if(state)
			{
#ifdef JANUS2
				io_hal_gpio_writepin(EDGE_SSD_HEAT_ENABLE,IOAL_GPIO_PIN_SET);
#endif
				ssd_heat_ipc.Msg_id=HEAT_COIL_ON_EVNT;
			}
			else
			{
#ifdef JANUS2
				io_hal_gpio_writepin(EDGE_SSD_HEAT_ENABLE,IOAL_GPIO_PIN_SET);
#endif
				ssd_heat_ipc.Msg_id=HEAT_COIL_OFF_EVNT;
			}
			os_itc_msg_send(&ssd_heat_ipc,OBC_SENSOR_HDLR,1000);
			eps_sts= EPS_SUCCESS;
		}
		else
			eps_sts= EPS_ERR;
		break;
#endif
        }
        eps_sts= EPS_SUCCESS;
	return eps_sts;
}


eps_err_sts_e eps_get_chnl_state_gps()
{
	eps_err_sts_e eps_sts;
	uint8_t data;
	uint8_t reg_addr = 0x09;
	eps_sts = io_hal_i2c_transmit(&ioal_hi2c4,GPS_EN_SLV_ADDR,&reg_addr,1,I2C_TIMEOUT);
	if(eps_sts == EPS_SUCCESS)
	{
		eps_sts = io_hal_i2c_receive(&ioal_hi2c4,GPS_EN_SLV_ADDR,&data,1,I2C_TIMEOUT);
		if(eps_sts == EPS_SUCCESS)
		{
			if((data&0xC2) == 0xC2)
			{
				eps_sts = EPS_SUCCESS;
			}
			else
			{
				eps_sts =  EPS_ERR;
			}
		}
		else
		{
			 eps_sts = EPS_COM_FAIL;
		}
	}
	else
	{
		eps_sts = EPS_COM_FAIL;
	}
	return eps_sts;
}



eps_err_sts_e eps_get_chnl_state_pl_srvr()
{
//uint8_t pin_state=0;
#if (defined(JANUS2) || defined(GEN2))
	if(io_hal_gpio_readpin(PAYLOAD_SSD_CORE_ENABLE) == IOAL_GPIO_PIN_SET)
#endif
	{
	//	pin_state = io_hal_gpio_readpin(PS_SSD_EN2);
	//	pin_state |= io_hal_gpio_readpin(PS_SSD_EN1);
	//	if(pin_state)
	//	{
#if 0
			if(io_hal_gpio_readpin(PS_ETH_DCDC1_ON) == IOAL_GPIO_PIN_SET)
			{
				if(io_hal_gpio_readpin(PS_ETH_DCDC2_ON) == IOAL_GPIO_PIN_SET)
				{
					//pin_state |= IOAL_GPIO_PIN_SET;
					return EPS_SUCCESS;

				}
			}
#endif
			return EPS_SUCCESS;
		//}
	}
		return EPS_ERR;
}


