/*
Copyright (C) 2008, 2009  GE Intelligent Platforms, Inc
 
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
 
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.
 
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include <common.h>
#include <command.h>
#include <asm/au1x00.h>
#include <asm/mipsregs.h>
#include "octeon_boot.h"
#include <pci.h>
#include <lib_octeon_shared.h>
#include <lib_octeon.h>

#include "prestReg.h"

#define PRESTERA_PORTS		10			/* Prestera has 10 ports to be managed					*/
#define PRESTERA_ADDR		0x50		/* Prestera appears on the MDIO bus at slave address 0	*/

#define MDIO_RW_STATUS			0x001F
#define MDIO_RW_READ_READY		0x0001
#define MDIO_RW_WRITE_DONE		0x0002
#define MDIO_WR_ADDR_MSB		0x0000
#define MDIO_WR_ADDR_LSB		0x0001
#define MDIO_WR_DATA_MSB		0x0002
#define MDIO_WR_DATA_LSB		0x0003
#define MDIO_RD_ADDR_MSB		0x0004
#define MDIO_RD_ADDR_LSB		0x0005
#define MDIO_RD_DATA_MSB		0x0006
#define MDIO_RD_DATA_LSB		0x0007
#define PRESTERA_TIMEOUT		0x0FFF
#define MAKE_QWORD( x, y )		( (u64)0 | ((u64)x << 32) | (u64)y )

int32_t npaPresteraReadReg( uint32_t reg, uint32_t * pVal ) ;
int npaPresteraReadPortReg( unsigned int port, unsigned int reg, unsigned int * pVal );
int32_t npaPresteraWriteReg( uint32_t reg, uint32_t val ) ;
int npaPresteraWritePortReg( unsigned int port, unsigned int reg, unsigned int val ) ;

extern void phyRegWrite (int, int, int, int, int);
extern void phyRegRead (int, int, unsigned short *);

int32_t npaPresteraReadReg( uint32_t reg, uint32_t * pVal )
{
	unsigned short val;
	uint32_t timeout = PRESTERA_TIMEOUT * 10;
	uint32_t regData = 0x0;

	/* write Read address MSB */
	phyRegWrite( PRESTERA_ADDR, MDIO_RD_ADDR_MSB, 0x0000, (unsigned short)((reg & 0xFFFF0000) >> 16 ), 0 );

	/* write read address LSB */
	phyRegWrite( PRESTERA_ADDR, MDIO_RD_ADDR_LSB, 0x0000, (unsigned short)( reg & 0xFFFF ), 0 );

	/* wait for read ready to be HIGH */
	while( timeout )  {
		phyRegRead( PRESTERA_ADDR, MDIO_RW_STATUS, &val );
		if( (val & MDIO_RW_READ_READY) == MDIO_RW_READ_READY )
			break;
		timeout--;
	}
	if( !timeout )  {
		printf("npaPresteraReadReg(): error reading register value\n");
		return -1;
	}

	/* read read data MSB */
	phyRegRead( PRESTERA_ADDR, MDIO_RD_DATA_MSB, &val );
	regData = val;
	regData = regData << 16;

	/* read read data LSB */
	phyRegRead( PRESTERA_ADDR, MDIO_RD_DATA_LSB, &val );
	regData |= val;

/*KPB*/
/*printf("\nnpaPresteraReadReg: reg:0x%08x, *pVal:0x%08x\n", reg, *pVal);*/

	*pVal = regData;

	return 0;
}

int32_t npaPresteraWriteReg( uint32_t reg, uint32_t val )
{
	uint32_t timeout = PRESTERA_TIMEOUT;
	unsigned short rVal;

/*KPB*/
/*printf("\nnpaPresteraWriteReg: reg:0x%08x, val:0x%08x\n", reg, val);*/

	/* write write address MSB */
	phyRegWrite( PRESTERA_ADDR, MDIO_WR_ADDR_MSB, 0x0000, (unsigned short)((reg & 0xFFFF0000) >> 16 ), 0 );

	/* write write address LSB */
	phyRegWrite( PRESTERA_ADDR, MDIO_WR_ADDR_LSB, 0x0000, (unsigned short)( reg & 0xFFFF ), 0 );

	/* write write data MSB */
	phyRegWrite( PRESTERA_ADDR, MDIO_WR_DATA_MSB, 0x0000, (unsigned short)((val & 0xFFFF0000) >> 16 ), 0 );

	/* write write data LSB */
	phyRegWrite( PRESTERA_ADDR, MDIO_WR_DATA_LSB, 0x0000, (unsigned short)( val & 0xFFFF ), 0 );

	/* wait for write done to be HIGH */
	while( timeout )  {
		phyRegRead( PRESTERA_ADDR, MDIO_RW_STATUS, &rVal );
		if( (rVal & MDIO_RW_WRITE_DONE) == MDIO_RW_WRITE_DONE )
			break;
		timeout--;
	}
	if( !timeout )  {
		printf("npaPresteraWriteReg(): error writing register value\n");
		return -1;
	}

#ifdef VERIFY_WRITE
	{
		uint32_t readBack = 0x0;

		if( 0 != npaPresteraReadReg( reg, &readBack )) {
			printf("npaPresteraWriteReg(): error reading write value @ 0x%x, %x != %x\n", reg, val, readBack );
		} else {
			if( readBack != val )  {
				printf("npaPresteraWriteReg(): error with write value @ 0x%x, %x != %x\n", reg, val, readBack );
			}
		}
	}
#endif

	return 0;
}

int npaPresteraReadPortReg( unsigned int port, unsigned int reg, unsigned int * pVal )
{
	/* calculate port region */
	if( ( reg & 0x4000 ) == 0x4000 )
		port = port * 6;
	if( port >= 0 && port <= 5 )
		reg |= ( 8 << 23 );				/* address is 0x4xxxxxx */
	else if( port >= 6 && port <= 11 )
		reg |= ( 9 << 23 );				/* address is 0x48xxxxx */
	else if( port >= 12 && port <= 17 )
		reg |= ( 10 << 23 );			/* address is 0x5xxxxxx */
	else if( port >= 18 && port <= 23 )
		reg |= ( 11 << 23 );			/* address is 0x58xxxxx */
	else
	{
		printf("npaPresteraReadPortReg(): invalid port: %d\n", port );
		return -1;
	}

	/* do not shift port offset into GOP registers */
	if( ( reg & 0x4000 ) == 0 )
	{
		/* add port offset */
		reg |= ( (port % 6) << 8 );
	}

	/* do the op */
	return npaPresteraReadReg( reg, pVal );
}

int npaPresteraWritePortReg( unsigned int port, unsigned int reg, unsigned int val )
{
	/* calculate port region */
	if( ( reg & 0x4000 ) == 0x4000 )
		port = port * 6;
	if( port >= 0 && port <= 5 )
		reg |= ( 8 << 23 );
	else if( port >= 6 && port <= 11 )
		reg |= ( 9 << 23 );
	else if( port >= 12 && port <= 17 )
		reg |= ( 10 << 23 );
	else if( port >= 18 && port <= 23 )
		reg |= ( 11 << 23 );
	else
	{
		printf("npaPresteraWritePortReg(): invalid port: %d\n", port );
		return -1;
	}

	/* do not shift port offset into GOP registers */
	if( ( reg & 0x4000 ) == 0 )
	{
		/* add port offset */
		reg |= ( (port % 6) << 8 );
	}

	/* do the op */
	return npaPresteraWriteReg( reg, val );
}

int npaPresteraConfigPort( int port, int type )
{
	uint32_t regVal = 0;

	if( type == PRESTERA_SGMII )
	{
		/* configure for SGMII */
		do  {
			if( 0 != npaPresteraReadPortReg( port, PORT_MAC_CONTROL, &regVal ))
				break;
			regVal &= ~( PORT_MAC_CONTROL_SERDES );	/* clear SERDES bit to set to copper mode */
			regVal |= ( PORT_MAC_ENABLE );			/* enable the port */
			if( 0 != npaPresteraWritePortReg( port, PORT_MAC_CONTROL, regVal ))
				break;
			if( 0 != npaPresteraReadPortReg( port, PORT_AUTO_NEG, &regVal ))
				break;

#ifdef GOODSTUFF
			regVal |= ( PORT_AUTO_NEG_ON );
			regVal |= ( PORT_AUTO_SPEED_ON );
#else
			regVal |= ( PORT_AUTO_NEG_SPEED );		/* enable auto negotiation */
			regVal &= ~( PORT_AUTO_NEG_DUPLEX );	/* disable auto speed negotiation */
			regVal |= ( PORT_AUTO_NEG_INBAND );		/* enable inband auto negotiation */
			regVal &= ~( PORT_AUTO_NEG_RESTART );	/* restart auto-neg process */
			regVal &= ~( PORT_AUTO_NEG_LINKDWN );	/* clear force link down bit */
			regVal &= ~( PORT_AUTO_NEG_LINKUP );	/* clear force link up bit */
#endif

			if( 0 != npaPresteraWritePortReg( port, PORT_AUTO_NEG, regVal ))
				break;

#if 1 /* straight from Marvell HackNote */
			npaPresteraWritePortReg( port / 6, 0x4040, 0x00019015 );
			npaPresteraWritePortReg( port, 0x10, 0xF8CC );
			npaPresteraWritePortReg( port, 0x10, 0x78CC );
#endif

			/* Put the SERDES through a reset */
			npaPresteraReadReg( PORT_SERDES_CFG(port), &regVal );
			regVal |= ( PORT_SERDES_CFG_RESET );
			npaPresteraWriteReg( PORT_SERDES_CFG(port), regVal );

			/* "Wait several milliseconds" according to Marvell */
			udelay(2000);

			/* Clear the SERDES from reset */
			npaPresteraReadReg( PORT_SERDES_CFG(port), &regVal );
			regVal &= ~( PORT_SERDES_CFG_RESET );
			npaPresteraWriteReg( PORT_SERDES_CFG(port), regVal );

			return 0;
		} while( 0 );
		printf("npaPresteraConfigPort(): error while configuring port %d to %d mode\n", port, type);
		return -1;
	}
	else if( type == PRESTERA_SERDES )
	{
		/* configure for Serdes */
		do  {
			if( 0 != npaPresteraReadPortReg( port, PORT_MAC_CONTROL, &regVal ))
				break;
			regVal |= ( PORT_MAC_CONTROL_SERDES );	/* set to SERDES port type */
			regVal |= ( PORT_MAC_ENABLE );			/* enable the port */
			if( 0 != npaPresteraWritePortReg( port, PORT_MAC_CONTROL, regVal ))
				break;

			if( 0 != npaPresteraReadPortReg( port, PORT_AUTO_NEG, &regVal ))
				break;

			regVal |= ( PORT_AUTO_NEG_SPEED );		/* enable auto negotiation */
			regVal &= ~( PORT_AUTO_NEG_DUPLEX );	/* disable auto speed negotiation */
			regVal &= ~( PORT_AUTO_NEG_INBAND );	/* disable inband auto negotiation */
			regVal &= ~( PORT_AUTO_NEG_RESTART );	/* don't restart auto-neg process */
			regVal |= ( PORT_AUTO_NEG_LINKUP );		/* set force link up bit */
			regVal |= ( PORT_AUTO_NEG_SFD );		/* set for full duplex */
			regVal &= ~( PORT_AUTO_NEG_LINKDWN );	/* clear force link down bit */

			if( 0 != npaPresteraWritePortReg( port, PORT_AUTO_NEG, regVal ))
				break;

#if 1 /* straight from Marvell HackNote */
			npaPresteraWritePortReg( port / 6, 0x4040, 0x00019015 );
			npaPresteraWritePortReg( port, 0x10, 0xF8CC );
			npaPresteraWritePortReg( port, 0x10, 0x78CC );
#endif

			/* Put the SERDES through a reset */
			npaPresteraReadReg( PORT_SERDES_CFG(port), &regVal );
			regVal |= ( PORT_SERDES_CFG_RESET );
			npaPresteraWriteReg( PORT_SERDES_CFG(port), regVal );

			/* "Wait several milliseconds" according to Marvell */
			udelay(4000);

			/* Clear the SERDES from reset */
			npaPresteraReadReg( PORT_SERDES_CFG(port), &regVal );
			regVal &= ~( PORT_SERDES_CFG_RESET );
			npaPresteraWriteReg( PORT_SERDES_CFG(port), regVal );

			return 0;
		} while( 0 );
		printf("npaPresteraConfigPort(): error while configuring port %d to %d mode\n", port, type);
		return -1;
	}
	else  {
		printf("npaPresteraConfigPort(): invalid configuration on port %d: %d\n", port, type );
		return -1;
	}

	return 0;
}

void npaPresteraDumpStats( void )
{
	int port = 0;
	unsigned char bGood = 0;
	uint32_t statusReg = 0;

	/* accumulate all card statitics */
	for( port = 0; port < PRESTERA_PORTS; port++ )  {
		uint32_t regVal = 0x0;
		uint32_t timeout = PRESTERA_TIMEOUT;
		unsigned char errFlag = 0;
		uint32_t dataBuf[sizeof(MIB_COUNTER_OVERLAY_t)/4];
		MIB_COUNTER_OVERLAY_t * pMIB = NULL;
		void *pTemp;
		uint32_t i;

		do  {
			/* verify link status */
			npaPresteraReadReg( PORT_MAC_STATUS(port), &statusReg );
			if( statusReg & PORT_MAC_STATUS_LINK_UP )
				printf("\nPort %d: Link Up", port);
			else
				printf("\nPort %d: Link Down", port);
			/* set which port to capture the MIB counters for */
			if( 0!= npaPresteraReadReg( MIB_COUNTER_CONTROL(port), &regVal ))
				break;
			regVal |= MIB_COUNTER_CONTROL_PORT_CAPTURE( port%6 );
			if( 0 != npaPresteraWriteReg( MIB_COUNTER_CONTROL(port), regVal ))
				break;
			/* poll for the capturedone to go low */
			while( timeout-- && !errFlag ) {
				if( 0!= npaPresteraReadReg( MIB_COUNTER_CONTROL(port), &regVal ))  {
					errFlag = 1;
					break;
				}
			if( ( regVal & MIB_COUNTER_CONTROL_COPYDONE ) == 0x0 )
				break;
			}
			if( timeout == 0 || errFlag )
				break; /* out of while */
			/* read the captured MIB counters */
			memset( dataBuf, 0x00, sizeof(dataBuf ));
			for( i = 0; i < (sizeof(MIB_COUNTER_OVERLAY_t)/4) && !errFlag; i++ )  {
				if( 0!= npaPresteraReadReg( MIB_COUNTER_PORT_OFFSET(port)+(i*4), &dataBuf[i])) {
					errFlag = 1;
					break;
				}
			}
			if( errFlag )
				break; /* out of while */
			/* accumulate the stats */
			pTemp = (void *)&dataBuf[0];
			pMIB = (MIB_COUNTER_OVERLAY_t *)pTemp;
			printf ("\n GFrmRx    : 0x%08x", pMIB->GoodFramesReceived);
			printf (" GFrmTx      : 0x%08x", pMIB->GoodFramesSent);
			printf (" GOctRxLsb   : 0x%08x", pMIB->GoodOctetsReceivedLsb);
			printf (" GOctRxMsb   : 0x%08x", pMIB->GoodOctetsReceivedMsb);
			printf ("\n BOctRx    : 0x%08x", pMIB->BadOctetsReceived);
			printf (" MACTransErr : 0x%08x", pMIB->MACTransErr);
			printf (" BFramesRx   : 0x%08x", pMIB->BadFramesReceived);
			printf (" BcstFrmRx   : 0x%08x", pMIB->BroadcastFramesReceived);
			printf ("\n McstFrmRx : 0x%08x", pMIB->MulticastFramesReceived);
			printf (" ExColl      : 0x%08x", pMIB->ExcessiveCollision);
			printf (" Collision   : 0x%08x", pMIB->Collisions);
			printf (" LateColl    : 0x%08x", pMIB->LateCollisions);
			printf ("\n BcstFrmTx : 0x%08x", pMIB->BroadcastFramesSent);
			printf (" McstFrmTx   : 0x%08x", pMIB->MulticastFramesSent);
			printf (" UnrMACCtlRx : 0x%08x", pMIB->UnrecogMACControlReceived);
			printf (" FCTx        : 0x%08x", pMIB->FCSent);
			printf ("\n GFCRx     : 0x%08x", pMIB->GoodFCReceived);
			printf (" DropEvent   : 0x%08x", pMIB->DropEvent);
			printf (" Undersize   : 0x%08x", pMIB->Undersize);
			printf (" Fragments   : 0x%08x", pMIB->Fragments);
			printf ("\n Oversize  : 0x%08x", pMIB->Oversize);
			printf (" Jabber      : 0x%08x", pMIB->Jabber);
			printf (" MACRxError  : 0x%08x", pMIB->MACRcvError);
			printf (" BadCRC      : 0x%08x", pMIB->BadCRC);

#if 0
			pNetStats->rx_packets += pMIB->GoodFramesReceived;
			pNetStats->tx_packets += pMIB->GoodFramesSent;
			pNetStats->rx_bytes += MAKE_QWORD( pMIB->GoodOctetsReceivedMsb, pMIB->GoodOctetsReceivedLsb );
			pNetStats->tx_bytes += MAKE_QWORD( pMIB->GoodOctetsSentMsb, pMIB->GoodOctetsSentLsb );
			pNetStats->rx_errors += pMIB->BadFramesReceived;
			pNetStats->rx_dropped += pMIB->DropEvent;
			pNetStats->collisions += ( pMIB->Collisions + pMIB->LateCollisions );
			pNetStats->rx_length_errors += ( pMIB->Undersize + pMIB->Oversize );
			pNetStats->rx_crc_errors += pMIB->BadCRC;
#endif

			bGood = 1;
		} while( 0 );
		if( !bGood )  {
	    /* just warning level */
			printf("npaPresteraTimer(): error gathering statistics on port %d\n", port );
		}
	}
	printf("\n");
}

int npaPresteraGlobalErrata( void )
{
	uint32_t regVal;

	npaPresteraReadReg (0x04004060, &regVal);
	regVal |= 0x00000004;
	npaPresteraWriteReg (0x04004060, regVal);

	npaPresteraReadReg (0x04804060, &regVal);
	regVal |= 0x00000004;
	npaPresteraWriteReg (0x04804060, regVal);

	npaPresteraReadReg (0x05004060, &regVal);
	regVal |= 0x00000004;
	npaPresteraWriteReg (0x05004060, regVal);

	npaPresteraReadReg (0x05804060, &regVal);
	regVal |= 0x00000004;
	npaPresteraWriteReg (0x05804060, regVal);

	npaPresteraReadReg (0x00000054, &regVal);
	regVal |= 0x00000004;
	npaPresteraWriteReg (0x00000054, regVal);

	npaPresteraWriteReg (0x03000020, 0x0360740e);
	npaPresteraWriteReg (0x03000000, 0x7FCFF3FC);
	npaPresteraWriteReg (0x01800004, 0x039227F3);
	npaPresteraWriteReg (0x01850000, 0x00F00490);

	return 0;
}

int32_t npaPresteraReadPhyReg( uint32_t phyNum, uint32_t reg, uint32_t * pVal )
{
	uint32_t regVal = 0;
	uint32_t timeout = PRESTERA_TIMEOUT;

	/* wait for SMI ready */
	while( timeout-- )
	{
		npaPresteraReadReg( SMI_MANAGEMENT_0, &regVal );
		if( ( regVal & SMI_MANAGEMENT_BUSY ) == 0 )
			break;
	}
	if( timeout == 0 )
	{
		printf("npaPresteraReadPhyReg(): error reading PHY register, prestera not ready!\n");
		return -1;
	}

	/* do read operation */
	regVal = 0;
	SMI_MANAGEMENT_SETREAD( regVal );
	regVal |= SMI_MANAGEMENT_SETPHY( phyNum );
	regVal |= SMI_MANAGEMENT_SETREG( reg );
	npaPresteraWriteReg( SMI_MANAGEMENT_0, regVal );

	/* wait for readvalid ready */
	timeout = PRESTERA_TIMEOUT;
	while( timeout-- )
	{
		npaPresteraReadReg( SMI_MANAGEMENT_0, &regVal );
		if( ( regVal & SMI_MANAGEMENT_READVALID ) ==  SMI_MANAGEMENT_READVALID )
			break;
	}
	if( timeout == 0 )
	{
		printf("npaPresteraReadPhyReg(): error reading PHY register, prestera read not ready!\n");
		return -1;
	}

	*pVal = SMI_MANAGEMENT_DATA(regVal);
	return 0;
}

int32_t npaPresteraWritePhyReg( uint32_t phyNum, uint32_t reg, uint32_t val )
{
	uint32_t regVal = 0;
	uint32_t timeout = PRESTERA_TIMEOUT;

	/* wait for SMI ready */
	while( timeout-- )
	{
		npaPresteraReadReg( SMI_MANAGEMENT_0, &regVal );
		if( ( regVal & SMI_MANAGEMENT_BUSY ) == 0 )
			break;
	}
	if( timeout == 0 )
	{
		printf("npaPresteraWritePhyReg(): error reading PHY register, prestera not ready!\n");
		return -1;
	}

	/* do write operation */
	regVal = 0;
	SMI_MANAGEMENT_SETWRITE( regVal );
	regVal |= SMI_MANAGEMENT_SETPHY( phyNum );
	regVal |= SMI_MANAGEMENT_SETREG( reg );
	regVal |= SMI_MANAGEMENT_DATA( val );
	npaPresteraWriteReg( SMI_MANAGEMENT_0, regVal );

	/* wait for SMI ready */
	while( timeout-- )
	{
		npaPresteraReadReg( SMI_MANAGEMENT_0, &regVal );
		if( ( regVal & SMI_MANAGEMENT_BUSY ) == 0 )
			break;
	}
	if( timeout == 0 )
	{
		printf("npaPresteraWritePhyReg(): error reading PHY register, prestera ready not ready!\n");
		return -1;
	}

	return 0;
}

int npaPresteraMRUInit( void )
{
	char *s;
	int mru, mruField, port;
	uint32_t regVal;

	/* initialize mruField */
	s = getenv ("presteramru");
	if (s != NULL)
	{
		mru = simple_strtoul(s, NULL, 10); /* octets */
		switch (mru)
		{
			case 1518:
				mruField = 0;
				break;
			case 1522:
				mruField = 1;
				break;
			case 1536:
				mruField = 6;
				break;
			case 1552:
				mruField = 2;
				break;
			case 1664:
				mruField = 3;
				break;
			case 2048:
				mruField = 4;
				break;
			case 10240:
			default:
				mruField = 5;
				break;
		}
	}
	else
	{
		mruField = 5; /* use MRU 10240 octets */
	}

	/* initialize MAC Control register */
	for( port = 0; port < PRESTERA_PORTS; port++ )
	{
		npaPresteraReadPortReg( port, PORT_MAC_CONTROL, &regVal );
		regVal &= ~(0x00000007 << 9);
		regVal |= (mruField << 9);
		npaPresteraWritePortReg( port, PORT_MAC_CONTROL, regVal );
	}

	return 0;
}

void npaPrestera88E1111BaseInit ( void )
{
	uint32_t regVal;

	regVal = 0x3FFFFFFF; /* default PHY addresses to all ones */
	regVal &= ~((0x1F << 10 /* PHY 2 */) | (0x1F << 15 /* PHY 3 */));
	/* initialize the MDIO addresses for the connected ports */
	regVal |= (( (0x40 & 0x1F) << 10 /* PHY 2 */ ) | ( (0x41 & 0x1F) << 15 /* PHY 3 */ ) );
	npaPresteraWriteReg( SMI_PHY_ADDR( 0 ), regVal );
	/* first 88E111 */
	npaPresteraWritePhyReg( 0, 22, 0x0000 ); /* command SFP/M88E1111 page to 0 (copper) */
	npaPresteraWritePhyReg( 0, 27, 0x9084 ); /* command SFP/M88E1111 into SGMII mode */
	npaPresteraWritePhyReg( 0, 9, 0x0F00 ); /* command SFP/M88E1111 to advertise 1000BASE-T Full/Half-Duplex */
	npaPresteraWritePhyReg( 0, 4, 0x0DE1 ); /* command SFP/M88E1111 to advertise 100/10BASE-T Full/Half-Duplex */
	/* second 88E111 */
	npaPresteraWritePhyReg( 1, 22, 0x0000 ); /* command SFP/M88E1111 page to 0 (copper) */
	npaPresteraWritePhyReg( 1, 27, 0x9084 ); /* command SFP/M88E1111 into SGMII mode */
	npaPresteraWritePhyReg( 1, 9, 0x0F00 ); /* command SFP/M88E1111 to advertise 1000BASE-T Full/Half-Duplex */
	npaPresteraWritePhyReg( 1, 4, 0x0DE1 ); /* command SFP/M88E1111 to advertise 100/10BASE-T Full/Half-Duplex */
}

void npaPrestera88E1111Clock125Enable ( void )
{
	uint32_t regVal;

	/* turn on the 125CLK from first 88E1111*/
	npaPresteraReadPhyReg( 0, 16, &regVal );
	regVal &= ~0x00000010;
	npaPresteraWritePhyReg( 0, 16, regVal );

	/* turn on the 125CLK from the second 88E1111*/
	npaPresteraReadPhyReg( 1, 16, &regVal );
	regVal &= ~0x00000010;
	npaPresteraWritePhyReg( 1, 16, regVal );
}

#define PHY_WAIT_TIMEOUT 10000000
void npaPrestera88E1111Reset ( void )
{
	uint32_t i, regVal;

	/* reset the first 88E1111 */
	npaPresteraWritePhyReg( 0, 0, 0x9140 );
	/* wait for reset to complete */
	for (i = 0; i < PHY_WAIT_TIMEOUT; i++)
	{
		npaPresteraReadPhyReg( 0, 0, &regVal );
		if ((regVal & 0x00008000) == 0)
		{
			break;
		}
	}
	if (i == PHY_WAIT_TIMEOUT)
	{
		printf("npaPresteraReset88E1111(): 88E1111 / PHY 0 is not working\n");
	}
	npaPresteraWritePhyReg( 0, 4, 0x0DE1 ); /* command SFP/M88E1111 to advertise 100/10BASE-T Full/Half-Duplex */
	/* reset the second 88E1111 */
	npaPresteraWritePhyReg( 1, 0, 0x9140 );
	/* wait for reset to complete */
	for (i = 0; i < PHY_WAIT_TIMEOUT; i++)
	{
		npaPresteraReadPhyReg( 1, 0, &regVal );
		if ((regVal & 0x00008000) == 0)
		{
			break;
		}
	}
	if (i == PHY_WAIT_TIMEOUT)
	{
		printf("npaPresteraReset88E1111(): 88E1111 / PHY 1 is not working\n");
	}
	npaPresteraWritePhyReg( 1, 4, 0x0DE1 ); /* command SFP/M88E1111 to advertise 100/10BASE-T Full/Half-Duplex */
}

int npaPrestera88E1111Init( void )
{
	npaPrestera88E1111BaseInit ();
	npaPrestera88E1111Clock125Enable ();
	npaPrestera88E1111Reset ();

	return 0;
}

int npaPresteraDeviceIdCheck( void )
{
	uint32_t regVal;

	/* verify the prestera device by trying to do a read */
	if( 0 != npaPresteraReadReg( GLOBAL_DEVID, &regVal ) )  {
		printf("npaPresteraInit(): failed to read global id register\n");
		return -1;
	}
	if (regVal != PRESTERA_GLOBAL_DEVICE_ID)
	{
		return 0;
	}
	return 1;
}

int npaPresteraVltInit (int vid)
{
	uint32_t offset, shift, regVal;

	offset = (vid / 2) * 4;
	shift = (vid % 2) * 9;
	if (npaPresteraReadReg(VLT_BASE + offset, &regVal) != 0)
	{
		return -1;
	}
	regVal &= ~(0x1FF << shift);
	regVal |= ((0x100 | vid) << shift);
	if (npaPresteraWriteReg(VLT_BASE + offset, regVal) != 0)
	{
		return -1;
	}

	return 1;
}

int npaPresteraIvtInit (int vid, int port)
{
	uint32_t offset, mask, update, regVal;

	offset = vid * 0x10;
	if ((port >= 0) && (port <= 3))
	{
		mask = ~(0x03000000 << (port * 2));
		update = (0x01000000 << (port * 2));
	}
	else if ((port >= 4) && (port <= 19))
	{
		offset += 4;
		mask = ~(0x00000003 << ((port - 4) * 2));
		update = (0x00000001 << ((port - 4) * 2));
	}
	else if ((port >= 20) && (port <= 23))
	{
		offset += 8;
		mask = ~(0x00000003 << ((port - 20) * 2));
		update = (0x00000001 << ((port - 20) * 2));
	}
	else
	{
		return -1;
	}
	if (npaPresteraReadReg(IVT_BASE + offset, &regVal) != 0)
	{
		return -1;
	}
	regVal &= mask;
	regVal |= update;
	if (npaPresteraWriteReg(IVT_BASE + offset, regVal) != 0)
	{
		return -1;
	}
	/* make sure VLAN table valid, learn enable and cpu member bits are set for this VID */
	offset = vid * 0x10;
	if (npaPresteraReadReg(IVT_BASE + offset, &regVal) != 0)
	{
		return -1;
	}
	regVal |= 0x00000013;
	if (npaPresteraWriteReg(IVT_BASE + offset, regVal) != 0)
	{
		return -1;
	}

	return 1;
}

int npaPresteraIvtClearVid0 (void)
{
	if (npaPresteraWriteReg(IVT_BASE + 0, 0) != 0)
	{
		return -1;
	}
	if (npaPresteraWriteReg(IVT_BASE + 4, 0) != 0)
	{
		return -1;
	}
	if (npaPresteraWriteReg(IVT_BASE + 8, 0) != 0)
	{
		return -1;
	}

	return 1;
}

int npaPresteraVlanCfg( void )
{
	char *s0, *s1, *s2, *s3, *s4, *s5, *s6, *s7, *s8, *s9;
	int p0Vid = 1, p1Vid = 1, p2Vid = 1, p3Vid = 1, p4Vid = 1;
	int p5Vid = 1, p6Vid = 1, p7Vid = 1, p8Vid = 1, p9Vid = 1;

	/* get the user defined VLAN IDs */
	if ((s0 = getenv ("p0vid")) != NULL)
	{
		p0Vid = simple_strtoul(s0, NULL, 16);
	}
	if ((s1 = getenv ("p1vid")) != NULL)
	{
		p1Vid = simple_strtoul(s1, NULL, 16);
	}
	if ((s2 = getenv ("p2vid")) != NULL)
	{
		p2Vid = simple_strtoul(s2, NULL, 16);
	}
	if ((s3 = getenv ("p3vid")) != NULL)
	{
		p3Vid = simple_strtoul(s3, NULL, 16);
	}
	if ((s4 = getenv ("p4vid")) != NULL)
	{
		p4Vid = simple_strtoul(s4, NULL, 16);
	}
	if ((s5 = getenv ("p5vid")) != NULL)
	{
		p5Vid = simple_strtoul(s5, NULL, 16);
	}
	if ((s6 = getenv ("p6vid")) != NULL)
	{
		p6Vid = simple_strtoul(s6, NULL, 16);
	}
	if ((s7 = getenv ("p7vid")) != NULL)
	{
		p7Vid = simple_strtoul(s7, NULL, 16);
	}
	if ((s8 = getenv ("p8vid")) != NULL)
	{
		p8Vid = simple_strtoul(s8, NULL, 16);
	}
	if ((s9 = getenv ("p9vid")) != NULL)
	{
		p9Vid = simple_strtoul(s9, NULL, 16);
	}
	if ((s0 == NULL) && (s1 == NULL) && (s2 == NULL) && (s3 == NULL) && (s4 == NULL) &&
		(s5 == NULL) && (s6 == NULL) && (s7 == NULL) && (s8 == NULL) && (s9 == NULL))
	{
		/* no VLAN IDs found so do nothing */
		return 0;
	}
	/* 802.1Q */
	if (npaPresteraWriteReg(GLOBAL_CONTROL, 0x00066103) != 0)
	{
		return -1;
	}
	/* VLAN Lookup Mode */
	if (npaPresteraWriteReg(MAC_TABLE_CONTROL, 0x000062B1) != 0)
	{
		return -1;
	}
	/* VLAN ID for each port */
	if (npaPresteraWriteReg(P0_P1_VID, ((p1Vid << 16) | p0Vid)) != 0)
	{
		return -1;
	}
	if (npaPresteraWriteReg(P2_P3_VID, ((p3Vid << 16) | p2Vid)) != 0)
	{
		return -1;
	}
	if (npaPresteraWriteReg(P4_P5_VID, ((p5Vid << 16) | p4Vid)) != 0)
	{
		return -1;
	}
	if (npaPresteraWriteReg(P6_P7_VID, ((p7Vid << 16) | p6Vid)) != 0)
	{
		return -1;
	}
	if (npaPresteraWriteReg(P8_P9_VID, ((p9Vid << 16) | p8Vid)) != 0)
	{
		return -1;
	}
	/* VLT (VLAN Translation Table) */
	/* The VLT translates the 4K VLAN space into the 256 VLANs supported by the Prestera */
	npaPresteraVltInit(p0Vid);
	npaPresteraVltInit(p1Vid);
	npaPresteraVltInit(p2Vid);
	npaPresteraVltInit(p3Vid);
	npaPresteraVltInit(p4Vid);
	npaPresteraVltInit(p5Vid);
	npaPresteraVltInit(p6Vid);
	npaPresteraVltInit(p7Vid);
	npaPresteraVltInit(p8Vid);
	npaPresteraVltInit(p9Vid);
	/* IVT (Internal VLAN Table) */
	/* The IVT defines which ports are assigned to each of the 256 VLANs supported by the Prestera */
	npaPresteraIvtClearVid0();
	npaPresteraIvtInit(p0Vid, 0);
	npaPresteraIvtInit(p1Vid, 1);
	npaPresteraIvtInit(p2Vid, 2);
	npaPresteraIvtInit(p3Vid, 3);
	npaPresteraIvtInit(p4Vid, 4);
	npaPresteraIvtInit(p5Vid, 5);
	npaPresteraIvtInit(p6Vid, 6);
	npaPresteraIvtInit(p7Vid, 7);
	npaPresteraIvtInit(p8Vid, 8);
	npaPresteraIvtInit(p9Vid, 9);

	return 1;
}

int npaPresteraInit( void )
{
	uint32_t regVal, i;

	/* reset the prestera */
	if (npaPresteraWriteReg( GLOBAL_CONTROL, 0x0007610B) != 0) {
		return -1;
	}
	/* wait for reset completion */
	for (i = 0; i < PRESTERA_RESET_WAIT; i++) {
		if( 0 != npaPresteraReadReg( GLOBAL_CONTROL, &regVal ) )  {
			printf("npaPresteraInit(): failed to read global control register\n");
			return -1;
		}
		if ((regVal & 0x00010000) == 0)
		{
			/* reset completed */
			break;
		}
		udelay(2000);
	}
	if (i == PRESTERA_RESET_WAIT)
	{
		printf("npaPresteraInit(): reset completion failed\n");
		return -1;
	}
	/* verify the prestera device by trying to do a read */
	if( 0 != npaPresteraReadReg( GLOBAL_DEVID, &regVal ) )  {
		printf("npaPresteraInit(): failed to read global id register\n");
		return -1;
	}
	if (regVal != PRESTERA_GLOBAL_DEVICE_ID)
	{
		printf( "npaPresteraInit(): Warning - read 0x%08x for global device id\n", regVal );

	}

#ifdef NOTNOW
	/* dump prestera global control register */
	if( 0 != npaPresteraReadReg( GLOBAL_CONTROL, &regVal ) )  {
		printf("npaPresteraInit(): failed to read global control register\n");
		return -1;
	}
	printf( "npaPresteraInit(): read 0x%08x for global control register\n", regVal );

	/* dump prestera global configuration register */
	if( 0 != npaPresteraReadReg( GLOBAL_CONFIG, &regVal ) )  {
		printf("npaPresteraInit(): failed to read global configuration register\n");
		return -1;
	}
	printf( "npaPresteraInit(): read 0x%08x for global configuration register\n", regVal );
#endif

	npaPresteraGlobalErrata ();
	npaPresteraMRUInit();
	npaPrestera88E1111Init();

	return 0;
}
