/*
 * discrete_signals_drv.cpp
 *
 */


#include <stdint.h>

#include "rtlog.h"

static volatile uint32_t bus_release;


#define FW_BASE_ADDRESS		(0x50000000)

#define FW_ADDRESS		(FW_BASE_ADDRESS+0x00080004)
#define FW_IN_ADDRESS	(FW_BASE_ADDRESS+0x00080008)
enum eif_gpio_in_bits_t
{
	eif_gpio_in_wow_3sec=(1<<25),
	eif_gpio_in_ac_fail=(1<<7),
};

#ifdef __MINGW32__

struct eif_host_gpio_sim_t
{
	unsigned int r[4];
};

volatile eif_host_gpio_sim_t eif_host_gpio_sim_;

static void pl_write(unsigned int value)
{
	eif_host_gpio_sim_.r[0]=value;
}

#if 0
static unsigned int pl_version()
{
	return 0x3500;
}
#endif

static unsigned int pl_gpio_read()
{
	return eif_host_gpio_sim_.r[1];
}

static int bsp_is_evm()
{
	return true;
}

#else

#include "bsp_platform.h"

/*
static void pl_lock()
{
}

static void pl_unlock()
{
}
*/


static unsigned int pl_gpio_read()
{
	if (bsp_is_evm())
		return 0;

	//volatile uint32_t* reg_cfg_1 = reinterpret_cast<volatile uint32_t*>(FW_ADDRESS);
	volatile uint32_t* reg_cfg_2 = reinterpret_cast<volatile uint32_t*>(FW_IN_ADDRESS);;

    //(*reg_cfg_1)|=(1<<2);
	uint32_t value = (*reg_cfg_2);
	//(*reg_cfg_1)&=~(1<<2);

	return value;
}

/*
static void pl_gpio_write(unsigned int value)
{
	if (bsp_is_evm())
		return 0;
	volatile uint32_t& reg=*reinterpret_cast<volatile uint32_t*>(FW_GPIO_OUTPUT_ADDRESS);

	reg=vlaue;
}
*/

static void pl_write(unsigned int value)
{
	if (bsp_is_evm())
		return;
	volatile uint32_t& reg=*reinterpret_cast<volatile uint32_t*>(FW_ADDRESS);

	reg=value;
}

#define FW_VER_ADDRESS		(FW_BASE_ADDRESS+0x80000)

#if 0
static unsigned int pl_version()
{
	if (bsp_is_evm())
		return 0x3500;
	unsigned int r=*reinterpret_cast<volatile uint32_t*>(FW_VER_ADDRESS);
	return r & 0x0FFFF;
}

#endif

#endif

#include "eif_discrete_signals.h"

#include "bsp_interrupts.h"

struct eif_gpio_t
{
	enum {
		out_fail_latch_reset=0,
		out_latch_out_enable=1,
		out_latch_in_enable=2,
		out_dac_enable=4,
		out_eht_phy_1_dis=5,
		out_eht_phy_2_dis=6,
		out_not_battle_short=11,
		out_gpo=28
	};

	uint32_t w_cache;
	uint32_t r_cache;
	uint32_t d_cache;

	uint32_t w_gpio_cache;

	bool fast_powerup_latched;
	int fpwp_override;
	int fpwd_invert;

	int acf_override;
	int acf_invert;

	int battle_short_ena;

	unsigned int battle_short_cmd;
	unsigned int battle_short_applied;

	unsigned int dac_enabled;

	unsigned int no_latch_release;

	unsigned int r_latch()
	{
		unsigned int tmp=r_cache|(1<<out_latch_in_enable);
		pl_write(tmp);
		unsigned int v=pl_gpio_read();
		pl_write(r_cache);
		d_cache=v;
		return v;
	}

	void w_latch(unsigned int value)
	{
		r_cache=value | (1<<out_latch_out_enable);
		pl_write(r_cache);
		if (!no_latch_release)
		{
			for(int i=0; i<2; ++i)
				++bus_release;
			r_cache=value;
			pl_write(r_cache);
		}
	}

	void w_update(unsigned int bit, bool enabled)
	{
		if (enabled)
			w_set(bit);
		else
			w_reset(bit);
	}

	void w_set(unsigned int bit)
	{
		unsigned int mask=1<<bit;
		unsigned int sts=bsp_interrupts_disable();

		w_cache|=mask;
		w_latch(w_cache);

		bsp_interrupts_restore(sts);
	}

	void w_reset(unsigned int bit)
	{
		unsigned int mask=1<<bit;
		unsigned int sts=bsp_interrupts_disable();
		w_cache&=~mask;
		w_latch(w_cache);

		bsp_interrupts_restore(sts);
	}

	static void __monitor_invokable_emgOn() __attribute__((used, noinline));
	static void __monitor_invokable_emgOff() __attribute__((used, noinline));
};

static eif_gpio_t autoMonitorable_eifGpx;

void eif_gpio_t::__monitor_invokable_emgOn()
{
	autoMonitorable_eifGpx.battle_short_applied=true;
	autoMonitorable_eifGpx.w_update(eif_gpio_t::out_not_battle_short, false); //
}

void eif_gpio_t::__monitor_invokable_emgOff()
{
	autoMonitorable_eifGpx.battle_short_applied=false;
	autoMonitorable_eifGpx.w_update(eif_gpio_t::out_not_battle_short, true); //
}

GrifoEifDiscreteSignal::GrifoEifDiscreteSignal()
{
	autoMonitorable_eifGpx.battle_short_ena=1;
	autoMonitorable_eifGpx.__monitor_invokable_emgOff();

	autoMonitorable_eifGpx.w_cache=1<<eif_gpio_t::out_eht_phy_2_dis;
	setVideoDacEnable(false);
	//setBattleShortEnable(false);
	setGPOut(false);

	latchFastPowerup(); //autoMonitorable_eifGpx.fast_powerup_latched=(pl_gpio_read() &  eif_gpio_in_wow_3sec) ? true : false;
}

GrifoEifDiscreteSignal& GrifoEifDiscreteSignal::instance()
{
	static GrifoEifDiscreteSignal theInstance;
	return theInstance;
}

bool GrifoEifDiscreteSignal::hasNewVideoIp() const
{
	return true; //pl_version()>=0x3500;
}

void GrifoEifDiscreteSignal::setVideoDacEnable(bool enabled)
{
	autoMonitorable_eifGpx.w_update(eif_gpio_t::out_dac_enable, enabled);
}

void GrifoEifDiscreteSignal::setBattleShortEnable(bool emergency)
{
	autoMonitorable_eifGpx.battle_short_cmd=emergency;

	bool actual=(autoMonitorable_eifGpx.w_cache & (1<<eif_gpio_t::out_not_battle_short))==0;
	if (actual==emergency)
	{
		return;
	}

	if (autoMonitorable_eifGpx.battle_short_ena)
	{
		if (emergency)
			autoMonitorable_eifGpx.__monitor_invokable_emgOn();
		else
			autoMonitorable_eifGpx.__monitor_invokable_emgOff();
	}
}

bool GrifoEifDiscreteSignal::battleShort() const
{
	return (autoMonitorable_eifGpx.w_cache & (1<<eif_gpio_t::out_not_battle_short))==0;
}

void GrifoEifDiscreteSignal::setGPOut(bool high)
{
	autoMonitorable_eifGpx.w_update(eif_gpio_t::out_gpo, high); //
}

bool GrifoEifDiscreteSignal::fastPowerup() const
{
	return (autoMonitorable_eifGpx.r_latch() &  eif_gpio_in_wow_3sec) ? true : false;
}

bool GrifoEifDiscreteSignal::fastPowerupLatched() const
{
	bool fast=autoMonitorable_eifGpx.fast_powerup_latched;
	if (autoMonitorable_eifGpx.fpwd_invert)
		fast=!fast;

	if (autoMonitorable_eifGpx.fpwp_override)
	{
		fast=autoMonitorable_eifGpx.fpwp_override>0 ? true : false;
	}
	return fast; //autoMonitorable_eifGpx.fast_powerup_latched;
}

bool GrifoEifDiscreteSignal::latchFastPowerup()
{
	autoMonitorable_eifGpx.fast_powerup_latched=fastPowerup();
	return fastPowerupLatched(); //autoMonitorable_eifGpx.fast_powerup_latched;
}

bool GrifoEifDiscreteSignal::acFail() const
{
	if (autoMonitorable_eifGpx.acf_override)
		return autoMonitorable_eifGpx.acf_override>0 ? true : false;
	else
	{
		bool ac_fail=(autoMonitorable_eifGpx.r_latch() &  eif_gpio_in_ac_fail) ? false : true;
		if (autoMonitorable_eifGpx.acf_invert)
			ac_fail=!ac_fail;
		return ac_fail;
	}
}

void GrifoEifDiscreteSignal::fastPowerupOverride(int fast_powerup)
{
	autoMonitorable_eifGpx.fpwp_override=fast_powerup;
}

void GrifoEifDiscreteSignal::setFastPowerupLogic(int inverted)
{
	autoMonitorable_eifGpx.fpwd_invert=inverted;
}

void GrifoEifDiscreteSignal::acFailOverride(int fail_active)
{
	autoMonitorable_eifGpx.acf_override=fail_active;
}

void GrifoEifDiscreteSignal::setAcFailLogic(int inverted)
{
	autoMonitorable_eifGpx.acf_invert=inverted;
}


#include "bsp_srio_mini_driver.h"

void GrifoEifDiscreteSignal::setFcrRdy(bool active)
{
	if (bsp_is_evm())
		return;
	static unsigned int tmp_buffer[256/sizeof(unsigned int)];
    tmp_buffer[0]=active ? 2 : 0;
    bsp_srio_mini_drv_memory_write_nowait(0x13, 0x44000100, tmp_buffer, 256, bsp_srio_mini_drv_no_doorbell, false);
    rtlog_printf("DISC", rtlog_breaking_news|rtlog_report, "FCR_RDY = %d", active);
}

void GrifoEifDiscreteSignal::setPpsOn(bool active)
{
	if (bsp_is_evm())
		return;
    static unsigned int tmp_buffer[256/sizeof(unsigned int)];
    tmp_buffer[0]=active ? 1 : 0; //PPS_ON
    bsp_srio_mini_drv_memory_write_nowait(0x16, 0x44000100, tmp_buffer, 256, bsp_srio_mini_drv_no_doorbell, false);
    //rtlog_printf("INIT", rtlog_breaking_news|rtlog_warning, "WARNING: PPS_ON");

}
