/*
 * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
 * 
 * 
 *  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.
 *
*/

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//#include <debug.h>

#include <xdc/std.h>
#include <xdc/runtime/Error.h>
#include <xdc/runtime/System.h>
#include <xdc/runtime/knl/Cache.h>
#include <xdc/runtime/Timestamp.h>
#include <xdc/runtime/Types.h>

#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/hal/Hwi.h>
#include <ti/sysbios/hal/Core.h>
#include <ti/sysbios/family/arm/a8/Mmu.h>

#include <ti/ndk/inc/netmain.h>
#include <ti/ndk/inc/stkmain.h>

#include <ti/drv/pruss/soc/pruicss_v1.h>

#include <ti/drv/icss_emac/icss_emacDrv.h>
#include <ti/drv/icss_emac/firmware/am437x/icss_emac_pru0_bin.h>
#include <ti/drv/icss_emac/firmware/am437x/icss_emac_pru1_bin.h>

#include <ti/drv/icss_emac/test/src/tiemac_pruss_intc_mapping.h>

#include <ti/transport/ndk/nimu_icss/nimu_icssEth.h>
#include <ti/transport/ndk/nimu_icss/example/src/osdrv_ndkdeviceconfig.h>


#include <ti/board/board.h>

/* TI-RTOS Header files */
#include <ti/drv/spi/soc/SPI_soc.h>
#include <ti/drv/spi/SPI.h>
#include <ti/drv/spi/soc/SPI_v1.h>
#include "ti/drv/spi/test/src/SPI_log.h"
#include "ti/drv/spi/test/src/SPI_board.h"


#include <ti/drv/gpio/GPIO.h>
#include <ti/drv/gpio/soc/GPIO_v1.h>
#include <ti/csl/src/ip/gpio/V1/gpio_v2.h>

#include <ti/starterware/include/error.h>
#include <ti/starterware/include/board.h>
#include <ti/starterware/include/device.h>
#include <ti/starterware/include/utils/console_utils.h>
#include <ti/starterware/include/utils/console_utils_defs.h>
#include <ti/starterware/include/hw/am437x.h>
#include <ti/starterware/include/hw/hw_control_am43xx.h>


/**********************************************************************
 ************************** Global Variables **************************
 **********************************************************************/

/* Reference Serializer data */
extern char serializerData[1];

/* Buffer containing the known data that needs to be written to flash */
uint8_t txBuf[1U];

/* Buffer containing the received data */
uint8_t rxBuf[1U] = {0xFFU};

/* Flag to check transfer completion in callback mode */
uint32_t txCompleteCallbackFlag = 1U;

/* Transfer length */
uint32_t transferLength;

char *VerStr = "\nTCP/IP by Flo\n";

inline void clearLink0ISR(ICSS_EmacHandle icssemacHandle);
inline void clearLink1ISR(ICSS_EmacHandle icssemacHandle);

static unsigned char prussIcssInstance = 2;

#define DELAY_VALUE       (1000U)


/* ON Board LED pins which are connected to GPIO pins. */
typedef enum GPIO_PIN {
    GPIO_PIN_LD_PIN = 0U,
    GPIO_PIN_COUNT
}GPIO_PIN;
/* GPIO pin value definitions */
#define GPIO_PIN_VAL_LOW     (0U)
#define GPIO_PIN_VAL_HIGH    (1U)

#define GPIO_TEST

#ifdef GPIO_TEST
typedef struct gpioPinCfg
{
    uint32_t dir;
    /**< pin direction . */
    uint32_t debounceEnable;
    /**< Enable/disable debounce feature. */
    uint32_t debounceTime;
    /**< deboune timing control. */
    uint32_t intrEnable;
    /**< Interrupt enable/disable. */
    uint32_t intrType;
    /**< Interrupt type edge or level . */
    uint32_t intrLine;
    /**< Interrupt line number. */
    uint32_t wakeEnable;
    /**< Wake up enable/disable control. */
    uint32_t wakeLine;
    /**< Wakeup line number. */
}gpioPinCfg_t;


/** @brief Structure holding the GPIO pin object data structure. */
typedef struct gpioPinObj
{
    uint32_t     pinNum;
    /**< GPIO pin number. */
    uint32_t     instNum;
    /**< GPIO instance number. */
    uint32_t     instAddr;
    /**< GPIO instance address. */
    gpioPinCfg_t pinCfg;
    /**< GPIO pin configuration structure.*/
}gpioPinObj_t;


gpioPinObj_t GPIOPINOBJ_DEFAULT =
{
    0U,  /* pin number */
    0U,  /* Instance number */
    0U,  /* Instance Address */
    {
        GPIO_DIR_OUTPUT, /* dir */
        FALSE,  /* debounceEnable */
        0U,     /* debounceTime */
        FALSE,  /* intrEnable */
        0U,     /* intrType */
        0U,     /* intrLine */
        FALSE,  /* wakeEnable */
        0U,     /* wakeLine */
    } /* gpioAppPinCfg_t */
};

void Board_GPIOPinConfig(uint32_t baseAddr,
                             uint32_t pinNum,
                             gpioPinCfg_t *pGpioPinConfig);

void Board_initGPIO(uint32_t deviceID, uint32_t instance, gpioPinObj_t *pGpioObj)
{
    uint32_t  gpioInstNum;
    uint32_t  pinNum;
    uint32_t  instAddr;

    PRCMModuleEnable(CHIPDB_MOD_ID_GPIO,pGpioObj->instNum, 0u);

    /* Enabling the GPIO module. */
    GPIOModuleEnable(pGpioObj->instAddr);

    /* GPIO pin characteristics configuration */
    Board_GPIOPinConfig(pGpioObj->instAddr, pGpioObj->pinNum, &pGpioObj->pinCfg);
}

void Board_GPIOPinConfig(uint32_t baseAddr,
                             uint32_t pinNum,
                             gpioPinCfg_t *pGpioPinConfig)
{
    /* Configure Pin as an Input/Output Pin. */
	GPIODirModeSet(baseAddr, pinNum, pGpioPinConfig->dir);

    /* Enable Debouncing feature for the GPIO Pin. */
	GPIODebounceFuncControl(baseAddr, pinNum, pGpioPinConfig->debounceEnable);

    if(TRUE == pGpioPinConfig->debounceEnable)
    {
        /*
         * Configure the Debouncing Time for all the input pins of
         * the selected GPIO instance.
         */
    	GPIODebounceTimeConfig(baseAddr, pGpioPinConfig->debounceTime);
    }

    if(FALSE == pGpioPinConfig->intrEnable)
    {
        /* Enable interrupt for the specified GPIO Pin. */
    	GPIOPinIntDisable(baseAddr, pGpioPinConfig->intrLine, pinNum);
    }
    else
    {
        /*
         * Configure interrupt generation on detection of a logic HIGH or
         * LOW levels or a rising or a falling edge.
         */
    	GPIOIntTypeSet(baseAddr, pinNum, pGpioPinConfig->intrType);

        /* Enable interrupt for the specified GPIO Pin. */
    	GPIOPinIntEnable(baseAddr, pGpioPinConfig->intrLine, pinNum);
    }

    if(FALSE == pGpioPinConfig->wakeEnable)
    {
        /* Enable wakeup generation for GPIO Module */
    	GPIOWakeupGlobalDisable(baseAddr);

        /* Configure input GPIO Pin to wakeup */
    	GPIOPinIntWakeUpDisable(baseAddr, pGpioPinConfig->intrLine, pinNum);
    }
    else
    {
        /* Configure input GPIO Pin to wakeup */
    	GPIOPinIntWakeUpEnable(baseAddr, pGpioPinConfig->intrLine, pinNum);
    }
}

static uint64_t DelayOSAL_getTimestampResolution(void)
{
    static uint64_t resolution_ps;
    Types_FreqHz freq;

    if (resolution_ps)
        return resolution_ps;

    Timestamp_getFreq(&freq);
    /* neglect .hi as chances of it being non-zero doesn't exist for AM437x */
    resolution_ps = 1000000000000 / freq.lo;

    return resolution_ps;
}
static unsigned DelayOSAL_getTimestampTimePrint(void)
{
    unsigned cnt1, cnt2;

    cnt1 = Timestamp_get32();
    cnt2 = Timestamp_get32();

    return (cnt2 - cnt1);
}

void DelayOSAL_microSeconds(uint64_t usDuration)
{
    Bits32 now, end;
    unsigned count = usDuration * 1000000 / DelayOSAL_getTimestampResolution();

    now = Timestamp_get32();

    /*
     * consider also timeprint of timestamp function, so that if now + count is almost equal to ~0,
     * next timestamp after timestamp just before end may wraparound and waiting will go on till
     * one cycle or in worst case may never end (multiply by 2 for safety)
     */
    /* no overflow for this delay */
    if ((unsigned)~0 - now > count + 2 * DelayOSAL_getTimestampTimePrint()) {
        end = now + count;
        while (Timestamp_get32() < end)
            ;
    /* overflow for this delay */
    } else {
        while (Timestamp_get32() > now)
            ;
        end = (unsigned)~0 - now;
        end = count - end;
        while (Timestamp_get32() < end)
            ;
    }
}


void Board_phyReset() {
    gpioPinObj_t gpioPhyReset = GPIOPINOBJ_DEFAULT;

     uint32_t i;
    gpioPhyReset.instAddr = 0x48320000;
    gpioPhyReset.instNum = 4;
    gpioPhyReset.pinNum = 0x14;

    Board_initGPIO(DEVICE_ID_RESET_PHY, 6, &gpioPhyReset);
    GPIOPinWrite(gpioPhyReset.instAddr, gpioPhyReset.pinNum ,0);
    DelayOSAL_microSeconds(300);
        GPIOPinWrite(gpioPhyReset.instAddr, gpioPhyReset.pinNum ,1);
}

#define SYS_MMU_BUFFERABLE      1
#define SYS_MMU_CACHEABLE       2
#define SYS_MMU_SHAREABLE       4
#define SYS_MMU_NO_EXECUTE      8
typedef struct _sys_mmu_entry
{
    
    void* address;
    /**< Address to be entered in MMU table. */
    unsigned int attributes;
    /**< Attributes of the memory. */
}SYS_MMU_ENTRY;


SYS_MMU_ENTRY applMmuEntries[] = {
    {(void*)0x30000000,SYS_MMU_CACHEABLE},  //QSPI CS0 Maddr1space - Cacheable
    {(void*)0x30100000,SYS_MMU_CACHEABLE},  //QSPI CS0 Maddr1space - Cacheable
    {(void*)0x30200000,SYS_MMU_CACHEABLE},  //QSPI CS0 Maddr1space - Cacheable
    {(void*)0x30300000,SYS_MMU_CACHEABLE},  //QSPI CS0 Maddr1space - Cacheable
    {(void*)0x40300000,0},  //OCMCRAM  - Cacheable
    {(void*)0x44D00000, SYS_MMU_BUFFERABLE},  //PRCM - Non bufferable| Non Cacheable
    {(void*)0x44E00000, SYS_MMU_BUFFERABLE},  //Clock Module, PRM, GPIO0, UART0, I2C0, - Non bufferable| Non Cacheable
    {(void*)0x47900000,SYS_MMU_BUFFERABLE},  //QSPI MMR Maddr0space
    {(void*)0x48000000, SYS_MMU_BUFFERABLE},  //UART1,UART2,I2C1,McSPI0,McASP0 CFG,McASP1 CFG,DMTIMER,GPIO1 -Non bufferable| Non Cacheable
    {(void*)0x48100000,0},  //I2C2,McSPI1,UART3,UART4,UART5, GPIO2,GPIO3,MMC1 - Non bufferable| Non Cacheable
    {(void*)0x48200000, SYS_MMU_BUFFERABLE},  //
    {(void*)0x48300000, SYS_MMU_BUFFERABLE},  //PWM - Non bufferable| Non Cacheable
    {(void*)0x49000000, SYS_MMU_BUFFERABLE},   //EDMA3CC - Non bufferable| Non Cacheable
    {(void*)0x4A000000, SYS_MMU_BUFFERABLE},  //L4 FAST CFG- Non bufferable| Non Cacheable
    {(void*)0x4A100000, SYS_MMU_BUFFERABLE},  //CPSW - Non bufferable| Non Cacheable
    {(void*)0x54400000, SYS_MMU_BUFFERABLE},  //PRU-ICSS0/1 -Bufferable| Non Cacheable | Shareable
    {(void*)0x80000000,SYS_MMU_CACHEABLE},  //QSPI CS0 Maddr1space - Non bufferable| Non Cacheable
    {(void*)0xFFFFFFFF,0xFFFFFFFF}
};
int SDKMMUInit(SYS_MMU_ENTRY mmuEntries[])
{
    unsigned short itr = 0;
    Mmu_FirstLevelDescAttrs attrs;

    if(NULL == mmuEntries)
        return -1;

    Mmu_disable();

    Mmu_initDescAttrs(&attrs);
    
    attrs.type = Mmu_FirstLevelDesc_SECTION;
    attrs.domain = 0;
    attrs.imp = 1; 
    attrs.accPerm = 3;
    
    
    for(itr = 0 ; mmuEntries[itr].address != (void*)0xFFFFFFFF ; itr++)
    {
        attrs.bufferable = ((mmuEntries[itr].attributes) & SYS_MMU_BUFFERABLE) && 1 ;
        attrs.cacheable  = ((mmuEntries[itr].attributes) & SYS_MMU_CACHEABLE) && 1;
        if (!attrs.bufferable && !attrs.cacheable)
            attrs.tex = 0; //tex is initialized to 1 and need this to force strongly ordered
        attrs.shareable  = ((mmuEntries[itr].attributes) & SYS_MMU_SHAREABLE) && 1;
        attrs.noexecute  = ((mmuEntries[itr].attributes) & SYS_MMU_NO_EXECUTE) && 1;
        Mmu_setFirstLevelDesc((Ptr)(mmuEntries[itr].address), (Ptr)(mmuEntries[itr].address) , &attrs);  // PWM
    }
    Mmu_enable();
    return 0;
}
#endif

void Delay1(uint32_t delay)
{
    volatile uint32_t delay1 = delay*1000;
    while (delay1--) ;
}

uint32_t packetLength = 0;
uint8_t packet_array[256] = {0};

#define PRU0_FIRMWARE_NAME      PRU0_FIRMWARE        // name of the C struct in PRU header file
#define PRU1_FIRMWARE_NAME      PRU1_FIRMWARE        // name of the C struct in PRU header file

#define    ICSS_PORT1_PHY_ADDR          2
#define    ICSS_PORT1_PHY_ADDR_ICE2     3
unsigned int uartInstance =0;
unsigned char board_Type = 0;

PRUICSS_Handle pruIcssHandle;
ICSS_EmacHandle emachandle;
ICSS_EmacHandle emachandle1;

ICSS_EmacInitConfig* switchEmacCfg;
ICSS_EmacInitConfig* switchEmacCfg1;

uint8_t lclMac[6];
uint8_t lclMac1[6];

void SOCCtrlGetPortMacAddr(uint32_t portNum, uint8_t *pMacAddr);
uint8_t ICSSEmacDRVInit(ICSS_EmacHandle handle, uint8_t instance);
extern ICSS_EmacBaseAddrCfgParams icss_EmacBaseAddrCfgParams[2];

/***********************************************************************/
/* local functions declaration                                         */
/***********************************************************************/

/***********************************************************************/
/* function definitions                                                */
/***********************************************************************/
int32_t CONSOLEUtilsPuts(const char *pString)
{
    return(0);
}

int32_t CONSOLEUtilsPrintf(const char *pFormat, ...) {
    return(0);
}
/*
 *  ======== Delay function ========
 */
void delay(unsigned int delayValue)
{
    volatile uint32_t delay1 = delayValue*10000;
    while (delay1--) ;
}
/*
 *  ======== Callback function ========
 */
void MCSPICallbackFxn(SPI_Handle handle, SPI_Transaction * transaction)
{
    txCompleteCallbackFlag = 0;
}
/*
 *  ======== Function to control load signal to load data ========
 */
void LoadData(void)
{
    GPIO_write(GPIO_PIN_LD_PIN, GPIO_PIN_VAL_LOW);
    delay(1);
    GPIO_write(GPIO_PIN_LD_PIN, GPIO_PIN_VAL_HIGH);
    delay(1);
}
/*
 *  ======== test function ========
 */
void spi_test(UArg arg0, UArg arg1)
{
    SPI_Params spiParams;                /* SPI params structure */
    SPI_Handle handle;                   /* SPI handle */
    SPI_Transaction transaction;         /* SPI transaction */
    bool retVal = false;                 /* return value */

    /* Init GPIO driver */
    SPI_log("Init GPIO...");
    GPIO_init();
    SPI_log("... done!\n");

    /* Init SPI driver */
    SPI_log("Init SPI...");
    SPI_init();
    SPI_log("... done!\n");

    /* Default SPI configuration parameters */
    SPI_Params_init(&spiParams);
    spiParams.frameFormat  = SPI_POL1_PHA0;

    /* Uncomment below lines of code to test callback mode */
    spiParams.transferMode = SPI_MODE_CALLBACK;
    spiParams.transferCallbackFxn = MCSPICallbackFxn;

    /* Open SPI driver */
    SPI_log("Open SPI driver..");
    handle = SPI_open(0, &spiParams);
    SPI_log("... done!\n");

    /* Load data */
    LoadData();

    /* Initiate transfer */
    txBuf[0] = 0xAAU;
    transferLength = 1U;

    SPI_log("Send via SPI..");
    transaction.status=-1; /* Setting an initial value to be replaced with a proper return status */
    transaction.count = transferLength;
    transaction.txBuf = &txBuf[0];
    transaction.rxBuf = &rxBuf[0];
    retVal = SPI_transfer(handle, &transaction);
    delay(DELAY_VALUE);
    SPI_log("... done!\n");

	if(transaction.status==-1) {
	     SPI_log("\n SPI_Transfer return transaction status not set! Error! \n");
	}  else {
	    SPI_log("\n SPI_Transfer returned transaction status = %d\n",transaction.status);
    }
	SPI_log("retvalue: Rx = %d\n\r",rxBuf[0]);
	while(1)
	{
	    LoadData();
	    SPI_log("SPI start: ");
	    retVal = SPI_transfer(handle, &transaction);
        if(SPI_MODE_CALLBACK == spiParams.transferMode)
        {
            while(txCompleteCallbackFlag == 1U);
        }
	    SPI_log("retvalue: Rx = %d\n\r",rxBuf[0]);
//	    delay(DELAY_VALUE);
//		if(false == retVal)
//	    {
//	        SPI_log("\n Error occurred in spi transfer \n");
//	    }
//	    else
//	    {
//	    	SPI_log("retvalue: Rx = %d\n\r",rxBuf[0]);
//	        /**
//	         *  This below is check is required for callback mode to indicate the
//	         *  transfer completion.
//	         */
//	    }
	}

    SPI_close(handle);

    while(1);
}


/*
 * ======== stackInitHook ========
 * hook called from ti_nkd_config_Global_stackThread() to run user setup code
 */
void stackInitHook(HANDLE hCfg)
{
    CI_IPNET NA;
    CI_ROUTE RT;

    /* Static IP Address settings for interface 1 */
    char *ipAddr      = "192.168.2.3";
    char *ipMask      = "255.255.255.0";
    char *ipGateway   = "192.168.2.1";

    /* Add IP address for interface 1 */
    NA.IPAddr = inet_addr(ipAddr);
    NA.IPMask = inet_addr(ipMask);
    CfgAddEntry(hCfg, CFGTAG_IPNET, 1, 0, sizeof(CI_IPNET),
            (UINT8 *)&NA, 0 );

    /*
     * Add gateway for interface 1
     * --> I *think* the below should work but you may need different values ...
     */
    bzero(&RT, sizeof(RT));
    RT.IPDestAddr = 0;
    RT.IPDestMask = 0;
    RT.IPGateAddr = inet_addr(ipGateway);

    CfgAddEntry(hCfg, CFGTAG_ROUTE, 0, 0,
            sizeof(CI_ROUTE), (UINT8 *)&RT, 0);

}

/* PRUICSS objects */
PRUICSS_V1_Object prussObjects[2];



/*
 *    ---task to initialize PRU---
 */
Void taskPruss(UArg a0, UArg a1)
{
    Uint8 firmwareLoad_done = FALSE;
    Uint8 count = 0;
    static Uint8 pktReceived = 0;
#if 1
    while(GetNdkStatus(0)== NIMU_STAT_PROGRESS || GetNdkStatus(0)==NIMU_STAT_UNKNOWN)
    {
        Task_sleep(100);
    }
#endif


    PRUICSS_pruDisable(pruIcssHandle, ICSS_EMAC_PORT_1-1);
    PRUICSS_pruDisable(pruIcssHandle, ICSS_EMAC_PORT_2-1);
    if(PRUICSS_pruWriteMemory(pruIcssHandle,PRU_ICSS_IRAM(0) ,0,
                          (uint32_t *) PRU0_FIRMWARE_NAME,
                          sizeof(PRU0_FIRMWARE_NAME)))
    {
        if(PRUICSS_pruWriteMemory(pruIcssHandle,PRU_ICSS_IRAM(1) ,0,
                              (uint32_t *) PRU1_FIRMWARE_NAME,
                              sizeof(PRU1_FIRMWARE_NAME)))
        {
            firmwareLoad_done = TRUE;
        }
    }

    if( firmwareLoad_done)
    {
        PRUICSS_pruEnable(pruIcssHandle, ICSS_EMAC_PORT_1-1);
        PRUICSS_pruEnable(pruIcssHandle, ICSS_EMAC_PORT_2-1);
    }
}

/*
 *  ======== main ========
 */
int main()
{ 
    Error_Block eb;
    Task_Params taskParams;
    int32_t ret;
    PRUICSS_Config  *pruIcssCfg;
    Error_init(&eb);
    Board_initCfg cfg = BOARD_INIT_MODULE_CLOCK | BOARD_INIT_ICSS_PINMUX |  BOARD_INIT_UART_STDIO | BOARD_INIT_PINMUX_CONFIG;



    SDKMMUInit(applMmuEntries);
    Board_init(cfg);

    SPI_log("Hello, NIMU ICSS Ethernet and SPI test project by FKaiser\n");



    SPI_log("Starting up Ethernet\n");
    SPI_log("Init PRU's.");
    pruIcssHandle = PRUICSS_create(pruss_config,prussIcssInstance);
    ret  = PRUICSS_socGetInitCfg(&pruIcssCfg);
    if (ret  != PRUICSS_RETURN_SUCCESS)
        return (ret);
    pruIcssHandle = PRUICSS_create((PRUICSS_Config*) pruIcssCfg,PRUICCSS_INSTANCE_TWO);
    SPI_log(".");

    Task_Params_init(&taskParams);
    taskParams.priority = 15;
    taskParams.instance->name = "SwitchTask";
    Task_create(taskPruss, &taskParams, &eb);

    Task_Handle task = Task_create(spi_test, NULL, &eb);
        if (task == NULL) {
            System_printf("Task_create() failed!\n");
            BIOS_exit(0);
        }


            BIOS_start();

    /*Port I initializations*/
    emachandle = (ICSS_EmacHandle)malloc(sizeof(ICSS_EmacConfig));

    switchEmacCfg = (ICSS_EmacInitConfig*)malloc(sizeof(ICSS_EmacInitConfig));

    SPI_log(".");
    if (switchEmacCfg == NULL)
        return -2;

    switchEmacCfg->phyAddr[0]=0;//BOARDGetDeviceDataModId(DEVICE_ID_ENET_PHY_MII, 0);
    switchEmacCfg->phyAddr[1]=0;
    switchEmacCfg->portMask = ICSS_EMAC_MODE_MAC1;
    switchEmacCfg->ethPrioQueue = ICSS_EMAC_QUEUE1;
    switchEmacCfg->halfDuplexEnable = 1;
    switchEmacCfg->enableIntrPacing = ICSS_EMAC_ENABLE_PACING;
    switchEmacCfg->ICSS_EmacIntrPacingMode = ICSS_EMAC_INTR_PACING_MODE1;
    switchEmacCfg->pacingThreshold = 100;
    switchEmacCfg->learningEn = 0;
    switchEmacCfg->rxIntNum = 52;//20;
    switchEmacCfg->linkIntNum=58;//26;
    SOCCtrlGetPortMacAddr(1,lclMac);
    switchEmacCfg->macId = lclMac;
    ICSSEmacDRVInit(emachandle, 1);     // ICSS_M instance 0
   

    ((ICSS_EmacObject*)emachandle->object)->pruIcssHandle = pruIcssHandle;
    ((ICSS_EmacObject*)emachandle->object)->emacInitcfg = switchEmacCfg;

    /*PORT2 Initializations*/
    emachandle1 = (ICSS_EmacHandle)malloc(sizeof(ICSS_EmacConfig));

    switchEmacCfg1 = (ICSS_EmacInitConfig*)malloc(sizeof(ICSS_EmacInitConfig));

    SPI_log(".");
    if (switchEmacCfg1 == NULL)
       return -2;

    switchEmacCfg1->phyAddr[0]= 1;//BOARDGetDeviceDataModId(DEVICE_ID_ENET_PHY_MII, 1);
    switchEmacCfg1->phyAddr[1]= 0;
    switchEmacCfg1->portMask = ICSS_EMAC_MODE_MAC2;
    switchEmacCfg1->ethPrioQueue = ICSS_EMAC_QUEUE3;
    switchEmacCfg1->halfDuplexEnable = 1;
    switchEmacCfg1->enableIntrPacing = ICSS_EMAC_DISABLE_PACING;
    switchEmacCfg1->pacingThreshold = 100;
    switchEmacCfg1->learningEn = 0;
    switchEmacCfg1->rxIntNum = 53;//21;
    switchEmacCfg1->linkIntNum=59;//27;
    SOCCtrlGetPortMacAddr(2,lclMac1);
    switchEmacCfg1->macId = lclMac1;
    ICSSEmacDRVInit(emachandle1,1); 	// ICSS_M instance 0


    ((ICSS_EmacObject*)emachandle1->object)->pruIcssHandle = pruIcssHandle;
    ((ICSS_EmacObject*)emachandle1->object)->emacInitcfg = switchEmacCfg1;

    PRUICSS_IntcInitData pruss_intc_initdata = PRUSS_INTC_INITDATA;
    ICSS_EmacInit(emachandle,&pruss_intc_initdata,ICSS_EMAC_MODE_MAC1|ICSS_EMAC_MODE_DUALMAC);

    ICSS_EmacInit(emachandle1,&pruss_intc_initdata,ICSS_EMAC_MODE_MAC2|ICSS_EMAC_MODE_DUALMAC);
    SPI_log(".");

    Task_Params_init(&taskParams);
    taskParams.priority = 10;
    taskParams.instance->name = (char*)"port0_rxTaskFnc";
    taskParams.stackSize = 0x1000;
    taskParams.arg0 = (UArg)emachandle;

    ((ICSS_EmacObject*)emachandle->object)->rxTaskHandle = Task_create(ICSS_EMacOsRxTaskFnc, &taskParams, NULL);


    SPI_log(".");
    if(((ICSS_EmacObject*)emachandle->object)->rxTaskHandle==NULL)
    {
        return -2;
    }

    Task_Params_init(&taskParams);
    taskParams.priority = 10;
    taskParams.instance->name = (char*)"port1_rxTaskFnc";
    taskParams.stackSize = 0x1000;
    taskParams.arg0 = (UArg)emachandle1;

    ((ICSS_EmacObject*)emachandle1->object)->rxTaskHandle = Task_create(ICSS_EMacOsRxTaskFnc, &taskParams, NULL);


    SPI_log(".");
    if(((ICSS_EmacObject*)emachandle1->object)->rxTaskHandle==NULL)
    {
        return -2;
    }

    Board_phyReset();
    SPI_log(".");
    if(OSDRV_addNetifEntry((NIMUInitFn)&EmacInit,emachandle) == 0)
			BIOS_exit(0);
    if(OSDRV_addNetifEntry((NIMUInitFn)&EmacInit,emachandle1) == 0)
			BIOS_exit(0);

    SPI_log(".....done!\n");
    BIOS_start();
    return(0);
}


#define CTRL_MAC_ID0_LO                                             (0x630U)
#define CTRL_MAC_ID0_HI                                             (0x634U)
#define CTRL_MAC_ID1_LO                                             (0x638U)
#define CTRL_MAC_ID1_HI                                             (0x63cU)


void SOCCtrlGetPortMacAddr(uint32_t portNum, uint8_t *pMacAddr)
{
    switch(portNum)
    {
        case 1U:
            pMacAddr[5U] = HW_RD_FIELD32(SOC_CONTROL_MODULE_REG + CTRL_MAC_ID0_LO,
                CTRL_MAC_ID0_LO_MACADDR_7_0);
            pMacAddr[4U] = HW_RD_FIELD32(SOC_CONTROL_MODULE_REG + CTRL_MAC_ID0_LO,
                CTRL_MAC_ID0_LO_MACADDR_15_8);
            pMacAddr[3U] = HW_RD_FIELD32(SOC_CONTROL_MODULE_REG + CTRL_MAC_ID0_HI,
                CTRL_MAC_ID0_HI_MACADDR_23_16);
            pMacAddr[2U] = HW_RD_FIELD32(SOC_CONTROL_MODULE_REG + CTRL_MAC_ID0_HI,
                CTRL_MAC_ID0_HI_MACADDR_31_24);
            pMacAddr[1U] = HW_RD_FIELD32(SOC_CONTROL_MODULE_REG + CTRL_MAC_ID0_HI,
                CTRL_MAC_ID0_HI_MACADDR_39_32);
            pMacAddr[0U] = HW_RD_FIELD32(SOC_CONTROL_MODULE_REG + CTRL_MAC_ID0_HI,
                CTRL_MAC_ID0_HI_MACADDR_47_40);
            break;

        case 2U:
            pMacAddr[5U] = HW_RD_FIELD32(SOC_CONTROL_MODULE_REG + CTRL_MAC_ID1_LO,
                CTRL_MAC_ID0_LO_MACADDR_7_0);
            pMacAddr[4U] = HW_RD_FIELD32(SOC_CONTROL_MODULE_REG + CTRL_MAC_ID1_LO,
                CTRL_MAC_ID0_LO_MACADDR_15_8);
            pMacAddr[3U] = HW_RD_FIELD32(SOC_CONTROL_MODULE_REG + CTRL_MAC_ID1_HI,
                CTRL_MAC_ID1_HI_MACADDR_23_16);
            pMacAddr[2U] = HW_RD_FIELD32(SOC_CONTROL_MODULE_REG + CTRL_MAC_ID1_HI,
                CTRL_MAC_ID1_HI_MACADDR_31_24);
            pMacAddr[1U] = HW_RD_FIELD32(SOC_CONTROL_MODULE_REG + CTRL_MAC_ID1_HI,
                CTRL_MAC_ID1_HI_MACADDR_39_32);
            pMacAddr[0U] = HW_RD_FIELD32(SOC_CONTROL_MODULE_REG + CTRL_MAC_ID1_HI,
                CTRL_MAC_ID1_HI_MACADDR_47_40);
            break;

        default:
            break;
    }
}



uint8_t ICSSEmacDRVInit(ICSS_EmacHandle handle, uint8_t instance)
{
    /* LLD attributes mallocs */
    handle->object = (ICSS_EmacObject*)malloc(sizeof(ICSS_EmacObject));
    handle->hwAttrs= (ICSS_EmacHwAttrs*)malloc(sizeof(ICSS_EmacHwAttrs));

    /* Callback mallocs */
    ICSS_EmacCallBackObject* callBackObj = (ICSS_EmacCallBackObject*)malloc(sizeof(ICSS_EmacCallBackObject));

    callBackObj->learningExCallBack=(ICSS_EmacCallBackConfig*)malloc(sizeof(ICSS_EmacCallBackConfig));
    callBackObj->rxRTCallBack=(ICSS_EmacCallBackConfig*)malloc(sizeof(ICSS_EmacCallBackConfig));
    callBackObj->rxCallBack=(ICSS_EmacCallBackConfig*)malloc(sizeof(ICSS_EmacCallBackConfig));
    callBackObj->txCallBack=(ICSS_EmacCallBackConfig*)malloc(sizeof(ICSS_EmacCallBackConfig));
    ((ICSS_EmacObject*)handle->object)->callBackHandle = callBackObj;

    /*Allocate memory for learning*/
    ((ICSS_EmacObject*)handle->object)->macTablePtr = (HashTable_t*)malloc(NUM_PORTS * sizeof(HashTable_t));

    /*Allocate memory for PRU Statistics*/
    ((ICSS_EmacObject*)handle->object)->pruStat = (ICSS_EmacPruStatistics_t*)malloc(NUM_PORTS * sizeof(ICSS_EmacPruStatistics_t));

    /*Allocate memory for Host Statistics*/
    ((ICSS_EmacObject*)handle->object)->hostStat = (ICSS_EmacHostStatistics_t*)malloc(NUM_PORTS * sizeof(ICSS_EmacHostStatistics_t));

    /*Allocate memory for Storm Prevention*/
    ((ICSS_EmacObject*)handle->object)->stormPrevPtr = (stormPrevention_t*)malloc(NUM_PORTS * sizeof(stormPrevention_t));

    /* Base address initialization */
    if(NULL == ((ICSS_EmacHwAttrs*)handle->hwAttrs)->emacBaseAddrCfg) {
        ((ICSS_EmacHwAttrs*)handle->hwAttrs)->emacBaseAddrCfg =
                        (ICSS_EmacBaseAddressHandle_T)malloc(sizeof(ICSS_EmacBaseAddrCfgParams));
    }
    ICSS_EmacBaseAddressHandle_T emacBaseAddr = ((ICSS_EmacHwAttrs*)handle->hwAttrs)->emacBaseAddrCfg;


    emacBaseAddr->dataRam0BaseAddr = icss_EmacBaseAddrCfgParams[instance-1].dataRam0BaseAddr;
    emacBaseAddr->dataRam1BaseAddr = icss_EmacBaseAddrCfgParams[instance-1].dataRam1BaseAddr;
    /*      Restricting l3OcmcBaseAddr to 0x40xxxx00.
     *  This is done because L3 OCMC Base Address must be 256 byte aligned and to support OCMC memory usage for Linux Power Management.
     */
    emacBaseAddr->l3OcmcBaseAddr =  (((icss_EmacBaseAddrCfgParams[instance-1].l3OcmcBaseAddr)&0xFFFF00)|0x40000000);
    emacBaseAddr->prussCfgRegs =  icss_EmacBaseAddrCfgParams[instance-1].prussCfgRegs;
    emacBaseAddr->prussIepRegs =  icss_EmacBaseAddrCfgParams[instance-1].prussIepRegs;
    emacBaseAddr->prussIntcRegs = icss_EmacBaseAddrCfgParams[instance-1].prussIntcRegs;
    emacBaseAddr->prussMiiMdioRegs = icss_EmacBaseAddrCfgParams[instance-1].prussMiiMdioRegs;
    emacBaseAddr->prussMiiRtCfgRegsBaseAddr = icss_EmacBaseAddrCfgParams[instance-1].prussMiiRtCfgRegsBaseAddr;
    emacBaseAddr->prussPru0CtrlRegs = icss_EmacBaseAddrCfgParams[instance-1].prussPru0CtrlRegs;
    emacBaseAddr->prussPru1CtrlRegs = icss_EmacBaseAddrCfgParams[instance-1].prussPru1CtrlRegs;
    emacBaseAddr->sharedDataRamBaseAddr = icss_EmacBaseAddrCfgParams[instance-1].sharedDataRamBaseAddr;
    return (0);


}



