/*
 * 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;

/**********************************************************************************
 * Init()
 * Initialize pins and ports upon reset or wake from hybernation. Set unused pins low.
 **********************************************************************************/
int init(void)
{

    int initErr_wdMonVal;
    int initErr_framInitFail = 0;
    int initErr_eepromSysData_Read_Fail = 0;
    int initErr_eepromSysData_Verify_Fail = 0;
    int initErr_FRAM_not_initialized = 0;
    int initErr_framSysSamp_Verify_Fail = 0;
    int initErr_framApfVars_Verify_Fail = 0;
    int initErr_eepromApfCfg_Read_Fail = 0;
    int initErr_eepromApfCfg_Verify_Fail = 0;


    int retVal;
    char ebuf[256];



    // 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();

    //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  || BOARD_MSC == 2
    //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

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


    /*** 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  || BOARD_MSC == 2
    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

    // 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;
    }
    else
    {
        initErr_framInitFail = 1;
    }

    /*** 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;
    }

    fram_sys_samp_retrieve();    // sys_samp contains signature indicating init'd  sys_samp.initNum
    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;
    }

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

    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;
    }

    // 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
    }

    // Find out what woke us and decide what to do
    //sleep_handler();  Move to main after init() 25 May 2021

    microSD_flg = 0;
#if BOARD_MFET == 2 || BOARD_MPHOX == 1  || BOARD_MSC == 2 || BOARD_MSC == 3
    // Initialize MicroSD card and FatFs (relies on eeprom_sys_data_retrieve)
    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
            retVal = init_uSDCard();
            if(retVal <= 0)
            {
                //uprintf("\r\nError: MicroSD init failed\r\n");
                apf_fram.microSD_errCnt++;      //init failed, uSD card is useless
                fram_apf_vars_store();
                microSD_flg = 0;
            }
            else
            {
                microSD_flg = 1;
            }
        }
        else
        {
            //uprintf("\r\nError: MicroSD card is not detected\r\n");
            apf_fram.microSD_errCnt++;      //card detect failed, uSD card is useless
            fram_apf_vars_store();
            microSD_flg = 0;
        }
    }
    else
    {
        //uprintf("MicroSD card is Disabled, to change it: 3 -- Config, A -- App Config....\r\n");
    }
#endif

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

        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);

    }

    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);
    }

    // ------ 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;
    }

    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
#ifdef REMOVEDFROMINIT    //removed as the osc is stopped error pops up, ds3231 is not needed with APF11
    if(0)   //ds3231 is not being used with APF11
    {
        bool ds3231_osc_stopped = false;
        retVal = ds3231_init(false, false, &ds3231_osc_stopped);
        if( retVal <= 0 )
        {
            // ds3231 failed, but we will keep going
            // uprintf("\r\nWarning: ds3231 init failed\r\n");
            sprintf(ebuf, "Warning: ds3231 init failed");
            LogErrorMsg(ebuf, (int)INIT_ERROR, apf_eeprom.sys_message_enable);
        }
        else
        {
             if(ds3231_osc_stopped)
             {
                // uprintf("\r\nWarning: ds3231 osc stopped\r\n");
                 sprintf(ebuf, "Warning: ds3231 osc stopped");
                 LogErrorMsg(ebuf, (int)INIT_ERROR, apf_eeprom.sys_message_enable);
             }
        }
    }
#endif
#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);
    }
#endif


#if BOARD_MSC == 3
    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;
}


