/**
* @file icss_pruInit.c
*
* @brief Contains routines for initialization of PRU
*
* \par
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
* \par
*/

/* ========================================================================== */
/*                             Include Files                                  */
/* ========================================================================== */
#include <ti/sysbios/knl/Task.h>
#include <soc_AM335x.h>
#include "hw/hw_control_AM335x.h"
#include <hw/hw_types.h>
#include <osdrv_pruss.h>
#include "icss_switch.h"
#include "pruss_register.h"
#include "icss_iSwitchDrv.h"
#include "icss_pruInit.h"
#include "icss_switch.h"
#include "icss_switch_emac.h"
#include "tiswitch_pruss_intc_mapping.h"
#include "osdrv_ndkdeviceconfig.h"

/* ========================================================================== */
/*                           Macros & Typedefs                                */
/* ========================================================================== */
/**
* @def SWITCH_MODE
*      If this is defined then use switch mode instead of EMAC mode
*/
#define SWITCH_MODE
/**
* @def USE_TI_MACS
*      Gets MAcid from soc Registers
*/
#define USE_TI_MACS
/**
* @def ICSS_SHARED_RAM_BASE_ADDRESS
*      Shared DRAM memory offset in PRU
*/
#define ICSS_SHARED_RAM_BASE_ADDRESS 	PRUSS_SHAREDRAM_BASE
/**
* @def PRU0_RAM_BASE_ADDRESS
*      PRU0 DRAM memory offset in PRU
*/
#define PRU0_RAM_BASE_ADDRESS 			DATARAM0_PHYS_BASE
/**
* @def PRU1_RAM_BASE_ADDRESS
*      PRU1 DRAM memory offset in PRU
*/
#define PRU1_RAM_BASE_ADDRESS 			DATARAM1_PHYS_BASE
/**
* @def PRU_IEP_GLB_CFG_REG
*      IEP Register Offset
*/
#define PRU_IEP_GLB_CFG_REG				0x00
/**
* @def IEP_GLOBAL_CFG_REG_VAL
*      IEP Global config value
*/
#define IEP_GLOBAL_CFG_REG_VAL			0x0551
/**
* @def TX_MIN_IPG
*      TX Minimum Inter packet gap
*/
#define TX_MIN_IPG						0xb8

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

/**Default MAC for PRU-ICSS and NDK use!*/
Uint8 ifMAC[] = { 0x00, 0x31, 0xDE, 0x00, 0x00, 0x00};

/* ========================================================================== */
/*                          Function Definitions                              */
/* ========================================================================== */
/**
* @internal
* @brief  function to configure PRUSS register
*
* @param none
*
* @retval none
*/
void PRUSSDRVPruCfgInit(void);
/**
* @internal
* @brief   function to configure PRUSS MII_RT register
*
* @param none
*
* @retval none
*/
void PRUSSDRVPruMiiRtCfgInit(void);

/**
* @internal
* @brief  Function to  Load ICSS Switch params to pru memory
*
* @param none
*
* @retval 0 on success
*/
Uint8 ICSSSwitchConfig()
{
	unsigned short *pTemp16;
	unsigned char * pTemp8;
	int i;

	// queue lookup table
	pTemp16 = (unsigned short *)(PRU1_RAM_BASE_ADDRESS + QUEUE_SIZE_ADDR);
	// host (port 2) queue
	*pTemp16++ = HOST_QUEUE_1_SIZE;
	*pTemp16++ = HOST_QUEUE_2_SIZE;
	*pTemp16++ = HOST_QUEUE_3_SIZE;
	*pTemp16++ = HOST_QUEUE_4_SIZE;
	// port 0 queue
	*pTemp16++ = QUEUE_1_SIZE;
	*pTemp16++ = QUEUE_2_SIZE;
	*pTemp16++ = QUEUE_3_SIZE;
	*pTemp16++ = QUEUE_4_SIZE;
	// port 1 queue
	*pTemp16++ = QUEUE_1_SIZE;
	*pTemp16++ = QUEUE_2_SIZE;
	*pTemp16++ = QUEUE_3_SIZE;
	*pTemp16++ = QUEUE_4_SIZE;

//*********************
	// Tx Context Initialize data. Port 1, (Q1,Q2,Q3,Q4)
	//*********************
	pTemp16 = (unsigned short *)(PRU1_RAM_BASE_ADDRESS + TX_CONTEXT_P1_Q1_OFFSET_ADDR);
	// Port 1 Queue 1
	*pTemp16++ = P1_Q1_BUFFER_OFFSET;
	*pTemp16++ = ((QUEUE_1_SIZE * 32) + P1_Q1_BUFFER_OFFSET - 32); 
	*pTemp16++ = P1_Q1_BD_OFFSET;
	*pTemp16++ = ((QUEUE_1_SIZE * 4) + P1_Q1_BD_OFFSET - 4);
	// Port 1 Queue 2
	*pTemp16++ = P1_Q2_BUFFER_OFFSET;
	*pTemp16++ = ((QUEUE_2_SIZE * 32) + P1_Q2_BUFFER_OFFSET - 32); 
	*pTemp16++ = P1_Q2_BD_OFFSET;
	*pTemp16++ = ((QUEUE_2_SIZE * 4) + P1_Q2_BD_OFFSET - 4);
	// Port 1 Queue 3
	*pTemp16++ = P1_Q3_BUFFER_OFFSET;
	*pTemp16++ = ((QUEUE_3_SIZE * 32) + P1_Q3_BUFFER_OFFSET - 32); 
	*pTemp16++ = P1_Q3_BD_OFFSET;
	*pTemp16++ = ((QUEUE_3_SIZE * 4) + P1_Q3_BD_OFFSET - 4);
	// Port 1 Queue 4
	*pTemp16++ = P1_Q4_BUFFER_OFFSET;
	*pTemp16++ = ((QUEUE_4_SIZE * 32) + P1_Q4_BUFFER_OFFSET - 32); 
	*pTemp16++ = P1_Q4_BD_OFFSET;
	*pTemp16++ = ((QUEUE_4_SIZE * 4) + P1_Q4_BD_OFFSET - 4);

	
	//*********************
	// Tx Context Initialize data. Port 2, (Q1,Q2,Q3,Q4)
	//*********************
	pTemp16 = (unsigned short *)(PRU1_RAM_BASE_ADDRESS + TX_CONTEXT_P2_Q1_OFFSET_ADDR);
	// Port 2 Queue 1
	*pTemp16++ = P2_Q1_BUFFER_OFFSET;
	*pTemp16++ = ((QUEUE_1_SIZE * 32) + P2_Q1_BUFFER_OFFSET - 32); 
	*pTemp16++ = P2_Q1_BD_OFFSET;
	*pTemp16++ = ((QUEUE_1_SIZE * 4) + P2_Q1_BD_OFFSET - 4);
	// Port 2 Queue 2
	*pTemp16++ = P2_Q2_BUFFER_OFFSET;
	*pTemp16++ = ((QUEUE_2_SIZE * 32) + P2_Q2_BUFFER_OFFSET - 32); 
	*pTemp16++ = P2_Q2_BD_OFFSET;
	*pTemp16++ = ((QUEUE_2_SIZE * 4) + P2_Q2_BD_OFFSET - 4);
	// Port 2 Queue 3
	*pTemp16++ = P2_Q3_BUFFER_OFFSET;
	*pTemp16++ = ((QUEUE_3_SIZE * 32) + P2_Q3_BUFFER_OFFSET - 32); 
	*pTemp16++ = P2_Q3_BD_OFFSET;
	*pTemp16++ = ((QUEUE_3_SIZE * 4) + P2_Q3_BD_OFFSET - 4);
	// Port 2 Queue 4
	*pTemp16++ = P2_Q4_BUFFER_OFFSET;
	*pTemp16++ = ((QUEUE_4_SIZE * 32) + P2_Q4_BUFFER_OFFSET - 32); 
	*pTemp16++ = P2_Q4_BD_OFFSET;
	*pTemp16++ = ((QUEUE_4_SIZE * 4) + P2_Q4_BD_OFFSET - 4);
		
	//*********************
	// Collision Tx Context Initialize data. Port 1, (Q1)
	//*********************
	pTemp16 = (unsigned short *)(PRU1_RAM_BASE_ADDRESS + COL_TX_CONTEXT_P1_Q1_OFFSET_ADDR);
	// Port 1 collision queue
	*pTemp16++ = P1_COL_BUFFER_OFFSET;
	*pTemp16++ = P1_COL_BUFFER_OFFSET;
	*pTemp16++ = ((COLLISION_QUEUE_SIZE * 32) + P1_COL_BUFFER_OFFSET - 32);
	
	//*********************
	// Collision Tx Context Initialize data. Port 2, (Q1)
	//*********************
	pTemp16 = (unsigned short *)(PRU1_RAM_BASE_ADDRESS + COL_TX_CONTEXT_P2_Q1_OFFSET_ADDR);
	// Port 2 collision queue
	*pTemp16++ = P2_COL_BUFFER_OFFSET;
	*pTemp16++ = P2_COL_BUFFER_OFFSET;
	*pTemp16++ = ((COLLISION_QUEUE_SIZE * 32) + P2_COL_BUFFER_OFFSET - 32);
	
	//*********************
	// Rx Context Initialize data for Host Port, (Q1,Q2,Q3,Q4)
	//*********************
	pTemp16 = (unsigned short *)(PRU1_RAM_BASE_ADDRESS + RX_CONTEXT_P0_Q1_OFFSET_ADDR);
	// Host Port Queue 1
	*pTemp16++ = P0_Q1_BUFFER_OFFSET;
	*pTemp16++ = P0_QUEUE_DESC_OFFSET;
	*pTemp16++ = P0_Q1_BD_OFFSET;
	*pTemp16++ = ((HOST_QUEUE_1_SIZE * 4) + P0_Q1_BD_OFFSET - 4);
	// Host Port Queue 2
	*pTemp16++ = P0_Q2_BUFFER_OFFSET;
	*pTemp16++ = P0_QUEUE_DESC_OFFSET + 8;
	*pTemp16++ = P0_Q2_BD_OFFSET;
	*pTemp16++ = ((HOST_QUEUE_2_SIZE * 4) + P0_Q2_BD_OFFSET - 4);
	// Host Port Queue 3
	*pTemp16++ = P0_Q3_BUFFER_OFFSET;
	*pTemp16++ = P0_QUEUE_DESC_OFFSET + 16;
	*pTemp16++ = P0_Q3_BD_OFFSET;
	*pTemp16++ = ((HOST_QUEUE_3_SIZE * 4) + P0_Q3_BD_OFFSET - 4);
	// Host Port Queue 4
	*pTemp16++ = P0_Q4_BUFFER_OFFSET;
	*pTemp16++ = P0_QUEUE_DESC_OFFSET + 24;
	*pTemp16++ = P0_Q4_BD_OFFSET;
	*pTemp16++ = ((HOST_QUEUE_4_SIZE * 4) + P0_Q4_BD_OFFSET - 4);
	
	//*********************
	// Rx Context Initialize data for Port 1, (Q1,Q2,Q3,Q4)
	//*********************
	pTemp16 = (unsigned short *)(PRU1_RAM_BASE_ADDRESS + RX_CONTEXT_P1_Q1_OFFSET_ADDR);
	// Host Port Queue 1
	*pTemp16++ = P1_Q1_BUFFER_OFFSET;
	*pTemp16++ = P1_QUEUE_DESC_OFFSET;
	*pTemp16++ = P1_Q1_BD_OFFSET;
	*pTemp16++ = ((QUEUE_1_SIZE * 4) + P1_Q1_BD_OFFSET - 4);
	// Host Port Queue 2
	*pTemp16++ = P1_Q2_BUFFER_OFFSET;
	*pTemp16++ = P1_QUEUE_DESC_OFFSET + 8;
	*pTemp16++ = P1_Q2_BD_OFFSET;
	*pTemp16++ = ((QUEUE_2_SIZE * 4) + P1_Q2_BD_OFFSET - 4);
	// Host Port Queue 3
	*pTemp16++ = P1_Q3_BUFFER_OFFSET;
	*pTemp16++ = P1_QUEUE_DESC_OFFSET + 16;
	*pTemp16++ = P1_Q3_BD_OFFSET;
	*pTemp16++ = ((QUEUE_3_SIZE * 4) + P1_Q3_BD_OFFSET - 4);
	// Host Port Queue 4
	*pTemp16++ = P1_Q4_BUFFER_OFFSET;
	*pTemp16++ = P1_QUEUE_DESC_OFFSET + 24;
	*pTemp16++ = P1_Q4_BD_OFFSET;
	*pTemp16++ = ((QUEUE_4_SIZE * 4) + P1_Q4_BD_OFFSET - 4);
	
	//*********************
	// Rx Context Initialize data for Port 2, (Q1,Q2,Q3,Q4)
	//*********************
	pTemp16 = (unsigned short *)(PRU1_RAM_BASE_ADDRESS + RX_CONTEXT_P2_Q1_OFFSET_ADDR);
	// Host Port Queue 1
	*pTemp16++ = P2_Q1_BUFFER_OFFSET;
	*pTemp16++ = P2_QUEUE_DESC_OFFSET;
	*pTemp16++ = P2_Q1_BD_OFFSET;
	*pTemp16++ = ((QUEUE_1_SIZE * 4) + P2_Q1_BD_OFFSET - 4);
	// Host Port Queue 2
	*pTemp16++ = P2_Q2_BUFFER_OFFSET;
	*pTemp16++ = P2_QUEUE_DESC_OFFSET + 8;
	*pTemp16++ = P2_Q2_BD_OFFSET;
	*pTemp16++ = ((QUEUE_2_SIZE * 4) + P2_Q2_BD_OFFSET - 4);
	// Host Port Queue 3
	*pTemp16++ = P2_Q3_BUFFER_OFFSET;
	*pTemp16++ = P2_QUEUE_DESC_OFFSET + 16;
	*pTemp16++ = P2_Q3_BD_OFFSET;
	*pTemp16++ = ((QUEUE_3_SIZE * 4) + P2_Q3_BD_OFFSET - 4);
	// Host Port Queue 4
	*pTemp16++ = P2_Q4_BUFFER_OFFSET;
	*pTemp16++ = P2_QUEUE_DESC_OFFSET + 24;
	*pTemp16++ = P2_Q4_BD_OFFSET;
	*pTemp16++ = ((QUEUE_4_SIZE * 4) + P2_Q4_BD_OFFSET - 4);
	
	//*********************
	// Collision Rx Context Initialize for Host port
	//*********************
	pTemp16 = (unsigned short *)(PRU1_RAM_BASE_ADDRESS + COL_RX_CONTEXT_P0_OFFSET_ADDR);
	// Port 0 collision queue
	*pTemp16++ = P0_COL_BUFFER_OFFSET;
	*pTemp16++ = P0_COL_BUFFER_OFFSET;
	*pTemp16++ = P0_COL_QUEUE_DESC_OFFSET;
	*pTemp16++ = P0_COL_BD_OFFSET;
	*pTemp16++ = ((COLLISION_QUEUE_SIZE * 4) + P0_COL_BD_OFFSET - 4);
	
	//*********************
	// Collision Rx Context Initialize for Port 1
	//*********************
	pTemp16 = (unsigned short *)(PRU1_RAM_BASE_ADDRESS + COL_RX_CONTEXT_P1_OFFSET_ADDR);
	// Port 1 collision queue
	*pTemp16++ = P1_COL_BUFFER_OFFSET;
	*pTemp16++ = P1_COL_BUFFER_OFFSET;
	*pTemp16++ = P1_COL_QUEUE_DESC_OFFSET;
	*pTemp16++ = P1_COL_BD_OFFSET;
	*pTemp16++ = ((COLLISION_QUEUE_SIZE * 4) + P1_COL_BD_OFFSET - 4);
	
	//*********************
	// Collision Rx Context Initialize for Port 2
	//*********************
	pTemp16 = (unsigned short *)(PRU1_RAM_BASE_ADDRESS + COL_RX_CONTEXT_P2_OFFSET_ADDR);
	// Port 2 collision queue
	*pTemp16++ = P2_COL_BUFFER_OFFSET;
	*pTemp16++ = P2_COL_BUFFER_OFFSET;
	*pTemp16++ = P2_COL_QUEUE_DESC_OFFSET;
	*pTemp16++ = P2_COL_BD_OFFSET;
	*pTemp16++ = ((COLLISION_QUEUE_SIZE * 4) + P2_COL_BD_OFFSET - 4);
	//*********************
	// buffer offset table
	//*********************
	pTemp16 = (unsigned short *)(PRU1_RAM_BASE_ADDRESS + QUEUE_OFFSET_ADDR);
	// host queue buffer
	*pTemp16++ = P0_Q1_BUFFER_OFFSET;
	*pTemp16++ = P0_Q2_BUFFER_OFFSET;
	*pTemp16++ = P0_Q3_BUFFER_OFFSET;
	*pTemp16++ = P0_Q4_BUFFER_OFFSET;
	// port 0 queue
	*pTemp16++ = P1_Q1_BUFFER_OFFSET;
	*pTemp16++ = P1_Q2_BUFFER_OFFSET;
	*pTemp16++ = P1_Q3_BUFFER_OFFSET;
	*pTemp16++ = P1_Q4_BUFFER_OFFSET;
	// port 1 queue
	*pTemp16++ = P2_Q1_BUFFER_OFFSET;
	*pTemp16++ = P2_Q2_BUFFER_OFFSET;
	*pTemp16++ = P2_Q3_BUFFER_OFFSET;
	*pTemp16++ = P2_Q4_BUFFER_OFFSET;

	//*************************
	// buffer descriptor
	//*************************
	pTemp16 = (unsigned short *)(PRU1_RAM_BASE_ADDRESS + QUEUE_DESCRIPTOR_OFFSET_ADDR);
	// host buffer descriptor
	*pTemp16++ = P0_Q1_BD_OFFSET;
	*pTemp16++ = P0_Q2_BD_OFFSET;
	*pTemp16++ = P0_Q3_BD_OFFSET;
	*pTemp16++ = P0_Q4_BD_OFFSET;
	// port 0 buffer descriptor
	*pTemp16++ = P1_Q1_BD_OFFSET;
	*pTemp16++ = P1_Q2_BD_OFFSET;
	*pTemp16++ = P1_Q3_BD_OFFSET;
	*pTemp16++ = P1_Q4_BD_OFFSET;
	// port 1 buffer descriptor
	*pTemp16++ = P2_Q1_BD_OFFSET;
	*pTemp16++ = P2_Q2_BD_OFFSET;
	*pTemp16++ = P2_Q3_BD_OFFSET;
	*pTemp16++ = P2_Q4_BD_OFFSET;


	SocMACAddrGet(INTERFACE_MAC, ifMAC);

	pTemp8 = (Uint8 *)(PRU1_RAM_BASE_ADDRESS + INTERFACE_MAC_ADDR);
	for (i=0; i<6; i++)
	{
		*pTemp8++ = ifMAC[i];
	}

	pTemp16 = (unsigned short *)(DATARAM1_PHYS_BASE + P0_COL_QUEUE_DESC_OFFSET);
	*pTemp16++ = P0_COL_BD_OFFSET;
	*pTemp16++ = P0_COL_BD_OFFSET;
	*pTemp16++ = 0x0000;
	*pTemp16++ = 0x0000;
	*pTemp16++ = P1_COL_BD_OFFSET;
	*pTemp16++ = P1_COL_BD_OFFSET;
	*pTemp16++ = 0x0000;
	*pTemp16++ = 0x0000;
	*pTemp16++ = P2_COL_BD_OFFSET;
	*pTemp16++ = P2_COL_BD_OFFSET;
	*pTemp16++ = 0x0000;
	*pTemp16++ = 0x0000;
	// Port 0
	*pTemp16++ = P0_Q1_BD_OFFSET;
	*pTemp16++ = P0_Q1_BD_OFFSET;
	*pTemp16++ = 0x0000;
	*pTemp16++ = 0x0000;
	*pTemp16++ = P0_Q2_BD_OFFSET;
	*pTemp16++ = P0_Q2_BD_OFFSET;
	*pTemp16++ = 0x0000;
	*pTemp16++ = 0x0000;
	*pTemp16++ = P0_Q3_BD_OFFSET;
	*pTemp16++ = P0_Q3_BD_OFFSET;
	*pTemp16++ = 0x0000;
	*pTemp16++ = 0x0000;
	*pTemp16++ = P0_Q4_BD_OFFSET;
	*pTemp16++ = P0_Q4_BD_OFFSET;
	*pTemp16++ = 0x0000;
	*pTemp16++ = 0x0000;
	//Port1
	*pTemp16++ = P1_Q1_BD_OFFSET;
	*pTemp16++ = P1_Q1_BD_OFFSET;
	*pTemp16++ = 0x0000;
	*pTemp16++ = 0x0000;
	*pTemp16++ = P1_Q2_BD_OFFSET;
	*pTemp16++ = P1_Q2_BD_OFFSET;
	*pTemp16++ = 0x0000;
	*pTemp16++ = 0x0000;
	*pTemp16++ = P1_Q3_BD_OFFSET;
	*pTemp16++ = P1_Q3_BD_OFFSET;
	*pTemp16++ = 0x0000;
	*pTemp16++ = 0x0000;
	*pTemp16++ = P1_Q4_BD_OFFSET;
	*pTemp16++ = P1_Q4_BD_OFFSET;
	*pTemp16++ = 0x0000;
	*pTemp16++ = 0x0000;
	//Port2
	*pTemp16++ = P2_Q1_BD_OFFSET;
	*pTemp16++ = P2_Q1_BD_OFFSET;
	*pTemp16++ = 0x0000;
	*pTemp16++ = 0x0000;
	*pTemp16++ = P2_Q2_BD_OFFSET;
	*pTemp16++ = P2_Q2_BD_OFFSET;
	*pTemp16++ = 0x0000;
	*pTemp16++ = 0x0000;
	*pTemp16++ = P2_Q3_BD_OFFSET;
	*pTemp16++ = P2_Q3_BD_OFFSET;
	*pTemp16++ = 0x0000;
	*pTemp16++ = 0x0000;
	*pTemp16++ = P2_Q4_BD_OFFSET;
	*pTemp16++ = P2_Q4_BD_OFFSET;
	*pTemp16++ = 0x0000;
	*pTemp16++ = 0x0000;

	ICSSIoctl(ICSSPORT1, PORT_DISABLE);
	ICSSIoctl(ICSSPORT2, PORT_DISABLE);

	PortInit();

	return 0;
}

/**
* @internal
* @brief  Function to clear ICSS shared,pru memory and L3 ocmc ram
*
* @param none
*
* @retval 0 on success
*/
Uint8 ICSSMemInit()
{
	unsigned int *pTemp;
	unsigned int i;
	pTemp = (unsigned int *)ICSS_SHARED_RAM_BASE_ADDRESS;
	//clear all registers, clear all Shared RAM (12K)
	for (i=0; i<(0x3000/4); i++)
	{
		*(pTemp + i) = 0x00000000;
	}

	pTemp = (unsigned int *)PRU0_RAM_BASE_ADDRESS;
	//clear all registers 8kB
	for (i=0; i<(0x2000/4); i++)
	{
		*(pTemp + i) = 0x00000000;
	}

	pTemp = (unsigned int *)PRU1_RAM_BASE_ADDRESS;
	//clear all registers 8kB
	for (i=0; i<(0x2000/4); i++)
	{
		*(pTemp + i) = 0x00000000;
	}

	pTemp = (unsigned int *)L3_OCMC_BASE_ADDRESS;
	//clear all registers, clear all OCMC (64K)
	for (i=0; i<(0x10000/4); i++)
	{
		*(pTemp + i) = 0x00000000;
	}

	return 0;
}

/**
* @brief  Function to initialize and configure ICSS
*
* @retval 0 on success
*/
Int8 ICSSSwitchInit(void)
{
	StopSwitch();

	tpruss_intc_initdata pruss_intc_initdata = PRUSS_INTC_INITDATA;

	ICSSMemInit();
	ICSSSwitchConfig();

	/* Set in constant table C28 to ICSS Shared memory 0x10000 */
	HWREG(PRUSS_BASE + PRUSS_PRU0_CTRL_CTPPR0) = (ICSS_SHARED_RAM_BASE_ADDRESS & 0x000FFFFF) >> 8;
	HWREG(PRUSS_BASE + PRUSS_PRU1_CTRL_CTPPR0) = (ICSS_SHARED_RAM_BASE_ADDRESS & 0x000FFFFF) >> 8;
	/* Set in constant table C30 to shared RAM 0x40300000 */
	HWREG(PRUSS_BASE + PRUSS_PRU0_CTRL_CTPPR1) = (L3_OCMC_BASE_ADDRESS & 0x00FFFF00) >> 8;
	HWREG(PRUSS_BASE + PRUSS_PRU1_CTRL_CTPPR1) = (L3_OCMC_BASE_ADDRESS & 0x00FFFF00) >> 8;
	/* configure PRUSS register CFG */
	PRUSSDRVPruCfgInit();

	/* configure PRUSS INTC */
	PRUSSDRVPruIntcInit(&pruss_intc_initdata);
	
	/* configure PRUSS register MII_RT */
	PRUSSDRVPruMiiRtCfgInit();

	// Enable IEP Counter
	HWREGH(PRUSS_IEP_BASE + PRU_IEP_GLB_CFG_REG) = IEP_GLOBAL_CFG_REG_VAL;

	PRUSSDRVPruDisable(0);
    PRUSSDRVPruDisable(1);

    return 0;
}

/**
* @brief  Function to enables the PRU's and start the switch
*
* @retval 0 on success
*/
Int8 StartSwitch(void)
{
	PRUSSDRVPruEnable(1);
	return PRUSSDRVPruEnable(0);
}

/**
* @brief  Function disable the prus and stops the switch
*
* @retval 0 on success
*/
Int8 StopSwitch(void)
{
	PRUSSDRVPruDisable(1);

	return PRUSSDRVPruDisable(0);
}

/**
* @brief  function to enable and disable Physical ports
*
* @param port port number
* @param command  whether disable or enable
*
* @retval 0 on success
*/
Int8 ICSSIoctl(enum t_portNum port, enum t_portCmd command)
{
	Uint8* pControl;
	Int8 ret = 0;

	if(port == ICSSPORT1)
		pControl = (Uint8*)(PRU1_RAM_BASE_ADDRESS + SWITCH_CONTROL_ADDR+2);
	else if(port == ICSSPORT2)
		pControl = (Uint8*)(PRU1_RAM_BASE_ADDRESS + SWITCH_CONTROL_ADDR+3);
	else
		return -2;

	switch (command)
	{
	case PORT_ENABLE:
		*pControl = 1;
		break;
	case PORT_DISABLE:
		*pControl = 0;
		break;
	default:
		ret = -1;
		break;
	}
	return ret;
}

/**
* @internal
* @brief  function to configure PRUSS register
*
* @param none
*
* @retval none
*/
void PRUSSDRVPruCfgInit(void)
{
	   // Enabling the master interface of ICSS
	HWREG(PRUSS_BASE + PRUSS_CFG_SYSCFG) = 0  | 	\
			(PRUSS_SYSCFG_STANDBY_INIT_ENABLE << PRUSS_SYSCFG_STANDBY_INIT_SHIFT) |  \
			(PRUSS_SYSCFG_STANDBY_MODE_SMART << PRUSS_SYSCFG_STANDBY_MODE_SHIFT)  | \
			(PRUSS_SYSCFG_IDLE_MODE_SMART << PRUSS_SYSCFG_IDLE_MODE_SHIFT);

	HWREG(PRUSS_BASE + PRUSS_CFG_GPCFG0) = 0 | \
			(PRUSS_GPCFG0_PRU0_GPI_MODE_MIIMODE << PRUSS_GPCFG0_PRU0_GPI_MODE_SHIFT);

	HWREG(PRUSS_BASE + PRUSS_CFG_GPCFG1) = 0 | \
			(PRUSS_GPCFG1_PRU1_GPI_MODE_MIIMODE << PRUSS_GPCFG1_PRU1_GPI_MODE_SHIFT);

	HWREG(PRUSS_BASE + PRUSS_CFG_MII_RT) = 0 | \
			(PRUSS_MII_RT_EVENT_EN_ENABLE << PRUSS_MII_RT_EVENT_EN_SHIFT);

	HWREG(PRUSS_BASE + PRUSS_CFG_SPP) = 0 | \
			(PRUSS_SPP_XFER_SHIFT_EN_ENABLE << PRUSS_SPP_XFER_SHIFT_EN_SHIFT);
}

/**
* @internal
* @brief   function to configure PRUSS MII_RT register
*
* @param none
*
* @retval none
*/
void PRUSSDRVPruMiiRtCfgInit(void)
{
	/* Configure the minimum inter packet gap for the Tx on bothe the ports */
    
	HWREGB(PRUSS_BASE + PRUSS_MII_RT_CFG_TXIPG0)	= TX_MIN_IPG;
	HWREGB(PRUSS_BASE + PRUSS_MII_RT_CFG_TXIPG1)	= TX_MIN_IPG;

	/* Configuration of Port 0 */
	HWREG(PRUSS_BASE + PRUSS_MII_RT_CFG_RXCFG0) = 0 | \
			(PRUSS_RXCFG0_RX_MUX_SELECT_PORT0 << PRUSS_RXCFG0_RX_MUX_SELECT_SHIFT) | \
			(PRUSS_RXCFG0_RX_L2_ENABLE << PRUSS_RXCFG0_RX_L2_SHIFT) | \
			(PRUSS_RXCFG0_RX_CUT_PREAMBLE_ENABLE << PRUSS_RXCFG0_RX_CUT_PREAMBLE_SHIFT) | \
			(PRUSS_RXCFG0_RX_ENABLE << PRUSS_RXCFG0_RX_ENABLE_SHIFT);


	/* Enable the output FIFO and set a delay of 0x40 in nibbles, route TX back to Port0 */
	HWREG(PRUSS_BASE + PRUSS_MII_RT_CFG_TXCFG0) = 0 | \
			(0x0 << PRUSS_TXCFG0_TX_START_DELAY_SHIFT) | \
			(PRUSS_TXCFG0_TX_MUX_SEL_PORT1 << PRUSS_TXCFG0_TX_MUX_SEL_SHIFT) | \
			(PRUSS_TXCFG0_TX_AUTO_PREAMBLE_ENABLE << PRUSS_TXCFG0_TX_AUTO_PREAMBLE_SHIFT) | \
			(PRUSS_TXCFG0_TX_ENABLE << PRUSS_TXCFG0_TX_ENABLE_SHIFT);


	/* Configuration of Port 1 */

	HWREG(PRUSS_BASE + PRUSS_MII_RT_CFG_RXCFG1) = 0 | \
			(PRUSS_RXCFG1_RX_L2_ENABLE << PRUSS_RXCFG1_RX_L2_SHIFT) | \
			(PRUSS_RXCFG1_RX_MUX_SELECT_PORT1 << PRUSS_RXCFG1_RX_MUX_SELECT_SHIFT) | \
			(PRUSS_RXCFG1_RX_CUT_PREAMBLE_ENABLE << PRUSS_RXCFG1_RX_CUT_PREAMBLE_SHIFT) | \
			(PRUSS_RXCFG1_RX_ENABLE << PRUSS_RXCFG1_RX_ENABLE_SHIFT);


	/* Enable the output FIFO and set a delay of 0x40 in nibbles, route TX back to Port0 */
	HWREG(PRUSS_BASE + PRUSS_MII_RT_CFG_TXCFG1) = 0 | \
			(0x0 << PRUSS_TXCFG0_TX_START_DELAY_SHIFT) | \
			(PRUSS_TXCFG0_TX_MUX_SEL_PORT0 << PRUSS_TXCFG0_TX_MUX_SEL_SHIFT) | \
			(PRUSS_TXCFG1_TX_AUTO_PREAMBLE_ENABLE << PRUSS_TXCFG1_TX_AUTO_PREAMBLE_SHIFT) | \
			(PRUSS_TXCFG1_TX_ENABLE << PRUSS_TXCFG1_TX_ENABLE_SHIFT);
			
		
}

/**
* @brief   This function returns the MAC address for the EVM
* @details This is a copy of the starterware code but writes the bytes
*			in reverse order to match PRU-ICSS switch
*     	 	Currently we read the second MAC address stored in the
*      		device in order to reserve first one for other ports.
*
* @param addrIdx the MAC address index.
* @param macAddr the Pointer where the MAC address shall be stored
*
* @retval none
*/
void SocMACAddrGet(unsigned int addrIdx, unsigned char *macAddr)
{
	//UInt64 mac_address =0;
	
	if(&macAddr == NULL)
		goto ERROR;

	macAddr[5] =  (HWREG(SOC_CONTROL_REGS + CONTROL_MAC_ID_LO(0))		// always get the base MAC
                   >> 8) & 0xFF;
	macAddr[4] =  (HWREG(SOC_CONTROL_REGS + CONTROL_MAC_ID_LO(0)))
                  & 0xFF;
	macAddr[3] =  (HWREG(SOC_CONTROL_REGS + CONTROL_MAC_ID_HI(0))
                   >> 24) & 0xFF;
	macAddr[2] =  (HWREG(SOC_CONTROL_REGS + CONTROL_MAC_ID_HI(0))
                   >> 16) & 0xFF;
	macAddr[1] =  (HWREG(SOC_CONTROL_REGS + CONTROL_MAC_ID_HI(0))
                   >> 8) & 0xFF;
	macAddr[0] =  (HWREG(SOC_CONTROL_REGS + CONTROL_MAC_ID_HI(0)))
                  & 0xFF;

	//mac_address = ( macAddr[0] << 8 | macAddr[1]);
	//mac_address = (mac_address << 32);
	//mac_address = (mac_address | ( macAddr[2] << 24 | macAddr[3] << 16 | macAddr[4] << 8 | macAddr[5]));
	
   switch (addrIdx)
	{
	case INTERFACE_MAC:
		break;
	case PORT1_MAC:
		macAddr[5] += 1;
		break;
		//mac_address = mac_address + 1;
		//goto ASSIGN_BACK_MAC_ADR;
	case PORT2_MAC:
		macAddr[5] += 2;
		//mac_address = mac_address + 2;
//ASSIGN_BACK_MAC_ADR:
		//macAddr[5] =  (mac_address) & 0xFF;
		//macAddr[4] =  (mac_address >> 8) & 0xFF;
		//macAddr[3] =  (mac_address >> 16) & 0xFF;
		//macAddr[2] =  (mac_address >> 24) & 0xFF;
		//macAddr[1] =  (mac_address >> 32) & 0xFF;
		//macAddr[0] =  (mac_address >> 40) & 0xFF;
		break;
	default:
		goto ERROR;
	}

	return;

ERROR:
	//System_printf("INIT: invalid parameter for socMACAddrGet\n");
	// memset(macAddr, 0, 6); // FW this doesn't work if the error case copies data!
	return;
}

/**
* @brief   API to load firmware to PRU
*
* @param firmware_load start address of firmware to be loaded
* @param firmware_length size of firmware
* @param pruinst the pru to which firmware is to be loaded
*
* @retval success(0) or failure (<0)
*/
Int8 LoadPruFirmware(unsigned int * firmware_load,unsigned int firmware_length,unsigned int pruinst)
{
	Int8 check;
	unsigned int length=0;
	if(pruinst==0)
	{
		length = PRUSSDRVPruWriteMemory(PRUSS0_PRU0_IRAM, 0,
				(unsigned int *) firmware_load, firmware_length);
		if(length >= firmware_length/4)
		{
			check=0;
		}
		else
		{
			check=-1;
		}
	}
	else if(pruinst==1)
	{
		length = PRUSSDRVPruWriteMemory(PRUSS0_PRU1_IRAM, 0,
				(unsigned int *) firmware_load, firmware_length);
		if(length >= firmware_length/4)
		{
			check=0;
		}
		else
		{
			check=-1;
		}
	}
	return check;
}

/**
* @brief   Loads multicast static table to ICSS memory
*
* @param staticTable	 		start address of the static table
* @param staticTableLength	length of the static table
* @param staticTableType	type of static table
* 			'static_table_type' can take one of the below values. \n
*			0 - Forward Table
*			1 - Receive Table
* @param portNumber Portnumber
*
* @retval success(0) or failure (<0)
*/
Int32 LoadStaticTable(const unsigned int * staticTable, Uint8 staticTableLength, Uint8 staticTableType, Uint8 portNumber)
{
	Int32 i;
    Int32 staticTableOffset;
	if(portNumber == PORT1)
	{
		if(staticTableType == STATIC_FORWARD_TABLE)
			staticTableOffset = STATIC_MAC_TABLE_FWD_PORT1;
		else if(staticTableType == STATIC_RECEIVE_TABLE)
			staticTableOffset = STATIC_MAC_TABLE_RCV_PORT1;
		else
			return -1;
	}
	else if(portNumber == PORT2)
	{
		if(staticTableType == STATIC_FORWARD_TABLE)
			staticTableOffset = STATIC_MAC_TABLE_FWD_PORT2;
		else if(staticTableType == STATIC_RECEIVE_TABLE)
			staticTableOffset = STATIC_MAC_TABLE_RCV_PORT2;
		else
			return -1;
	
	}
	else
		return -1;
		
    for (i = 0; i < staticTableLength; i++)
        HWREG(ICSS_SHARED_RAM_BASE_ADDRESS +  staticTableOffset+ (i << 2)) = staticTable[i];

    return 0;
}

