//=============================================================================
//
//      ether_test.c - Cyclone Diagnostics
//
//=============================================================================
//####ECOSGPLCOPYRIGHTBEGIN####
// -------------------------------------------
// This file is part of eCos, the Embedded Configurable Operating System.
// Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
//
// eCos 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 or (at your option) any later version.
//
// eCos 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 eCos; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
//
// As a special exception, if other files instantiate templates or use macros
// or inline functions from this file, or you compile this file and link it
// with other works to produce a work based on this file, this file does not
// by itself cause the resulting work to be covered by the GNU General Public
// License. However the source code for this file must still be made available
// in accordance with section (3) of the GNU General Public License.
//
// This exception does not invalidate any other reasons why a work based on
// this file might be covered by the GNU General Public License.
//
// Alternative licenses for eCos may be arranged by contacting Red Hat, Inc.
// at http://sources.redhat.com/ecos/ecos-license/
// -------------------------------------------
//####ECOSGPLCOPYRIGHTEND####
//=============================================================================
//#####DESCRIPTIONBEGIN####
//
// Author(s):   Scott Coulter, Jeff Frazier, Eric Breeden
// Contributors:
// Date:        2001-01-25
// Purpose:     
// Description: 
//
//####DESCRIPTIONEND####
//
//===========================================================================*/

#include "pci_bios.h"
#include "iq80310.h"
#include "ether_test.h"

/* Forward declarations */
static int i557SelfTest ();
static int i557Init ();
static int i557Config (UINT8 loopBackMode);
static int i557AddrSet ();
static int i557RUStart ();
static void setUpPacket ();
static int txPacket ();
static char *malloc ();
static void bzero ();
static void Wait();
static int waitForRxInt();
static int get_ether_addr();

/* Externals */
//void printf();
void bcopy();
extern void sgets();
extern int atod();
extern int enable_external_interrupt (int int_id);
extern int isr_connect(int int_num, void (*handler)(int), int arg);
extern STATUS pci_isr_connect (int intline, int bus, int device, int (*handler)(int), int arg);
extern ULONG sys_read_config_dword (UINT32 busno,UINT32 devno,UINT32 funcno,UINT32 offset,UINT32 *data);
extern void delay_ms(int msecs);

extern int eeprom_read (UINT32 pci_base,/* PCI Base address */
                 int eeprom_addr,       /* word offset from start of eeprom */
                 UINT16 *p_data,/* where to put data in memory */
                 int nwords             /* number of 16bit words to read */
                 );
extern int eeprom_write (UINT32 pci_base,/* PCI Base address */
                 int eeprom_addr,       /* word offset from start of eeprom */
                 UINT16 *p_data,/* data location in memory */
                 int nwords             /* number of 16bit words to write */
                 );

/* Globals needed by both main program and irq handler */
static volatile struct SCBtype *pSCB;	/* Pointer to SCB in use */
static volatile UINT32 waitSem;	/* Used to block test until interrupt */
static volatile UINT32 rxSem;	/* Used to block test until rx sinterrupt */
static UINT16 i557Status;	/* Status code from SCB */
static volatile char *mem_pool; /* Ptr to malloc's free memory pool */
static UINT32 adapter[2];		/* Ptr to PCI Ethernet adapter */
static UINT8 node_address[6];
/*static long timer0_ticks = 0;*/
static char buf[4];
static int count = 0;
static int forever_flag = FALSE;
static UINT32 phy_id = 0;

/* 82557 required data structures which must be allocated */
static struct rfd *pRfd;
static union cmdBlock *pCmdBlock;
static char	*pPacketBuf;

#define SPEED_NOLINK	0
#define SPEED_10M		10
#define SPEED_100M		100
static int link_speed = SPEED_NOLINK;

UINT8 unit_intpin;
int unit_devno, unit_busno, unit_funcno;

#define BUSY_WAIT_LIMIT		0xf000			/* the upper limit on a busy wait
											   for command completion, etc. */

#if 0
/* names for MDI registers */
static char *mdi_reg_name [] =
    {
	"MDI Control Register                               ",
	"MDI Status Register                                ",
	"MDI PHY Identification Register (Word 1)           ",
	"MDI PHY Identification Register (Word 2)           ",
	"MDI Auto-Negotiation Advertisement Register        ",
	"MDI Auto-Negotiation Link Partner Ability Register ",
	"MDI Auto-Negotiation Expansion Register            ",
    };
#endif

static void mask_557_ints (void)
{
	pSCB->cmdStat.bits.m = 1;
}

static void unmask_557_ints (void)
{
	pSCB->cmdStat.bits.m = 0;
}

/*****************************************************************************
* pci_ether_test - i8255x PCI Ethernet test
*
* Main diagnostic routine for the Intel 8255x 10/100BaseT Ethernet Controller
* family.  Arguments include the PCI bus, device and function numbers of the
* controller that is to be tested.
*
*/
void pci_ether_test (UINT32 busno, UINT32 devno, UINT32 funcno)
{
	volatile int i;
	int ntimes;
	int broadcom_flag =  FALSE;
	UINT16 phy_addr_reg, temp1, temp2;
	char inputLine[80];

	count = 0;

	/* read the PCI BAR for the Ethernet controller */
	if (sys_read_config_dword(busno, devno, funcno, 0x10, &adapter[0]) == ERROR)
	{
		printf ("Error Reading Adapter PCI Address\n");
		return;
	}

	/* strip off BAR indicator bits */
	adapter[0] &= 0xfffffff0;

	unit_devno	= devno;
	unit_busno	= busno;
	unit_funcno = funcno;

	/* pointer to on-chip SCB */
	pSCB = (struct SCBtype *)(adapter[0] + SCB_OFFSET);

	unit_intpin = INTA;

	printf ("PCI Base Address  = 0x%X\n", adapter[0]);
	printf ("PCI Interrupt Pin = 0x%02X\n", unit_intpin);

	/* Initialize malloc's memory pool pointer */
	mem_pool = (char *) ETHER_MEM_POOL;

	
	/* Start the timer for delay implementation 
	printf("Starting timer... ");
	StartTimer(); */
	printf("Done.\n Resetting chip... ");

	/* reset the 82557 to start with a clean slate */
	resetChip();
	printf("Done.\n");

	/* Get the UUT's ethernet address */
	if (get_ether_addr (0, node_address, TRUE) == ERROR)
	{
		printf("Error Reading Adapter Ethernet Address\n");
		return;
	}

	temp1 = readMDI (0 ,MDI_DEFAULT_PHY_ADDR, MDI_PHY_ID_1);
	temp2 = readMDI (0 ,MDI_DEFAULT_PHY_ADDR, MDI_PHY_ID_2);
	phy_id = ((temp1 << 16) | temp2);

	if ((phy_id & 0xfffffff0) == I82555_PHY_ID)
	{
		printf ("Intel 82555/558 PHY detected...\n");

		/* dummy read for reliable status */
		(void)readMDI (0, MDI_DEFAULT_PHY_ADDR, MDI_PHY_STAT);
    
		temp1 = readMDI (0, MDI_DEFAULT_PHY_ADDR, MDI_PHY_STAT);
		printf ("Status Register Link Status is %s\n", (temp1 & MDI_STAT_LINK) ? "UP" : "DOWN");

		phy_addr_reg = readMDI (0, MDI_DEFAULT_PHY_ADDR, I82555_STATCTRL_REG);

		if (temp1 & MDI_STAT_LINK)	/* speed only valid with good LNK */
  		{
		   	printf ("Connect Speed is %s\n", (phy_addr_reg & I82555_100_MBPS) ? "100Mbps" : "10Mbps");
			link_speed = (phy_addr_reg & I82555_100_MBPS) ? SPEED_100M : SPEED_10M;
		}
		else
			printf ("Connect Speed is NOT VALID\n");
	}

	if ((phy_id & 0xfffffff0) == ICS1890_PHY_ID)
	{
		printf ("Integrated Circuit Systems ICS1890 PHY detected...\n");
		printf ("Revision = %c\n", 'A' + (phy_id & REVISION_MASK));

		/* dummy read for reliable status */
		(void)readMDI (0, MDI_DEFAULT_PHY_ADDR, ICS1890_QUICKPOLL_REG);
		temp1 = readMDI (0, MDI_DEFAULT_PHY_ADDR, ICS1890_QUICKPOLL_REG);
		printf ("Status Register Link Status is %s\n", (temp1 & QUICK_LINK_VALID) ? "UP" : "DOWN");

		if (temp1 & QUICK_LINK_VALID) /* speed only valid with good LNK */
		{
			printf ("Connect Speed is %s\n", (temp1 & QUICK_100_MBPS) ? "100Mbps" : "10Mbps");
			link_speed = (temp1 & QUICK_100_MBPS) ? 
			SPEED_100M : SPEED_10M;
		}
		else printf ("Connect Speed is NOT VALID\n");
	}

	if ((phy_id & 0xfffffff0) == DP83840_PHY_ID)
	{
		printf ("National DP83840 PHY detected...\n");
		printf ("Revision = %c\n", 'A' + (phy_id & REVISION_MASK));

		/* dummy read for reliable status */
		(void)readMDI (0, MDI_DEFAULT_PHY_ADDR, MDI_PHY_STAT);
		temp1 = readMDI (0, MDI_DEFAULT_PHY_ADDR, MDI_PHY_STAT);
		printf ("Status Register Link Status is %s\n", (temp1 & MDI_STAT_LINK) ? "UP" : "DOWN");

		phy_addr_reg = readMDI (0 ,MDI_DEFAULT_PHY_ADDR, DP83840_PHY_ADDR_REG);

		if (temp1 & MDI_STAT_LINK)	/* speed only valid with good LNK */
  		{
			printf ("Connect Speed is %s\n", (phy_addr_reg & PHY_ADDR_SPEED_10_MBPS) ? "10Mbps" : "100Mbps");
			link_speed = (phy_addr_reg & PHY_ADDR_SPEED_10_MBPS) ? SPEED_10M : SPEED_100M;
		}
		else printf ("Connect Speed is NOT VALID\n");
	}

	if ((phy_id & 0xfffffff0) == I82553_PHY_ID)
	{
		printf ("Intel 82553 PHY detected...\n");
		printf ("Revision = %c\n", 'A' + (phy_id & REVISION_MASK));
		broadcom_flag = TRUE;
	}

	if (phy_id == I82553_REVAB_PHY_ID)
	{
		printf ("Intel 82553 PHY detected...\n");
		printf ("Revision = B\n");
		broadcom_flag = TRUE;
	}

	if (broadcom_flag == TRUE)
	{
		temp2 = readMDI (0,MDI_DEFAULT_PHY_ADDR, I82553_PHY_EXT_REG0);
		printf ("Stepping = %02X\n", GET_REV_CNTR(temp2));

		/* dummy read for reliable status */
		(void)readMDI (0 ,MDI_DEFAULT_PHY_ADDR, MDI_PHY_STAT);
		temp1 = readMDI (0 ,MDI_DEFAULT_PHY_ADDR, MDI_PHY_STAT);
		printf ("Status Register Link Status is %s\n", (temp1 & MDI_STAT_LINK) ? "UP" : "DOWN");

		if (temp1 & MDI_STAT_LINK)	/* speed only valid with good LNK */
		{	
			printf ("Connect Speed is %s\n", (temp2 & EXT_REG0_100_MBPS) ? "100Mbps" : "10Mbps");
			link_speed = (temp2 & EXT_REG0_100_MBPS) ? SPEED_100M : SPEED_10M;
		}
		else printf ("Connect Speed is NOT VALID\n");
	}

	printf ("\n");

	/* Run the built-in self test through the port register */
	if (i557SelfTest () == ERROR)
	{
		mask_557_ints ();      /* Disable 557 interrupt */
		return;
	}

	/* Reset clears the interrupt mask */
	mask_557_ints();

	printf ("Press return to initialize ethernet controller.\n");
	sgets (buf);

	/* Initialize data structures */
	if (i557Init () == ERROR)
	{
		mask_557_ints ();      /* Disable 557 interrupt */
		return;
	}

	/* Set hardware address */
	if (i557AddrSet () == ERROR)
	{
		mask_557_ints ();      /* Disable 557 interrupt */
		return;
	}

	printf ("Press return to perform internal loopback test.\n");
	sgets (buf);

	/* Configure for internal loopback */
	if (i557Config (INT_LOOP_BACK) == ERROR)
	{
		mask_557_ints ();      /* Disable 557 interrupt */
		return;
	}

	Wait(100);

	/* Initialize receive buffer and enable receiver */
	if (i557RUStart () == ERROR)
	{
		mask_557_ints ();      /* Disable 557 interrupt */
		return;
	}	

	/* Send a packet */
	setUpPacket (pPacketBuf);
	if (txPacket (pPacketBuf) == ERROR)
	{
		mask_557_ints ();      /* Disable 557 interrupt */
		return;
	}

	printf ("Press return to perform loopback through PHY.\n");
	sgets (buf);

	/* Configure for external loopback */
	if (i557Config (EXT_LOOP_BACK) == ERROR)
	{
		mask_557_ints ();      /* Disable 557 interrupt */
		return;
	}

	Wait(100);

	/* Initialize receive buffer and enable receiver */
	if (i557RUStart () == ERROR)
	{
		mask_557_ints ();      /* Disable 557 interrupt */
		return;
	}

	/* Send a packet */
	setUpPacket (pPacketBuf);
	if (txPacket (pPacketBuf) == ERROR)
	{
		mask_557_ints ();      /* Disable 557 interrupt */
		return;
	}

	printf ("Press return to perform external loopback through\n");
	printf ("10/100 Base T Hub.  NOTE: If test duration is not forever,\n");
	printf ("this test will work only if a properly functioning Hub\n"); 
	printf ("and Twisted Pair cable are attached to the network connector\n");
	printf ("on the front panel.\n");
	sgets (buf);

	printf ("Enter the number of times to run test (0 = forever): ");
	ntimes = decIn();
	printf ("\n\n");

/*	if (atod (inputLine, &ntimes) == FALSE) 
		ntimes = 0;
*/
	if (i557RUStart () == ERROR)
	{
		mask_557_ints ();      /* Disable 557 interrupt */
		return;
	}

	setUpPacket (pPacketBuf);

	if (ntimes == 0)
	{
		forever_flag = TRUE;

		while (1)
		{
			if ((i557RUStart() == ERROR)||(txPacket (pPacketBuf) == ERROR))
			{
				printf ("Double-check TP cable and 10/100 Base T Hub\n");
				printf ("Try testing them with another system\n");
				printf ("(such as a workstation) that is working correctly.\n");
				mask_557_ints ();      /* Disable 557 interrupt */
				return;
			}

			count++;
			if (((count) % 1000) == 0) 
				printf("Loopback Cycle Count = %d\n", count);
		}
	}
	else
	{
		forever_flag = FALSE;

		for (i=0; i<ntimes; i++)
		{
			if ((i557RUStart() == ERROR)||(txPacket (pPacketBuf) == ERROR))
			{
				printf ("Double-check TP cable and 10/100 Base T Hub\n");
				printf ("Try testing them with another system\n");
				printf ("(such as a workstation) that is working correctly.\n");
				mask_557_ints ();      /* Disable 557 interrupt */
				return;
			}
	  
			count++;
			printf("Loopback Cycle Count = %d\n", count);
		}

		/* It worked! */

		mask_557_ints ();       /* Disable 557 interrupt */

		printf ("\nEthernet controller passed.  Press return to continue.\n");

		sgets (buf);
	}
}


/* Perform internal self test - returns OK if sucessful, ERROR if not. */
static int i557SelfTest ()
{
volatile struct selfTest *pSelfTestMem;
UINT32 oldWord2;
long delay;
UINT32 temp;
int	rtnVal;

	/* reset the 82557 to start with a clean slate */
	resetChip();

	/* Allocate some memory for the self test */
	pSelfTestMem = (struct selfTest *) malloc (sizeof(struct selfTest));

	if (pSelfTestMem == NULL)
	{
		printf ("Couldn't get memory for self test.\n");
		return (ERROR);
	}

	printf ("Sending PORT* self-test command...\n");
	printf ("Local Dump address = 0x%X\n", pSelfTestMem);

	/* Set all bits in  second word, wait until it changes or a timeout */
	pSelfTestMem->u.word2 = ~0;
	oldWord2 = pSelfTestMem->u.word2;

	temp = ((UINT32) pSelfTestMem) + PORT_SELF_TEST;

	portWrite (temp);

	/* Wait for test completion or for timeout */
	for (delay = 0; (delay < MAX_DELAY) && (pSelfTestMem->u.word2 == oldWord2); delay++);	/* Wait... */

	/* Print results */
	printf ("Self test result: %s\n", (pSelfTestMem->u.bits.selfTest) ? "Fail" : "Pass");
	printf ("ROM content test: %s\n", (pSelfTestMem->u.bits.romTest) ? "Fail" : "Pass");
	printf ("Register test:    %s\n", (pSelfTestMem->u.bits.regTest) ? "Fail" : "Pass");
	printf ("Diagnose test:    %s\n", (pSelfTestMem->u.bits.diagnTest) ? "Fail" : "Pass");
	printf ("ROM signature:    0x%X\n", pSelfTestMem->romSig);

	rtnVal = pSelfTestMem->u.bits.selfTest ? ERROR : OK;

	return (rtnVal);
}


/* Initialize the 82557. */
static int i557Init (void)
{
	/* Get memory for system data structures */
	if ( ((pRfd = (struct rfd *) malloc (sizeof(struct rfd))) == NULL) || 
				((pPacketBuf = malloc(ETHERMTU + sizeof(UINT16) + 6)) == NULL) ||
				((pCmdBlock = (union cmdBlock *) malloc (sizeof(union cmdBlock))) == NULL) )
	{
		printf ("Memory allocation failed.\n");
		return (ERROR);
	}

	/* Set EL bits in command block and rfd so we don't fall of the end */
	pCmdBlock->nop.el = END_OF_LIST;
	pRfd->el = END_OF_LIST;

	/* Reset chip and initialize */
	printf ("Initializing... ");

	/* Reset 82557 */
	resetChip ();
		
	/* set up the CU and RU base values to 0x0 */
	sendCommand (LOAD_CU_BASE, RU_NOP, 0);
	sendCommand (CU_NOP, LOAD_RU_BASE, 0);
	
	/* Initialize interrupts */
	
	/* if it is the onboard i82559, it does not use the conventional PCI
	   interrupt routines because the interrupt is not multiplexed onto
	   the PCI bus */
	if ((unit_busno == 2) && (unit_devno == 0) && (unit_funcno == 0)) 
	{
		if (isr_connect (ENET_INT_ID, (VOIDFUNCPTR)i557IntHandler, 0xdeadbeef) != OK)
		{
			printf ("Error connecting Ethernet interrupt!\n");
			return (ERROR);
		}
		if (enable_external_interrupt (ENET_INT_ID) != OK)
		{
			printf ("Error enabling Ethernet interrupt!\n");
			return (ERROR);
		}
	}

	else  /* use regular PCI int connect scheme */
	{
		if (pci_isr_connect (unit_intpin, unit_busno, unit_devno, i557IntHandler, 0xdeadbeef) != OK)
		{
			printf ("Error connecting Ethernet interrupt!\n");
			return (ERROR);
		}

	}
	unmask_557_ints();

	printf ("Done\n");

	return (OK);
}


static int initPHY (UINT32 device_type, int loop_mode)
{
UINT16 temp_reg;
UINT8 revision;

	/* strip off revision and phy. id information */ 
	revision = (UINT8)(device_type & REVISION_MASK);
	device_type &= ~REVISION_MASK;

    switch (device_type)
	{
		case ICS1890_PHY_ID: 
			temp_reg = readMDI (0, MDI_DEFAULT_PHY_ADDR, MDI_PHY_CTRL);  /* get ready for loopback setting */
	    
			switch (loop_mode)
			{
				case EXT_LOOP_BACK:  /* loopback on the MII interface */
    			temp_reg |= MDI_CTRL_LOOPBACK;	/* MII loopback */
					break;

				case INT_LOOP_BACK:
				default:
					break;
			}
    	    
			writeMDI(0, MDI_DEFAULT_PHY_ADDR, MDI_PHY_CTRL, temp_reg);
			break;
		
		case DP83840_PHY_ID:  /* set the Intel-specified "must set" bits */
    		temp_reg = readMDI (0,MDI_DEFAULT_PHY_ADDR, DP83840_PCR_REG);
    		temp_reg |= (PCR_TXREADY_SEL | PCR_FCONNECT);
    		writeMDI (0,MDI_DEFAULT_PHY_ADDR, DP83840_PCR_REG, temp_reg);

			/* get ready for loopback setting */
    		temp_reg = readMDI (0,MDI_DEFAULT_PHY_ADDR, DP83840_LOOPBACK_REG);
    		temp_reg &= CLEAR_LOOP_BITS;

			switch (loop_mode)
			{
				case EXT_LOOP_BACK:
    			temp_reg |= TWISTER_LOOPBACK;
				break;

				case INT_LOOP_BACK:
				default:
				break;
			}

    		writeMDI (0,MDI_DEFAULT_PHY_ADDR, DP83840_LOOPBACK_REG, temp_reg);
			break;

		case I82553_PHY_ID:
		case I82553_REVAB_PHY_ID:
		case I82555_PHY_ID:
			break;

		default:
			return (ERROR);
			break;
  }

  return (OK);
}


/* Set hardware address of the 82557. */
static int i557AddrSet ()
{
    printf ("Setting hardware ethernet address to ");
    printf ("%02X:%02X:%02X:", node_address[0], node_address[1], node_address[2]);
    printf ("%02X:%02X:%02X... ", node_address[3], node_address[4], node_address[5]);

	/* Set up iaSetup command block and execute */
	bzero ((char *) pCmdBlock, sizeof(union cmdBlock));
	pCmdBlock->iaSetup.code = IA_SETUP;
	pCmdBlock->iaSetup.el = END_OF_LIST;
	bcopy (node_address, pCmdBlock->iaSetup.enetAddr, sizeof(node_address));

	sendCommand (CU_START, RU_NOP, ((UINT32)pCmdBlock));

	if ((waitForInt() == ERROR) || (pCmdBlock->iaSetup.ok != 1))
	{
		printf ("failed.  Status: 0x%04X.\n", pSCB->cmdStat.words.status);
		printf ("C bit = %d\n",pCmdBlock->iaSetup.c);
		printf ("OK bit = %d\n",pCmdBlock->iaSetup.ok);
		return (ERROR);
	}
    
	printf ("done.\n");

	return (OK);
}


/* Configure the 82557. */
static int i557Config (UINT8 loopBackMode)	/* None, int, or ext 1, 2 (see etherTest.h) */
{
	printf ("\nConfiguring for ");

	switch (loopBackMode)
	{
		case INT_LOOP_BACK:
			printf ("internal loopback... ");
			break;
		case EXT_LOOP_BACK:
			printf ("external loopback, LPBK* active... ");
			break;
		default:
			printf ("Unknown loopback mode, exiting...\n");
			return (ERROR);
	}

	/* Set up configure command block and execute */
	bzero ((char *) pCmdBlock, sizeof(union cmdBlock));
	pCmdBlock->configure.code = CONFIGURE;
	pCmdBlock->configure.el = END_OF_LIST;
	pCmdBlock->configure.configData[ 0] = CONFIG_BYTE_00;
	pCmdBlock->configure.configData[ 1] = CONFIG_BYTE_01;
	pCmdBlock->configure.configData[ 2] = CONFIG_BYTE_02;
	pCmdBlock->configure.configData[ 3] = CONFIG_BYTE_03;
	pCmdBlock->configure.configData[ 4] = CONFIG_BYTE_04;
	pCmdBlock->configure.configData[ 5] = CONFIG_BYTE_05;
	pCmdBlock->configure.configData[ 6] = CONFIG_BYTE_06;
	pCmdBlock->configure.configData[ 7] = CONFIG_BYTE_07;
	pCmdBlock->configure.configData[ 8] = CONFIG_BYTE_08;
	pCmdBlock->configure.configData[ 9] = CONFIG_BYTE_09;
	pCmdBlock->configure.configData[10] = CONFIG_BYTE_10 | loopBackMode;
	pCmdBlock->configure.configData[11] = CONFIG_BYTE_11;
	pCmdBlock->configure.configData[12] = CONFIG_BYTE_12;
	pCmdBlock->configure.configData[13] = CONFIG_BYTE_13;
	pCmdBlock->configure.configData[14] = CONFIG_BYTE_14;
	pCmdBlock->configure.configData[15] = CONFIG_BYTE_15;
	pCmdBlock->configure.configData[16] = CONFIG_BYTE_16;
	pCmdBlock->configure.configData[17] = CONFIG_BYTE_17;
	pCmdBlock->configure.configData[18] = CONFIG_BYTE_18;

	if (link_speed == SPEED_100M) 
		pCmdBlock->configure.configData[19] = CONFIG_BYTE_19_100T;
	else
	{
		pCmdBlock->configure.configData[19] = CONFIG_BYTE_19_10T;
		pCmdBlock->configure.configData[20] = CONFIG_BYTE_20;
		pCmdBlock->configure.configData[21] = CONFIG_BYTE_21;
	}

	sendCommand (CU_START, RU_NOP, ((UINT32)pCmdBlock));

	if ((waitForInt() == ERROR) || (pCmdBlock->configure.ok != 1))
	{
		printf ("failed.  Status: 0x%04X.\n",
		pSCB->cmdStat.words.status);

		return (ERROR);
	}

	initPHY (phy_id, loopBackMode);  /* set up the PHY interface appropriately */

	printf ("done.\n");

	return (OK);
}


static int i557RUStart ()
{
	volatile long delay;

#if 0
	printf ("Enabling receiver... ");
#endif

	bzero ((char *) pRfd, sizeof(struct rfd));

	/* Set end-of-list bit in the rfd so we don't fall off the end */
	pRfd->el = END_OF_LIST;
	pRfd->s = 1;
	pRfd->sf = 0;				/* Simplified mode */
	pRfd->rbdAddr = (UINT8 *) 0xffffffff; 	/* No RBD */
	/* buffer size: */
	pRfd->size = sizeof (pRfd->rxData) + sizeof (pRfd->destAddr) + sizeof (pRfd->sourceAddr) + sizeof (pRfd->length); 

	sendCommand (CU_NOP, RU_START, ((UINT32)pRfd));

	/*
	 * Poll, can't use waitForInt (), as this step doesn't generate interrupts.
	 */

	i557Status = 0;

	/* Wait for timeout (i557Status changes) or RU_STAT is RU_READY */
	for (delay = 0; (delay < MAX_DELAY) && (pSCB->cmdStat.bits.rus != RU_READY); delay++)
		;	/* Wait... */

	if (pSCB->cmdStat.bits.rus != RU_READY)
	{
		printf ("failed.  Status: 0x%04X.\n",
		pSCB->cmdStat.words.status);
		return (ERROR);
	}

#if 0
	printf ("done.  Status: 0x%04X.\n", pSCB->cmdStat.words.status);
#endif

	return (OK);
}


/*
 * Get packet ready to send out over the network.  Buffer should be
 * ETHERMTU + sizeof(enet_addr) + sizeof(UINT16)
 */
static void setUpPacket (char *pBuf)/* Where to put it */
{
	bcopy (node_address, pBuf, sizeof(node_address));
	pBuf += sizeof(node_address); /* skip dest. address */

	*((UINT16 *) pBuf) = 0;
	pBuf += sizeof(UINT16);      /* skip length field */

	makePacket (pBuf, ETHERMTU);
}


/* Send and verify a packet using the current loopback mode. */
static int txPacket (char *pBuf) /* Dest addr, ethertype, buffer */
{
	int status = OK;

	/* Set up transmit command block and execute */
	bzero ((char *) pCmdBlock, sizeof(union cmdBlock));
	pCmdBlock->transmit.code = TRANSMIT;
	pCmdBlock->transmit.el = END_OF_LIST;
	pCmdBlock->transmit.sf = 0;				/* Simplified mode */
	pCmdBlock->transmit.tbdAddr = (UINT8 *) 0xffffffff; /* No TBD */
	pCmdBlock->transmit.eof = 1;			/* Entire frame here */
	/* # bytes to tx: */
	pCmdBlock->transmit.tcbCount = sizeof (pCmdBlock->transmit.destAddr) + sizeof (pCmdBlock->transmit.length) +
																 sizeof (pCmdBlock->transmit.txData);

#if 0
	printf ("destAddr size = %d\n", sizeof (pCmdBlock->transmit.destAddr));
	printf ("length size = %d\n", sizeof (pCmdBlock->transmit.length));
	printf ("Transmitting %d bytes\n", pCmdBlock->transmit.tcbCount);
#endif

	bcopy (pBuf, pCmdBlock->transmit.destAddr, sizeof(node_address) + sizeof(UINT16) + ETHERMTU);

	rxSem = 0;	/* no Receive interrupt */

	sendCommand (CU_START, RU_NOP, ((UINT32)pCmdBlock));

	if (waitForInt() == ERROR)
	{
		printf ("No Transmit Interrupt\n");
		status = ERROR;
	}

	if (pCmdBlock->transmit.ok != 1)
	{	
		printf ("tx failed.  Status: 0x%04X.\n",
		pSCB->cmdStat.words.status);
		status = ERROR;
	}

	if (status == ERROR)
	{
		printf ("Transmit OK = %d\n", pCmdBlock->transmit.ok);
		return (ERROR);
	}

#if 1
	if (waitForRxInt() == ERROR)
	{
		printf ("No Receive Interrupt\n");
		status = ERROR;
	}

	if (pRfd->ok != 1)
	{
		printf ("rx failed.  Status: 0x%04X.\n", pSCB->cmdStat.words.status);
		status = ERROR;
	}

#if 1
	/* If RU still ready, hang for receive interrupt */
	if (pSCB->cmdStat.bits.rus == RU_READY)
	{
		if (waitForRxInt() == ERROR)
		{
		    printf ("No Receive Interrupt\n");
			status = ERROR;
		}

		if (pRfd->ok != 1)
		{
			printf ("rx failed.  Status: 0x%04X.\n", pSCB->cmdStat.words.status);
			status = ERROR;
		}
	}
#endif

	if (status == ERROR)
	{
		printf ("\nTransmit Stats:\n");
		printf ("---------------\n");
		printf ("Transmit OK = %d\n", pCmdBlock->transmit.ok);

		printf ("\nReceive Stats:\n");
		printf ("---------------\n\n");
		printf ("Receive OK = %d\n", pRfd->ok);
		printf ("CRC Error = %d\n", pRfd->crcErr);
		printf ("Alignment Error = %d\n", pRfd->alignErr);
		printf ("Resource Error = %d\n", pRfd->noRsrc);
		printf ("DMA Overrun Error = %d\n", pRfd->dmaOverrun);
		printf ("Frame Too Short Error = %d\n", pRfd->frameTooshort);
		printf ("Receive Collision Error = %d\n", pRfd->rxColl);
		return (ERROR);
	}

#if 0
	printf ("Packet Actual Size = %d\n", pRfd->actCount);
#endif

	if (checkPacket (pCmdBlock->transmit.txData, pRfd->rxData, ETHERMTU) == ERROR)
	{
		printf ("data verify error.\n");
		return (ERROR);
	}
    
	if (forever_flag == FALSE) printf ("data OK.\n");
#endif

	return (OK);
}


/*
 * "Poor Man's Malloc" - return a pointer to a block of memory at least
 * The block returned will have been zeroed.
 */
static char *malloc (int numBytes) /* number of bytes needed */
{
	volatile char *rtnPtr;	/* ptr to return to caller */
	long	new_mem_pool;	/* For figuring new pool base address */

	rtnPtr = mem_pool;	/* Return pointer to start of free pool */

	/* Now calculate new base of free memory pool (round to >= 16 bytes) */
	new_mem_pool = (UINT32) mem_pool;
	new_mem_pool = ((new_mem_pool + numBytes + 0x10) & (~((UINT32) 0x0f)));
	mem_pool = (volatile char *) new_mem_pool;

	bzero (rtnPtr, numBytes);

	return ((char *) rtnPtr);
}


/* "Poor Man's bzero" - zero's a block of memory. */
static void bzero (volatile char *ptr, long num_bytes)
{
volatile long i;		/* loop counter */

	/* zero out space */
	for (i = 0; i < num_bytes; *ptr++ = 0, i++)
	  ;
}


/*
 * Write "value" to PORT register of PRO/100.
 */
static void portWrite (UINT32 value)
{
	*PORT_REG(adapter[0]) = value;
}


/******************************************************************************
*
* sendCommand - send a command to the 82557 via the on-chip SCB
*
* Send a command to the 82557.  On the 82557, the Channel Attention signal
* has been replaced by an on-chip SCB.  Accesses to the Command Word portion
* of the SCB automatically forces the '557 to look at the various data
* structures which make up its interface.
*/
static void sendCommand (UINT8 cuc, UINT8 ruc, UINT32 scb_general_ptr)
{
	register CMD_STAT_U temp_cmdStat;
	volatile union cmdBlock *pBlock = (union cmdBlock *)scb_general_ptr;
	volatile int loop_ctr;
	
	/* Mask adapter interrupts to prevent the interrupt handler from
	   playing with the SCB */
	mask_557_ints();

	/* must wait for the Command Unit to become idle to prevent
       us from issueing a CU_START to an active Command Unit */
    for (loop_ctr = BUSY_WAIT_LIMIT; loop_ctr > 0; loop_ctr--)
    {
        if ((pSCB->cmdStat.words.status & SCB_S_CUMASK) == SCB_S_CUIDLE)
            break;
    }
    if (loop_ctr == 0)
    {
        printf("sendCommand: CU won't go idle, command ignored\n");
		unmask_557_ints();
        return;
    }

	/* when setting the command word, read the current word from
	   the SCB and preserve the upper byte which contains the interrupt
	   mask bit */
	temp_cmdStat.words.command = (pSCB->cmdStat.words.command & 0xff00);
	temp_cmdStat.words.status = 0;

	/* set up the Command and Receive unit commands */
	temp_cmdStat.bits.cuc = cuc & 0x07;
	temp_cmdStat.bits.ruc = ruc & 0x07;

	/* Clear flag */
	waitSem = 0;

	/* write the General Pointer portion of the SCB first */
	pSCB->scb_general_ptr = scb_general_ptr;

	/* write the Command Word of the SCB */
	pSCB->cmdStat.words.command = temp_cmdStat.words.command;

	/* only wait for command which will complete immediately */
	if ((scb_general_ptr != 0/* NULL*/) && (ruc != RU_START))
	{
		/* wait for command acceptance and completion */
		for (loop_ctr = BUSY_WAIT_LIMIT; loop_ctr > 0; loop_ctr--)
		{
			if ((pSCB->cmdStat.bits.cuc == 0) && (pBlock->nop.c == 1))
				break;
		}
		if (loop_ctr == 0)
		{
			printf("sendCommand: Timeout on command complete\n");
			printf("Cmd Complete bit = %02X\n", pBlock->nop.c);
			printf("CU command       = 0x%02X\n", cuc);
			printf("RU command       = 0x%02X\n", ruc);
			printf("SCB Gen Ptr      = 0x%X\n", scb_general_ptr);
			printf("scb status       = 0x%04X\n", pSCB->cmdStat.words.status);
			printf("scb command      = 0x%04X\n", pSCB->cmdStat.words.command);
		}
	}

#if 0
	/* DEBUG */
	printf("scb command = 0x%04X\n", pSCB->cmdStat.words.command);
	printf("scb status  = 0x%04X\n", pSCB->cmdStat.words.status);
#endif

	unmask_557_ints();
	return;
}


/*
 * Do a port reset on 82557.
 */
static void resetChip ()
{
	portWrite (PORT_RESET);	/* bits 4-31 not used for reset */

	/* wait 5 msec for device to stabilize */
	Wait(5);
}


/*
 * Setup contents of a packet.
 */
static void makePacket (UINT8 *pPacket, int length)
{
int byteNum;	/* Current byte number */

	for (byteNum = 0; byteNum < length; byteNum++)
		*pPacket++ = byteNum + ' ';
}


/*
 * Verify contents of a received packet to what was transmitted.
 * Returns OK if they match, ERROR if not.
 */
static int checkPacket (pTxBuffer, pRxBuffer, length)
UINT8 *pTxBuffer;		/* Pointer data that was transmitted */
UINT8 *pRxBuffer;		/* Pointer data that was received */
int	length;				/* How many bytes to check */
{
int byteNum;	/* Current byte number */

#if 0
     printf ("\n");
     printf ("Transmit Buffer at 0x%08X\n", pTxBuffer);
     printf ("Receive  Buffer at 0x%08X\n", pRxBuffer);
#endif
     for (byteNum = 0; byteNum < length; byteNum++)
     {
		if (*pTxBuffer++ != *pRxBuffer++)
		{
			printf("Error at byte 0x%x\n", byteNum);
			printf("Expected 0x%02X, got 0x%02X\n", *(pTxBuffer - 1), *(pRxBuffer - 1));
            return (ERROR);
		}
     }
     return (OK);
}


/*
 * Interrupt handler for i82557.  It acknowledges the interrupt
 * by setting the ACK bits in the command word and issuing a
 * channel attention.  It then updates the global status variable
 * and gives the semaphore to wake up the main routine.
 */
int i557IntHandler (int arg)  /* should return int */
{
register CMD_STAT_U temp_cmdStat;
register int rxFlag = FALSE;

    temp_cmdStat.words.status = pSCB->cmdStat.words.status;

    /* check to see if it was the PRO/100 */
    if (temp_cmdStat.words.status & I557_INT)
    {
		/* Wait for command word to clear - indicates no pending commands */
		while (pSCB->cmdStat.words.command)
			;

		/* Update global status variable */
		i557Status = temp_cmdStat.words.status;

#if 0
		printf ("i557IntHandler: i557Status = 0x%04x\n", i557Status);
#endif

		/* If the interrupt was due to a received frame... */
		if (temp_cmdStat.bits.statack_fr)
		    rxFlag = TRUE;

		temp_cmdStat.words.status = temp_cmdStat.words.status & I557_INT;

		/* Acknowledge interrupt by setting ack bits */
		pSCB->cmdStat.words.status = temp_cmdStat.words.status;

		/* Wait for command word to clear - indicates IACK accepted */
		while (pSCB->cmdStat.words.command)
			;

#if 0
		/* DEBUG */
		printf ("give waitSem\n");
#endif

		/* Update global status variable and unblock task */
		waitSem = 1;

		if (rxFlag == TRUE)
			rxSem = 1;

		return(1);		/* serviced - return 1 */
	}
    
	return(0);			/* nothing serviced - return 0 */
}



/*
 * Take the semaphore and block until i557 interrupt or timeout.
 * Returns OK if an interrupt occured, ERROR if a timeout.
 */
static int waitForInt()
{
int num_ms = 0; 

	while ((waitSem == 0) && (num_ms != 2000)) /* wait max 2secs for the interrupt */
	{	
		delay_ms(1);
		num_ms++;
	}

    if (!waitSem)
    {
		printf("Wait error!\n"); 
		return (ERROR);
    }
    else
		return (OK);
	
}


static int waitForRxInt()
{
int num_ms = 0; 

	while ((rxSem == 0) && (num_ms != 2000)) /* wait max 2secs for the interrupt */
	{	
		delay_ms(1);
		num_ms++;
	}

    if (!rxSem)
    {
		printf("Rx Wait error!\n"); 
		return (ERROR);
    }
    else
		return (OK);
}


static UINT16 readMDI (int unit, UINT8	phyAdd, UINT8	regAdd)
{

register MDI_CONTROL_U mdiCtrl;
int num_ms = 0; 


	/* prepare for the MDI operation */
	mdiCtrl.bits.ready = MDI_NOT_READY;
	mdiCtrl.bits.intEnab = MDI_POLLED;	/* no interrupts */
	mdiCtrl.bits.op = MDI_READ_OP;
	mdiCtrl.bits.phyAdd = phyAdd & 0x1f;
	mdiCtrl.bits.regAdd = regAdd & 0x1f;

	/* start the operation */
	*MDI_CTL_REG(adapter[unit]) = mdiCtrl.word;

	/* delay a bit */
	Wait (1);

	/* poll for completion */
	mdiCtrl.word = *MDI_CTL_REG(adapter[unit]);

	while ((mdiCtrl.bits.ready == MDI_NOT_READY) && (num_ms != 2000)) /* wait max 2secs */
	{	
		mdiCtrl.word = *MDI_CTL_REG(adapter[unit]);
		delay_ms(1);
		num_ms++;
	}	
	
	if (num_ms >= 2000)
	{
		printf ("readMDI Timeout!\n");
		return (-1);
	}
	else 
		return ((UINT16)mdiCtrl.bits.data);
}


static void writeMDI (int unit, UINT8 phyAdd, UINT8 regAdd, UINT16 data)
{
	
register MDI_CONTROL_U mdiCtrl;
int num_ms = 0;


	/* prepare for the MDI operation */
	mdiCtrl.bits.ready = MDI_NOT_READY;
	mdiCtrl.bits.intEnab = MDI_POLLED;  /* no interrupts */
	mdiCtrl.bits.op = MDI_WRITE_OP;
	mdiCtrl.bits.phyAdd = phyAdd & 0x1f;
	mdiCtrl.bits.regAdd = regAdd & 0x1f;
	mdiCtrl.bits.data = data & 0xffff;

	/* start the operation */
	*MDI_CTL_REG(adapter[unit]) = mdiCtrl.word;

	/* delay a bit */
	Wait (1);

	/* poll for completion */
	mdiCtrl.word = *MDI_CTL_REG(adapter[unit]);

	while ((mdiCtrl.bits.ready == MDI_NOT_READY) && (num_ms != 2000)) 
	{
		mdiCtrl.word = *MDI_CTL_REG(adapter[unit]);
		delay_ms(1);
		num_ms++;
	}	
	if (num_ms >= 2000)
		printf ("writeMDI Timeout!\n");
 
	return;
}


static void Wait (int msecs)
{
	delay_ms(msecs);
}


static int get_ether_addr (
                int     unit,
                UINT8   *buffer,
                int     print_flag      /* TRUE to print the information */
		)
{
     UINT16 temp_node_addr[3] = {0,0,0};
     register int i;

    /* Get the adapter's node address */
    if (eeprom_read (adapter[unit],IA_OFFSET,temp_node_addr,3) != OK)
    {
         printf ("Error reading the IA address from Serial EEPROM.\n");
         return (ERROR);
    }
 
    buffer[0] = (UINT8)(temp_node_addr[0] & 0x00ff);
    buffer[1] = (UINT8)((temp_node_addr[0] & 0xff00)>>8);
    buffer[2] = (UINT8)(temp_node_addr[1] & 0x00ff);
    buffer[3] = (UINT8)((temp_node_addr[1] & 0xff00)>>8);
    buffer[4] = (UINT8)(temp_node_addr[2] & 0x00ff);
    buffer[5] = (UINT8)((temp_node_addr[2] & 0xff00)>>8);
 
    if (print_flag == TRUE)
    {
        printf("Ethernet Address = [ ");
        for (i=0; i<6; i++)
        {
            printf("0x%02X ", buffer[i]);
        }
        printf("]\n\n");
    }
    return (OK);
}


void bcopy(UINT32* src, UINT32* dst, int num_bytes)
{
int i;
	for (i = 0; i < num_bytes; i++)
		*dst++ = *src++;
}

