/*
 * MOUSSE Onboard DEC 21143-TD (DC1096B) Ethernet support.
 * 
 * (C) Copyright 2000, 2001
 * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 * 
 * (C) Copyright 2001
 * James Dougherty (jfd@cs.stanford.edu)
 *
 * See file CREDITS for list of people who contributed to this
 * project.
 *
 * 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., 59 Temple Place, Suite 330, Boston,
 * MA 02111-1307 USA
 */
/*
 * DEC 21143 Ethernet driver.
 */
#include <ppcboot.h>
#include <mpc824x.h>
#include <net.h>
#include "pci.h"
#include "mousse.h"
/*
 * PCI Registers.
 */
#define PCI_VENDOR_ID       PCI_CFG_VENDOR_ID
#define PCI_COMMAND         PCI_CFG_COMMAND
#define PCI_CLASS_REVISION  PCI_CFG_REVISION
#define PCI_BASE_ADDRESS_0  PCI_CFG_BASE_ADDRESS_0
#define PCI_CFDA_PSM        PCI_CFG_MODE

#define PCI_COMMAND_IO      PCI_CMD_IO_ENABLE
#define PCI_COMMAND_MASTER  PCI_CMD_MASTER_ENABLE

#define CFRV_RN     0x000000f0       /* Revision Number */
#define CBIO_MASK   -128

#define WAKEUP      0x00             /* Power Saving Wakeup */
#define SLEEP       0x80             /* Power Saving Sleep Mode */

#define DC21140_VENDOR_ID		0x1011
#define DC21140_DEVICE_ID		0x0009
#define DC21143_VENDOR_ID		0x1011
#define DC21143_DEVICE_ID		0x0019


#define DC2114x_VID 0x1011           /* DC2114[23] Manufacturer */
#define DC2114x_DID 0x1900           /* Unique Device ID # */
#define DC2114x_BRK 0x0020           /* CFRV break between DC21142 & DC21143 */
#define DC21142     (DC2114x_DID | 0x0010)
#define DC21143     (DC2114x_DID | 0x0030)

#define is_DC2114x ((vendor == DC21143_VENDOR_ID) &&\
                    (device == DC21143_DEVICE_ID))

/*
 * DEC 2114x Ethernet chip registers.
 */
#define DE4X5_BMR    iobase + 0x000  /* Bus Mode Register */
#define DE4X5_TPD    iobase + 0x008  /* Transmit Poll Demand Reg */
#define DE4X5_RRBA   iobase + 0x018  /* RX Ring Base Address Reg */
#define DE4X5_TRBA   iobase + 0x020  /* TX Ring Base Address Reg */
#define DE4X5_STS    iobase + 0x028  /* Status Register */
#define DE4X5_OMR    iobase + 0x030  /* Operation Mode Register */
#define DE4X5_SICR   iobase + 0x068  /* SIA Connectivity Register */
#define DE4X5_TXRX   iobase + 0x070  /* SIA Transmit and Receive Register */
#define DE4X5_GPPR   iobase + 0x078  /* General Purpose Port register */
#define DE4X5_APROM  iobase + 0x048  /* Ethernet Address PROM */

/* Defines for Bus mode register */
#define DE_BMR_BE       0x00000080      /* CSR0: Big Endian mode (bit 7) */
#define	DE_BMR_TAP_02	0x00000002	/* CSR0: Tx poll every 200 usec */
#define	DE_BMR_TAP_08	0x00000004	/* CSR0: Tx poll every 800 usec */
#define	DE_BMR_TAP_16	0x00000006	/* CSR0: Tx poll every 1.6 msec */
#define DE_BMR_TAP_012	0x00000008      /* CSR0: Tx poll every 12.8 usec */
#define DE_BMR_TAP_025	0x0000000A      /* CSR0: Tx poll every 25.6 usec */
#define DE_BMR_TAP_051	0x0000000C      /* CSR0: Tx poll every 51.2 usec */
#define DE_BMR_TAP_102	0x0000000E      /* CSR0: Tx poll every 102.4 usec */
#define DE_BMR_TAP_MSK 0x0000000E
#define DE_BMR_TAP_SHF	16

#define	DE_BMR_CAL_08	0x00000010	/* CSR0: Cache adrs aligned  8 lwords */
#define	DE_BMR_CAL_16	0x00000020	/* CSR0: Cache adrs aligned 16 lwords */
#define	DE_BMR_CAL_32	0x00000030	/* CSR0: Cache adrs aligned 32 lwords */
#define	DE_BMR_CAL_MSK	0x00000030
#define DE_BMR_CAL_SHF	10
    
#define	DE_BMR_PBL_01	0x00000040	/* CSR0: DMA burst len  1 lword */
#define	DE_BMR_PBL_02	0x00000080	/* CSR0: DMA burst len  2 lwords */
#define	DE_BMR_PBL_04	0x00000100	/* CSR0: DMA burst len  4 lwords */
#define	DE_BMR_PBL_08	0x00000200	/* CSR0: DMA burst len  8 lwords */
#define	DE_BMR_PBL_16	0x00000400	/* CSR0: DMA burst len 16 lwords */
#define	DE_BMR_PBL_32	0x00000800	/* CSR0: DMA burst len 32 lwords */
#define	DE_BMR_PBL_MSK	0x00000FC0
#define DE_BMR_PBL_SHF	2
#define	DE_BMR_RML	0x00002000	/* CSR0: PCI memory-read-multiple */


/*
 * Descriptor RDES0/TDES0 (status) Error summary bit defines
 */
#define ES_RX_FF (1<<30)
#define ES_RX_FL (1<<23)
#define ES_TX_FL (1<<23)
#define ES_TX_ES (1<<15)
#define ES_RX_DE (1<<14)
#define ES_TX_TO (1<<14)
#define ES_RX_RF (1<<11)
#define ES_TX_LI (1<<11)
#define ES_RX_TL (1<<7)
#define ES_TX_LC (1<<9)
#define ES_TX_EC (1<<8)
#define ES_TX_TL (1<<7)
#define ES_RX_CS (1<<6)
#define ES_RX_RW (1<<4)
#define ES_RX_RE (1<<3)
#define ES_RX_DB (1<<2)
#define ES_TX_LF (1<<2)
#define ES_RX_CE (1<<1)
#define ES_TX_UF (1<<1)
#define ES_RX_Z  (1<<0)	  
#define ES_TX_DE (1<<0)

/*
 * Defines for status bits in STS status register.
 */
#define STS_LC    (1<<27)
#define STS_GPI   (1<<26)
#define STS_NIS   (1<<16)
#define STS_AIS   (1<<15)
#define STS_ERI   (1<<14)
#define STS_FBE   (1<<13)
#define STS_LNF   (1<<12)
#define STS_GTE   (1<<11)
#define STS_ETI   (1<<10)
#define STS_RWT   (1<<9)
#define STS_RPS   (1<<8)
#define STS_RU    (1<<7)
#define STS_RI    (1<<6)
#define STS_UNF   (1<<5)
#define STS_ANC   (1<<4)
#define STS_TJT   (1<<3)
#define STS_TU    (1<<2)
#define STS_TPS   (1<<1)
#define STS_TI    (1<<0)

/*
 * Masks for STS receive/transmit process state, error bits
 * See Also: Intel 21143 H/W Ref: Table 3-67
 */
#define STS_ES_EB_MASK     0x03800000
#define STS_ES_EB_SHIFT    20
#define STS_ES_TS_MASK     0x00700000
#define STS_ES_TS_SHIFT    20
#define STS_ES_RS_MASK     0x000E0000
#define STS_ES_RS_SHIFT    16

/*
 * Misc Register bits.
 */
#define BMR_SWR    0x00000001       /* Software Reset */
#define STS_TS     0x00700000       /* Transmit Process State */
#define STS_RS     0x000e0000       /* Receive Process State */

/* OMR : (CSR6) Operation Mode Register bit defines */
#define OMR_ST          0x00002000      /* Start/Stop Transmission Command */
#define OMR_PS          0x00040000      /* Port Select */
#define OMR_SDP         0x02000000      /* SD Polarity - MUST BE ASSERTED */
#define	OMR_SC	        0x80000000	/* special capture effect enable */
#define	OMR_RA	        0x40000000	/* receive all */
#define	OMR_SCR	        0x01000000	/* scrambler mode */
#define	OMR_PCS	        0x00800000	/* PCS function */
#define	OMR_TTM	        0x00400000	/* tx threshold mode */
#define	OMR_SF	        0x00200000	/* store and forward */
#define	OMR_HBD	        0x00080000	/* heartbeat disable */
#define	OMR_CAE	        0x00020000	/* capture effect enable */
#define	OMR_THR_072	0x00000000	/* threshold bytes 72 */
#define	OMR_THR_096	0x00004000	/* threshold bytes 96 */
#define	OMR_THR_128	0x00008000	/* threshold bytes 128 */
#define	OMR_THR_160	0x0000C000	/* threshold bytes 160 */

#define	OMR_ST		0x00002000	/* start/stop Xmit command */
#define	OMR_FC		0x00001000	/* Force collision mode */
#define	OMR_OM_EL	0x00000800	/* External loopback mode */
#define	OMR_OM_IL	0x00000400	/* Internal loopback mode */
#define	OMR_OM_NOR	0x00000000	/* normal mode */
#define	OMR_FD		0x00000200	/* Full Duplex mode */
#define OMR_PM          0x00000080      /* Pass All Multicast */
#define	OMR_PR		0x00000040	/* promiscuous mode */
#define	OMR_SB		0x00000020	/* Start/Stop Back off counter */
#define	OMR_IF		0x00000010	/* inverse filtering [RO] */
#define	OMR_PB		0x00000008	/* pass bad frames */
#define	OMR_HO		0x00000004	/* hash only filter mode [RO] */
#define OMR_SR          0x00000002      /* Start/Stop Receive */
#define	OMR_HP		0x00000001	/* hash/perfect filter mode [RO] */

/*
 * DMA Descriptor bits.
 */
#define R_OWN      0x80000000       /* Own Bit */
#define RD_RER     0x02000000       /* Receive End Of Ring */
#define RD_LS      0x00000100       /* Last Descriptor */
#define RD_ES      0x00008000       /* Error Summary */
#define TD_TER     0x02000000       /* Transmit End Of Ring */
#define T_OWN      0x80000000       /* Own Bit */
#define TD_LS      0x40000000       /* Last Segment */
#define TD_FS      0x20000000       /* First Segment */
#define TD_ES      0x00008000       /* Error Summary */
#define TD_SET     0x08000000       /* Setup Packet */
#define TD_AC      (1<<26)          /* Add CRC Disable: Disable CRC Addition */
#define TD_DPD     (1<<23)          /* Disable padding on frames < 64 */

#define SROM_HWADD     0x0014       /* Hardware Address offset in SROM */
#define SROM_RD    0x00004000       /* Read from Boot ROM */
#define SROM_SR    0x00000800       /* Select Serial ROM when set */

#define DT_IN      0x00000004       /* Serial Data In */
#define DT_CLK     0x00000002       /* Serial ROM Clock */
#define DT_CS      0x00000001       /* Serial ROM Chip Select */

#define POLL_DEMAND          1

#define RESET_DE4X5 {\
    int i;\
    i=inl(DE4X5_BMR);\
    udelay(1000);\
    outl(i | BMR_SWR, DE4X5_BMR);\
    udelay(1000);\
    outl(i, DE4X5_BMR);\
    udelay(1000);\
    for (i=0;i<5;i++) {inl(DE4X5_BMR); udelay(10000);}\
    udelay(1000);\
}

#define START_DE4X5 {\
    s32 omr; \
    omr = inl(DE4X5_OMR);\
    omr |= OMR_ST | OMR_SR;\
    outl(omr, DE4X5_OMR);               /* Enable the TX and/or RX */\
}

#define STOP_DE4X5 {\
    s32 omr; \
    omr = inl(DE4X5_OMR);\
    omr &= ~(OMR_ST|OMR_SR);\
    outl(omr, DE4X5_OMR);               /* Disable the TX and/or RX */ \
}

#define NUM_RX_DESC PKTBUFSRX
#define NUM_TX_DESC 1                   /* Number of TX descriptors   */
#define RX_BUFF_SZ  PKTSIZE_ALIGN

#define TOUT_LOOP   1000000

#define SETUP_FRAME_LEN 192
#define ETH_ALEN        6

struct de4x5_desc {
  volatile s32 status;
#define tdes0  status
#define rdes0  status  
  u32 des1;
#define tdes1  des1
#define rdes1  des1
  u32 buf;
#define tdes2  buf
#define rdes2  buf  
  u32 next;
#define tdes3  next
#define rdes3  next  
};


static struct de4x5_desc rx_ring[NUM_RX_DESC]; /* RX descriptor ring         */
static struct de4x5_desc tx_ring[NUM_TX_DESC]; /* TX descriptor ring         */
static int rx_new;                             /* RX descriptor ring pointer */
static int tx_new;                             /* TX descriptor ring pointer */

static char rxRingSize;
static char txRingSize;

static u32 iobase, eth_online;
static int device=0, vendor=0, devno=0xd, bus=0, func=0;

static void  send_setup_frame(bd_t * bis);
static void  check_hw_addr(bd_t * bis);
static short srom_rd(u_long address, u_char offset);
static void  srom_latch(u_int command, u_long address);
static void  srom_command(u_int command, u_long address);
static void  srom_address(u_int command, u_long address, u_char offset);
static short srom_data(u_int command, u_long address);
static void  sendto_srom(u_int command, u_long addr);
static int   getfrom_srom(u_long addr);

static inline int inl(u_long addr)
{
  return le32_to_cpu(*(volatile u_long *)(addr));
}

static inline void outl (int command, u_long addr)
{
  *(volatile u_long *)(addr) = cpu_to_le32(command);
}


void dc21x4x_init(bd_t *bis)
{
  int cfrv;
  uint32 tmp;
  
  /* Find PCI device */
  if ( OK != pci_dev_find(DC21143_VENDOR_ID,
			   DC21143_DEVICE_ID,0,
			   &bus,&devno,&func)){
    printf("Error: Can not find an ethernet card on the PCI bus\n");
    goto Done;
  }
  printf("eth0: DC21143 Ethernet adapter @0x%x"
	 "(bus=%d, device=%d, func=%d)\n",
	 PCI_ENET_MEMADDR, bus,devno,func);
  
  /* Check revision */
  pci_config_inl(bus,devno,func, PCI_CFG_VENDOR_ID,&vendor);
  device = (vendor >> 16) & 0x0000ffff;
  vendor = vendor & 0x0000ffff;
  if (! is_DC2114x){
    printf("Error: The chip is not DC2114x.\n");
    goto Done;
  }
  
  pci_config_inl(bus,devno,func, PCI_CFG_REVISION, &cfrv);
  /* Get the chip configuration revision register.
   */
  device = ((cfrv & CFRV_RN) < DC2114x_BRK ? DC21142 : DC21143);
  
  if (device != DC21143){
    printf("Error: The chip is not DC21143.\n");
    goto Done;
  }
  
  if(OK != pci_dev_config(bus, devno, func,
			PCI_ENET_IOADDR,
			PCI_ENET_MEMADDR,
			PCI_CMD_MEM_ENABLE |
			PCI_CMD_MEM_ENABLE |			  
			PCI_CMD_MASTER_ENABLE)){

    printf("DEC 21143 PCI Device configuration failed.\n");
    goto Done;
  }

  /* Set IRQ (ILINE) */
  pci_config_outl(bus, devno, func,
		   PCI_CFG_DEV_INT_LINE, MOUSSE_IRQ_ENET);
  
  /*
   * Turn off snooze mode.
   */
  pci_config_inl(bus,devno,func,0x40, &tmp);
  if(tmp)
    pci_config_outl(bus,devno,func,0x40,0x0);

  /* Read back memory base address */
  pci_config_inl(bus, devno, func,
		  PCI_CFG_BASE_ADDRESS_0, &iobase);
  if (iobase == 0xffffffff){
    printf("Error: Can not set I/O base register (MBAR0).\n");
    goto Done;
  }

  /* Clear status bits */
  iobase &= CBIO_MASK;
  
  /* Disable doze mode */
  pci_config_outl(bus, devno, func, PCI_CFG_MODE, WAKEUP);

  check_hw_addr(bis);
  udelay(10 * 1000);

  /* Reset Chip */
  RESET_DE4X5;

  /*
   * Initialization
   * 
   * Full Duplex Mode: MII/SYM Mode: Port Select Procedure.
   *  CSR13<15:0> = 0 (DE4X5_SICR)
   *  CSR14<15:0> = 0 (DE4X5_TXRX)
   *  CSR15<15:0> = 0008 (DE4X5_GPPR)
   *  CSR6<PS,FD> = 1,1 (DE4X5_OMR) 
   *  CSR6<OM> = 00
   * 
   */
  /* CSR13, CSR14 */
  tmp = inl(DE4X5_SICR);
  tmp &= 0xffff0000;
  outl(tmp, DE4X5_SICR);
  tmp = inl(DE4X5_TXRX);
  tmp &= 0xffff0000;
  outl(tmp, DE4X5_TXRX);

  /* AUI/BNC Mode */
  tmp = inl(DE4X5_GPPR);
  tmp &= 0xffff0000;
  tmp |= 0x8;
  outl(tmp, DE4X5_GPPR);

  /*
   * CSR6: Setup to work with transmit threshold mode,
   * port select, pass all multicast, full-duplex.
   */

  tmp = inl(DE4X5_OMR);
  tmp |= (OMR_SF|OMR_SDP|OMR_TTM|OMR_PS|OMR_PM|OMR_FD);
  outl(tmp, DE4X5_OMR);
    
  eth_online = 1;
  /*   pci_dev_show(0); */
 Done:
  return;
}

void
dc21x4x_log_error(void)
{
  u32 stat = le32_to_cpu(tx_ring[tx_new].tdes0);
  printf("TX (TDES0) error status = 0x%08X\n", stat);
  if(stat & T_OWN)
    printf("TXO ");
  if(stat & R_OWN)
    printf("RXO ");
  if(stat & ES_TX_ES)
    printf("ES ");
  if(stat & ES_TX_DE)
    printf("DE ");
  if(stat & ES_TX_UF)
    printf("UF ");
  if(stat & ES_TX_EC)
    printf("EC ");
  if(stat & ES_TX_LI)
    printf("Lost Carrier ");
  if(stat & ES_TX_LC)
    printf("LC ");
  if(stat & ES_TX_TO)
    printf("TJBR Timeout ");
  if(stat & ES_TX_LF)
    printf("Link Fail ");
  printf("\nTX TDES1=0x%x (tbs1=%d tbs2=%d) TDES2=0x%x TDES3=0x%x\n",
	 le32_to_cpu(tx_ring[tx_new].tdes1),
	 le32_to_cpu(tx_ring[tx_new].tdes1) & 0x000007ff,
	 (le32_to_cpu(tx_ring[tx_new].tdes1) & 0x003ff800) >> 11,	 
	 le32_to_cpu(tx_ring[tx_new].tdes2),
	 le32_to_cpu(tx_ring[tx_new].tdes3)
	 );
#if RX_DEBUG
  u32 stat = le32_to_cpu(rx_ring[rx_new].status);
  printf("RX (RDES0) error status = 0x%08X\n", stat);
  if(stat & T_OWN)
    printf("TXO ");
  if(stat & R_OWN)
    printf("RXO ");
  if(stat & ES_RX_FF)
    printf("FF ");
  if(stat & ES_RX_FL)
    printf("FL ");
  if(stat & ES_RX_DE)
    printf("DE ");
  if(stat & ES_RX_RF)
    printf("RF ");
  if(stat & ES_RX_TL)
    printf("TL ");
  if(stat & ES_RX_CS)
    printf("CS ");
  if(stat & ES_RX_RW)
    printf("RW ");
  if(stat & ES_RX_RE)
    printf("MII ");
  if(stat & ES_RX_DB)
    printf("DB ");
  if(stat & ES_RX_CE)
    printf("CE ");
#endif
  
  printf("status: ");
  stat = inl(DE4X5_STS);
  
  if(stat & STS_LC)
    printf("lc ");
  if(stat & STS_GPI)
    printf("gpi ");
  if(stat & STS_NIS)
    printf("nis ");
  if(stat & STS_AIS)
    printf("ais ");
  if(stat & STS_ERI)
    printf("eri ");
  if(stat & STS_FBE)
    printf("fbe ");
  if(stat & STS_LNF)
    printf("lnf ");
  if(stat & STS_GTE)
    printf("gte ");
  if(stat & STS_ETI)
    printf("eti ");
  if(stat & STS_RWT)
    printf("rwt ");
  if(stat & STS_RPS)
    printf("rps ");
  if(stat & STS_RU)
    printf("ru ");
  if(stat & STS_RI)
    printf("ri ");
  if(stat & STS_UNF)
    printf("unf ");
  if(stat & STS_ANC)
    printf("link ");
  if(stat & STS_TJT)
    printf("tjt ");
  if(stat & STS_TU)
    printf("tu ");
  if(stat & STS_TPS)
    printf("tps ");
  if(stat & STS_TI)
    printf("ti ");
  printf("\n");
  
  printf("eb=%d tps=%d rps=%d\n",
	 (stat & STS_ES_EB_MASK) >> STS_ES_EB_SHIFT,
	 (stat & STS_ES_TS_MASK) >> STS_ES_TS_SHIFT,
	 (stat & STS_ES_RS_MASK) >> STS_ES_RS_SHIFT);
}
  
  


int eth_init(bd_t *bis)
{
	int i;

	dc21x4x_init(bis);

	if ((inl(DE4X5_STS) & (STS_TS | STS_RS)) != 0)
	{
		printf("Error: Can not reset ethernet controller.\n");
		goto Done;
	}
	
	outl(OMR_SDP | OMR_PS | OMR_PM, DE4X5_OMR);

	for (i = 0; i < NUM_RX_DESC; i++)
	{
		rx_ring[i].status = cpu_to_le32(R_OWN);
		rx_ring[i].des1 = cpu_to_le32(RX_BUFF_SZ);
		rx_ring[i].buf = cpu_to_le32((u_long)NetRxPackets[i]);
		rx_ring[i].next = 0;
	}

	for (i=0; i < NUM_TX_DESC; i++)
	{
		tx_ring[i].status = 0;
		tx_ring[i].des1 = 0;
		tx_ring[i].buf = 0;
		tx_ring[i].next = 0;
	}

	rxRingSize = NUM_RX_DESC;
	txRingSize = NUM_TX_DESC;

		/* Write the end of list marker to the descriptor lists.
		 */
	rx_ring[rxRingSize - 1].des1 |= cpu_to_le32(RD_RER);
	tx_ring[txRingSize - 1].des1 |= cpu_to_le32(TD_TER);

		/* Tell the adapter where the TX/RX rings are located.
		 */
	outl((u_long)&rx_ring, DE4X5_RRBA);
	outl((u_long)&tx_ring, DE4X5_TRBA);

	START_DE4X5;

	tx_new = 0;
	rx_new = 0;

	send_setup_frame(bis);

Done:

	return 0;
}

int eth_send(volatile void *packet, int length)
{
	int    status = 0;
	int i;

#if TX_DEBUG
	unsigned char* pkt = (unsigned char*)packet;
	printf("\neth_send: frame data follows ...\n============\n");
	for( i = 0; i < length; i++){
	  printf("%d ", pkt[i]);
	  if(i > 0 && ((i % 16) == 0))
	    printf("\n");
	}
	printf("\n=============\n");
#endif
	
	if (length <= 0)
	{
		printf("eth: bad packet size: %d\n", length);
		goto out;
	}

	for(i = 0; tx_ring[tx_new].status & cpu_to_le32(T_OWN); i++)
	{
		if (i >= TOUT_LOOP)
		{
			printf("eth: tx error buffer not ready\n");
			goto out;
		}
	}
	tx_ring[tx_new].next = 0;
	tx_ring[tx_new].buf = cpu_to_le32((u_long)packet);	
	tx_ring[tx_new].des1 = cpu_to_le32(TD_TER | TD_LS | TD_FS | length);
	tx_ring[tx_new].status = cpu_to_le32(T_OWN);

	outl(inl(DE4X5_STS), DE4X5_STS); /* Clear status bits */
	outl(POLL_DEMAND, DE4X5_TPD);

	for(i = 0; tx_ring[tx_new].status & cpu_to_le32(T_OWN); i++)
	{
		if (i >= TOUT_LOOP)
		{
			printf("eth: tx buffer not ready\n");
			goto out;
		}
	}
#if 0
	if(inl(DE4X5_STS) & STS_TU){
	  printf("Transmit buffer unavailable. Retrying ...\n");
	  outl(inl(DE4X5_STS), DE4X5_STS); /* Clear status bits */
	  goto start;
	}
#endif
	
	/* Determine which error, if any, we detected.
	 * See also: Intel 21143 Hardware Reference
	 * Table 3-66
	 * Ignore: Transmit Process stopped due to Buffer Unavailable.
	 */
#if 1
	if ((le32_to_cpu(tx_ring[tx_new].status) & TD_ES) &&
	    !(inl(DE4X5_STS) & STS_TU))
#else  
	  if ((le32_to_cpu(tx_ring[tx_new].status) & TD_ES))
#endif
	  {
	  dc21x4x_log_error();
	  status++;
	}

	out:
	return status;
}

int eth_rx(void)
{
	s32 status;
	int length    = 0;

	for ( ; ; )
	{
		status = (s32)le32_to_cpu(rx_ring[rx_new].status);

		if (status & R_OWN)
		{
			break;
		}

		if (status & RD_LS)
		{
				/* Valid frame status.
				 */
			if (status & RD_ES)
			{
					/* There was an error.
					 */
				printf("RX error status = 0x%08X\n", status);
			}
			else
			{
					/* A valid frame received.
					 */
				length = (le32_to_cpu(rx_ring[rx_new].status) >>
				                                          16);

					/* Pass the packet up to the protocol
					 * layers.
					 */
				NetReceive(NetRxPackets[rx_new], length - 4);
			}

				/* Change buffer ownership for this frame, back
				 * to the adapter.
				 */
			rx_ring[rx_new].status = cpu_to_le32(R_OWN);
		}

			/* Update entry information.
			 */
		rx_new = (rx_new + 1) % rxRingSize;
	}

  return length;
}

void eth_halt(void)
{
  if(eth_online){
    STOP_DE4X5;
    outl(0, DE4X5_SICR);
    pci_config_outb(bus, device, func, PCI_CFG_MODE, SLEEP);
    eth_online = 0;
  }
}

static void check_hw_addr(bd_t *bis)
{
	unsigned char hw_addr[ETH_ALEN];
	u_short tmp, *p = (short *)(&hw_addr[0]);
	int i, j = 0;

	for (i = 0; i < (ETH_ALEN >> 1); i++)
	{
		tmp = srom_rd(DE4X5_APROM, (SROM_HWADD >> 1) + i);
		*p = le16_to_cpu(tmp);
		j += *p++;
	}

	if ((j == 0) || (j == 0x2fffd))
	{
	  //		printf("Warning: can't read HW address from SROM.\n");
		goto Done;
	}

	for (i = 0; i < ETH_ALEN; i++)
	{
		if (hw_addr[i] != bis->bi_enetaddr[i])
		{
			printf("Warning: HW addresses don't match:\n");
			printf("Address in SROM is         "
			       "%02X:%02X:%02X:%02X:%02X:%02X\n",
			       hw_addr[0], hw_addr[1], hw_addr[2],
			       hw_addr[3], hw_addr[4], hw_addr[5]);
			printf("Address used by ppcboot is "
			       "%02X:%02X:%02X:%02X:%02X:%02X\n",
			       bis->bi_enetaddr[0], bis->bi_enetaddr[1],
			       bis->bi_enetaddr[2], bis->bi_enetaddr[3],
			       bis->bi_enetaddr[4], bis->bi_enetaddr[5]);
			goto Done;
		}
	}

Done:
	return;
}

static void send_setup_frame(bd_t *bis)
{
	int    i;
	char   setup_frame[SETUP_FRAME_LEN];
	char * pa                            = &setup_frame[0];

	memset(pa, 0xff, SETUP_FRAME_LEN);

	for (i = 0; i < ETH_ALEN; i++)
	{
		*(pa + (i & 1)) = bis->bi_enetaddr[i];
		if (i & 0x01)
		{
			pa += 4;
		}
	}

	for(i = 0; tx_ring[tx_new].status & cpu_to_le32(T_OWN); i++)
	{
		if (i >= TOUT_LOOP)
		{
			printf("eth: tx error buffer not ready\n");
			goto out;
		}
	}
	
	tx_ring[tx_new].buf = cpu_to_le32((u_long)&setup_frame[0]);
	tx_ring[tx_new].des1 = cpu_to_le32(TD_TER | TD_SET| SETUP_FRAME_LEN);
	tx_ring[tx_new].status = cpu_to_le32(T_OWN);

	outl(POLL_DEMAND, DE4X5_TPD);

	for(i = 0; tx_ring[tx_new].status & cpu_to_le32(T_OWN); i++)
	{
		if (i >= TOUT_LOOP)
		{
			printf("eth: tx buffer not ready\n");
			goto out;
		}
	}

	if (le32_to_cpu(tx_ring[tx_new].status) != 0x7FFFFFFF)
	{
		printf("Setup Frame TX error status = 0x%08X\n",
		       le32_to_cpu(tx_ring[tx_new].status));
	}
out:
	return;
}

	/* SROM Read.
	 */
static short
srom_rd(u_long addr, u_char offset)
{
    sendto_srom(SROM_RD | SROM_SR, addr);
    
    srom_latch(SROM_RD | SROM_SR | DT_CS, addr);
    srom_command(SROM_RD | SROM_SR | DT_IN | DT_CS, addr);
    srom_address(SROM_RD | SROM_SR | DT_CS, addr, offset);
    
    return srom_data(SROM_RD | SROM_SR | DT_CS, addr);
}

static void
srom_latch(u_int command, u_long addr)
{
    sendto_srom(command, addr);
    sendto_srom(command | DT_CLK, addr);
    sendto_srom(command, addr);
    
    return;
}

static void
srom_command(u_int command, u_long addr)
{
    srom_latch(command, addr);
    srom_latch(command, addr);
    srom_latch((command & 0x0000ff00) | DT_CS, addr);
    
    return;
}

static void
srom_address(u_int command, u_long addr, u_char offset)
{
    int i;
    signed char a;
    
    a = (char)(offset << 2);
    for (i=0; i<6; i++, a <<= 1) {
        srom_latch(command | ((a < 0) ? DT_IN : 0), addr);
    }
    udelay(1);
    
    i = (getfrom_srom(addr) >> 3) & 0x01;
    
    return;
}

static short
srom_data(u_int command, u_long addr)
{
    int i;
    short word = 0;
    s32 tmp;
    
    for (i=0; i<16; i++) {
        sendto_srom(command  | DT_CLK, addr);
        tmp = getfrom_srom(addr);
        sendto_srom(command, addr);
	
        word = (word << 1) | ((tmp >> 3) & 0x01);
    }
    
    sendto_srom(command & 0x0000ff00, addr);
    
    return word;
}

static void
sendto_srom(u_int command, u_long addr)
{
    outl(command, addr);
    udelay(1);
    
    return;
}

static int
getfrom_srom(u_long addr)
{
    s32 tmp;
    
    tmp = inl(addr);
    udelay(1);
    
    return tmp;
}

