/*
 * Init.c
 *
 *  Created on: Jan 23, 2014
 *      Author: rob
 *  Modified starting Mar 2018 by Thom Maughan (TM)
 *    Added MFET/MCAP board support
 */

#include "system.h"
#include "init.h"
#include "microsd.h"
#include "uartstdio.h"	// User local version with larger RX buffer
#include "sleep.h"
#include "pwm.h"
#include "board_util.h"
#include "i2c0.h"
#include "i2c_fram.h"
#include "i2c_bme280.h"
#include "i2c_ina260.h"
#include "i2c_ds3231.h"
#include "i2c_pcal64xx.h"
#include "sample.h"
#include "apf.h"
#include "apf_init.h"
#include "apf_util.h"
#include "main.h"
#include "serial_cmd_fxn.h"
#include "init_defaults.h"

int microSD_flg;

extern void AUX1_init(void);
extern void optode_init(void);
//extern void microCat_init(void);
extern void ctd_init(void);
extern void led_init(void);

extern void profile2_toggle(int target);
extern void ADS1248_gpio_init(void);

extern void init_TM4C123GH6PGE_144pin(void);
extern void init_TM4C123GH6PM_64pin(void);
extern void init_TM4C1290NCPDT_128pin(void);

extern int gBootError;      //defd in main.c

uint32_t g_ui32SysClock;

#if BOARD_MSC == 3
#define EEPROM_APFCfgGet() eepromAPFCfgGet()
#else
#define EEPROM_APFCfgGet()
#endif

static int systemClockInit(void)
{
    //set the system clock...50MHZ
    // THOM TODO, experiment with slower clock settings to balance compute vs power consumption
    //MAP_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN);
    //g_ui32SysClock = MAP_SysCtlClockGet();

    //g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_240), 120000000);

#if BOARD_MFET >= 1 || BOARD_SEAPHOX == 1 || BOARD_MPHOX == 1
    //set the system clock...50MHZ
    MAP_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN);
    g_ui32SysClock = MAP_SysCtlClockGet();

    init_TM4C123GH6PGE_144pin();

    // PK0 = en_xcvr  (output)
    // PE0 = CTS (input)
    // PE1 = RTS (output)
#endif

#if BOARD_NANOFET >= 1
    //set the system clock...50MHZ
    MAP_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN);
    g_ui32SysClock = MAP_SysCtlClockGet();

    init_TM4C123GH6PM_64pin();
#endif

#if BOARD_MSC == 3
    #ifdef PART_TM4C123GH6PM
        //set the system clock...50MHZ
        MAP_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN);
        g_ui32SysClock = MAP_SysCtlClockGet();

        init_TM4C123GH6PM_64pin();
    #endif
    #ifdef PART_TM4C1290NCPDT
        //Note had to comment out a line in drivers/hibernate.c with 2.14.178 TivaWare


        //g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_240), 4000000);     //27mA @ 15v, wake
        //g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_240), 5000000);   //27mA @ 15v, wake
        //g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_240), 60000000);    //33mA @15v, wake
        //g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_160), 80000000);      //34mA @15v, wake
        g_ui32SysClock = MAP_SysCtlClockFreqSet((SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN | SYSCTL_USE_PLL | SYSCTL_CFG_VCO_240), 120000000);   //39mA @ 15v, wake

        init_TM4C1290NCPDT_128pin();
    #endif
#endif

    // code from Thomz personal project, draft implementation
#if BOARD_TESTFIT >= 1
    MAP_SysCtlClockSet(SYSCTL_SYSDIV_4 | SYSCTL_USE_PLL | SYSCTL_XTAL_16MHZ | SYSCTL_OSC_MAIN);
    g_ui32SysClock = MAP_SysCtlClockGet();

    init_TM4C123GH6PZ_100pin();
#endif

#if BOARD_NANOFET >= 1
        ROM_SysCtlPeripheralEnable(SYSCTL_PERIPH_EEPROM0);
#endif
    return OK;
}

static char initErr_eepromSysData_Read_Fail = 0;
static char initErr_eepromSysData_Verify_Fail = 0;
static char initErr_framInitFail = 0;
static char initErr_FRAM_not_initialized = 0;
static char initErr_framSysSamp_Verify_Fail = 0;

#if BOARD_MSC == 3
static char initErr_eepromApfCfg_Read_Fail = 0;
static char initErr_eepromApfCfg_Verify_Fail = 0;
static char initErr_framApfVars_Verify_Fail = 0;
#endif

static int eepromInit(void)
{
    uint32_t retVal = MAP_EEPROMInit();

    if (retVal != EEPROM_INIT_OK)
        return retVal;

    /*** Retrieve sys_data variables, check for unintialized FRAM ***/
    retVal = eeprom_sys_data_retrieve();         // defd in system.c, moved down in init until after i2c init (in fram)
    if (retVal == 1) {
        initErr_eepromSysData_Read_Fail = 0;
    }
    else  {
        initErr_eepromSysData_Read_Fail = 1;        //fault happened while reading eeprom
    }

    retVal = eeprom_sys_data_verify();
    if (retVal == 1) {
        initErr_eepromSysData_Verify_Fail = 0;
    }
    else {
        initErr_eepromSysData_Verify_Fail = 1;
    }
    return EEPROM_INIT_OK;
}

static int framInit(void)
{
    int retVal;

    // THOM TODO  If fram does not init, the board is BAD, cannot be used without retooling volatile persistent data support
    retVal = fram_init();        // was calling i2c0_init(TRUE);     // fast mode 400kpbs, defd in i2c_fram.c
    if (retVal == 1)
    {
        initErr_framInitFail = 0;

        retVal = fram_sys_samp_retrieve();    // sys_samp contains signature indicating init'd  sys_samp.initNum

        if (retVal == 1)
        {
            retVal = fram_sys_samp_verify();        //defd in init_defaults
            if(retVal == 1)
            {
                // All is well - both Verified - and initialized.
                initErr_framSysSamp_Verify_Fail = 0;
                initErr_FRAM_not_initialized = 0;
            }
            else if(retVal == -1)       // fram is FFFFFFFF. verify ok but still needs init. Not really an ERROR! deployment issue maybe?
            {
                //FRAM is uninitialized, sys_samp.initNum != 0x55AA55AA
                initErr_FRAM_not_initialized = 1;
                initErr_framSysSamp_Verify_Fail = 0;
            }
            else
            {
                /// fram is corrupted in some way - not the signature, and not FFFFFFFF - this is an ERROR!
                initErr_framSysSamp_Verify_Fail = 1;
                initErr_FRAM_not_initialized = 0;
            }
        }
        else
        {
            initErr_framSysSamp_Verify_Fail = 1;
            initErr_FRAM_not_initialized = 1;
        }
    }
    else
    {
        initErr_framInitFail = 1;
        return ERROR;
    }

#ifdef MOVEDTOMMS_ISUS_TAKESAMPLE
    retVal = fram_apfArray_retrieve();
    if(retVal < 0)
    {
        return retVal;
    }
#endif
    return OK;
}
#if BOARD_MSC == 3

void eepromAPFCfgGet(void)
{
    int retVal = eeprom_apf_cfg_retrieve();
    if (retVal == 1)
    {
        initErr_eepromApfCfg_Read_Fail = 0;
    }
    else
    {
        initErr_eepromApfCfg_Read_Fail = 1;        //fault happened while reading eeprom
    }
    retVal = eeprom_apf_cfg_verify();
    if (retVal == 1)
    {
        initErr_eepromApfCfg_Verify_Fail = 0;
    }
    else
    {
        initErr_eepromApfCfg_Verify_Fail = 1;
    }

    fram_apf_vars_retrieve();
    retVal = fram_apf_vars_verify();
    if(retVal == 1)
    {
        // All is well - both Verified - and initialized.
        initErr_framApfVars_Verify_Fail = 0;
        initErr_FRAM_not_initialized = 0;

    }
    else if(retVal == -1)       // fram is FFFFFFFF. verify ok but still needs init. Not really an ERROR! deployment issue maybe?
    {
        //FRAM is uninitialized, sys_samp.initNum != 0x55AA55AA
        initErr_FRAM_not_initialized++;
        initErr_framApfVars_Verify_Fail = 0;
    }
    else
    {
        /// fram is corrupted in some way - not the signature, and not FFFFFFFF - this is an ERROR!
        initErr_framApfVars_Verify_Fail = 1;
    }
}
#endif

void showInitErrors(void)
{
#if BOARD_MSC == 3
    char ebuf[256];

    if(initErr_eepromSysData_Read_Fail)
    {
        sprintf(ebuf, "Fatal Error: eeprom_sys_data_retrieve failed, EEPROM");
        LOGERRORMSG(ebuf, (int)EEPROM_FAIL, apf_eeprom.sys_message_enable);
        //APFTODO might want to power down on this error and try booting again
    }
    if(initErr_eepromSysData_Verify_Fail)
    {
        sprintf(ebuf, "Error: sys_data_verify failed");
        LOGERRORMSG(ebuf, (int)EEPROM_FAIL, apf_eeprom.sys_message_enable);
    }
    if(initErr_FRAM_not_initialized)
    {
        sprintf(ebuf, "sys_samp.initNum != 0x55AA55AA");
        LOGERRORMSG(ebuf, (int)FRAM_FAIL, apf_eeprom.sys_message_enable);
    }
    if(initErr_framInitFail)
    {
        sprintf(ebuf, "Fatal Error: FRAM init failed");
        LOGERRORMSG(ebuf, (int)FRAM_FAIL, apf_eeprom.sys_message_enable);

        //APFTODO, might want to power down
    }
    if(initErr_framSysSamp_Verify_Fail)
    {
        sprintf(ebuf, "fram_sys_samp_verify failed");
        LOGERRORMSG(ebuf, (int)FRAM_FAIL, apf_eeprom.sys_message_enable);
    }
    if(initErr_framApfVars_Verify_Fail)
    {
        sprintf(ebuf, "fram_apf_vars_verify failed");
        LOGERRORMSG(ebuf, (int)FRAM_FAIL, apf_eeprom.sys_message_enable);
    }
    if (initErr_eepromApfCfg_Read_Fail)
    {
        sprintf(ebuf, "eepromApfCfg_Read_Fail failed");
        LOGERRORMSG(ebuf, (int)FRAM_FAIL, apf_eeprom.sys_message_enable);
    }
    if (initErr_eepromApfCfg_Verify_Fail)
    {
        sprintf(ebuf, "eepromApfCfg_verify failed");
        LOGERRORMSG(ebuf, (int)FRAM_FAIL, apf_eeprom.sys_message_enable);
    }

#else
    if (initErr_framInitFail            || initErr_eepromSysData_Read_Fail || initErr_eepromSysData_Verify_Fail ||
        initErr_FRAM_not_initialized    || initErr_framSysSamp_Verify_Fail )
    {
        uprintf("Init Fail: EEr:%d v:%d, FRAM:%d nI:%d v:%d",
                initErr_eepromSysData_Read_Fail,
                initErr_eepromSysData_Verify_Fail,
                initErr_framInitFail,
                initErr_FRAM_not_initialized,
                initErr_framSysSamp_Verify_Fail);
        prn_nl();
    }
#endif
}

void sysBaudRateInit(void)
{
    // default is 115200 for all platforms except MSC3 and this is set in the init() above - if sys_data.baud_rate is not 115200, then re(set) here
    // APF has 9600 as default, changed to reset baud rate
    if(sys_data.baud_rate == 4800L)
    {
        UARTStdioConfig(0, 4800, 16000000);       // 4800 bits per second
    }
    else if(sys_data.baud_rate == 9600L)
    {
        UARTStdioConfig(0, 9600, 16000000);       // 9600 bits per second
    }
    else if(sys_data.baud_rate == 19200L)
    {
        UARTStdioConfig(0, 19200, 16000000);       // 19200 bits per second
    }
    else if(sys_data.baud_rate == 38400L)
    {
        UARTStdioConfig(0, 38400, 16000000);       // 38400 bits per second
    }
    else if(sys_data.baud_rate == 57600L)
    {
        UARTStdioConfig(0, 57600, 16000000);       // 57600 bits per second
    }
    else if(sys_data.baud_rate == 115200L)
    {
        UARTStdioConfig(0, 115200, 16000000);       // 115200 bits per second
    }
    else if(sys_data.baud_rate == 230400L)
    {
        UARTStdioConfig(0, 230400, 16000000);       // 230400 bits per second
    }
    else if(sys_data.baud_rate == 460800L)
    {
        UARTStdioConfig(0, 460800, 16000000);       // 460800 bits per second
    }
    else
    {
        UARTStdioConfig(0, DEFAULT_BAUDRATE, 16000000);       // 9600 bits per second
    }
}

#if BOARD_NANOFET == 1
int microSDInit(void) { return 1; }
#else
int microSDInit(void)
{
    int flg = 0;
    if(sys_data.app_cfg[APPCFG_MICROSD_ENABLE] != 0)        //if config for MICROSD is on
    {
        if( uSD_present() )
        {
            //TODO: we could retry a few times, maybe with or without a card power cycle
            int retVal = init_uSDCard();
            if(retVal <= 0)
            {
                uprintf("Error: MicroSD init failed"); prn_nl();
#if BOARD_MSC == 3
                apf_fram.microSD_errCnt++;      //init failed, uSD card is useless
                fram_apf_vars_store();
#endif // #if BOARD_MSC == 3
            }
            else
            {
                flg = 1;
            }
        }
        else
        {
            uprintf("MicroSD card is not detected!");
            prn_nl();
        }
    }
    else
    {
        if( uSD_present() )
            flg = close_uSDCard();;
    }
    return flg;
}
#endif

/**********************************************************************************
 * Init()
 * Initialize pins and ports upon reset or wake from hybernation. Set unused pins low.
 **********************************************************************************/
int init(void)
{
    int retVal;
    int initErr_wdMonVal;
#if BOARD_NANOFET >= 1 || BOARD_MSC == 3
    char ebuf[256];
#endif

    // Enable lazy stacking for interrupt handlers.  This allows floating-point
    // instructions to be used within interrupt handlers, but at the expense of2
    // extra stack usage.
    MAP_FPULazyStackingEnable();

    //enable floats
    MAP_FPUEnable();

    systemClockInit();

    retVal = eepromInit();
    if( retVal != EEPROM_INIT_OK)
    {
        uprintf("EEPROM Initialization error.\r\n");
        return 0;
    }

    // JLD-TGM - setting this here to fix the issue that its not set anywhere else.  Should it be saved in EEPROM?
    sys_data.terminal_mode = DEFAULT_TERMINAL_MODE;

    /*** Configure SysTick for a 100Hz interrupt.  The FatFs driver and 10 ms timer share a 10 ms tick ***/
    /*** Configure SysTick for a 1000Hz / 1msec interrupt.  General purpose timing while maintaining the
     * The FatFs driver and 10 ms timer share a 10 ms tick ***/
    // void SysTickHandler(void) is in system.c
    //MAP_SysTickPeriodSet(MAP_SysCtlClockGet() / 100);     // 10msec tick (100 Hz)
    MAP_SysTickPeriodSet(GetSysClkHz() / 1000);      // 1msec tick (1K Hz)
    MAP_SysTickEnable();
    MAP_SysTickIntEnable();
    MAP_IntMasterEnable();

#if BOARD_MPHOX >= 1
    pwm_init();
#endif

    // if reset happened because of watchdog, log it and announce it.
    initErr_wdMonVal = wdog_readMon();      //read the flip flop that is set when WDOG goes active
    retVal = wdog_init();
    if( retVal <= 0 )
    {
        //uprintf("\r\nError: wd init failed\r\n");
        // TODO: what now?
        // Although wdog_init init's the external watchdog, so perhaps wdog_init should be made void since it can't fail
        // return 0;
    }

    // if i2c does not have a device, the fram_probe hangs (on MFET v1)
    //i2c0_init(FALSE);   // FALSE = 100khz, TRUE = fast mode 400kpbs, defd in i2c_fram.c   THOM TODO figure out if i2c periphs can run at 400khz
    i2c0_init(TRUE);        // TRUE = 400Khz, requires 1290 rB hardware

    framInit();

    EEPROM_APFCfgGet();

    showInitErrors();

    microSD_flg = microSDInit();

    sysBaudRateInit();

    // handle error logging from prior to microsd setup
    if(initErr_wdMonVal)
    {
        wdog_resetMon();  // reset the wdog flip flop

#if BOARD_MSC == 3
        apf_fram.sys_wdog_ctr++;     //sys_por_ctr is the watchdog

        fram_apf_vars_store();

        sprintf(ebuf, "Error: External Watchdog reset detected, likely due to crash");
        LOGERRORMSG(ebuf, (int)WATCHDOG_RESET, apf_eeprom.sys_message_enable);
#endif // #if BOARD_MSC == 3
    }

#if BOARD_MSC == 3
    // ------ init_msc --------
    // call init_msc after microSD init
    retVal = apf_init_msc();   //defd in apf_init.c  init_apf_global_vars();         // apf_init.c, calls fram_apf_vars_retrieve();
    if( retVal <= 0 )
    {
        sprintf(ebuf, "Error: apf_init_msc failed");
        LOGERRORMSG(ebuf, (int)INIT_ERROR, apf_eeprom.sys_message_enable);
        return 0;
    }
#endif // #if BOARD_MSC == 3

    gBootError = 0;
    if(initErr_framSysSamp_Verify_Fail || initErr_FRAM_not_initialized || initErr_eepromSysData_Verify_Fail)
    {
        uprintf("gBootError is set cuz %d, %d, %d", initErr_framSysSamp_Verify_Fail, initErr_FRAM_not_initialized, initErr_eepromSysData_Verify_Fail ); prn_nl();
        apf_min_defaults_to_run();
        gBootError = 1;
    }

    //Due to the size of apfArray, it is loaded only on demand by an isus related command, see serial_cmd_fxn.c fram_apfArray_refresh()
#if BOARD_NANOFET >= 1 || BOARD_MSC == 3
    retVal = bme280_init();
    if( retVal <= 0 )
    {
        // bme280 failed, but we will keep going
        // uprintf("\r\nWarning: bme280 init failed\r\n");
        sprintf(ebuf, "Warning: bme280 init failed");
        LOGERRORMSG(ebuf, (int)INIT_ERROR, apf_eeprom.sys_message_enable);
    }
#endif

#if BOARD_MSC == 3
    retVal = ina260_init(INA260_LAMP_I2C_ADDR);
    if( retVal <= 0 )
    {
        // ina260 INA260_LAMP_I2C_ADDR failed, but we will keep going
        // uprintf("\r\nWarning: ina260 INA260_LAMP_I2C_ADDR init failed\r\n");
        sprintf(ebuf, "Warning: INA260_LAMP_I2C_ADDR init failed");
        LOGERRORMSG(ebuf, (int)INIT_ERROR, apf_eeprom.sys_message_enable);
    }

    retVal = ina260_init(INA260_SYS_I2C_ADDR);
    if( retVal <= 0 )
    {
        // ina260 INA260_SYS_I2C_ADDR failed, but we will keep going
        // uprintf("\r\nWarning: ina260 INA260_SYS_I2C_ADDR init failed\r\n");
        sprintf(ebuf, "Warning: INA260_SYS_I2C_ADDR init failed");
        LOGERRORMSG(ebuf, (int)INIT_ERROR, apf_eeprom.sys_message_enable);
    }

    retVal = pcal6416_init();
    if( retVal <= 0 )
    {
        // pcal6416 failed, but we will keep going
        // uprintf("\r\nWarning: pcal6416 init failed\r\n");
        sprintf(ebuf, "Error: pcal6416 init failed");
        LOGERRORMSG(ebuf, (int)INIT_ERROR, apf_eeprom.sys_message_enable);
    }
#endif

    pump_var_init();

#warning "TODO, handle fault recovery in the event of FRAM or EEPROM read failure"

#ifdef NOCODE
    // JLD!!!  - you indicated that maybe these should power down.
    // Recommend here to return 0 and let the main() that called this handle as it
    // already seems to do something indicating fatal error
    if (initErr_eepromSysData_Read_Fail || initErr_framInitFail)
        return -1;
#endif
    return 1;
}
