/**
 * @file main.c
 *
 * @brief Basic example for a PROFINET RT device running on AM335x devices.
 * Requires a full ICSS system (both PRUs) and two external phys connected to
 * ICSS MII/MDIO interfaces (e.g. ICEv2)
 *
*/
/*
 * Copyright (c) 2015, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 **/

/*! \mainpage PROFINET RT Device Application
 * \version 1.0.1
 *
 * \section intro_sec Introduction
 *
 * This is an example PROFINET RT device application based on IA-SDK, Interniche SNMP
 * stack and Molex PROFINET stack. This example may be used for evaluation and test of
 * the TI PROFINET RT device solution. It may also serve as a starting project for
 * customer applications.
 *
 * \section docs_sec More Documentation
 * Further documentation for the required components:
 * \subsection RT_driver ICSS API
 * IA-SDK: /sdk/protocols/profinet_slave/Docs/ICSS_API/html/index.html
 * \subsection pn_stack PROFINET RT Stack
 * Molex: tbd - some material/source here: /sdk/protocols/profinet_slave/Docs
 * \subsection snmp_stack Interniche SNMP Stack
 * Interniche: tbd
 * \section install_sec Installation
 *
 * Build any configuration of the examples and use resulting binaries on
 * ICEv2 boards. See the GSD folder for required GSD file. Testing requires a PROFINET RT capable PLC.
 * How to use a PLC is not part of this documentation.
 *
 */

#include <stdio.h>
#include <xdc/std.h>
#include <xdc/cfg/global.h>
#include <xdc/runtime/Error.h>
#include <xdc/runtime/System.h>
#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Semaphore.h>
#include <ti/ndk/inc/stkmain.h>

#include "soc_AM335x.h"
#include "types.h"
#include "hw/hw_types.h"
#include "osdrv_version.h"
#include "console_utils.h"
#include "osdrv_pruss.h"
#include "osdrv_emac.h"
#include "osdrv_utils.h"
#include "osdrv_edma.h"

#include "soc_control.h"

#include "icss_emacSwitch.h"
#include "icss_emacFwInit.h"
#include "icss_emacLearning.h"
#include "icss_emacStatistics.h"
#include "appl_cnfg.h"
#include "iPnOs.h"
#include "iPNDrv.h"
#include "firmware_version.h"
#include "snmp_ndk_interface.h"
#include "osdrv_ndkdeviceconfig.h"
#include <icss_emacStormControl.h>
#include "os.h"
#include "board_support.h"
#include "board_phy.h"
#include "pruicss.h"
#include "tiswitch_pruss_intc_mapping.h"
#include "icss_emacDrv.h"

#include "types_pn.h"                                           // Molex types!
#include "system.h"
#include "board.h"
#include "device.h"
#include "prcm.h"
#include "platform_pn.h"

/*
 *  global data declaration
 */
/** application version */
#define APP_VERSION "2.0.0.0"

boardId_t board_Type = 0;


/** @brief rotary switch value on ICEv2 */
Uint8 nodeAddr;                             /**< rotary switch value on ICEv2 */
unsigned int alarmCounter = 0;
// local functions
void getMacid(char *buf_str);

/** \brief PRU-ICSS LLD handle */
PRUICSS_Handle handle;
/** \brief ICSSEmac LLD handle */
ICSSEMAC_Handle appEmacHandle;
uint8_t lclMac[6];

/*
 * Molex stack references
 */
/** @brief 'main' function of Molex stack (extern) */
extern void main_pn(void);
/** @brief SNMP stack initialization (mib_impl.c) */
extern void initSNMP(void);
/** @brief stack input data buffer */
extern APP_BYTE  gbyInput_Data[MAX_DATA_FRAME_IO];
/** @brief stack output data buffer */
extern APP_BYTE  gbyOutputData[MAX_DATA_FRAME_IO];
/** @brief stack AR status info */
extern APP_BYTE  gbyOutputAPDU[MAX_ARD_PER_DEVICE];
/** @brief stack current data */
extern APP_BYTE  gbyCurrentData;
/**@brief stack user ALARM flag */
extern volatile unsigned char appAlarmActive;

extern int EmacInit(STKEVENT_Handle hEvent);
/** @brief stack debug buffer */
char byOsBuff[500] = {0};

/***********************************************************************/
/* Macros                                        */
/***********************************************************************/

/***********************************************************************/
/* local functions declaration                                         */
/***********************************************************************/
/**
 * @brief programs the OLED with two lines of text
 *
 * @param line1 pointer for first line string
 * @param line2 pointer for second line string
 * @param opt 1: enable vertical scrolling

 * use with care as this may run for a long time (several 10ms)
 */

void updateLED(const char *line1, const char *line2, Uint8 opt);

/**
 * \brief Wrapper API to manage GPIO Tri Color LEDs
 *
 * \param gpioLeds - LEDs to be triggered Eg: TRI_COLOR0_RED_BIT
 * \param value - 0/1 - On/Off
 * \return none
 */
void  LEDDriveTriColorLeds(uint32_t gpioLeds, uint8_t value);

/***********************************************************************/
/* function definitions                                                */
/***********************************************************************/

/**
 * \brief main task to initialize all software components and run the stack
 *
 * \param a0 not used
 * \param a1 not used
 * \callgraph
 */
Void taskPruss(UArg a0, UArg a1)
{
    CONSOLEUtilsPrintf("\nTI Industrial SDK Version - ");
    CONSOLEUtilsPrintf(IND_SDK_VERSION);

    CONSOLEUtilsPrintf("\n\rDevice name \t: ");
    CONSOLEUtilsPrintf(SOCGetSocFamilyName());

    char t_str[8];
    CONSOLEUtilsPrintf("\n\rChip Revision \t: ");
    CONSOLEUtilsPrintf(Board_getChipRevision());

    sprintf(t_str, "%d MHz", Board_getArmClockRate());
    CONSOLEUtilsPrintf("\n\rARM Clock rate \t: ");
    CONSOLEUtilsPrintf(t_str);

    CONSOLEUtilsPrintf("\n\rSYS/BIOS Profinet RT+MRP Sample application running on ");
    CONSOLEUtilsPrintf(BOARDGetBoardName());

    #ifndef AM43XX_FAMILY_BUILD

    if(BOARD_ICEV2 == board_Type)
    {
        char *lcd_str1 = IND_SDK_VERSION;
        char lcd_str2[] = {"PROFINET RT+MRP Demo"};

        Board_setLCDString((uint8_t *)lcd_str1, 0);
        Board_setLCDString((uint8_t *)lcd_str2, 1);
        Board_setLCDScroll(0);
        Board_setLCDScroll(1);
    }

    #endif
    Board_phyMDIXFixInit((((ICSSEMAC_HwAttrs *)
                           appEmacHandle->hwAttrs)->emacBaseAddrCfg)->prussMiiMdioRegs);
    /* Enables CPM/PPM copy in Profinet stack via EDMA */
    pEdmaObj = (edmaDrvObj_t *)malloc(sizeof(edmaDrvObj_t));
    pEdmaObj->pMemCpyObj = (edmaMemCpyObj_t *)malloc(sizeof(edmaMemCpyObj_t));
    pEdmaObj->pMemCpyObj->instance = 0;
    pEdmaObj->pMemCpyObj->dummyParam = 4;
    pEdmaObj->pMemCpyObj->transferParam = 2;
    pEdmaObj->pMemCpyObj->pFnCallBack = NULL;
    pEdmaObj->pMemCpyObj->queueNum = 0;
    EDMAMemCpyInit(pEdmaObj);

    if(PN_initDrv(appEmacHandle) == 0)
    {
        char logText[255];
        sprintf(logText, "\nPN RT app: v%s\r\n", APP_VERSION);
        CONSOLEUtilsPrintf(logText);
        sprintf(logText, "Firmware : v%d.%d.%d-%d\r\n", FIRMWARE_VERSION_MAJOR,
                FIRMWARE_VERSION_MINOR,
                FIRMWARE_VERSION_BUILD, FIRMWARE_RELEASE_TYPE);
        CONSOLEUtilsPrintf(logText);

        initSNMP();                             // enable SNMP module

        PRUICSS_pruEnable(handle, 1);
        PRUICSS_pruEnable(handle, 0);
        #ifdef FACTORY_TESTING
        iRtcEnableIsr();        // usually done inside stack...
        Semaphore_post(
            switchReady);           // this will enable all tasks waiting for a link

        while(1)
        {
            Task_sleep(100);
        }

        #else
        Semaphore_post(
            switchReady);           // this will enable all tasks waiting for a link
        //Log_info0("Starting stack");
        main_pn();                              // this is a while(1) until error!
        #endif
    }

    DbgPrintf(DBG_ERROR, "exit taskPruss()\n");   // we get here on stack errors!
}

/**
 * @brief task to get input data and/or generate test data on cyclic basis
 *
 * @details
 * Digital Input postion
 * Position 1 - Generates alarms
 *
 * Based on Rotary Position(on ICEv2) the application has the following behavior \n
 * Position 0
 * It will display app name in LCD Scrolling display top line. Will display Chassis MAC address in bottom line
 * Position 1
 * Displaying ‘DEBUG’ in display bottom line. No other visible functionality
 * Position 2
 * Displaying ‘MOVE’ in display bottom line. No other visible functionality
 * Position 3
 * Displaying ‘COUNT’ in display bottom line. No other visible functionality
 * Position 4
 * Displaying ‘LOOPBACK’  in display bottom line. 8 DO LED’s are toggled continuously indicating input change. This can be used to change inputs from the slave side.
 * Position 5,6,7 and 8
 * Displaying ‘CONST’  in display bottom line. No other visible functionality
 * Position 9
 * Displaying ‘ALARM’ in LCD display bottom line. But the functionality is not there (Obvious as per the code)
 *
 *
 * @param arg0 not used
 * @param arg1 not used
 */
void appInData(UArg arg0, UArg arg1)
{
    Uint16 inJ9;
    uint8_t digIn = 0;
    Uint8 oldNode = 0xff;
    char strMode[20];

    Semaphore_pend(switchReady, BIOS_WAIT_FOREVER);
    Semaphore_post(switchReady);
    appAlarmActive = 0;

    do
    {
        Board_getDigInput(&digIn);                      // trigger reading of J9 input
        inJ9 = digIn & 0xFF;
        //inJ9 = ReadHVS2() & 0xFF;                     // get the data
        //if(BOARD_ICEVer2 == board_Type)
        //{
        //if(RotarySwitchRead(&temp, 1) == 0)
        //nodeAddr = (Uint8)temp & 0xF;
        //} else

        nodeAddr = inJ9;

        if(0 == (inJ9 & 0x8))                          // check on alarm jumper removed
        {
            // we only want a single alarm
            if(appAlarmActive ==
                    2)                   // reset the ALARM flag if ack by stack
            {
                Task_sleep(500);
                appAlarmActive = 0;
            }
        }

        // normal application processing of inputs
        switch(nodeAddr)
        {
            case 0:                                        // MAC ID display only
                getMacid(strMode);
                break;

            case 1:                                        // enable debug output on OLED
                strcpy(strMode, "DEBUG");
                gbyCurrentData = gbyCurrentData << 1;

                if(gbyCurrentData == 0)
                {
                    gbyCurrentData = 1;
                }

                break;

            case 2:
                strcpy(strMode, "MOVE");
                gbyCurrentData = gbyCurrentData << 1;       // moving light

                if(gbyCurrentData == 0)
                {
                    gbyCurrentData = 1;
                }

                break;

            case 3:
                strcpy(strMode, "COUNT");
                gbyCurrentData++;                           // counting
                break;

            case 4:
                strcpy(strMode, "LOOPBACK");
                gbyCurrentData++;                           // loopback(Digital output LED's will be toggled continuously)
                break;

            case 8:                                          // create user ALARM for PN tester. In ICEv2.1 board, HVS Pin 4 should be used
                strcpy(strMode, "ALARM");

                if(appAlarmActive == 0)
                {
                    appAlarmActive = 1;
                    alarmCounter++;
                }

                break;

            default:                                         // no or other jumper combinations
                strcpy(strMode, "CONST");
                gbyCurrentData = 0x55;                       // fixed test data
                break;
        }

        if(oldNode != nodeAddr)                          // mode update
        {
            updateLED("PROFINET RT+MRP", strMode, 1);
            oldNode = nodeAddr;
        }

        Task_sleep(
            500);                                // Make sure we create single alarms...
    }
    while(1);
}


/**
 * @brief task to set DO's(8 LEDs) output data
 *
 * @param arg0 not used
 * @param arg1 not used
 */
void appOutData(UArg arg0, UArg arg1)
{
    Semaphore_pend(switchReady, BIOS_WAIT_FOREVER);
    Semaphore_post(switchReady);
    Uint8 prevState = gbyInput_Data[10];

    do
    {
        if(nodeAddr == 4)   // local loopback mode
        {
            Board_setDigOutput(gbyCurrentData);      // single output byte internal data
        }

        else                       // communication data output
        {
            if(gbyInput_Data[10] != prevState)
            {
                Board_setDigOutput(gbyInput_Data[10]);
            }
        }

        prevState = gbyInput_Data[10];
        Task_sleep(2);         // update every 1ms
    }
    while(1);
}


APP_BOOL gBlinkActive = APP_FALSE;

/**
 * @brief alive task - indicates running host processor by blinkind LED
 * also counts states and other conditions signaling
 *
 * @param arg0 not used
 * @param arg1 not used
 */
void taskLedBlink(UArg arg0, UArg arg1)
{
    int CurrentLedsValue = 0;
    int Blink = 0;
    int i = 0;
    volatile int connectCount =
        0;  // make sure it gets written back to DRAM for monitoring via DAP
    int connectStat = 0;            // 2 - drop occurred!

    Semaphore_pend(switchReady, BIOS_WAIT_FOREVER);   // wait for init
    Semaphore_post(switchReady);

    do
    {
        if(APP_TRUE == gBlinkActive)
        {
            gBlinkActive = APP_FALSE;                   //This implements a DCP blink!

            for(i = 0; i < 3; i++)
            {
                //LEDDriveTriColorLeds(LED5G);
                LEDDriveTriColorLeds(BOARD_TRICOLOR0_GREEN, 1);
                Task_sleep(500);
                //LEDDriveTriColorLeds(NOLED);
                LEDDriveTriColorLeds(BOARD_TRICOLOR0_GREEN, 0);
                Board_setTriColorLED(BOARD_TRICOLOR0_GREEN, 0);
                Task_sleep(500);
            }
        }

        // set status LED for valid input
        switch(gbyOutputAPDU[0])
        {
            case 0xFF:
                {
                    /* Never connected */
                    if((CurrentLedsValue & BOARD_TRICOLOR1_YELLOW) ==
                            BOARD_TRICOLOR1_YELLOW)      // LED6B Yes!
                    {
                        CurrentLedsValue = CurrentLedsValue & ~BOARD_TRICOLOR1_YELLOW;   // Off LED6B
                        CurrentLedsValue = CurrentLedsValue | BOARD_TRICOLOR1_GREEN;     // On LED6G
                    }

                    else
                    {

                        if((CurrentLedsValue & BOARD_TRICOLOR1_RED) ==
                                BOARD_TRICOLOR1_RED)  // LED6R Yes!
                        {
                            CurrentLedsValue = CurrentLedsValue & ~BOARD_TRICOLOR1_RED;    //Off LED6R
                            CurrentLedsValue = CurrentLedsValue | BOARD_TRICOLOR1_YELLOW;  // On LED6B
                        }

                        else
                        {
                            CurrentLedsValue = CurrentLedsValue & ~BOARD_TRICOLOR1_GREEN;  // Off LED6G
                            CurrentLedsValue = CurrentLedsValue | BOARD_TRICOLOR1_RED; // On LED6R
                        }
                    }

                    if(connectStat == 1)
                    {
                        connectStat = 0;
                    }

                    break;
                }

            case 0x35:
                {
                    /* connected */
                    CurrentLedsValue = CurrentLedsValue & ~(BOARD_TRICOLOR1_YELLOW |
                                                            BOARD_TRICOLOR1_RED) ;      // Off LED6B/6R
                    CurrentLedsValue = CurrentLedsValue |
                                       BOARD_TRICOLOR1_GREEN;                // On LED6G

                    if(connectStat == 0)
                    {
                        connectStat = 1;
                        connectCount++;

                        if(1 == nodeAddr)
                        {
                            char line2[20];
                            sprintf(line2, "Cnt: %d", connectCount);
                            updateLED("Connect ON", line2, 0);
                        }
                    }

                    break;
                }

            default:/*Not connected */
                CurrentLedsValue = CurrentLedsValue & ~(BOARD_TRICOLOR1_GREEN |
                                                        BOARD_TRICOLOR1_RED);        //Off LED6G/6R
                CurrentLedsValue = CurrentLedsValue |
                                   BOARD_TRICOLOR1_YELLOW;               // On LED6B

                if(connectStat == 1)
                {
                    connectStat = 0;

                    if(1 == nodeAddr)
                    {
                        char line2[20];
                        sprintf(line2, "Cnt: %d", connectCount);
                        updateLED("Connect OFF", line2, 0);
                    }
                }
        }

        /* small blink on current value only for LED6 */
        //LEDDriveTriColorLeds(LED6B | LED6R | LED6G ,0);
        LEDDriveTriColorLeds(BOARD_TRICOLOR1_YELLOW | BOARD_TRICOLOR1_RED |
                             BOARD_TRICOLOR1_GREEN , 0);

        OsWait_ms(200);

        if((CurrentLedsValue & BOARD_TRICOLOR0_GREEN) ==
                BOARD_TRICOLOR0_GREEN){/*Blink management*/        // LED5G Yes!
            Blink ++ ;
            CurrentLedsValue = CurrentLedsValue &
                               ~BOARD_TRICOLOR0_GREEN; /* LED OFF*/        // LED5G Off
        }

        //      if(connectStat == 2)
        //          CurrentLedsValue = CurrentLedsValue | TRI_COLOR1_RED_BIT;       // indicate a connect drop - sticky
        LEDDriveTriColorLeds(CurrentLedsValue , 1);
        OsWait_ms(200);

    }
    while(1);
}

/**
 * \brief the application entry point - initializes low level hardware, creates tasks and starts SYSBIOS
 *
 */
int main()
{
    SDKMMUInit(applMmuEntries);
    BOARDInit(NULL);
    PN_RTC_disableISR();
    Task_Params taskParams;

    board_Type = BOARDGetId();


    handle = (PRUICSS_Handle)malloc(sizeof(PRUICSS_Config));
    handle->object = (PRUICSS_V1_Object *)malloc(sizeof(PRUICSS_V1_Object));
    handle->hwAttrs = (PRUICSS_HwAttrs *)malloc(sizeof(PRUICSS_HwAttrs));
    appEmacHandle = (ICSSEMAC_Handle)malloc(sizeof(ICSSEMAC_Config));

    ICSSEMAC_InitConfig *switchEmacCfg;
    switchEmacCfg = (ICSSEMAC_InitConfig *)malloc(sizeof(
                        ICSSEMAC_InitConfig));
    switchEmacCfg->phyAddr[0] = BOARDGetDeviceDataModId(DEVICE_ID_ENET_PHY_MII,
                                0);
    switchEmacCfg->phyAddr[1] = BOARDGetDeviceDataModId(DEVICE_ID_ENET_PHY_MII,
                                1);

    switchEmacCfg->portMask = ICSS_EMAC_MODE_SWITCH;
    switchEmacCfg->ethPrioQueue = ICSS_EMAC_QUEUE3;
    switchEmacCfg->halfDuplexEnable = 0;
    switchEmacCfg->enableIntrPacing = enablePacing;
    switchEmacCfg->intrPacingMode = INTR_PACING_MODE2;
    switchEmacCfg->pacingThreshold = 100;

    switchEmacCfg->learningEn = 1;

    SOCCtrlGetPortMacAddr(1, lclMac);
    switchEmacCfg->macId = lclMac;
    #ifdef AM335X_FAMILY_BUILD
    ((PRUICSS_V1_Object *)handle->object)->instance = 0;
    Board_pinMuxConfig(iceV2Mux);
    board_init(BOARD_LCD_DISPLAY);
    ICSSEmacDRVInit(appEmacHandle, 0);   // ICSS_M instance 0

    switchEmacCfg->rxIntNum = 20;
    switchEmacCfg->linkIntNum = 26;
    #else
    Board_pinMuxConfig(am437xIdkMux);
    ((PRUICSS_V1_Object *)handle->object)->instance = 1;
    ICSSEmacDRVInit(appEmacHandle, 1);   // ICSS_M instance 1

    switchEmacCfg->rxIntNum = 52;
    switchEmacCfg->linkIntNum = 58;
    #endif

    board_init(BOARD_TRICOLOR0_GREEN | BOARD_TRICOLOR1_YELLOW |
               BOARD_TRICOLOR1_GREEN | BOARD_TRICOLOR1_RED | BOARD_TRICOLOR0_RED
               | BOARD_LED_DIGOUT | BOARD_HVS_DIGIN | BOARD_FLASH_MEMORY);

    #ifdef AM335X_FAMILY_BUILD
    UTILsInitCpswIcssPorts();
    #endif
    PRUSSDRVInit(handle);   /* ICSS_M instance 0 */

    ((ICSSEMAC_Object *)appEmacHandle->object)->pruIcssHandle = handle;
    ((ICSSEMAC_Object *)appEmacHandle->object)->emacInitcfg = switchEmacCfg;

    PRUICSS_IntcInitData pruss_intc_initdata = PRUSS_INTC_INITDATA;
    ICSS_EmacInit(appEmacHandle, &pruss_intc_initdata, ICSS_EMAC_MODE_SWITCH);
    Board_phyReset();


    // BIOS task setup
    Task_Params_init(&taskParams);
    taskParams.stackSize = 0x600;
    taskParams.priority = 1;
    Task_create(taskLedBlink, &taskParams, NULL);
    Task_Params_init(&taskParams);
    taskParams.priority = 2;        // want stack prio higher than LED I/O
    taskParams.stackSize = 8192;
    taskParams.instance->name = "SwitchTask";
    Task_create(taskPruss, &taskParams, NULL);

    Task_Params_init(&taskParams);
    taskParams.priority = 1;
    taskParams.instance->name = "appInData";
    taskParams.stackSize = 2048;
    Task_create(appInData, &taskParams, NULL);

    Task_Params_init(&taskParams);
    taskParams.priority = 1;
    taskParams.instance->name = "appOutData";
    taskParams.stackSize = 2048;
    Task_create(appOutData, &taskParams, NULL);

    // all PN driver tasks moved to iPnOs.c
    if(PN_initOs(appEmacHandle) < 0)
    {
        System_printf("Can't initialize PN tasks\n");
        exit(-1);           // fatal
    }

    if(OSDRV_addNetifEntry((NIMUInitFn)&EmacInit, appEmacHandle) == 0)
    {
        BIOS_exit(0);
    }

    CONSOLEUtilsInit();
    CONSOLEUtilsSetType(CONSOLE_UTILS_TYPE_UART);
    #ifdef AM43XX_FAMILY_BUILD
    /* Clock source selection */
    SOCCtrlTimerClkSrcSelect(PN_TIMER_ID,
                             SOC_CTRL_DMTIMER_CLK_SRC_M_OSC_24M);
    PRCMModuleEnable(CHIPDB_MOD_ID_DMTIMER, PN_TIMER_ID , FALSE);
    #else
    /* Clock source selection */
    SOCCtrlTimerClkSrcSelect(PN_TIMER_ID + 2,
                             SOC_CTRL_DMTIMER_CLK_SRC_M_OSC_24M);
    #endif

    PRCMModuleEnable(CHIPDB_MOD_ID_DMTIMER, PN_TIMER_ID + 2, FALSE);
    #ifdef FACTORY_TESTING
    initFactoryTest();
    #endif // FACTORY_TESTING

    BIOS_start();
    return 0;
}

/*
 * utility code from here on....
 */

/**
 * @brief programs the OLED with two lines of text
 *
 * @param line1 pointer for first line string
 * @param line2 pointer for second line string
 * @param opt 1: enable vertical scrolling

 * use with care as this may run for a long time (several 10ms)
 */
void updateLED(const char *line1, const char *line2, Uint8 opt)
{
    #ifndef AM43XX_FAMILY_BUILD

    if(BOARD_ICEV2 == board_Type)
    {
        Board_setLCDScroll(0);
        Board_clearLCDString();             // Clear Screen
        Board_setLCDString((uint8_t *)line1, 0);
        Board_setLCDString((uint8_t *)line2, 1);

        if(1 == opt)        // enable scrolling
        {
            Board_setLCDScroll(1);
        }
    }   // tbd other boards...

    #endif
    return;
}
/**
 * \brief returns PROFINET interface MAC as a string
 *
 * \retval buf_str return buffer for MAC string (min length x bytes!)
 */
void getMacid(char *buf_str)
{
    int i;
    Uint8 bMacAddr[6];
    NIMU_IF_REQ if_req;
    if_req.name[0] = 0;
    if_req.index   = 1;

    NIMUIoctl(NIMU_GET_DEVICE_MAC, &if_req, &bMacAddr, sizeof(bMacAddr));

    char *buf_ptr = buf_str;

    for(i = 0; i < 5; i++)
    {
        if(i != 5)
        {
            buf_ptr += sprintf(buf_ptr, "%02x:", (char)bMacAddr[i]);
        }
    }

    buf_ptr += sprintf(buf_ptr, "%02x", (char)bMacAddr[5]);
}

void  LEDDriveTriColorLeds(uint32_t gpioLeds, uint8_t value)
{
    if(gpioLeds & BOARD_TRICOLOR0_RED)
    {
        Board_setTriColorLED(BOARD_TRICOLOR0_RED, value);
    }

    if(gpioLeds & BOARD_TRICOLOR0_GREEN)
    {
        Board_setTriColorLED(BOARD_TRICOLOR0_GREEN, value);
    }

    /*else if(gpioLeds & TRI_COLOR0_YELLOW_BIT)     // Not used
        GPIOSetLed(&gpioLedYellow0, value);*/
    if(gpioLeds & BOARD_TRICOLOR1_RED)
    {
        Board_setTriColorLED(BOARD_TRICOLOR1_RED, value);
    }

    if(gpioLeds & BOARD_TRICOLOR1_GREEN)
    {
        Board_setTriColorLED(BOARD_TRICOLOR1_GREEN, value);
    }

    if(gpioLeds & BOARD_TRICOLOR1_YELLOW)
    {
        Board_setTriColorLED(BOARD_TRICOLOR1_YELLOW, value);
    }
}
