/*
 * dx160_tsec.c
 * Marvell DX160 to Freescale Three Speed Ethernet Controller driver
 *
 * This software may be used and distributed according to the
 * terms of the GNU Public License, Version 2, incorporated
 * herein by reference.
 *
 * Copyright 2004 Freescale Semiconductor.
 * (C) Copyright 2003, Motorola, Inc.
 * author Andy Fleming
 *
 */

#include <config.h>
#include <mpc85xx.h>
#include <common.h>
#include <malloc.h>
#include <net.h>
#include <command.h>

#if defined(CONFIG_DX160_TSEC_ENET)
#include "dx160_tsec.h"
#include "miiphy.h"

#ifndef TRUE
#define TRUE 1
#endif
#ifndef FALSE
#define FALSE 0
#endif

#define CALC_MASK(fieldLen, fieldOffset, mask)     \
            if(((fieldLen) + (fieldOffset)) >= 32)     \
                (mask) = (0 - (1<< (fieldOffset)));    \
            else                                   \
          (mask) = (((1<<((fieldLen) + (fieldOffset)))) - (1 << (fieldOffset)))

#define TX_BUF_CNT		2
#define DEBUG_PRNT   		printf
#define DEBUG_TRACE(format,arg...) printf("%s:%d " format "\n",__FILE__,__LINE__,arg)

static uint rxIdx;	/* index of the current RX buffer */
static uint txIdx;	/* index of the current TX buffer */

typedef volatile struct rtxbd {
	txbd8_t txbd[TX_BUF_CNT];
	rxbd8_t rxbd[PKTBUFSRX];
}  RTXBD;

struct tsec_info_struct {
	unsigned int phyaddr;
	u32 flags;
	unsigned int phyregidx;
};


/* The tsec_info structure contains 3 values which the
 * driver uses to determine how to operate a given ethernet
 * device.  For now, the structure is initialized with the
 * knowledge that all current implementations have 2 TSEC
 * devices, and one FEC.  The information needed is:
 *  phyaddr - The address of the PHY which is attached to
 *	the given device.
 *
 *  flags - This variable indicates whether the device
 *	supports gigabit speed ethernet, and whether it should be
 *	in reduced mode.
 *
 *  phyregidx - This variable specifies which ethernet device
 *	controls the MII Management registers which are connected
 *	to the PHY.  For 8540/8560, only TSEC1 (index 0) has
 *	access to the PHYs, so all of the entries have "0".
 *
 * The values specified in the table are taken from the board's
 * config file in include/configs/.  When implementing a new
 * board with ethernet capability, it is necessary to define:
 *   TSEC1_PHY_ADDR
 *   TSEC1_PHYIDX
 *   TSEC2_PHY_ADDR
 *   TSEC2_PHYIDX
 *
 * and for 8560:
 *   FEC_PHY_ADDR
 *   FEC_PHYIDX
 */
static struct tsec_info_struct tsec_info[] = {
#if defined(CONFIG_MPC85XX_TSEC1) || defined(CONFIG_MPC83XX_TSEC1)
#if !defined(CONFIG_RGMII)
  {TSEC1_PHY_ADDR, TSEC_GIGABIT, TSEC1_PHYIDX},
#else
  {TSEC1_PHY_ADDR, TSEC_GIGABIT | TSEC_REDUCED, TSEC1_PHYIDX},
#endif
#else
	{ 0, 0, 0},
#endif
#if defined(CONFIG_MPC85XX_TSEC2) || defined(CONFIG_MPC83XX_TSEC2)
	{TSEC2_PHY_ADDR, TSEC_GIGABIT, TSEC2_PHYIDX},
#else
	{ 0, 0, 0},
#endif
#ifdef CONFIG_MPC85XX_FEC
	{FEC_PHY_ADDR, 0, FEC_PHYIDX},
#else
#    if defined(CONFIG_MPC85XX_TSEC3) || defined(CONFIG_MPC83XX_TSEC3)
	{TSEC3_PHY_ADDR, TSEC_GIGABIT | TSEC_REDUCED, TSEC3_PHYIDX},
#    else
	{ 0, 0, 0},
#    endif
#    if defined(CONFIG_MPC85XX_TSEC4) || defined(CONFIG_MPC83XX_TSEC4)
	{TSEC4_PHY_ADDR, TSEC_REDUCED, TSEC4_PHYIDX},
#    else
	{ 0, 0, 0},
#    endif
#endif
};

#define MAXCONTROLLERS	(4)

// static int relocated = 0;
static uint dx160_phyid = 0x10;
static uint dx160_numOfPorts = 16;
static uint dx160_portPhyIds[ DX160_MAX_NUM_PORTS ];
static struct tsec_private *privlist[MAXCONTROLLERS];


#ifdef __GNUC__
static RTXBD rtx __attribute__ ((aligned(8)));
#else
#error "rtx must be 64-bit aligned"
#endif

static int tsec_send(struct eth_device* dev, volatile void *packet, int length);
static int tsec_recv(struct eth_device* dev);
static int tsec_init(struct eth_device* dev, bd_t * bd);
static void tsec_halt(struct eth_device* dev);
static void init_registers(volatile tsec_t *regs);
static void startup_tsec(struct eth_device *dev);
static int init_phy(struct eth_device *dev);
void write_phy_reg(struct tsec_private *priv, uint phyid, uint regnum, uint value);
uint read_phy_reg(struct tsec_private *priv, uint phyid, uint regnum);
static void adjust_link(struct eth_device *dev);

static uint dx160_smi_read_mii( struct eth_device *dev, uint port, uint phyReg, ushort *data );
static uint dx160_smi_write_mii( struct eth_device *dev, uint port, uint phyReg, ushort data );
static uint dx160_cmd_read( struct eth_device *dev, uint regAddr, uint *dataPt );
static uint dx160_cmd_write( struct eth_device *dev, uint regAddr, uint value );
static uint dx160_read_register( struct eth_device *dev, uint regAddr, uint *dataPt );
static uint dx160_write_register( struct eth_device *dev, uint regAddr, uint value );
static uint dx160_get_reg_field( struct eth_device *dev, uint regAddr, uint fieldOffset, 
					 uint fieldLength, uint *fieldData );
static uint dx160_set_reg_field( struct eth_device *dev, uint regAddr, uint fieldOffset, 
					 uint fieldLength, uint fieldData );

static int dx160_initialise(struct eth_device *dev);
int dx160_phy_marvell88E1143_GetPhyRegs(struct eth_device *dev, uint phyid);

static uint dx160_phy_marvell88E1143init(struct eth_device *dev, uint phyid, uint initialise);
static uint dx160_phy_marvell88E1112init(struct eth_device *dev, uint port, uint initialise);
uint dx160_phy_marvell_startup(struct eth_device *dev);
uint dx160_phy_marvell_stutdown(struct eth_device *dev);


#if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) 
static void dx160_tsec_miiphy_register(const char *devname, unchar addr);
static int dx160_tsec_miiphy_read(char *devname, unchar addr,unchar reg, ushort *value);
static int dx160_tsec_miiphy_write(char *devname, unchar addr,unchar reg, ushort value);
static void dx160_tsec_miiphy_extract_devname(char *phyname, char **ethname);
#endif /* defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) */

                                                                 
/* Initialize device structure. Returns success if PHY
 * initialization succeeded (i.e. if it recognizes the PHY)
 */
int dx160_tsec_initialize(bd_t *bis, int index, char *devname)
{
	struct eth_device* dev;
	int i;
	struct tsec_private *priv;

	dev = (struct eth_device*) malloc(sizeof *dev);

	if(NULL == dev)
		return 0;

	memset(dev, 0, sizeof *dev);

	priv = (struct tsec_private *) malloc(sizeof(*priv));

	if(NULL == priv)
		return 0;

	privlist[index] = priv;
	priv->regs = (volatile tsec_t *)(TSEC_BASE_ADDR + index*TSEC_SIZE);
	priv->phyregs = (volatile tsec_t *)(TSEC_BASE_ADDR +
			tsec_info[index].phyregidx*TSEC_SIZE);

	priv->phyaddr = tsec_info[index].phyaddr;
	priv->flags = tsec_info[index].flags;

	sprintf(dev->name, devname);
	dev->iobase = 0;
	dev->priv   = priv;
	dev->init   = tsec_init;
	dev->halt   = tsec_halt;
	dev->send   = tsec_send;
	dev->recv   = tsec_recv;

	/* Tell u-boot to get the addr from the env */
	for(i=0;i<6;i++)
		dev->enetaddr[i] = 0;

	eth_register(dev);


	/* Reset the MAC */
	priv->regs->maccfg1 |= MACCFG1_SOFT_RESET;
	priv->regs->maccfg1 &= ~(MACCFG1_SOFT_RESET);

	/* Try to initialize MDIO interface */
	init_phy(dev);

	return (dx160_initialise(dev));

}


/* Initializes data structures and registers for the controller,
 * and brings the interface up.	 Returns the link status, meaning
 * that it returns success if the link is up, failure otherwise.
 * This allows u-boot to find the first active controller. */
int tsec_init(struct eth_device* dev, bd_t * bd)
{
	uint tempval;
	char tmpbuf[MAC_ADDR_LEN];
	int i;
	struct tsec_private *priv = (struct tsec_private *)dev->priv;
	volatile tsec_t *regs = priv->regs;

	/* Make sure the controller is stopped */
	tsec_halt(dev);

	/* Init MACCFG2.  Defaults to GMII */
	regs->maccfg2 = MACCFG2_INIT_SETTINGS;

	/* Init ECNTRL */
	regs->ecntrl = ECNTRL_INIT_SETTINGS;
  if (priv->flags & TSEC_REDUCED)
  {
  	regs->ecntrl = ECNTRL_INIT_SETTINGS | ECNTRL_RP_MODE;
  }
  else
  {
    regs->ecntrl = ECNTRL_INIT_SETTINGS;
  }

	/* Copy the station address into the address registers.
	 * Backwards, because little endian MACS are dumb */
	for(i=0;i<MAC_ADDR_LEN;i++) {
		tmpbuf[MAC_ADDR_LEN - 1 - i] = dev->enetaddr[i];
	}
	regs->macstnaddr1 = *((uint *)(tmpbuf));

	tempval = *((uint *)(tmpbuf +4));

	regs->macstnaddr2 = tempval;

	/* reset the indices to zero */
	rxIdx = 0;
	txIdx = 0;

	/* Clear out (for the most part) the other registers */
	init_registers(regs);

	/* Ready the device for tx/rx */
	startup_tsec(dev);

	/* If there's no link, fail */
	return priv->link;

}


/* Write value to the device's PHY through the registers
 * specified in priv, modifying the register specified in regnum.
 * It will wait for the write to be done (or for a timeout to
 * expire) before exiting
 */
void write_phy_reg(struct tsec_private *priv, uint phyid, uint regnum, uint value)
{
	volatile tsec_t *regbase = priv->phyregs;
//	uint phyid = priv->phyaddr;
	int timeout=1000000;

	regbase->miimadd = (phyid << 8) | regnum;
	regbase->miimcon = value;
	asm("sync");

	timeout=1000000;
	while((regbase->miimind & MIIMIND_BUSY) && timeout--);
}


/* Reads register regnum on the device's PHY through the
 * registers specified in priv.	 It lowers and raises the read
 * command, and waits for the data to become valid (miimind
 * notvalid bit cleared), and the bus to cease activity (miimind
 * busy bit cleared), and then returns the value
 */
uint read_phy_reg(struct tsec_private *priv, uint phyid, uint regnum)
{
	uint value;
	volatile tsec_t *regbase = priv->phyregs;

	/* Put the address of the phy, and the register
	 * number into MIIMADD */
	regbase->miimadd = (phyid << 8) | regnum;

	/* Clear the command register, and wait */
	regbase->miimcom = 0;
	asm("sync");

	/* Initiate a read command, and wait */
	regbase->miimcom = MIIM_READ_COMMAND;
	asm("sync");

	/* Wait for the the indication that the read is done */
	while((regbase->miimind & (MIIMIND_NOTVALID | MIIMIND_BUSY)));

	/* Grab the value read from the PHY */
	value = regbase->miimstat;
	return value;
}


/* Discover which PHY is attached to the device, and configure it
 * properly.  If the PHY is not recognized, then return 0
 * (failure).  Otherwise, return 1
 */
static int init_phy(struct eth_device *dev)
{
	struct tsec_private *priv = (struct tsec_private *)dev->priv;

	/* Assign a Physical address to the TBI */

	{
		volatile tsec_t *regs = (volatile tsec_t *)(TSEC_BASE_ADDR);
		regs->tbipa = TBIPA_VALUE;
		regs = (volatile tsec_t *)(TSEC_BASE_ADDR + TSEC_SIZE);
		regs->tbipa = TBIPA_VALUE;
		asm("sync");
	}

	/* Reset MII (due to new addresses) */
	priv->phyregs->miimcfg = MIIMCFG_RESET;
	asm("sync");
	priv->phyregs->miimcfg = MIIMCFG_INIT_VALUE;
	asm("sync");
	while(priv->phyregs->miimind & MIIMIND_BUSY);

	return 1;
}

/* Initialized required registers to appropriate values, zeroing
 * those we don't care about (unless zero is bad, in which case,
 * choose a more appropriate value) */
static void init_registers(volatile tsec_t *regs)
{
	/* Clear IEVENT */
	regs->ievent = IEVENT_INIT_CLEAR;

	regs->imask = IMASK_INIT_CLEAR;

	regs->hash.iaddr0 = 0;
	regs->hash.iaddr1 = 0;
	regs->hash.iaddr2 = 0;
	regs->hash.iaddr3 = 0;
	regs->hash.iaddr4 = 0;
	regs->hash.iaddr5 = 0;
	regs->hash.iaddr6 = 0;
	regs->hash.iaddr7 = 0;

	regs->hash.gaddr0 = 0;
	regs->hash.gaddr1 = 0;
	regs->hash.gaddr2 = 0;
	regs->hash.gaddr3 = 0;
	regs->hash.gaddr4 = 0;
	regs->hash.gaddr5 = 0;
	regs->hash.gaddr6 = 0;
	regs->hash.gaddr7 = 0;

	regs->rctrl = 0x00000000;

	/* Init RMON mib registers */
	memset((void *)&(regs->rmon), 0, sizeof(rmon_mib_t));

	regs->rmon.cam1 = 0xffffffff;
	regs->rmon.cam2 = 0xffffffff;

	regs->mrblr = MRBLR_INIT_SETTINGS;

	regs->minflr = MINFLR_INIT_SETTINGS;

	regs->attr = ATTR_INIT_SETTINGS;
	regs->attreli = ATTRELI_INIT_SETTINGS;

}


/* Configure maccfg2 based on negotiated speed and duplex
 * reported by PHY handling code */
static void adjust_link(struct eth_device *dev)
{
	struct tsec_private *priv = (struct tsec_private *)dev->priv;
	volatile tsec_t *regs = priv->regs;

  if(priv->link) {
		if(priv->duplexity != 0)
			regs->maccfg2 |= MACCFG2_FULL_DUPLEX;
		else
			regs->maccfg2 &= ~(MACCFG2_FULL_DUPLEX);

		regs->maccfg2 = ((regs->maccfg2&~(MACCFG2_IF))	| MACCFG2_GMII);
// 		switch(priv->speed) {
// 			case 1000:
// 				regs->maccfg2 = ((regs->maccfg2&~(MACCFG2_IF))
// 					| MACCFG2_GMII);
// 				break;
// 			case 100:
// 			case 10:
// 				regs->maccfg2 = ((regs->maccfg2&~(MACCFG2_IF))
// 					| MACCFG2_MII);
// 
// 				/* If We're in reduced mode, we need
// 				 * to say whether we're 10 or 100 MB.
// 				 */
// 				if ((priv->speed == 100)
// 				    && (priv->flags & TSEC_REDUCED))
// 					regs->ecntrl |= ECNTRL_R100;
// 				else
// 					regs->ecntrl &= ~(ECNTRL_R100);
// 				break;
// 			default:
// 				printf("%s: Speed was bad\n", dev->name);
// 				break;
// 		}

		printf("Speed: %d, %s duplex\n", priv->speed,
				(priv->duplexity) ? "full" : "half");

	} else {
		printf("%s: No link.\n", dev->name);
	}
}


/* Set up the buffers and their descriptors, and bring up the
 * interface */
static void startup_tsec(struct eth_device *dev)
{
	int i;
	struct tsec_private *priv = (struct tsec_private *)dev->priv;
	volatile tsec_t *regs = priv->regs;

	/* Point to the buffer descriptors */
	regs->tbase = (unsigned int)(&rtx.txbd[txIdx]);
	regs->rbase = (unsigned int)(&rtx.rxbd[rxIdx]);

	/* Initialize the Rx Buffer descriptors */
	for (i = 0; i < PKTBUFSRX; i++) {
		rtx.rxbd[i].status = RXBD_EMPTY;
		rtx.rxbd[i].length = 0;
		rtx.rxbd[i].bufPtr = (uint)NetRxPackets[i];
	}
	rtx.rxbd[PKTBUFSRX -1].status |= RXBD_WRAP;

	/* Initialize the TX Buffer Descriptors */
	for(i=0; i<TX_BUF_CNT; i++) {
		rtx.txbd[i].status = 0;
		rtx.txbd[i].length = 0;
		rtx.txbd[i].bufPtr = 0;
	}
	rtx.txbd[TX_BUF_CNT -1].status |= TXBD_WRAP;

	/* Start up the PHY */
  	dx160_phy_marvell_startup(dev);
//	phy_run_commands(priv, priv->phyinfo->startup);
	adjust_link(dev);

	/* Enable Transmit and Receive */
	regs->maccfg1 |= (MACCFG1_RX_EN | MACCFG1_TX_EN);

	/* Tell the DMA it is clear to go */
	regs->dmactrl |= DMACTRL_INIT_SETTINGS;
	regs->tstat = TSTAT_CLEAR_THALT;
	regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS);
}

/* This returns the status bits of the device.	The return value
 * is never checked, and this is what the 8260 driver did, so we
 * do the same.	 Presumably, this would be zero if there were no
 * errors */
static int tsec_send(struct eth_device* dev, volatile void *packet, int length)
{
	int i;
	int result = 0;
	struct tsec_private *priv = (struct tsec_private *)dev->priv;
	volatile tsec_t *regs = priv->regs;


	/* Find an empty buffer descriptor */
	for(i=0; rtx.txbd[txIdx].status & TXBD_READY; i++) {
		if (i >= TOUT_LOOP) {
			debug ("%s: tsec: tx buffers full\n", dev->name);
			return result;
		}
	}


	VLAN_Ethernet_t *vet = (VLAN_Ethernet_t *)packet;

	if ( vet->vet_vlan_type != htons(PROT_VLAN) )
	{
		puts("VLAN tag not set dropping packet.  use the command 'setenv vlan 1' \n");
		return result;
	};

	vet->vet_vlan_type = htons(0x4020);	/* TAG of VLAN			*/

	rtx.txbd[txIdx].bufPtr = (uint)packet;
	rtx.txbd[txIdx].length = length;
	rtx.txbd[txIdx].status |= (TXBD_READY | TXBD_LAST | TXBD_CRC | TXBD_INTERRUPT);

	/* Tell the DMA to go */
	regs->tstat = TSTAT_CLEAR_THALT;

	/* Wait for buffer to be transmitted */
	for(i=0; rtx.txbd[txIdx].status & TXBD_READY; i++) {
		if (i >= TOUT_LOOP) {
			debug ("%s: tsec: tx error\n", dev->name);
			return result;
		}
	}

	txIdx = (txIdx + 1) % TX_BUF_CNT;
	result = rtx.txbd[txIdx].status & TXBD_STATS;

	return result;
}

static int tsec_recv(struct eth_device* dev)
{
	int length;
	struct tsec_private *priv = (struct tsec_private *)dev->priv;
	volatile tsec_t *regs = priv->regs;

	while(!(rtx.rxbd[rxIdx].status & RXBD_EMPTY)) {

		length = rtx.rxbd[rxIdx].length;

		/* Send the packet up if there were no errors */
		if (!(rtx.rxbd[rxIdx].status & RXBD_STATS)) 
		{
			VLAN_Ethernet_t *vet = (VLAN_Ethernet_t *)NetRxPackets[rxIdx];
			vet->vet_vlan_type = htons(PROT_VLAN);	/* TAG of VLAN			*/
			NetReceive(NetRxPackets[rxIdx], length - 4);
		} else {
			printf("Got error %x\n",
					(rtx.rxbd[rxIdx].status & RXBD_STATS));
		}

		rtx.rxbd[rxIdx].length = 0;

		/* Set the wrap bit if this is the last element in the list */
		rtx.rxbd[rxIdx].status = RXBD_EMPTY | (((rxIdx + 1) == PKTBUFSRX) ? RXBD_WRAP : 0);

		rxIdx = (rxIdx + 1) % PKTBUFSRX;
	}

	if(regs->ievent&IEVENT_BSY) {
		regs->ievent = IEVENT_BSY;
		regs->rstat = RSTAT_CLEAR_RHALT;
	}

	return -1;

}


/* Stop the interface */
static void tsec_halt(struct eth_device* dev)
{
	struct tsec_private *priv = (struct tsec_private *)dev->priv;
	volatile tsec_t *regs = priv->regs;

	regs->dmactrl &= ~(DMACTRL_GRS | DMACTRL_GTS);
	regs->dmactrl |= (DMACTRL_GRS | DMACTRL_GTS);

	while(!(regs->ievent & (IEVENT_GRSC | IEVENT_GTSC)));

	regs->maccfg1 &= ~(MACCFG1_TX_EN | MACCFG1_RX_EN);

	/* Shut down the PHY, as needed */
        dx160_phy_marvell_stutdown(dev);
}


static uint
dx160_smi_read_mii( struct eth_device *dev, uint port, uint phyReg, ushort *data )
{
	uint 	        value;           /* value to write into the register */
	uint 		regAddr;         /* register address                 */
	uint 		reg;             /* register address                 */
	uint 		portAddr;        /* port address                     */
	uint 		copm;
	uint 		busy;
	uint 		state;
	volatile uint   retryCnt;   /* retry counter for busy SMI reg   */

	if ( port > dx160_numOfPorts )
	{
		return ( FALSE );
	} 

	portAddr = dx160_portPhyIds[port];
    	value = ((portAddr & 0x1F) << 16) | ((phyReg & 0x1F) << 21) | (1 << 26);

	regAddr = 0x04004054;
	if ( port >= 12 )
	{
    		regAddr += 0x1000000;
	}

        reg = 0x04004034 + ((port / 6 )* 0x800000);
	if ( !dx160_get_reg_field( dev, reg, 7, 1, &state ) )
	{
		printf(  "%s  %s:%u port %u reg read fail\n", __FUNCTION__, __FILE__, __LINE__, port );
		return ( FALSE );
	}

	if ( !state )
	{
		if ( !dx160_set_reg_field( dev, reg, 7, 1, 1 ) )
		{
			printf(  "%s  %s:%u port %u reg write fail\n", __FUNCTION__, __FILE__, __LINE__, port );
			return ( FALSE );
		}
	}

	retryCnt = SMI_RETRY_COUNTER_CNS;
	do
	{
		if ( !dx160_get_reg_field( dev, regAddr,28,1,&busy ) )
		{
			printf(  "%s  %s:%u ERROR regAddr %x value %x\n", __FUNCTION__, __FILE__, __LINE__, regAddr, 8, value,8);
			dx160_set_reg_field( dev, reg, 7, 1, state );
			return ( FALSE );
		}
	
		retryCnt--;
	} while ( busy && retryCnt != 0 );


	dx160_write_register( dev, regAddr, value );

	retryCnt = SMI_RETRY_COUNTER_CNS;
	do
	{
		if ( !dx160_read_register( dev, regAddr, &value ) )
		{
			printf("%s  %s:%u failed to read reg %x\n", __FUNCTION__, __FILE__, __LINE__, regAddr);
			return -1;
		}
		
		copm = (value >> 27) & 0x1;
		retryCnt--;
	} while ( !copm && retryCnt != 0 );

	if ( !dx160_set_reg_field( dev, reg, 7, 1, state ) )
	{
		printf(  "%s  %s:%u port %u write fail\n", __FUNCTION__, __FILE__, __LINE__, port );
		return ( FALSE );
	}

	if ( retryCnt == 0 )
	{
		printf(  "%s  %s:%u retryCnt == 0 regAddr %x value %x\n", __FUNCTION__, __FILE__, __LINE__, regAddr, 8, value,8);
		return ( FALSE );
	}
	*data = (ushort)(value & 0xFFFF);
	return ( TRUE );
}


static uint
dx160_smi_write_mii( struct eth_device *dev, uint port, uint phyReg, ushort data )
{
	uint 			value;           /* value to write into the register */
	uint 			regAddr;         /* register address                 */
	uint 			reg;             /* register address                 */
	uint 			portAddr;        /* port address                     */
	uint 			busy;
	uint 			state;
	volatile uint  retryCnt;   /* retry counter for busy SMI reg   */

	if ( port > dx160_numOfPorts )
	{
		return ( FALSE );
	} 

        reg = 0x04004034 + ((port / 6 )* 0x800000);
	if ( !dx160_get_reg_field( dev, reg, 7, 1, &state ) )
	{
		printf(  "%s  %s:%u port %u reg write fail\n", __FUNCTION__, __FILE__, __LINE__, port );
		return ( FALSE );
	}

	if ( !state )
	{
		if ( !dx160_set_reg_field( dev, reg, 7, 1, 1 ) )
		{
			printf(  "%s  %s:%u port %u reg write fail\n", __FUNCTION__, __FILE__, __LINE__, port );
			return ( FALSE );
		}
	}

	portAddr = dx160_portPhyIds[port];
	value = ( data & 0xffff ) | ( ( portAddr & 0x1F ) << 16 ) | ( ( phyReg & 0x1F ) << 21 );

	regAddr = 0x04004054;
	if ( port >= 12 )
	{
	    	regAddr += 0x1000000;
	}

	dx160_write_register( dev, regAddr, value );

	retryCnt = SMI_RETRY_COUNTER_CNS;
	do
	{
		if ( !dx160_get_reg_field( dev, regAddr,28,1,&busy ) )
		{
			printf(  "%s  %s:%u Failed to ret %x register\n", __FUNCTION__, __FILE__, __LINE__, regAddr );
			return ( FALSE );
		}

        	retryCnt--;
	} while ( busy && retryCnt != 0 );

	if ( !dx160_set_reg_field( dev, reg, 7, 1, state ) )
	{
		printf(  "%s  %s:%u port %u reg write fail\n", __FUNCTION__, __FILE__, __LINE__, port );
		return ( FALSE );
	}

	if ( retryCnt == 0 )
	{
		return ( FALSE );
	}
	return ( TRUE );

} 


static uint
dx160_cmd_read( struct eth_device *dev, uint regAddr, uint *dataPt )
{				
    uint    		timeOut;
    ushort	 	stat;	 
    ushort    		msb;    
    ushort    		lsb;    
    struct tsec_private *priv = (struct tsec_private *)dev->priv;

    /* write addr to read */
    msb = regAddr >> 16;
    lsb = regAddr & 0xFFFF;

    write_phy_reg( priv, dx160_phyid, SMI_READ_ADDRESS_MSB_REGISTER, msb );
    write_phy_reg( priv, dx160_phyid, SMI_READ_ADDRESS_LSB_REGISTER, lsb );

/*  wait for read done 
*/
    for ( timeOut = SMI_TIMEOUT_COUNTER; ;timeOut--)
    {
	stat = read_phy_reg( priv, dx160_phyid, SMI_STATUS_REGISTER );
        
        if ((stat & SMI_STATUS_WRITE_DONE) != 0)
        {
            break;
        }

	if ( timeOut == 0 )
	{
	    printf(  "%s  %s:%u status read timeout\n", __FUNCTION__, __FILE__, __LINE__ );
	    printf(  "%s  %s:%u phyId %x stat %x", __FUNCTION__, __FILE__, __LINE__, dx160_phyid, stat );
	    return ( FALSE );
	}
    }

    msb = read_phy_reg( priv, dx160_phyid, SMI_READ_DATA_MSB_REGISTER );

    lsb = read_phy_reg( priv, dx160_phyid, SMI_READ_DATA_LSB_REGISTER );

    *dataPt = ((msb & 0xFFFF) << 16) | (lsb & 0xFFFF);
    return ( TRUE );    
} /* dx160_cmd_read */

static uint
dx160_cmd_write( struct eth_device *dev, uint regAddr, uint value )
{	
    uint    		timeOut;
    ushort	 	stat;	 
    ushort    		msb;    
    ushort    		lsb;    
    struct tsec_private *priv = (struct tsec_private *)dev->priv;

    for ( timeOut = SMI_TIMEOUT_COUNTER; ;timeOut--)
    {
	stat = read_phy_reg( priv, dx160_phyid, SMI_STATUS_REGISTER );
        
        if ((stat & SMI_STATUS_WRITE_DONE) != 0)
        {
            break;
        }

	if ( timeOut == 0 )
	{
	    printf(  "%s  %s:%u status read timeout\n", __FUNCTION__, __FILE__, __LINE__ );
	    return ( FALSE );
	}
    }

    
    msb = regAddr >> 16;
    lsb = regAddr & 0xFFFF;
    write_phy_reg( priv, dx160_phyid, SMI_WRITE_ADDRESS_MSB_REGISTER, msb );
    write_phy_reg( priv, dx160_phyid, SMI_WRITE_ADDRESS_LSB_REGISTER, lsb );

    msb = value >> 16;
    lsb = value & 0xFFFF;
    write_phy_reg( priv, dx160_phyid, SMI_WRITE_DATA_MSB_REGISTER, msb );
    write_phy_reg( priv, dx160_phyid, SMI_WRITE_DATA_LSB_REGISTER, lsb );
    return ( TRUE );
} /* dx160_cmd_write */


static uint
dx160_read_register( struct eth_device *dev, uint regAddr, uint *dataPt )
{
    uint 	retVal;
    uint	temp;

    temp = 0;
    retVal = dx160_cmd_read( dev, regAddr, &temp );

    *dataPt = temp;
    return retVal;
} /* dx160_read_register */


static uint
dx160_write_register( struct eth_device *dev, uint regAddr, uint value )
{
    uint 	retVal;

    retVal = dx160_cmd_write( dev, regAddr, value );

    return retVal;
} /* dx160_write_register */


static uint
dx160_get_reg_field( struct eth_device *dev, uint regAddr, uint fieldOffset, 
					 uint fieldLength, uint *fieldData )
{
uint 	retVal;
uint	data;
uint	mask;

    
    retVal = dx160_read_register( dev, regAddr, &data);
    
    CALC_MASK(fieldLength, fieldOffset, mask);

    *fieldData = (uint)((data & mask) >> fieldOffset);

//    DEBUG_PRNT("data 0x%x.\n", *fieldData,8);

    return retVal;
} /* dx160_get_reg_field */


static uint
dx160_set_reg_field( struct eth_device *dev, uint regAddr, uint fieldOffset, 
					 uint fieldLength, uint fieldData )
{
uint 	retVal;
uint	data;
uint	mask;

//    DEBUG_PRNT("SetField to %x, reg 0x%x, offset %u, length %u, data 0x%x.\n",
//    			dev,8, regAddr, 8,fieldOffset, fieldLength, fieldData, 8);

    retVal = dx160_read_register( dev, regAddr,&data );
    if ( !retVal )
    {
        return retVal;
    }

    CALC_MASK(fieldLength, fieldOffset, mask);
    data &= ~mask;   /* turn the field off */
    data |= ((fieldData << fieldOffset) & mask);

    retVal = dx160_write_register( dev, regAddr, data );

    return retVal;
}


static int dx160_initialise(struct eth_device *dev)
{
	uint			timeout;
	uint			port;
	uint			reg;
	uint			value;
	uint			i;

	*(unchar *)0xFF000010 = 1;

/*	Wait about 100us for the output lines to stabilise. */
	udelay( 100 );

	*(unchar *)0xFF000001 = 0;

/*	Wait about 100ms for the reset to complete. */
	for ( i = 0; i < 110; i++ )
	{
		udelay( 1000 );
	}

	*(unchar *)0xFF000001 = 1;

	udelay( 250 );


	
 /*	Initialise the dynamic data
  */
	for (i=0; i<6; i++)
	{
		dx160_portPhyIds[ i ] = i + 1;
	}
	dx160_portPhyIds[ 12 ] = 7;

	if ( !dx160_read_register( dev, 0x4c, &value ) )
	{
		printf("%s  %s:%u failed to read deviceID\n", __FUNCTION__, __FILE__, __LINE__);
		return -1;
	}
//	printf("%s  %s:%u marvell device %08x\n", __FUNCTION__, __FILE__, __LINE__, value);
 	if ( !dx160_write_register( dev, 0x00000004, 0x604ce100 ) )
 	{
 		DEBUG_PRNT( "Failed to set 0x00000004 register" );
		return -1;
 	}

	if ( !dx160_write_register( dev, 0x04004030, 0xc520c41 ) )
	{
		DEBUG_PRNT( "Failed to set 0x04004030 register" );
		return -1;
	}

	if ( !dx160_write_register( dev, 0x04804030, 0x0000000 ) )
	{
		DEBUG_PRNT( "Failed to set 0x04804030 register" );
		return -1;
	}

	if ( !dx160_write_register( dev, 0x05004030, 0x00000107 ) )
	{
		DEBUG_PRNT( "Failed to set 0x05004030 register" );
		return -1;
	}

	if ( !dx160_write_register( dev, 0x05804030, 0x00000000 ) )
	{
		DEBUG_PRNT( "Failed to set 0x05804030 register" );
		return -1;
	}


	if ( !dx160_set_reg_field( dev, 0x04004034,4,1,1 ) )
	{
		DEBUG_PRNT( "Failed to set 0x04004034 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x04004034,8,1,1 ) )
	{
		DEBUG_PRNT( "Failed to set 0x04004034 register" );
		return -1;
	}


	if ( !dx160_write_register( dev, 0x04004040, 0x00019015 ) )
	{
		DEBUG_PRNT( "Failed to set 0x04004040 register" );
		return -1;
	}

	if ( !dx160_write_register( dev, 0x04804040, 0x00019015 ) )
	{
		DEBUG_PRNT( "Failed to set 0x04004040 register" );
		return -1;
	}

	if ( !dx160_write_register( dev, 0x05004040, 0x00019015 ) )
	{
		DEBUG_PRNT( "Failed to set 0x04004040 register" );
		return -1;
	}

	if ( !dx160_write_register( dev, 0x05804040, 0x00019015 ) )
	{
		DEBUG_PRNT( "Failed to set 0x04004040 register" );
		return -1;
	}

	for( port = 0; port < dx160_numOfPorts; port++)
	{
		if ( port < 6 || port == DX160_CPU_PORT )
		{
			reg = 0x04000010 + ((port / 6) * 0x800000) + ((port % 6) * 0x100);
			if ( !dx160_write_register( dev, reg, 0x78cc ) )
			{
				DEBUG_PRNT( "Failed to set port %d, %08x register", port, reg );
				return -1;
			}
		}
	}

	if ( !dx160_set_reg_field( dev, 0, 4, 5, 0 ) )
	{
		DEBUG_PRNT( "Failed to set 0x00000000 register" );
		return -1;
	}
	
	if ( !dx160_set_reg_field( dev, 0, 1, 1, 1 ) )
	{
		DEBUG_PRNT( "Failed to set 0x00000000 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0,3,1, 0 ) )
	{
		DEBUG_PRNT( "Failed to set 0x00000000 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0,14,2,0x01 ) )
	{
		DEBUG_PRNT( "Failed to set 0x00000000 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x02040000,18,1,0 ) )
	{
		DEBUG_PRNT( "Failed to set 0x02040000 register" );
		return -1;
	}

	if ( !dx160_write_register( dev, 0x02040028, 0 ) )
	{
		DEBUG_PRNT( "Failed to set 0x02040028 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x02018000,26,1,1 ) )
	{
		DEBUG_PRNT( "Failed to set 0x02018000 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x02018000,30,1,1 ) )
	{
		DEBUG_PRNT( "Failed to set 0x02018000 register" );
		return -1;
	}

	for( port = 0; port < dx160_numOfPorts; port++ )
	{
		reg = 0x02000000 + (port * 0x1000);
		if ( !dx160_set_reg_field( dev, reg,26,1,0 ) )
		{
			DEBUG_PRNT( "Failed to set port %d, %08x register", port, reg );
			return -1;
		}

		if ( !dx160_set_reg_field( dev, reg,30,1,1 ) )
		{
			DEBUG_PRNT( "Failed to set port %d, %08x register", port, reg );
			return -1;
		}
	
		if ( !dx160_read_register( dev, reg, &value ) )
		{
			DEBUG_PRNT( "Failed to set port %d, %08x register", port, reg );
			return -1;
		}
	
		value &= ~((0x1 << 8) | (0x1 << 12) | (0x1 << 19));  /* reset the bits */
		if ( !dx160_write_register( dev, reg, value ) )
		{
			DEBUG_PRNT( "Failed to set port %d, %08x register", port, reg );
			return -1;
		}
	}

	if ( !dx160_write_register( dev, 0x01810500, 0x02020202 ) )
	{
		DEBUG_PRNT( "Failed to write 0x01810500 register" );
		return -1;
	}

	if ( !dx160_write_register( dev, 0x01800500, 0x02020202 ) )
	{
		DEBUG_PRNT( "Failed to write 0x01800500 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x01800004,16,16,0x392 ) )
	{
		DEBUG_PRNT( "Failed to set 0x01800004 register" );
		return -1;
	}

	for (port = 0; port < dx160_numOfPorts; port++)
        {
		reg = 0x04000000 + ((port / 6) * 0x800000) + ((port % 6) * 0x100);
		if ( !dx160_set_reg_field( dev, reg,17,1,0 ) )
		{
			DEBUG_PRNT( "Failed to set port %d, %08x register", port, reg );
			return -1;
		}
        }

	if ( !dx160_set_reg_field( dev, 0x000000A0,17,1,0 ) )
	{
		DEBUG_PRNT( "Failed to set 0x000000A0 register" );
		return -1;
	}

	if ( !dx160_get_reg_field( dev,0x00000000,17,2,&value ) )
	{
		DEBUG_PRNT( "Failed to read 0x00000000 register" );
		return -1;
	}

        if( value != 0x3)
        {
		if ( !dx160_set_reg_field( dev, 0x000000A0,0,1,0 ) )
		{
			DEBUG_PRNT( "Failed to set 0x000000A0 register" );
			return -1;
		}
	
		timeout = 100;
		while( value != 3 )
		{
			if(timeout == 0)
			{
				DEBUG_PRNT( "Failed to initialise device" );
				return -1;
			}
	
			if ( !dx160_get_reg_field( dev,0x00000000,17,2,&value ) )
			{
				DEBUG_PRNT( "Failed to read 0x00000000 register" );
				return -1;
			}
			udelay(10);
			timeout--;
		}
	
		for( port = 0; port < dx160_numOfPorts; port++)
		{
			reg = 0x04000000 + ((port / 6) * 0x800000) + ((port % 6) * 0x100);
			if ( !dx160_set_reg_field( dev, reg,0,1,0 ) )
			{
				DEBUG_PRNT( "Failed to set port %d, %08x register", port, reg );
				return -1;
			}
		}
	
		if ( !dx160_set_reg_field( dev, 0x000000A0,0,1,0 ) )
		{
			DEBUG_PRNT( "Failed to set 0x000000A0 register" );
			return -1;
		}

	}

	if ( !dx160_set_reg_field( dev, 0x00000000,4,5, 0 ) )
	{
		DEBUG_PRNT( "Failed to set 0x00000000 register" );
		return -1;
	}
	
	if ( !dx160_set_reg_field( dev, 0x01800004,16,16, 0x392 ) )
	{
		DEBUG_PRNT( "Failed to set 0x01800004 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x01800004,11,5, 0x1F ) )
	{
		DEBUG_PRNT( "Failed to set 0x01800004 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x01800004,3,1, 1 ) )
	{
		DEBUG_PRNT( "Failed to set 0x01800004 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x01850000,20,4, 0xF ) )
	{
		DEBUG_PRNT( "Failed to set 0x01850000 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x01850000,25,1, 0 ) )
	{
		DEBUG_PRNT( "Failed to set 0x01850000 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x06000000,7,1, 0 ) )
	{
		DEBUG_PRNT( "Failed to set 0x06000000 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x06000000,4,1, 1 ) )
	{
		DEBUG_PRNT( "Failed to set 0x06000000 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x02040000,1,1, 0 ) )
	{
		DEBUG_PRNT( "Failed to set 0x02040000 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x02040000,3,1, 0 ) )
	{
		DEBUG_PRNT( "Failed to set 0x02040000 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x02040000,20,1, 1 ) )
	{
		DEBUG_PRNT( "Failed to set 0x02040000 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x06000000,9,1, 0 ) )
	{
		DEBUG_PRNT( "Failed to set 0x06000000 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x000000A0,0,1,0 ) )
	{
		DEBUG_PRNT( "Failed to set 0x000000A0 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x000000A0,12,1,0 ) )
	{
		DEBUG_PRNT( "Failed to set 0x000000A0 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x000000A0,26,1,0 ) )
	{
		DEBUG_PRNT( "Failed to set 0x000000A0 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x01800004,4,5,DX160_CPU_PORT ) )
	{
		DEBUG_PRNT( "Failed to set 0x01800004 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x07800004,24,1, 1 ) )
	{
		DEBUG_PRNT( "Failed to set 0x07800004 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x00000028,26,1, 0 ) )
	{
		DEBUG_PRNT( "Failed to set 0x00000028 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x020400A8,16,12, VLAN_VID_MASK ) )
	{
		DEBUG_PRNT( "Failed to set 0x020400A8 register" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x020400AC,16,12, VLAN_VID_MASK ) )
	{
		DEBUG_PRNT( "Failed to set 0x020400AC register" );
		return -1;
	}

	for ( port = 0; port < 6; port++ )
	{
		if ( !dx160_set_reg_field( dev, 0x03000030, port, 1, 1 ) )
		{
			DEBUG_PRNT( "Failed to set 0x03000030 register for port%P", port );
			return -1;
		}
	}
		
	if ( !dx160_write_register( dev, 0x03000020, 0x07F1980E ) )
	{
		DEBUG_PRNT( "Failed to write 0x03000020" );
		return -1;
	}

	if ( !dx160_write_register( dev, 0x03000024, 0x07F1980E ) )
	{
		DEBUG_PRNT( "Failed to write 0x03000024" );
		return -1;
	}

	if ( !dx160_write_register( dev, 0x03000000, 0x7FCCD172 ) )
	{
		DEBUG_PRNT( "Failed to write 0x03000000" );
		return -1;
	}

	if ( !dx160_set_reg_field( dev, 0x02040000,24,1,0 ) )
	{
		DEBUG_PRNT( "Failed to set 0x02040000 register" );
		return -1;
	}

	for ( port = DX160_CPU_PORT, i = 0; i < 1; i++, port++ )
	{
		reg = 0x04000000 + ((port / 6) * 0x800000) + ((port % 6) * 0x100);
		if ( !dx160_set_reg_field( dev, reg,14,1,1 ) )
		{
			DEBUG_PRNT( "Failed to set port %d reg %08x\n", port, reg );
			return -1;
		}

		if ( !dx160_set_reg_field( dev, reg,15,1,0 ) )
		{
			DEBUG_PRNT( "Failed to set port %d reg %08x\n", port, reg );
			return -1;
		}

		if ( !dx160_set_reg_field( dev, reg,9,3,1 ) )
		{
			DEBUG_PRNT( "Failed to set port %d reg %08x\n", port, reg );
			return -1;
		}

		reg = 0x04000008 + ((port / 6) * 0x800000) + ((port % 6) * 0x100);
		if ( !dx160_set_reg_field( dev, reg,0,12,0x1548 ) )
		{
			DEBUG_PRNT( "Failed to set port %d reg %08x\n", port, reg );
			return -1;
		}

		reg = 0x04000000 + ((port / 6) * 0x800000) + ((port % 6) * 0x100);
		if ( !dx160_set_reg_field( dev, reg,12,1,1 ) )
		{
			DEBUG_PRNT( "Failed to set port %d reg %08x\n", port, reg );
			return -1;
		}

		if ( !dx160_set_reg_field( dev, 0x07800004,port,1, 1 ) )
		{
			DEBUG_PRNT( "Failed to set port %d reg 0x07800004\n", port );
			return -1;
		}

		reg = 0x02000004 + ((port/2) * 0x2000);
		if ( !dx160_set_reg_field( dev, reg,(i*16),12,0 ) )
		{
			DEBUG_PRNT( "Failed to set port %d reg %08x\n", port, reg );
			return -1;
		}
	}

	if ( !dx160_write_register( dev, 0x04004100, 0x34a45300 ) )
	{
		DEBUG_PRNT( "Failed to write 0x04004100 register" );
		return -1;
	}

	for( port = 0; port < DX160_MAX_NUM_PORTS; port++)
	{
		if ( port < 6 || port == DX160_CPU_PORT )
		{
			reg = 0x04000008 + ((port / 6) * 0x800000) + ((port % 6) * 0x100);
			if ( !dx160_set_reg_field( dev, reg,0,1,0 ) )
			{
				DEBUG_PRNT( "Failed to set port %d reg %08x\n", port, reg );
				return -1;
			}

			reg = 0x04000000 + ((port / 6) * 0x800000) + ((port % 6) * 0x100);
			if ( !dx160_set_reg_field( dev, reg,15,1,0 ) )
			{
				DEBUG_PRNT( "Failed to set port %d reg %08x\n", port, reg );
				return -1;
			}

			reg = 0x04000000 + ((port / 6) * 0x800000) + ((port % 6) * 0x100);
			if ( !dx160_set_reg_field( dev, reg,9,3,1 ) )
			{
				DEBUG_PRNT( "Failed to set port %d reg %08x\n", port, reg );
				return -1;
			}

			reg = 0x04000000 + ((port / 6) * 0x800000) + ((port % 6) * 0x100);
			if ( !dx160_set_reg_field( dev, reg,0,1,1 ) )
			{
				DEBUG_PRNT( "Failed to set port %d reg %08x\n", port, reg );
				return -1;
			}

			if ( port < 6 )
			{
				reg = 0x04000000 + ((port / 6) * 0x800000) + ((port % 6) * 0x100);
				if ( !dx160_set_reg_field( dev, reg,14,1,0 ) )
				{
					DEBUG_PRNT( "Failed to set port %d reg %08x\n", port, reg );
					return -1;
				}
				reg = 0x04000008 + ((port / 6) * 0x800000) + ((port % 6) * 0x100);
				if ( !dx160_set_reg_field( dev, reg,10,1,0 ) )
				{
					DEBUG_PRNT( "Failed to set port %d reg %08x\n", port, reg );
					return -1;
				}
			}

		}
		else
		{
			reg = 0x04000008 + ((port / 6) * 0x800000) + ((port % 6) * 0x100);
			if ( !dx160_set_reg_field( dev, reg,0,1,1 ) )
			{
				DEBUG_PRNT( "Failed to set port %d reg %08x\n", port, reg );
				return -1;
			}

			reg = 0x04000000 + ((port / 6) * 0x800000) + ((port % 6) * 0x100);
			if ( !dx160_set_reg_field( dev, reg,0,1,0 ) )
			{
				DEBUG_PRNT( "Failed to set port %d reg %08x\n", port, reg );
				return -1;
			}
		}
    }

/*  setup the phys
*/
    dx160_phy_marvell88E1143init( dev, 7, 1 );
    dx160_phy_marvell88E1112init( dev, 4, 1 );

    *(unchar *)0xFF000010 = 3;

    udelay( 10 );


    return 0;
}/* dx160_initialise */


static uint
dx160_phy_marvell88E1143init(struct eth_device *dev, uint phyid, uint initialise)
{
	uint			  i;
	ushort			  phyValue;
	ushort			  ctrlValue;
	ushort  			  page;
	struct tsec_private *priv = (struct tsec_private *)dev->priv;
	

/* One bit controls all four phys, so only reset for first phy
*/
	if ( initialise )
	{
/*  		Reset the phy with a hardware reset pulse minimum time 10ms 
*/
		*(unsigned char *)DX160_PHYMARVELL88E1143_RESET = 0;
		asm("sync");

		for ( i = 0; i < 15; i++ )
		{
/*			delay for 1ms
*/
			udelay(1000);
		}

/*  		take the phy out of reset
*/
		*(unsigned char *)DX160_PHYMARVELL88E1143_RESET = 1;
		asm("sync");

/*		wait another ms
*/
		udelay(1000);
	}

	page = read_phy_reg( priv, phyid, DX160_PHY_MII_REG_EXTEND_ADDR );
//      printf("dx160_phy_marvell88E1143init - phyid %d page %04x\n", phyid, page);
	page &= 0xff00;
	page |= 1;
	write_phy_reg( priv, phyid, DX160_PHY_MII_REG_EXTEND_ADDR, page );

	ctrlValue = DX160_PHY_CTRL_DUPLEX | DX160_PHY_CTRL_SPEED_MSB;
	write_phy_reg( priv, phyid, DX160_PHY_MII_REG_CONTROL, ctrlValue );

	phyValue = read_phy_reg( priv, phyid, DX160_PHY_MII_REG_SPECIFIC_CTRL );
	phyValue |= 0xf2;
	write_phy_reg( priv, phyid, DX160_PHY_MII_REG_SPECIFIC_CTRL, phyValue );

	ctrlValue |= DX160_PHY_CTRL_SW_RESET;
	write_phy_reg( priv, phyid, DX160_PHY_MII_REG_CONTROL, ctrlValue );

	ctrlValue = read_phy_reg( priv, phyid, DX160_PHY_MII_REG_CONTROL );

	page &= 0xff00;
	write_phy_reg( priv, phyid, DX160_PHY_MII_REG_EXTEND_ADDR, page );
    
#if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) 
/*  Register the PHY with the MII command handler. 
 */
    dx160_tsec_miiphy_register(dev->name, phyid);     
#endif /* defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) */
    

	return ( TRUE );
} 


uint dx160_phy_marvell_stutdown(struct eth_device *dev)
{
	return 0;
}

/* Parse the 88E1011's status register for speed and duplex
 * information */
uint dx160_phy_marvell_startup(struct eth_device *dev)
{
	struct tsec_private *priv = (struct tsec_private *)dev->priv;
	uint status;
	uint regAddr;
	int i;

	puts ("Waiting for PHY link");


// 	regAddr = 0x04000400;
// 	dx160_read_register( dev, regAddr, &status );
// 	printf("%s  %s:%u reg %x value %x\n", __FUNCTION__, __FILE__, __LINE__, regAddr, status);
// 
// 	regAddr = 0x04000408;
// 	dx160_read_register( dev, regAddr, &status );
// 	printf("%s  %s:%u reg %x value %x\n", __FUNCTION__, __FILE__, __LINE__, regAddr, status);

	regAddr = 0x04000404;
	priv->link = 0;
	for (i = 0; i < PHY_AUTONEGOTIATE_TIMEOUT; i++)
	{
		if ( !dx160_read_register( dev, regAddr, &status ) )
		{
			printf("%s  %s:%u failed to read reg %x\n", __FUNCTION__, __FILE__, __LINE__, regAddr);
			return -1;
		}
	
		if ((status & 1))
		{
			priv->link = 1;
			if  ((status & 4))
			{
				priv->speed = 1000;
			}
			else if ((status & 8))
			{
				priv->speed = 100;
			}
			else
			{
				priv->speed = 10;
			}
			if ((status & 16))
			{
				priv->duplexity = 1;
			}
			else
			{
				priv->duplexity = 0;
			}
		}

		if (priv->link == 0 )
		{
			if ((i++ % 1000) == 0) 
			{
//				printf("%s  %s:%u reg %x value %x\n", __FUNCTION__, __FILE__, __LINE__, regAddr, status);
				putc ('.');
			}
			udelay (1000);	/* 1 ms */
		}
	}

	puts (" done\n");
	udelay (500000);	/* another 500 ms (results in faster booting) */
	return 0;
}

static uint
dx160_phy_marvell88E1112init(struct eth_device *dev, uint port, uint initialise)
{
    uint			  i;
    uint 			  reg;             /* register address                 */
    uint			  status;
    ushort			  ctrlValue;
    ushort			  regSpecValue;
    ushort  			  page;
    ushort  			  select;
    ushort  			  value;
    ushort  			  macSpec;


        reg = 0x04004034 + ((port / 6 )* 0x800000);
	if ( !dx160_set_reg_field( dev, reg, 7, 1, 1 ) )
	{
		printf( "%s  %s:%u port %u write fail\r\n", __FUNCTION__, __FILE__, __LINE__, port );
		return ( FALSE );
	}

	ctrlValue = DX160_MIIM_CONTROL_INIT;
	status = dx160_smi_write_mii( dev, port, DX160_PHY_MII_REG_CONTROL, ctrlValue );

	if ( initialise )
	{
/*  		Reset the phy with a hardware reset pulse minimum time 10ms 
*/
		*(unchar *)DX160_PHYMARVELL88E1112_RESET = 0;

		for ( i = 0; i < 15; i++ )
		{
/*			delay for 1ms
*/
			udelay(1000);
		}

/*  		take the phy out of reset
*/
		*(unchar *)DX160_PHYMARVELL88E1112_RESET = 1;

/*		wait another ms
*/
		udelay(1000);


		if ( status )
		{
			status = dx160_smi_read_mii( dev, port, DX160_PHY_MII_REG_EXTEND_ADDR, &page );
			page &= 0x7f00;
			if ( !status )
			{
				dx160_set_reg_field( dev, reg, 7, 1, 0 );
				return ( FALSE );
			}
		}

		if ( status )
		{
			select = 8;
			status = dx160_smi_write_mii( dev, port, DX160_PHY_MII_REG_EXTEXT_ADDR, select );
		}

		if ( status )
		{
			status = dx160_smi_read_mii( dev, port, 30, &value );
		}

		if ( status )
		{
			value &= 0xfffc;
			status = dx160_smi_write_mii( dev, port, 30, value );
		}

		if ( status )
		{
			select = 12;
			status = dx160_smi_write_mii( dev, port, DX160_PHY_MII_REG_EXTEXT_ADDR, select );
		}

		if ( status )
		{
			status = dx160_smi_read_mii( dev, port, 30, &value );
		}

		if ( status )
		{
			value &= 0xfffc;
			status = dx160_smi_write_mii( dev, port, 30, value );
		}

/*		wait another ms
*/
		udelay(1300);

		if ( status )
		{
			select = 0x17;
			status = dx160_smi_write_mii( dev, port, DX160_PHY_MII_REG_EXTEXT_ADDR, select );
		}
		
		if ( status )
		{
			status = dx160_smi_write_mii( dev, port, 30, 0x4008 );
		}

		if ( status )
		{
			status = dx160_smi_write_mii( dev, port, 30, 0x0000 );
		}

		if ( status )
		{
			select = 0x18;
			status = dx160_smi_write_mii( dev, port, DX160_PHY_MII_REG_EXTEXT_ADDR, select );
		}

		if ( status )
		{
			status = dx160_smi_write_mii( dev, port, 30, 0x00ca );
		}

		if ( status )
		{
			status = dx160_smi_write_mii( dev, port, 0x1e, 0x0000 );
		}

/*		wait 2 ms
*/
		udelay(2000);
	}

	if ( status )
	{
		status = dx160_smi_read_mii( dev, port, DX160_PHY_MII_REG_EXTEND_ADDR, &page );
		if ( !status )
		{
			dx160_set_reg_field( dev, reg, 7, 1, 0 );
			return ( FALSE );
		}
	}

	if ( status )
	{
		page &= 0x7f00;
		status = dx160_smi_write_mii( dev, port, DX160_PHY_MII_REG_EXTEND_ADDR, page );
	}

	if ( status )
	{
		status = dx160_smi_read_mii( dev, port, DX160_PHY_MII_REG_SPECIFIC, &regSpecValue );
	}

	if ( status )
	{
		regSpecValue |= 0x800;
		status = dx160_smi_write_mii( dev, port, DX160_PHY_MII_REG_SPECIFIC, regSpecValue );
	}

	if ( status )
	{
		status = dx160_smi_write_mii( dev, port, DX160_PHY_MII_REG_CONTROL, 0x8000 | ctrlValue );
	}

	if ( status )
	{
		ctrlValue = 0x1140;
		page &= 0x7f00;
		page |= 1;
		status = dx160_smi_write_mii( dev, port, DX160_PHY_MII_REG_EXTEND_ADDR, page );
	}

	if ( status )
	{
		status = dx160_smi_read_mii( dev, port, 26, &macSpec );
	}

	if ( status )
	{
		macSpec &= 0x7fff;  /* disable autoneg bypass */
		status = dx160_smi_write_mii( dev, port, 26, macSpec );
	}

	if ( status )
	{
		status = dx160_smi_write_mii( dev, port, DX160_PHY_MII_REG_CONTROL, ctrlValue );
	}

	if ( status )
	{
		ctrlValue |= DX160_PHY_CTRL_SW_RESET;
		status = dx160_smi_write_mii( dev, port, DX160_PHY_MII_REG_CONTROL, ctrlValue );
	}

	if ( status )
	{
		status = dx160_smi_write_mii( dev, port, 16, 0x200 );
	}

	if ( status )
	{
		page &= 0x7f00;
		page |= 2;
		status = dx160_smi_write_mii( dev, port, DX160_PHY_MII_REG_EXTEND_ADDR, page );
	}

	if ( status )
	{
		status = dx160_smi_read_mii( dev, port, 26, &macSpec );
	}

	if ( status )
	{
		macSpec |= 0x8000;
		status = dx160_smi_write_mii( dev, port, 26, macSpec );
	}

	if ( status )
	{
		ctrlValue = DX160_PHY_CTRL_SPEED_MSB;
		status = dx160_smi_write_mii( dev, port, DX160_PHY_MII_REG_CONTROL, ctrlValue );
	}

	if ( status )
	{
		ctrlValue |= DX160_PHY_CTRL_SW_RESET;
		status = dx160_smi_write_mii( dev, port, DX160_PHY_MII_REG_CONTROL, ctrlValue );
	}

	if ( status )
	{
		status = dx160_smi_read_mii( dev, port, DX160_PHY_MII_REG_SPECIFIC, &macSpec );
	}

	if ( status )
	{
		macSpec |= 0x400;
		status = dx160_smi_write_mii( dev, port, DX160_PHY_MII_REG_SPECIFIC, macSpec );
	}

	if ( status )
	{
		page &= 0x7f00;
		status = dx160_smi_write_mii( dev, port, DX160_PHY_MII_REG_EXTEND_ADDR, page );
	}

	if ( !dx160_set_reg_field( dev, reg, 7, 1, 0 ) )
	{
		printf( "%s  %s:%u port %u write fail\r\n", __FUNCTION__, __FILE__, __LINE__, port );
		return ( FALSE );
	}

//	printf( "%s  %s:%u port %u status %d\r\n", __FUNCTION__, __FILE__, __LINE__, port, status );
#if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) 
/*  Register the PHY with the MII command handler. 
 */
    dx160_tsec_miiphy_register(dev->name, port);     
#endif /* defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) */

	return ( status );
}                          


#if defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) 
/*
* Register a PHY with the MII command handler.
*
*/
static void dx160_tsec_miiphy_register(const char *devname, unchar addr)
{
    char *phyname;
    
/*  Create a unique name for the PHY by appending its address to the parent device name.
 *  Use '-' as the arbitrarily chosen seperator.  
 *  eg TSEC0-7
 */    
    phyname = malloc(strlen(devname) + 4 + 1);
    sprintf(phyname,"%s%c%u", devname,'-', addr);
    //DEBUG_TRACE("%s", phyname);
    miiphy_register(phyname, dx160_tsec_miiphy_read,dx160_tsec_miiphy_write);    
    free(phyname);
}    
 

/*
* Read a MII PHY register.
*
* Returns:
*  0 on success
*/
static int dx160_tsec_miiphy_read(char *phyname, unchar addr,
		unchar reg, ushort *value)
{	
	struct eth_device* dev;
    char *devname;
    uint ret;  
    
    dx160_tsec_miiphy_extract_devname(phyname, &devname);
    //DEBUG_TRACE("ADDR %u REG %u\n", addr, reg);
	dev = eth_get_dev_by_name(devname);
    free(devname);
    
	if(NULL == dev)
	{
		DEBUG_PRNT("Unknown PHY %s\n", phyname);
		return -1;
	}
	    
    switch(addr)
    {
    case 7:
		ret = read_phy_reg((struct tsec_private *)dev->priv, addr, reg);         
		*value = (ushort)ret;
        break;
	
	case 4:		
		dx160_smi_read_mii(dev, addr, reg, value);
        break;
	
	default:       
		DEBUG_PRNT("Can't read PHY at address %d\n", addr);
		return -1;
	}
    //DEBUG_TRACE("reg value %.8x\n", *value);	
	return 0;
}

/*
* Write a MII PHY register.
*
* Returns:
*  0 on success
*/
static int dx160_tsec_miiphy_write(char *phyname, unchar addr,
		unchar reg, ushort value)
{
	struct eth_device* dev;
    char *devname;
    dx160_tsec_miiphy_extract_devname(phyname, &devname); 
        
    //DEBUG_TRACE("ADDR %u REG %u VALUE %.4x\n", addr, reg, value);	
    dev = eth_get_dev_by_name(devname);
    free(devname);
    
	if(NULL == dev)
	{
		DEBUG_PRNT("Unknown PHY %s\n", phyname);
		return -1;
	}

    switch(addr)
    {
    
	case 7:
		write_phy_reg((struct tsec_private *)dev->priv, addr, reg, value); 
        break;
	
    case 4:
		dx160_smi_write_mii(dev, addr, reg, value);
        break;
        
    default:
		DEBUG_PRNT("Can't write PHY at address %d", addr);
		return -1;
	}	
	return 0;
}

/*
* Extract the  device name from a PHY name.
*
*/
static void dx160_tsec_miiphy_extract_devname(char *phyname, char **ethname)
{
    char *seperator;
	ulong phynamelen;
    
    phynamelen = strlen(phyname);
    *ethname = malloc(phynamelen + 1);
    strncpy(*ethname, phyname, phynamelen);
    seperator = strpbrk(*ethname, "-");
    *seperator = '\0';
    //DEBUG_TRACE("original name %s phy name %s\n", *ethname,phyname);
}

#endif /* defined(CONFIG_MII) || (CONFIG_COMMANDS & CFG_CMD_MII) */


#endif /* CONFIG_DX160_TSEC_ENET */
