/*
 * Copyright (c) 2003, 2004 PMC-Sierra, Inc. (www.pmc-sierra.com)
 * Author: Brad Larson (brad_larson@pmc-sierra.com)
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *      This product includes software developed by PMC-Sierra, Inc.
 * 4. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 */
#include "bpfilter.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <sys/param.h>
#include <sys/mbuf.h>
#include <sys/malloc.h>
#include <sys/kernel.h>
#include <sys/socket.h>
#include <sys/syslog.h>

#include <net/if.h>
#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/if_types.h>

#include <autoconf.h>

#ifdef INET
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netinet/in_var.h>
#include <netinet/ip.h>
#endif

#ifdef IPX
#include <netipx/ipx.h>
#include <netipx/ipx_if.h>
#endif

#ifdef NS
#include <netns/ns.h>
#include <netns/ns_if.h>
#endif

#include <sys/ioctl.h>
#include <sys/errno.h>
#include <sys/device.h>

#include <netinet/if_ether.h>
#include <vm/vm.h>

#include <machine/cpu.h>
#include <machine/bus.h>
#include <machine/intr.h>
#include <machine/rm9000_reg.h>
#include <machine/rm9000_mac.h>

#include <dev/mii/mii.h>
#include <dev/mii/miivar.h>

#include <dev/pci/pcivar.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcidevs.h>
#include <dev/ic/if_rme.h>
#include <unistd.h>

#include <pmon.h>
#include <inline_asm.h>

#if defined(IO_COHERENT)
#if defined(CACHESYNC)
#undef CACHESYNC
#endif
#define CACHESYNC(addr, size, flag)
#endif

#if defined(YOSEMITE)
#define EEPROM      0x0057
#elif defined(SEQUOIA)
#define EEPROM      0x0057
#else
#error eeprom address for boardinfo unknown
#endif

#define UNCACHED_MEMORY_ADDR2   0xa0000000

extern int cmd_dump (int ac, char *av[]);
extern int eeprom_read_byte (int device, int offset);

/* Prototypes */
int rme_match(struct device *, void *, void *);
void rme_attach(struct device *, struct device *, void *);
static void reset_tx_desc(struct rme_softc *);
static void reset_rx_desc(struct rme_softc *);
static int rme_ioctl(struct ifnet *, u_long, caddr_t);
static void rme_init(void *);
static void rme_start(struct ifnet *);
static void rme_mac_reset(struct rme_softc *);
static void rme_watchdog(struct ifnet *);
static int rme_get_mbuf(struct rme_softc *, struct mbuf **);
static int rme_intr(void *);
static int rme_rx(struct rme_softc *, u_int32_t, u_int32_t);
static int rme_ifmedia_upd(struct ifnet *);
static void rme_ifmedia_sts(struct ifnet *, struct ifmediareq *);
static void rme_mii_statchg(struct device *);
static int rme_interface_speed(struct rme_softc *sc);
static void rme_set_mac_address(struct rme_softc *sc);
static int rm9000_mdio_poll(void);
static int rme_mii_read_cfg (void *sc, int phyaddr, int regaddr);
static void rme_mii_write_cfg (void *sc, int phyaddr, int regaddr, int data);
static void rme_tx_enable(struct rme_softc *sc);
static void rme_rx_enable(struct rme_softc *sc);
static void rme_trtg_enable(struct rme_softc *sc);
static void rme_mii_enable(struct rme_softc *sc);
static void rme_xdma_enable(struct rme_softc *sc);
static void rme_fifo_enable(struct rme_softc *sc);
static void dump_mbuf(struct mbuf *,int);
static void dump_pkt(int addr, int size);
static int getMstatxCounter(struct rme_softc *sc, int mstatxReg, unsigned long long *result);
void rme_reg_dump(void);
int cmd_netstat(int, char *[]);
int cmd_mii(int, char *[]);

void rme_read_mib_counters(struct rme_softc *);

int initAddressTable(int, int, int, int);
int addAddressTableEntry(int, uint, uint, uint, uint);

/* Compensate for lack of a generic ether_ioctl() */
static int rme_ether_ioctl (struct ifnet *, u_int32_t, caddr_t);
#define ether_ioctl     rme_ether_ioctl

struct cfattach rme_ca = {                              
    sizeof(struct rme_softc), rme_match, rme_attach 
};                                                      
                                                        
struct cfdriver rme_cd = {                              
    NULL, "rme", DV_IFNET                           
};

/* 
 *  Debug definitions 
 */
#define DEBUG_RME
#ifdef DEBUG_RME
static uint8_t rme0_verbose = 0;
static uint8_t rme1_verbose = 0;
static uint8_t rme2_verbose = 0;

#define PRINTF(x)	if( (sc->sc_port==0 && rme0_verbose>0) || \
			    (sc->sc_port==1 && rme1_verbose>0) || \
			    (sc->sc_port==2 && rme2_verbose>0) ) printf x
#else
#define PRINTF(x)	
#endif

static volatile int tx_norecurse = 0;
static volatile int rx_norecurse = 0;
#define TX_NORECURSE(x)    if (tx_norecurse == 0) { tx_norecurse = 1; x; tx_norecurse = 0; }
#define RX_NORECURSE(x)    if (rx_norecurse == 0) { rx_norecurse = 1; x; rx_norecurse = 0; }

/*
 *  Local constants
 */
#define MAX_CLKA            	1023
#define MAX_PHY_DEV         	31
#define MAX_PHY_REG         	31
#define WRITEADDRS_OPCODE   	0x0
#define READ_OPCODE         	0x200
#define WRITE_OPCODE        	0x100
#define MAX_MDIO_POLL       	500
#define DUMP_TX 		1
#define DUMP_RX 		2
#define RFA_ALIGNMENT_FUDGE	2	/* RX buffer magic offset */
#define EOF			(-1)

int rme_debug = 0;

/* 
 *  Local globals 
 */
static struct rme_softc *sc_port0;
static struct rme_softc *sc_port1;
static struct rme_softc *sc_port2;

/* Define ethernet MAC address */
//extern char hwethadr[]; 


/*
 *  Verify this device has an ethernet port.  The other
 *  possibilities for the RM9000 family include GPI or none.
 */
int
rme_match(parent, match, aux)
	struct device *parent;
	void *match, *aux;
{
	struct rme_softc *sc = (struct rme_softc *)match;
	uint32_t regdata;
	uint16_t devId;
	uint16_t version;

        regdata = RM9000_GE_READ(RM9000_GE_DEVICE_ID_REG);
	devId = regdata & 0xffff;
	version = regdata >> 16;

        if (devId == RM9220_GE_DEVICE_ID) {
		if (sc->sc_dev.dv_unit == 0)
			PRINTF(("rme: found device 0x%x, version %d\n", devId, version));
                return (1);
	}
        else {
		PRINTF(("rme%d: ethernet device not found\n", sc->sc_dev.dv_unit));
                return (0);
	}
}


/* 
 *  Attach the interface 
 */
void 
rme_attach (parent, self, aux)
	struct device *parent, *self;
	void *aux;
{
	struct rme_softc *sc = (struct rme_softc *)self;
	struct confargs *cf = aux;
	struct ifnet *ifp;
	char *v;
	int i;
	int ethaddr_error = 0;

        /*
         *  MAC base address
         */
        for (i=0; i<6; i++) {
		sc->arpcom.ac_enaddr[i] = (uint8_t) eeprom_read_byte(EEPROM, 0x1a + i);
        }

	if ((sc->arpcom.ac_enaddr[0] != 0x00) |
	    (sc->arpcom.ac_enaddr[1] != 0xe0) |
	    (sc->arpcom.ac_enaddr[2] != 0x04)) {

		sc->arpcom.ac_enaddr[0] = 0x00;
		sc->arpcom.ac_enaddr[1] = 0xe0;
		sc->arpcom.ac_enaddr[2] = 0x04;
		sc->arpcom.ac_enaddr[3] = 0x00;
		sc->arpcom.ac_enaddr[4] = 0x00;
		sc->arpcom.ac_enaddr[5] = 0x00;
		ethaddr_error = 1;
	}

#ifdef DEBUG_RME
	/* Debug verbosity check */
    	v = getenv("rme0verbose");
    	if (v) {
  		rme0_verbose = atol(v);
	}
    	v = getenv("rme1verbose");
    	if (v) {
  		rme1_verbose = atol(v);
	}
    	v = getenv("rme2verbose");
    	if (v) {
  		rme2_verbose = atol(v);
	}
#endif

        /* Fill in the interface name and hardware address */
	ifp = &sc->arpcom.ac_if;
	bcopy(sc->sc_dev.dv_xname, ifp->if_xname, IFNAMSIZ);
        sc->arpcom.ac_enaddr[5] += sc->sc_dev.dv_unit;
	sc->sc_port = sc->sc_dev.dv_unit;
	printf(": adr %s ", ether_sprintf(sc->arpcom.ac_enaddr));

	/* The PHY address comes from the config */
	sc->phy_addr = cf->ca_phyaddr;
	if (sc->phy_addr == -1)
		printf("PHY undefined!");
	printf("\n");

	if (ethaddr_error)
		printf("%s: eeprom ethernet address error, using default\n",
			sc->sc_dev.dv_xname);

        /* Determine interface speed and duplex from PHY */
        rme_interface_speed(sc);

	ifp->if_softc = sc;
	ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
	ifp->if_ioctl = rme_ioctl;
	ifp->if_start = rme_start; 
	ifp->if_watchdog = rme_watchdog;

	/* For netstat to get at device softc */
	if (sc->sc_port == 0)	
		sc_port0 = sc;
	else if (sc->sc_port == 1)
		sc_port1 = sc;
	else if (sc->sc_port == 2)
		sc_port2 = sc;

	/*
  	 *  Allocate Tx and Rx descriptor rings 
 	 */
        sc->tx_ring = (TX_DESC *)malloc(TX_RING_SIZE*sizeof(TX_DESC), M_DEVBUF, M_NOWAIT);
        sc->rx_ring = (RX_DESC *)malloc(RX_RING_SIZE*sizeof(RX_DESC), M_DEVBUF, M_NOWAIT);
	bzero(sc->tx_ring, TX_RING_SIZE * sizeof(TX_DESC));
	bzero(sc->rx_ring, RX_RING_SIZE * sizeof(RX_DESC));

	/* 
	 *  Allocate Tx data buffers 
	 */
	sc->tx_buf = (uint8_t *)malloc(TX_RING_SIZE*TX_BUF_SIZE, M_DEVBUF, M_NOWAIT);
	bzero(sc->tx_buf, TX_RING_SIZE * TX_BUF_SIZE);

	/*
  	 *  Allocate Rx data buffers
 	 */
        sc->rx_buf = (uint8_t *)malloc(RX_RING_SIZE*RX_BUF_SIZE, M_DEVBUF, M_NOWAIT);
	bzero(sc->rx_buf, RX_RING_SIZE * RX_BUF_SIZE);

	PRINTF(("rme%d: %d tx descriptors at 0x%08x size 0x%x\n", sc->sc_port, TX_RING_SIZE,  
		sc->tx_ring, TX_RING_SIZE*sizeof(TX_DESC)));

	PRINTF(("rme%d: %d rx descriptors at 0x%08x size 0x%x\n", sc->sc_port, RX_RING_SIZE,
		sc->rx_ring, RX_RING_SIZE*sizeof(RX_DESC)));

	PRINTF(("rme%d: %d tx buffers at 0x%08x, size 0x%x\n", sc->sc_port, TX_RING_SIZE,
		sc->tx_buf, TX_RING_SIZE * TX_BUF_SIZE));

	PRINTF(("rme%d: %d rx buffers at 0x%08x, size 0x%x\n", sc->sc_port, RX_RING_SIZE,
		sc->rx_buf, RX_RING_SIZE * RX_BUF_SIZE));

	/* Initialize Tx and Rx descriptors */
	reset_tx_desc(sc);
	reset_rx_desc(sc);

	/* TMAC/RMAC into reset */
	rme_mac_reset(sc);

	/* Attach the MII */
	ifmedia_init(&sc->mii_data.mii_media, 0, rme_ifmedia_upd, rme_ifmedia_sts);
	sc->mii_data.mii_ifp = ifp;
	sc->mii_data.mii_readreg = (mii_readreg_t) rme_mii_read_cfg;
	sc->mii_data.mii_writereg = (mii_writereg_t) rme_mii_write_cfg;
	sc->mii_data.mii_statchg = (mii_statchg_t) rme_mii_statchg;
	mii_phy_probe(&sc->sc_dev, &sc->mii_data, 0xffffffff);
	if (LIST_FIRST(&sc->mii_data.mii_phys) == NULL) {
		ifmedia_add(&sc->mii_data.mii_media, IFM_ETHER|IFM_NONE, 0, NULL);
		ifmedia_set(&sc->mii_data.mii_media, IFM_ETHER|IFM_NONE);
	} 
        else {
		ifmedia_set(&sc->mii_data.mii_media, IFM_ETHER|IFM_AUTO);
	}

	/* Set port ethernet address and init unicast address table */
        rme_set_mac_address(sc);

	/*
	 *  Enable the device from center to external interface
	 */

	/* Enable XDMA channels */
	rme_xdma_enable(sc);

	/* Enable Transmit and Receive FIFOs */
	rme_fifo_enable(sc);

	/* Enable Transmit and Receive MAC (TMAC/RMAC) */
	rme_tx_enable(sc);
	rme_rx_enable(sc);

	/* Enable Traffic Grooming Block (TRTG) */
	rme_trtg_enable(sc);

	/* Set up interrupt callback */
	tgt_poll_register(IPL_NET, rme_intr, sc);

	/* Attach the interface */
	if_attach(ifp);

	/* Let the system queue as many tx packets as available */
	ifp->if_snd.ifq_maxlen = TX_RING_SIZE - 1;
	ether_ifattach(ifp);
}


/*
 *  Update transmit and receive station addresses using 
 *  the three TMAC and RMAC station registers.
 */
#define ETHER_ADDR_LEN    6
static void 
rme_set_mac_address(sc)
        struct rme_softc *sc;
{
        uint8_t i;
        uint8_t ethaddr[ETHER_ADDR_LEN];
     
        for (i=0; i<ETHER_ADDR_LEN; i++)
                ethaddr[i] = sc->arpcom.ac_enaddr[i];

	/* Transmit station address */
	RM9000_GE_WRITE(RM9000_GE_TMAC_STATION_HI,  ((ethaddr[5] << 8) | ethaddr[4]));
	RM9000_GE_WRITE(RM9000_GE_TMAC_STATION_MID, ((ethaddr[3] << 8) | ethaddr[2]));
	RM9000_GE_WRITE(RM9000_GE_TMAC_STATION_LOW, ((ethaddr[1] << 8) | ethaddr[0]));

        /* Receive station address */
	RM9000_GE_WRITE(RM9000_GE_RMAC_STATION_HI,  ((ethaddr[5] << 8) | ethaddr[4]));
	RM9000_GE_WRITE(RM9000_GE_RMAC_STATION_MID, ((ethaddr[3] << 8) | ethaddr[2]));
	RM9000_GE_WRITE(RM9000_GE_RMAC_STATION_LOW, ((ethaddr[1] << 8) | ethaddr[0]));

	return;
}


/*
 *  Reset the ethernet port
 */
static void 
rme_mac_reset(sc)
        struct rme_softc *sc;
{
	unsigned int regdata;

	/* Stop Tx and Rx port activity */
        if (sc->sc_port == 0) {
	        regdata = RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_1_PORT_0);
	        regdata &= ~(0x0001);
	        RM9000_GE_WRITE(RM9000_GE_TMAC_CONFIG_1_PORT_0, regdata);    /* Tx */

                regdata = RM9000_GE_READ(RM9000_GE_RMAC_CONFIG_1_PORT_0);
                regdata &= ~(0x0001);
                RM9000_GE_WRITE(RM9000_GE_RMAC_CONFIG_1_PORT_0, regdata);    /* Rx */
        }
        else if (sc->sc_port == 1) {
	        regdata = RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_1_PORT_1);
	        regdata &= ~(0x0001);
	        RM9000_GE_WRITE(RM9000_GE_TMAC_CONFIG_1_PORT_1, regdata);    /* Tx */

                regdata = RM9000_GE_READ(RM9000_GE_RMAC_CONFIG_1_PORT_1);
                regdata &= ~(0x0001);
                RM9000_GE_WRITE(RM9000_GE_RMAC_CONFIG_1_PORT_1, regdata);    /* Rx */
        }
        else if (sc->sc_port == 2) {
	        regdata = RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_1_PORT_2);
	        regdata &= ~(0x0001);
	        RM9000_GE_WRITE(RM9000_GE_TMAC_CONFIG_1_PORT_2, regdata);    /* Tx */

                regdata = RM9000_GE_READ(RM9000_GE_RMAC_CONFIG_1_PORT_2);
                regdata &= ~(0x0001);
                RM9000_GE_WRITE(RM9000_GE_RMAC_CONFIG_1_PORT_2, regdata);    /* Rx */
        }

	return;
}


/*
 *  Enable Transmit MAC (TMAC)
 */
static void 
rme_tx_enable(sc)
        struct rme_softc *sc;
{
	uint32_t regdata;

        if (sc->sc_port == 0) {
	        regdata = RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_1_PORT_0);
        	if (!(regdata & 0x0001)) {
                    	regdata |= 0x4000;     /* CRC Check Enable */
                	regdata |= 0x2000;     /* Padding enable */
                	regdata |= 0x0800;     /* CRC Add enable */
                	regdata |= 0x0080;     /* Pause frame */
	        	RM9000_GE_WRITE(RM9000_GE_TMAC_CONFIG_1_PORT_0, regdata);

			/* Config 2 setup */
	        	regdata = RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_2_PORT_0);
                    	regdata |= 0x8000;     /* Link speed enable */
                    	regdata |= 0x2000;     /* Link duplex enable */
	        	RM9000_GE_WRITE(RM9000_GE_TMAC_CONFIG_2_PORT_0, regdata);

			/* Set default packet priority to 0 */
			regdata = RM9000_GE_READ(RM9000_PRIORITY_CHECKSUM_PORT_0);
			regdata &= ~(0x00f00000);
			RM9000_GE_WRITE(RM9000_PRIORITY_CHECKSUM_PORT_0, regdata);

			/* All setup done, set the enable bit */
	        	regdata = RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_1_PORT_0);
                    	regdata |= 0x0001;     /* Enable TMAC */
	        	RM9000_GE_WRITE(RM9000_GE_TMAC_CONFIG_1_PORT_0, regdata);

			PRINTF(("rme%d: tmac config 1 0x%04x\n", sc->sc_port,
				RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_1_PORT_0) & 0xffff));
			PRINTF(("rme%d: tmac config 2 0x%04x\n", sc->sc_port,
				RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_2_PORT_0) & 0xffff));
			PRINTF(("rme%d: priority checksum (default priority to 0) 0x%08x\n",
				sc->sc_port,
				RM9000_GE_READ(RM9000_PRIORITY_CHECKSUM_PORT_0)));
        	}
        }
        else if (sc->sc_port == 1) {
	        regdata = RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_1_PORT_1);
        	if (!(regdata & 0x0001)) {
                    	regdata |= 0x4000;     /* CRC Check Enable */
                	regdata |= 0x2000;     /* Padding enable */
                	regdata |= 0x0800;     /* CRC Add enable */
                	regdata |= 0x0080;     /* Pause frame */
	        	RM9000_GE_WRITE(RM9000_GE_TMAC_CONFIG_1_PORT_1, regdata);

			/* Config 2 setup */
	        	regdata = RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_2_PORT_1);
                    	regdata |= 0x8000;     /* Link speed enable */
                    	regdata |= 0x2000;     /* Link duplex enable */
	        	RM9000_GE_WRITE(RM9000_GE_TMAC_CONFIG_2_PORT_1, regdata);

			/* Set default packet priority to 0 */
			regdata = RM9000_GE_READ(RM9000_PRIORITY_CHECKSUM_PORT_1);
			regdata &= ~(0x00f00000);
			RM9000_GE_WRITE(RM9000_PRIORITY_CHECKSUM_PORT_1, regdata);

			/* All setup done, set the enable bit */
	        	regdata = RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_1_PORT_1);
                    	regdata |= 0x0001;     /* Enable TMAC */
	        	RM9000_GE_WRITE(RM9000_GE_TMAC_CONFIG_1_PORT_1, regdata);

			PRINTF(("rme%d: tmac config 1 0x%04x\n", sc->sc_port,
				RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_1_PORT_1) & 0xffff));
			PRINTF(("rme%d: tmac config 2 0x%04x\n", sc->sc_port,
				RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_2_PORT_1) & 0xffff));
			PRINTF(("rme%d: priority checksum (default priority to 0) 0x%08x\n",
				sc->sc_port,
				RM9000_GE_READ(RM9000_PRIORITY_CHECKSUM_PORT_0)));
        	}
        }
        else if (sc->sc_port == 2) {
	        regdata = RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_1_PORT_2);
        	if (!(regdata & 0x0001)) {
                    	regdata |= 0x4000;     /* CRC Check Enable */
                	regdata |= 0x2000;     /* Padding enable */
                	regdata |= 0x0800;     /* CRC Add enable */
                	regdata |= 0x0080;     /* Pause frame */
	        	RM9000_GE_WRITE(RM9000_GE_TMAC_CONFIG_1_PORT_2, regdata);

			/* Config 2 setup */
	        	regdata = RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_2_PORT_2);
                    	regdata |= 0x8000;     /* Link speed enable */
                    	regdata |= 0x2000;     /* Link duplex enable */
	        	RM9000_GE_WRITE(RM9000_GE_TMAC_CONFIG_2_PORT_2, regdata);

			/* Set default packet priority to 0 */
			regdata = RM9000_GE_READ(RM9000_PRIORITY_CHECKSUM_PORT_2);
			regdata &= ~(0x00f00000);
			RM9000_GE_WRITE(RM9000_PRIORITY_CHECKSUM_PORT_2, regdata);

			/* All setup done, set the enable bit */
	        	regdata = RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_1_PORT_2);
                    	regdata |= 0x0001;     /* Enable TMAC */
	        	RM9000_GE_WRITE(RM9000_GE_TMAC_CONFIG_1_PORT_2, regdata);

			PRINTF(("rme%d: tmac config 1 0x%04x\n", sc->sc_port,
				RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_1_PORT_2) & 0xffff));
			PRINTF(("rme%d: tmac config 2 0x%04x\n", sc->sc_port,
				RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_2_PORT_2) & 0xffff));
			PRINTF(("rme%d: priority checksum (default priority to 0) 0x%08x\n", 
				sc->sc_port,
				RM9000_GE_READ(RM9000_PRIORITY_CHECKSUM_PORT_0)));
        	}
        }

	return;
}


/*
 *  Enable Receive MAC (RMAC)
 */
static void 
rme_rx_enable(sc)
        struct rme_softc *sc;
{
	uint32_t regdata;

        if (sc->sc_port == 0) {
	        regdata = RM9000_GE_READ(RM9000_GE_RMAC_CONFIG_1_PORT_0);
        	if (!(regdata & 0x0001)) {
                    	regdata |= 0x0001;     /* Enable RMAC */
                    	regdata |= 0x0010;     /* CRC Check Enable */
        		regdata |= 0x0040;     /* Min Frame check enable */
        		regdata |= 0x0400;     /* Max Frame check enable */
	        	RM9000_GE_WRITE(RM9000_GE_RMAC_CONFIG_1_PORT_0, regdata);

			/* Receive link configuration */
	        	regdata = RM9000_GE_READ(RM9000_GE_RMAC_LINK_CONFIG_PORT_0);
                    	regdata |= 0x0001;     /* Link speed enable */
	        	RM9000_GE_WRITE(RM9000_GE_RMAC_LINK_CONFIG_PORT_0, regdata);

			PRINTF(("rme%d: rmac config 1 0x%04x\n", sc->sc_port,
				RM9000_GE_READ(RM9000_GE_RMAC_CONFIG_1_PORT_0) & 0xffff));
			PRINTF(("rme%d: rmac config 2 0x%04x\n", sc->sc_port,
				RM9000_GE_READ(RM9000_GE_RMAC_CONFIG_2_PORT_0) & 0xffff));
			PRINTF(("rme%d: link config 0x%04x\n", sc->sc_port,
				RM9000_GE_READ(RM9000_GE_RMAC_LINK_CONFIG_PORT_0) & 0xffff));
        	}
        }
        else if (sc->sc_port == 1) {
	        regdata = RM9000_GE_READ(RM9000_GE_RMAC_CONFIG_1_PORT_1);
        	if (!(regdata & 0x0001)) {
                    	regdata |= 0x0001;     /* Enable RMAC */
                    	regdata |= 0x0010;     /* CRC Check Enable */
        		regdata |= 0x0040;     /* Min Frame check enable */
        		regdata |= 0x0400;     /* Max Frame check enable */
	        	RM9000_GE_WRITE(RM9000_GE_RMAC_CONFIG_1_PORT_1, regdata);

			/* Receive link configuration */
	        	regdata = RM9000_GE_READ(RM9000_GE_RMAC_LINK_CONFIG_PORT_1);
                    	regdata |= 0x0001;     /* Link speed enable */
	        	RM9000_GE_WRITE(RM9000_GE_RMAC_LINK_CONFIG_PORT_1, regdata);

			PRINTF(("rme%d: rmac config 1 0x%04x\n", sc->sc_port,
				RM9000_GE_READ(RM9000_GE_RMAC_CONFIG_1_PORT_1) & 0xffff));
			PRINTF(("rme%d: rmac config 2 0x%04x\n", sc->sc_port,
				RM9000_GE_READ(RM9000_GE_RMAC_CONFIG_2_PORT_1) & 0xffff));
			PRINTF(("rme%d: link config 0x%04x\n", sc->sc_port,
				RM9000_GE_READ(RM9000_GE_RMAC_LINK_CONFIG_PORT_1) & 0xffff));
        	}
        }
        else if (sc->sc_port == 2) {
	        regdata = RM9000_GE_READ(RM9000_GE_RMAC_CONFIG_1_PORT_2);
        	if (!(regdata & 0x0001)) {
                    	regdata |= 0x0001;     /* Enable RMAC */
                    	regdata |= 0x0010;     /* CRC Check Enable */
        		regdata |= 0x0040;     /* Min Frame check enable */
        		regdata |= 0x0400;     /* Max Frame check enable */
                	regdata |= 0x0080;     /* Pause frame */
	        	RM9000_GE_WRITE(RM9000_GE_RMAC_CONFIG_1_PORT_2, regdata);

			/* Receive link configuration */
	        	regdata = RM9000_GE_READ(RM9000_GE_RMAC_LINK_CONFIG_PORT_2);
                    	regdata |= 0x0001;     /* Link speed enable */
	        	RM9000_GE_WRITE(RM9000_GE_RMAC_LINK_CONFIG_PORT_2, regdata);

			PRINTF(("rme%d: rmac config 1 0x%04x\n", sc->sc_port,
				RM9000_GE_READ(RM9000_GE_RMAC_CONFIG_1_PORT_2) & 0xffff));
			PRINTF(("rme%d: rmac config 2 0x%04x\n", sc->sc_port,
				RM9000_GE_READ(RM9000_GE_RMAC_CONFIG_2_PORT_2) & 0xffff));
			PRINTF(("rme%d: link config 0x%04x\n", sc->sc_port,
				RM9000_GE_READ(RM9000_GE_RMAC_LINK_CONFIG_PORT_2) & 0xffff));
        	}
        }

	return;
}


/*
 *  Enable Traffic Grooming Block (TRTG)
 */
static void 
rme_trtg_enable(sc)
        struct rme_softc *sc;
{
	uint32_t regdata;

        if (sc->sc_port == 0) {
	        regdata = RM9000_GE_READ(RM9000_GE_TRTG_CONFIG_PORT_0);
            	regdata |= 0x0001;     /* Enable TRTG */
	      	RM9000_GE_WRITE(RM9000_GE_TRTG_CONFIG_PORT_0, regdata);

		PRINTF(("rme%d: trtg config 0 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_GE_TRTG_CONFIG_PORT_0)));
        }
        else if (sc->sc_port == 1) {
	        regdata = RM9000_GE_READ(RM9000_GE_TRTG_CONFIG_PORT_1);
            	regdata |= 0x0001;     /* Enable TRTG */
	      	RM9000_GE_WRITE(RM9000_GE_TRTG_CONFIG_PORT_1, regdata);

		PRINTF(("rme%d: trtg config 1 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_GE_TRTG_CONFIG_PORT_1)));
        }
        else if (sc->sc_port == 2) {
	        regdata = RM9000_GE_READ(RM9000_GE_TRTG_CONFIG_PORT_2);
            	regdata |= 0x0001;     /* Enable TRTG */
	      	RM9000_GE_WRITE(RM9000_GE_TRTG_CONFIG_PORT_2, regdata);

		PRINTF(("rme%d: trtg config 2 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_GE_TRTG_CONFIG_PORT_2)));
        }

	return;
}


/*
 *  Enable MII/GMII
 */
static void 
rme_mii_enable(sc)
        struct rme_softc *sc;
{
	uint32_t status;
	uint32_t regdata;
	uint8_t resolved;
	uint8_t speed;
	uint8_t duplex;
	uint8_t link;
	int i;

	/* 
	 *  Bail if PHY can't resolve speed and duplex
	 */
	for (i=200 ; i>0; i--) {
		resolved = ((rme_mii_read (sc->sc_port, PHY_SPECIFIC_STATUS_REG) >> 11) & 0x1);
		if (resolved) break;
		delay(10000);
	}

	/* 
	 *  Make sure the link comes up
	 */
	for (i=200 ; i>0; i--) {
		link = ((rme_mii_read (sc->sc_port, PHY_SPECIFIC_STATUS_REG) >> 10) & 0x1);
		if (link) break;
		delay(10000);
	}

	/* 
	 *  Exit if autoneg fails to resolve speed/duplex 
	 *  or the real time link status stays down
	 */ 
	if (!resolved || !link) {
		status = rme_mii_read (sc->sc_port, PHY_STATUS_REG) & 0xffff;
		regdata = rme_mii_read (sc->sc_port, PHY_SPECIFIC_STATUS_REG) & 0xffff;
		printf("rme%d: link is down, PHY status 0x%04x, specific status 0x%04x\n", 
			sc->sc_port, status, regdata); 
		return;
	}

	/* 
	 *  Get resolved speed and duplex
	 */
	regdata = rme_mii_read (sc->sc_port, PHY_SPECIFIC_STATUS_REG);
	speed = (regdata >> 14) & 0x3;
	duplex = (regdata >> 13) & 0x1;

	printf("rme%d: link is up, ", sc->sc_port);
	if (speed == 0x2 || speed == 0x3) {
		printf("GMII, 1000 Mbit, ");
       		regdata = RM9000_LINK_SPEED_1000;
		regdata |= RM9000_GMII_MODE;
	}
	else if (speed == 0x1) {
		printf("MII, 100 Mbit, ");
       		regdata = RM9000_LINK_SPEED_100;
	}
	else if (speed == 0) {
		printf("MII, 10 Mbit, ");
		regdata = 0;
	}

	if (duplex) {
		printf("full duplex\n");
	}
	else {
		printf("half duplex\n");
		regdata |= RM9000_HALF_DUPLEX;
	}

	/*
	 *  Set the speed, duplex and MII mode 
	 *  based on PHY provided status.
	 */
	if (sc->sc_port == 0) {
		/* Set the mode */
	      	RM9000_GE_WRITE(RM9000_GE_GMII_CONFIG_MODE_PORT_0, regdata);

		/* Enable datapath */
	        regdata = RM9000_GE_READ(RM9000_GE_GMII_CONFIG_GENERAL_PORT_0);
            	regdata |= 0x02;     	/* Tx enable */
            	regdata |= 0x01;     	/* Rx enable */
	      	RM9000_GE_WRITE(RM9000_GE_GMII_CONFIG_GENERAL_PORT_0, regdata);

		PRINTF(("rme: gmii config mode 0x%04x\n", 
			RM9000_GE_READ(RM9000_GE_GMII_CONFIG_MODE_PORT_0) & 0xffff));
		PRINTF(("rme: gmii config general 0x%04x\n", 
			RM9000_GE_READ(RM9000_GE_GMII_CONFIG_GENERAL_PORT_0) & 0xffff));
	}
	else if (sc->sc_port == 1) {

		/* Set the mode */
	      	RM9000_GE_WRITE(RM9000_GE_GMII_CONFIG_MODE_PORT_1, regdata);

		/* Enable datapath */
	        regdata = RM9000_GE_READ(RM9000_GE_GMII_CONFIG_GENERAL_PORT_1);
            	regdata |= 0x02;     	/* Tx enable */
            	regdata |= 0x01;     	/* Rx enable */
	      	RM9000_GE_WRITE(RM9000_GE_GMII_CONFIG_GENERAL_PORT_1, regdata);

		PRINTF(("rme: gmii config mode 0x%04x\n", 
			RM9000_GE_READ(RM9000_GE_GMII_CONFIG_MODE_PORT_1) & 0xffff));
		PRINTF(("rme: gmii config general 0x%04x\n", 
			RM9000_GE_READ(RM9000_GE_GMII_CONFIG_GENERAL_PORT_1) & 0xffff));
	}
	else if (sc->sc_port == 2) {

		/* Set the mode */
	      	RM9000_GE_WRITE(RM9000_GE_GMII_CONFIG_MODE_PORT_2, regdata);

		/* Enable datapath */
	        regdata = RM9000_GE_READ(RM9000_GE_GMII_CONFIG_GENERAL_PORT_2);
            	regdata |= 0x02;     	/* Tx enable */
            	regdata |= 0x01;     	/* Rx enable */
	      	RM9000_GE_WRITE(RM9000_GE_GMII_CONFIG_GENERAL_PORT_2, regdata);

		PRINTF(("rme: gmii config mode 0x%04x\n", 
			RM9000_GE_READ(RM9000_GE_GMII_CONFIG_MODE_PORT_2) & 0xffff));
		PRINTF(("rme: gmii config general 0x%04x\n", 
			RM9000_GE_READ(RM9000_GE_GMII_CONFIG_GENERAL_PORT_2) & 0xffff));
	}

	return;
}


/*
 *  XDMA Configure
 *
 *	Port   Slice   Rx FIFO   Tx FIFO   XDMA Channel
 *        0      0        0         0          0
 *        1      1        4         1          4
 *        2      2        8         2          8
 *
 *  Note: Only priority 0 is used
 */
static void 
rme_xdma_enable(sc)
        struct rme_softc *sc;
{
	uint32_t regdata;
	static uint8_t done = 0;

	/* 
	 *  Set common XDMA configuration once, not for
	 *  each device attach.
	 */
	if (done == 0) {
		done = 1;

		/* xdma configuration */
	       	regdata = RM9000_GE_READ(RM9000_XDMA_CONFIG);
               	regdata &= ~(0x80000000);     	/* clear reset */
               	regdata |= 0x1 << 28;     	/* sparse rx descriptor spacing */
               	regdata |= 0x1 << 29;     	/* sparse tx descriptor spacing */
               	regdata |= 0x1 << 15;     	/* rx writeback descriptors enable */
#if defined(IO_COHERENT)
		regdata |= 0x1 << 24;		/* descriptor read coherent */
		regdata |= 0x1 << 23;		/* descriptor write coherent */
		regdata |= 0x1 << 22;		/* data read coherent */
		regdata |= 0x1 << 21;		/* data write coherent */
#endif
	       	RM9000_GE_WRITE(RM9000_XDMA_CONFIG, regdata);

		PRINTF(("rme: xdma configuration 0x%08x\n", 
			RM9000_GE_READ(RM9000_XDMA_CONFIG)));
	}

        if (sc->sc_port == 0) {
		/* XDMA in reset, default it is */
		RM9000_GE_WRITE(RM9000_XDMA_CHANNEL0_CONFIG, 0x80080000);

		PRINTF(("rme%d: xdma channel 0 in reset, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_XDMA_CHANNEL0_CONFIG)));

		/* Set Tx and Rx XDMA descriptor start address */
		RM9000_GE_WRITE(RM9000_XDMA_CHANNEL0_TX_DESC, VA_TO_PA(sc->tx_ring) & 0xfffffff8);
		RM9000_GE_WRITE(RM9000_XDMA_CHANNEL0_RX_DESC, VA_TO_PA(sc->rx_ring) & 0xfffffff8);

		PRINTF(("rme%d: xdma tx channel 0 start address 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_XDMA_CHANNEL0_TX_DESC)));
		PRINTF(("rme%d: xdma rx channel 0 start address 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_XDMA_CHANNEL0_RX_DESC)));

		/* Set Tx/Rx XDMA descriptor address prefix (upper 8 physical address bits 39:32) */
		RM9000_GE_WRITE(RM9000_XDMA_DESCRIPTOR_PREFIX, 0x0);

		PRINTF(("rme%d: xdma tx/rx descriptor address prefix 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_XDMA_DESCRIPTOR_PREFIX)));

		/* Set Tx/Rx XDMA buffer address prefix (upper 11 physical address bits 39:29) */
		RM9000_GE_WRITE(RM9000_XDMA_BUFFER_PREFIX, 0x0);

		PRINTF(("rme%d: xdma tx/rx buffer address prefix 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_XDMA_BUFFER_PREFIX)));

		/* Enable xdma channel 0 */
	       	regdata = RM9000_GE_READ(RM9000_XDMA_CHANNEL0_CONFIG);
               	regdata &= 0x7fffffff;     /* Clear tx reset */
               	regdata |= 0x30000000;     /* Tx descriptor ring size (64) */
               	regdata &= 0xfff7ffff;     /* Clear rx reset */
               	regdata |= 0x00030000;     /* Rx descriptor ring size (64) */
               	regdata |= (0x1 << 6);     /* Receive dma buffer size 2KB */
	       	RM9000_GE_WRITE(RM9000_XDMA_CHANNEL0_CONFIG, regdata);

		PRINTF(("rme%d: xdma channel 0 enable 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_XDMA_CHANNEL0_CONFIG)));

		/* Give xdma channel 0 all the descriptors */
		RM9000_GE_WRITE(RM9000_XDMA_CHANNEL0_RX_DMA_STATUS, RX_RING_SIZE);

		PRINTF(("rme%d: xdma channel 0 owns %d descriptors\n", sc->sc_port,
			RM9000_GE_READ(RM9000_XDMA_CHANNEL0_RX_DMA_STATUS)));
        }
        else if (sc->sc_port == 1) {
		/* XDMA in reset, default it is */
		RM9000_GE_WRITE(RM9000_XDMA_CHANNEL4_CONFIG, 0x80080000);

		PRINTF(("rme%d: xdma channel 4 in reset, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_XDMA_CHANNEL4_CONFIG)));

		/* Set Tx and Rx XDMA descriptor start address */
		RM9000_GE_WRITE(RM9000_XDMA_CHANNEL4_TX_DESC, VA_TO_PA(sc->tx_ring) & 0xfffffff8);
		RM9000_GE_WRITE(RM9000_XDMA_CHANNEL4_RX_DESC, VA_TO_PA(sc->rx_ring) & 0xfffffff8);

		PRINTF(("rme%d: xdma tx channel 4 start address 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_XDMA_CHANNEL4_TX_DESC)));
		PRINTF(("rme%d: xdma rx channel 4 start address 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_XDMA_CHANNEL4_RX_DESC)));

		/* Enable xdma channel 4 */
	       	regdata = RM9000_GE_READ(RM9000_XDMA_CHANNEL4_CONFIG);
               	regdata &= 0x7fffffff;     /* Clear tx reset */
               	regdata |= 0x30000000;     /* Tx descriptor ring size (64) */
               	regdata &= 0xfff7ffff;     /* Clear rx reset */
               	regdata |= 0x00030000;     /* Rx descriptor ring size (64) */
               	regdata |= (0x1 << 6);     /* Receive dma buffer size 2KB */
	       	RM9000_GE_WRITE(RM9000_XDMA_CHANNEL4_CONFIG, regdata);

		PRINTF(("rme%d: xdma channel 4 enable 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_XDMA_CHANNEL4_CONFIG)));

		/* Give xdma channel 4 all the descriptors */
		RM9000_GE_WRITE(RM9000_XDMA_CHANNEL4_RX_DMA_STATUS, RX_RING_SIZE);

		PRINTF(("rme%d: xdma channel 4 owns %d descriptors\n", sc->sc_port,
			RM9000_GE_READ(RM9000_XDMA_CHANNEL4_RX_DMA_STATUS)));
        }
        else if (sc->sc_port == 2) {
		/* XDMA in reset, default it is */
		RM9000_GE_WRITE(RM9000_XDMA_CHANNEL8_CONFIG, 0x80080000);

		PRINTF(("rme%d: xdma channel 8 in reset, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_XDMA_CHANNEL8_CONFIG)));

		/* Set Tx and Rx XDMA descriptor start address */
		RM9000_GE_WRITE(RM9000_XDMA_CHANNEL8_TX_DESC, VA_TO_PA(sc->tx_ring) & 0xfffffff8);
		RM9000_GE_WRITE(RM9000_XDMA_CHANNEL8_RX_DESC, VA_TO_PA(sc->rx_ring) & 0xfffffff8);

		PRINTF(("rme%d: xdma tx channel 8 start address 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_XDMA_CHANNEL8_TX_DESC)));
		PRINTF(("rme%d: xdma rx channel 8 start address 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_XDMA_CHANNEL8_RX_DESC)));

		/* Enable xdma channel 8 */
	       	regdata = RM9000_GE_READ(RM9000_XDMA_CHANNEL8_CONFIG);
               	regdata &= 0x7fffffff;     /* Clear tx reset */
               	regdata |= 0x30000000;     /* Tx descriptor ring size (64) */
               	regdata &= 0xfff7ffff;     /* Clear rx reset */
               	regdata |= 0x00030000;     /* Rx descriptor ring size (64) */
               	regdata |= (0x1 << 6);     /* Receive dma buffer size 2KB */
	       	RM9000_GE_WRITE(RM9000_XDMA_CHANNEL8_CONFIG, regdata);

		PRINTF(("rme%d: xdma channel 8 enable 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_XDMA_CHANNEL8_CONFIG)));

		/* Give xdma channel 8 all the descriptors */
		RM9000_GE_WRITE(RM9000_XDMA_CHANNEL8_RX_DMA_STATUS, RX_RING_SIZE);

		PRINTF(("rme%d: xdma channel 8 owns %d descriptors\n", sc->sc_port,
			RM9000_GE_READ(RM9000_XDMA_CHANNEL8_RX_DMA_STATUS)));
        }

	return;
}


/*
 *  FIFO Configure
 *
 *	Port   Slice   Rx FIFO   Tx FIFO   XDMA Channel
 *        0      0        0         0          0
 *        1      1        4         1          4
 *        2      2        8         2          8
 *
 *  Note: Only priority 0 is used
 */
static void 
rme_fifo_enable(sc)
        struct rme_softc *sc;
{
	uint32_t regdata;
	static uint8_t done = 0;

#define FIFO_FLUSH	(0x1 << 20)
#define FIFO_ENABLE	(0x1 << 21)
#define DAV		0x10
#define BAV_LOW		0x40
#define BAV_HIGH	0x20
#define SIZE		0xff

	if (done == 0) {
		/* 
		 *  Take the FIFO block out of reset once
		 */
		done = 1;

		/* Tx fifo out of reset */
	        regdata = RM9000_GE_READ(RM9000_TX_FIFO_CONTROL);
	        regdata &= ~(0x0001);		/* clear reset */
	       	RM9000_GE_WRITE(RM9000_TX_FIFO_CONTROL, regdata);

		/* Rx fifo out of reset */
	        regdata = RM9000_GE_READ(RM9000_RX_FIFO_CONTROL);
	        regdata &= ~(0x0001);		/* clear reset */
	       	RM9000_GE_WRITE(RM9000_RX_FIFO_CONTROL, regdata);

		PRINTF(("rme: tx fifo control 0x%08x\n",
			RM9000_GE_READ(RM9000_TX_FIFO_CONTROL)));
		PRINTF(("rme: rx fifo control 0x%08x\n",
			RM9000_GE_READ(RM9000_RX_FIFO_CONTROL)));
	}
		
        if (sc->sc_port == 0) {
		/* 
		 *  Put rx fifo 0 in flush state to change config
		 */
	        regdata = RM9000_GE_READ(RM9000_RX_FIFO_0_CONFIG);
                regdata |= FIFO_FLUSH;     	/* FIFO flush */
	       	RM9000_GE_WRITE(RM9000_RX_FIFO_0_CONFIG, regdata);

		PRINTF(("rme%d: flush rx fifo 0, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_RX_FIFO_0_CONFIG)));

		/*
		 *  Set rx fifo 0 config (SIZE, PTR)
		 */
	        regdata = RM9000_GE_READ(RM9000_RX_FIFO_0_CONFIG);
                regdata &= ~(0x000ffc00);     	/* clear size */
                regdata |= (SIZE << 10);     	/* SIZE */
	       	RM9000_GE_WRITE(RM9000_RX_FIFO_0_CONFIG, regdata);

		PRINTF(("rme%d: set rx fifo 0 size/ptr, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_RX_FIFO_0_CONFIG)));

		/*
		 *  Set rx fifo 0 threshold (BAV_LOW, BAV_HIGH, DAV)
		 */
	        regdata = RM9000_GE_READ(RM9000_RX_FIFO_0_THRESHOLD);
                regdata |= DAV;     			/* DAV */
                regdata |= (BAV_HIGH << 10);     	/* BAV_HIGH */
		regdata &= ~(0x3ff00000);		/* clear BAV_LOW default */
                regdata |= (BAV_LOW << 20);     	/* BAV_LOW */
	       	RM9000_GE_WRITE(RM9000_RX_FIFO_0_THRESHOLD, regdata);

		PRINTF(("rme%d: set rx fifo 0 BAV/DAV, threshold 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_RX_FIFO_0_THRESHOLD)));

		/* 
		 *  Enable rx fifo 0 for xdma channel 0 
		 */
	        regdata = RM9000_GE_READ(RM9000_RX_FIFO_0_CONFIG);
                regdata &= ~(FIFO_FLUSH);     	/* remove FIFO flush */
                regdata |= FIFO_ENABLE;     	/* FIFO enable */
	       	RM9000_GE_WRITE(RM9000_RX_FIFO_0_CONFIG, regdata);

		PRINTF(("rme%d: enable rx fifo 0, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_RX_FIFO_0_CONFIG)));
		PRINTF(("rme%d: rx fifo 0 threshold 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_RX_FIFO_0_THRESHOLD)));
		PRINTF(("rme%d: rx fifo 0 count 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_RX_FIFO_0_COUNT)));

		/* 
		 *  Put tx fifo 0 in flush state to change config
		 */
	        regdata = RM9000_GE_READ(RM9000_TX_FIFO_0_CONFIG);
                regdata |= FIFO_FLUSH;     	/* FIFO flush */
	       	RM9000_GE_WRITE(RM9000_TX_FIFO_0_CONFIG, regdata);

		PRINTF(("rme%d: flush tx fifo 0, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_TX_FIFO_0_CONFIG)));

		/*
		 *  Set tx fifo 0 config (SIZE, PTR)
		 */
	        regdata = RM9000_GE_READ(RM9000_TX_FIFO_0_CONFIG);
                regdata &= ~(0x000ffc00);     	/* clear size */
                regdata |= (SIZE << 10);     	/* SIZE */
	       	RM9000_GE_WRITE(RM9000_TX_FIFO_0_CONFIG, regdata);

		PRINTF(("rme%d: set tx fifo 0 size/ptr, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_TX_FIFO_0_CONFIG)));

		/*
		 *  Set tx fifo 0 threshold (BAV_LOW, BAV_HIGH, DAV)
		 */
	        regdata = RM9000_GE_READ(RM9000_TX_FIFO_0_THRESHOLD);
                regdata |= DAV;     		/* DAV */
                regdata |= (BAV_HIGH << 10);    /* BAV_HIGH */
		regdata &= ~(0x3ff00000);	/* clear BAV_LOW default */
                regdata |= (BAV_LOW << 20);     /* BAV_LOW */
	       	RM9000_GE_WRITE(RM9000_TX_FIFO_0_THRESHOLD, regdata);

		PRINTF(("rme%d: set tx fifo 0 BAV/DAV, threshold 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_TX_FIFO_0_THRESHOLD)));

		/* 
	   	 *  Enable tx fifo 0 for xdma channel 0 
		 */
	        regdata = RM9000_GE_READ(RM9000_TX_FIFO_0_CONFIG);
                regdata &= ~(FIFO_FLUSH);     	/* remove FIFO flush */
                regdata |= FIFO_ENABLE;     	/* FIFO enable */
	       	RM9000_GE_WRITE(RM9000_TX_FIFO_0_CONFIG, regdata);

		PRINTF(("rme%d: enable tx fifo 0, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_TX_FIFO_0_CONFIG)));
		PRINTF(("rme%d: tx fifo 0 threshold 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_TX_FIFO_0_THRESHOLD)));
		PRINTF(("rme%d: tx fifo 0 count 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_TX_FIFO_0_COUNT)));
        }
        else if (sc->sc_port == 1) {
		/* 
		 *  Put rx fifo 4 in flush state to change config
		 */
	        regdata = RM9000_GE_READ(RM9000_RX_FIFO_4_CONFIG);
                regdata |= FIFO_FLUSH;     	/* FIFO flush */
	       	RM9000_GE_WRITE(RM9000_RX_FIFO_4_CONFIG, regdata);

		PRINTF(("rme%d: flush rx fifo 4, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_RX_FIFO_4_CONFIG)));

		/*
		 *  Set rx fifo 4 config (SIZE, PTR)
		 */
	        regdata = RM9000_GE_READ(RM9000_RX_FIFO_4_CONFIG);
                regdata &= ~(0x000ffc00);     	/* clear size */
                regdata |= (SIZE << 10);     	/* SIZE */
                regdata += (SIZE+1);     	/* PTR */
	       	RM9000_GE_WRITE(RM9000_RX_FIFO_4_CONFIG, regdata);

		PRINTF(("rme%d: set rx fifo 4 size/ptr, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_RX_FIFO_4_CONFIG)));

		/*
		 *  Set rx fifo 4 threshold (BAV_LOW, BAV_HIGH, DAV)
		 */
	        regdata = RM9000_GE_READ(RM9000_RX_FIFO_4_THRESHOLD);
                regdata |= DAV;     		/* DAV */
                regdata |= (BAV_HIGH << 10);    /* BAV_HIGH */
		regdata &= ~(0x3ff00000);	/* clear BAV_LOW default */
                regdata |= (BAV_LOW << 20);     /* BAV_LOW */
	       	RM9000_GE_WRITE(RM9000_RX_FIFO_4_THRESHOLD, regdata);

		PRINTF(("rme%d: set rx fifo 4 BAV/DAV, threshold 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_RX_FIFO_4_THRESHOLD)));

		/* 
		 *  Enable rx fifo 4 for xdma channel 4 
		 */
	        regdata = RM9000_GE_READ(RM9000_RX_FIFO_4_CONFIG);
                regdata &= ~(FIFO_FLUSH);     	/* remove FIFO flush */
                regdata |= FIFO_ENABLE;     	/* FIFO enable */
	       	RM9000_GE_WRITE(RM9000_RX_FIFO_4_CONFIG, regdata);

		PRINTF(("rme%d: enable rx fifo 4, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_RX_FIFO_4_CONFIG)));
		PRINTF(("rme%d: rx fifo 4 threshold 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_RX_FIFO_4_THRESHOLD)));
		PRINTF(("rme%d: rx fifo 4 count 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_RX_FIFO_4_COUNT)));

		/* 
		 *  Put tx fifo 1 in flush state to change config
		 */
	        regdata = RM9000_GE_READ(RM9000_TX_FIFO_1_CONFIG);
                regdata |= FIFO_FLUSH;     	/* FIFO flush */
	       	RM9000_GE_WRITE(RM9000_TX_FIFO_1_CONFIG, regdata);

		PRINTF(("rme%d: flush tx fifo 1, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_TX_FIFO_1_CONFIG)));

		/*
		 *  Set tx fifo 1 config (SIZE, PTR)
		 */
	        regdata = RM9000_GE_READ(RM9000_TX_FIFO_1_CONFIG);
                regdata &= ~(0x000ffc00);     	/* clear size */
                regdata |= (SIZE << 10);     	/* SIZE */
                regdata += (SIZE+1);     	/* PTR */
	       	RM9000_GE_WRITE(RM9000_TX_FIFO_1_CONFIG, regdata);

		PRINTF(("rme%d: set tx fifo 1 size/ptr, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_TX_FIFO_1_CONFIG)));

		/*
		 *  Set tx fifo 1 threshold (BAV_LOW, BAV_HIGH, DAV)
		 */
	        regdata = RM9000_GE_READ(RM9000_TX_FIFO_1_THRESHOLD);
                regdata |= DAV;     		/* DAV */
                regdata |= (BAV_HIGH << 10);    /* BAV_HIGH */
		regdata &= ~(0x3ff00000);	/* clear BAV_LOW default */
                regdata |= (BAV_LOW << 20);     /* BAV_LOW */
	       	RM9000_GE_WRITE(RM9000_TX_FIFO_1_THRESHOLD, regdata);

		PRINTF(("rme%d: set tx fifo 1 BAV/DAV, threshold 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_TX_FIFO_1_THRESHOLD)));

		/* 
	   	 *  Enable tx fifo 1 for xdma channel 4 
		 */
	        regdata = RM9000_GE_READ(RM9000_TX_FIFO_1_CONFIG);
                regdata &= ~(FIFO_FLUSH);     	/* remove FIFO flush */
                regdata |= FIFO_ENABLE;     	/* FIFO enable */
	       	RM9000_GE_WRITE(RM9000_TX_FIFO_1_CONFIG, regdata);

		PRINTF(("rme%d: enable tx fifo 1, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_TX_FIFO_1_CONFIG)));
		PRINTF(("rme%d: tx fifo 1 threshold 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_TX_FIFO_1_THRESHOLD)));
		PRINTF(("rme%d: tx fifo 1 count 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_TX_FIFO_1_COUNT)));
        }
        else if (sc->sc_port == 2) {
		/* 
		 *  Put rx fifo 8 in flush state to change config
		 */
	        regdata = RM9000_GE_READ(RM9000_RX_FIFO_8_CONFIG);
                regdata |= FIFO_FLUSH;     	/* FIFO flush */
	       	RM9000_GE_WRITE(RM9000_RX_FIFO_8_CONFIG, regdata);

		PRINTF(("rme%d: flush rx fifo 8, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_RX_FIFO_8_CONFIG)));

		/*
		 *  Set rx fifo 8 config (SIZE, PTR)
		 */
	        regdata = RM9000_GE_READ(RM9000_RX_FIFO_8_CONFIG);
                regdata &= ~(0x000ffc00);     	/* clear size */
                regdata |= (SIZE << 10);     	/* SIZE */
                regdata += 2*(SIZE+1);     	/* PTR */
	       	RM9000_GE_WRITE(RM9000_RX_FIFO_8_CONFIG, regdata);

		PRINTF(("rme%d: set rx fifo 8 size/ptr, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_RX_FIFO_8_CONFIG)));

		/*
		 *  Set rx fifo 8 threshold (BAV_LOW, BAV_HIGH, DAV)
		 */
	        regdata = RM9000_GE_READ(RM9000_RX_FIFO_8_THRESHOLD);
                regdata |= DAV;     		/* DAV */
                regdata |= (BAV_HIGH << 10);    /* BAV_HIGH */
		regdata &= ~(0x3ff00000);	/* clear BAV_LOW default */
                regdata |= (BAV_LOW << 20);     /* BAV_LOW */
	       	RM9000_GE_WRITE(RM9000_RX_FIFO_8_THRESHOLD, regdata);

		PRINTF(("rme%d: set rx fifo 8 BAV/DAV, threshold 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_RX_FIFO_8_THRESHOLD)));

		/* 
		 *  Enable rx fifo 8 for xdma channel 8 
		 */
	        regdata = RM9000_GE_READ(RM9000_RX_FIFO_8_CONFIG);
                regdata &= ~(FIFO_FLUSH);     	/* remove FIFO flush */
                regdata |= FIFO_ENABLE;     	/* FIFO enable */
	       	RM9000_GE_WRITE(RM9000_RX_FIFO_8_CONFIG, regdata);

		PRINTF(("rme%d: enable rx fifo 8, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_RX_FIFO_8_CONFIG)));
		PRINTF(("rme%d: rx fifo 8 threshold 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_RX_FIFO_8_THRESHOLD)));
		PRINTF(("rme%d: rx fifo 8 count 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_RX_FIFO_8_COUNT)));

		/* 
		 *  Put tx fifo 2 in flush state to change config
		 */
	        regdata = RM9000_GE_READ(RM9000_TX_FIFO_2_CONFIG);
                regdata |= FIFO_FLUSH;     	/* FIFO flush */
	       	RM9000_GE_WRITE(RM9000_TX_FIFO_2_CONFIG, regdata);

		PRINTF(("rme%d: flush tx fifo 2, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_TX_FIFO_2_CONFIG)));

		/*
		 *  Set tx fifo 2 config (SIZE, PTR)
		 */
	        regdata = RM9000_GE_READ(RM9000_TX_FIFO_2_CONFIG);
                regdata &= ~(0x000ffc00);     	/* clear size */
                regdata |= (SIZE << 10);     	/* SIZE */
                regdata += 2*(SIZE+1);     	/* PTR */
	       	RM9000_GE_WRITE(RM9000_TX_FIFO_2_CONFIG, regdata);

		PRINTF(("rme%d: set tx fifo 2 size/ptr, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_TX_FIFO_2_CONFIG)));

		/*
		 *  Set tx fifo 2 threshold (BAV_LOW, BAV_HIGH, DAV)
		 */
	        regdata = RM9000_GE_READ(RM9000_TX_FIFO_2_THRESHOLD);
                regdata |= DAV;     		/* DAV */
                regdata |= (BAV_HIGH << 10);    /* BAV_HIGH */
		regdata &= ~(0x3ff00000);	/* clear BAV_LOW default */
                regdata |= (BAV_LOW << 20);     /* BAV_LOW */
	       	RM9000_GE_WRITE(RM9000_TX_FIFO_2_THRESHOLD, regdata);

		PRINTF(("rme%d: set tx fifo 2 BAV/DAV, threshold 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_TX_FIFO_2_THRESHOLD)));

		/* 
	   	 *  Enable tx fifo 2 for xdma channel 8 
		 */
	        regdata = RM9000_GE_READ(RM9000_TX_FIFO_2_CONFIG);
                regdata &= ~(FIFO_FLUSH);     	/* remove FIFO flush */
                regdata |= FIFO_ENABLE;      	/* FIFO enable */
	       	RM9000_GE_WRITE(RM9000_TX_FIFO_2_CONFIG, regdata);

		PRINTF(("rme%d: enable tx fifo 2, config 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_TX_FIFO_2_CONFIG)));
		PRINTF(("rme%d: tx fifo 2 threshold 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_TX_FIFO_2_THRESHOLD)));
		PRINTF(("rme%d: tx fifo 2 count 0x%08x\n", sc->sc_port,
			RM9000_GE_READ(RM9000_TX_FIFO_2_COUNT)));
        }

	return;
}


static int 
rme_interface_speed(sc)
     struct rme_softc *sc;
{
return 0;
#if 0
	int		err = 0;
	unsigned int	regdata = 0;

	err = rm9000_mdio_read(MARVEL_88E1041_PHY_ID, RM9000_MDIO_MII_EXTENDED, &regdata);
	if (err == RM9000_MDIO_ERROR) {
        	printf("Error reading PHY status register\n");
		return RM9000_MDIO_ERROR;
	}

        /* Check 1000 Mbps first */
	if (regdata & 0x1010) {
	        sc->speed = RM9200_GE_SPEED_1000;
		sc->duplex = RM9000_GE_FULL_DUPLEX;
		return RM9000_MDIO_GOOD;
	}
        else if (regdata & 0x0101) {
		sc->speed = RM9000_GE_SPEED_1000;
		sc->duplex = RM9000_GE_HALF_DUPLEX;
		return RM9000_MDIO_GOOD;
	}

	/* Check for 100 Mbps next */
	err = rm9000_mdio_read(MARVEL_PHY_ID, MII_BMSR, &regdata);
	if (err == RM9000_MDIO_ERROR) {
                print("Error reading PHY status register\n");
		return RM9000_MDIO_ERROR;
	}

	if (regdata & 0x4000) {
		sc->speed = RM9000_GE_SPEED_100;
		sc->duplex = RM9000_GE_FULL_DUPLEX;
		return RM9000_MDIO_GOOD;
	}

	if (regdata & 0x2000) {
		sc->speed = RM9000_GE_SPEED_100;
		sc->duplex = RM9000_GE_HALF_DUPLEX;
		return RM9000_MDIO_GOOD;
	}

	/* Check for 10 Mbps */
	if (regdata & 0x1000) {
		sc->speed = RM9000_GE_SPEED_10
		sc->duplex = RM9000_GE_FULL_DUPLEX;;
		return RM9000_MDIO_GOOD;
	}

	if (regdata & 0x0800) {
		sc->speed = RM9000_GE_SPEED_10;
		sc->duplex = RM9000_GE_HALF_DUPLEX;
		return RM9000_MDIO_GOOD;
	}

        return RM9000_MDIO_ERROR;
#endif
}


/*
 * Start packet transmission on the interface.
 */
static void
rme_start(ifp)
	struct ifnet *ifp;
{
    	struct rme_softc *sc = ifp->if_softc;
    	uint16_t total_len;
    	uint8_t *p;
	uint32_t tx_pending;
	TX_DESC *tx_desc;
	int i;

	/* 
 	 *  See if ownership needs to be changed back to cpu
 	 */
	if (sc->sc_port == 0)
		tx_pending = RM9000_GE_READ(RM9000_XDMA_CHANNEL0_TX_DMA_STATUS);
	else if (sc->sc_port == 1)
		tx_pending = RM9000_GE_READ(RM9000_XDMA_CHANNEL4_TX_DMA_STATUS);
	else if (sc->sc_port == 2)
		tx_pending = RM9000_GE_READ(RM9000_XDMA_CHANNEL8_TX_DMA_STATUS);

	/* Create a list (XXX) */
	if (tx_pending == 0) {
		for (i = 0; i < TX_RING_SIZE; i++) {
			tx_desc = &sc->tx_ring[i];
    			tx_desc->cmdstat &= (~TX_BUFFER_OWNED);	/* cpu owns */
		}
		CACHESYNC(tx_desc, TX_RING_SIZE*sizeof(TX_DESC), SYNC_W);
	}

   	/*
    	 *  Process all mbufs ready for transmit or until all available
    	 *  transmit buffers are full.
    	 */
    	CACHESYNC(&sc->tx_ring[sc->nextTxDesc], sizeof(TX_DESC), SYNC_R);
    	if (sc->tx_ring[sc->nextTxDesc].cmdstat & TX_BUFFER_OWNED) {
        	printf("rme%d: no transmit buffers, desc 0x%08x\n", 
			sc->sc_port, sc->tx_ring[sc->nextTxDesc]);
		return;		/* No buffers */
    	}

    	while (ifp->if_snd.ifq_head != NULL) {
        	struct mbuf *m, *mb_head;
        	uint32_t currTxDesc;
	
        	/*
         	 *  Grab a packet to transmit.
         	 */
        	IF_DEQUEUE(&ifp->if_snd, mb_head);

        	/*
         	 *  Go through each of the mbufs in the chain and copy the data
         	 *  to the transmit descriptors data buffer.  For speed this 
         	 *  should use multiple TX descriptors instead.
         	 */
        	currTxDesc = sc->nextTxDesc;
		total_len = 0;
		p = (uint8_t *)(sc->tx_ring[currTxDesc].buf_ptr | UNCACHED_MEMORY_ADDR2);
        	for (m = mb_head; m != NULL; m = m->m_next) {
            		bcopy((char *)(mtod(m, vm_offset_t)), p, m->m_len);
            		total_len += m->m_len;
	    		p += m->m_len;
        	}

		/* 
         	 *  Free the mbuf chain
         	 */
        	m_freem(mb_head);

		/* 
	 	 *  Send the packet
	 	 */
        	sc->tx_ring[currTxDesc].buf_len = total_len;
       		sc->tx_ring[currTxDesc].cmdstat = TX_BUFFER_OWNED | 
			TX_ENABLE_INTERRUPT | 0x1;
		CACHESYNC(&sc->tx_ring[currTxDesc], sizeof(TX_DESC), SYNC_W);

		/* 
		 *  Tell xdma how many packets to fetch 
		 */
		if (sc->sc_port == 0)
			RM9000_GE_WRITE(RM9000_XDMA_CHANNEL0_TX_DMA_STATUS, 1);
		else if (sc->sc_port == 1)
			RM9000_GE_WRITE(RM9000_XDMA_CHANNEL4_TX_DMA_STATUS, 1);
		else if (sc->sc_port == 2)
			RM9000_GE_WRITE(RM9000_XDMA_CHANNEL8_TX_DMA_STATUS, 1);
		
    		sc->tx_queued++;

		/* Debug output */
            	if (rme_debug==1 || rme_debug==2)
               		TX_NORECURSE(printf("rme%d: tx pkt %d, desc %d, size %d\n", 
					    sc->sc_port, sc->tx_queued, currTxDesc, total_len));
            	if (rme_debug == 2)
               		dump_pkt((int)p, total_len);
            	if (rme_debug == 3)
               		dump_mbuf(m, DUMP_TX);

		/*
	  	 *  Update control block with next descriptor to use
	 	 */
        	sc->nextTxDesc = (currTxDesc + 1) % TX_RING_SIZE;

        	/*
         	 *  Set the timer 
	 	 */
        	//ifp->if_timer = 300;

    	} /* end while */

}


/* 
 * Watchdog timeout handler.  This routine is called when 
 * transmission has started on the interface and no
 * interrupt was received before the timeout.
 */
void
rme_watchdog(ifp)
	struct ifnet *ifp;
{
#if 0
	struct rme_softc *sc = ifp->if_softc;

	log(LOG_ERR, "%s: device timeout\n", sc->sc_dev.dv_xname);
	ifp->if_oerrors++;
	//rme_init(sc);
#endif
}


static void
reset_tx_desc(sc)
	struct rme_softc *sc;
{
	int i;
	TX_DESC *tx_desc;

	for (i = 0; i < TX_RING_SIZE; i++) {
		tx_desc = &sc->tx_ring[i];

		tx_desc->cmdstat = 0; 		/* cpu owns */
		tx_desc->buf_len = 0;
		tx_desc->buf_ptr = (uint32_t)(VA_TO_PA(sc->tx_buf) + (i * TX_BUF_SIZE));
		CACHESYNC(tx_desc, sizeof(TX_DESC), SYNC_W);
	}
	sc->nextTxDesc = 0;
    	sc->tx_queued = 0;
}


static void
reset_rx_desc(sc)
	struct rme_softc *sc;
{
	int i;
	RX_DESC *rx_desc;

	for (i=0; i<RX_RING_SIZE; i++) {
 		rx_desc = &sc->rx_ring[i];

		rx_desc->cpu_buf_ptr = (uint32_t)(VA_TO_PA(sc->rx_buf) + (i * RX_BUF_SIZE));
		rx_desc->cmdstat = RX_BUFFER_OWNED;	/* xdma owns */
		CACHESYNC(rx_desc, sizeof(RX_DESC), SYNC_W);
	}

	sc->rx_next_out = 0;
    	sc->rx_queued = 0;
    	sc->rx_perr = 0;
    	sc->rx_no_stp = 0;
    	sc->rx_skipped = 0;
}


int
rme_ioctl(ifp, command, data)
        struct ifnet *ifp;
        u_long command;
        caddr_t data;
{
        struct rme_softc *sc = ifp->if_softc;
        int s;
	int error = 0;

        s = splimp();

        switch (command) {
        case SIOCSIFADDR:
                error = ether_ioctl(ifp, command, data);
                break;

        case SIOCSIFFLAGS:
                printf("case SIOCSIFFLAGS, marking interface up/down...\n");

                //sc->all_mcasts = (ifp->if_flags & IFF_ALLMULTI) ? 1 : 0;

                /*
                 * If interface is marked up and not running, then start it.
                 * If it is marked down and running, stop it.
                 * XXX If it's up then re-initialize it. This is so flags
                 * such as IFF_PROMISC are handled.
                 */
                if (ifp->if_flags & IFF_UP) {
			printf("rme_ioctl!!!\n");
                        rme_init(sc);
                } else {
#if 0
                        if (ifp->if_flags & IFF_RUNNING)
                                rme_mac_reset(sc);
#endif
                }
                break;

        case SIOCSIFMEDIA:
        case SIOCGIFMEDIA:
                /*error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, command);*/
                break;

        default:
                error = EINVAL;
        }
        (void) splx(s);
        return (error);
}


void
rme_init(xsc)
    	void *xsc;
{
    	struct rme_softc *sc = xsc;
    	struct ifnet *ifp = &sc->arpcom.ac_if;

	/* Enable the interface */
	rme_mii_enable(sc);

    	/* Cancel any pending I/O */
    	ifp->if_flags |= IFF_RUNNING;
}


static int
rme_ether_ioctl(ifp, cmd, data)
        struct ifnet *ifp;
        uint32_t cmd;
        caddr_t data;
{
        struct ifaddr *ifa = (struct ifaddr *) data;
        struct rme_softc *sc = ifp->if_softc;

        switch (cmd) {
        case SIOCSIFADDR:
                ifp->if_flags |= IFF_UP;

                switch (ifa->ifa_addr->sa_family) {
#ifdef INET
                case AF_INET:
                        rme_init(sc);
                        arp_ifinit(&sc->arpcom, ifa);
                        break;
#endif
#ifdef NS
                case AF_NS:
                    {
                         struct ns_addr *ina = &IA_SNS(ifa)->sns_addr;

                         if (ns_nullhost(*ina))
                                ina->x_host = *(union ns_host *)
                                    LLADDR(ifp->if_sadl);
                         else
                                bcopy(ina->x_host.c_host, LLADDR(ifp->if_sadl),
                                    ifp->if_addrlen);
                         /* Set new address. */
                         rme_init(sc);
                         break;
                    }
#endif
                default:
                        rme_init(sc);
                        break;
                }
                break;

        default:
                return (EINVAL);
        }

        return (0);
}

static int
rme_intr(arg)
    	void *arg;
{
	struct rme_softc *sc = arg;
	struct ifnet *ifp = &sc->arpcom.ac_if;
	uint32_t icr, xicr;
    	char *v;

if (rx_norecurse == 0) { 
rx_norecurse = 1; 

        /*
         *  Allow user to control polling of interfaces for
         *  downloadable programs.
         */
        if ((sc->sc_port == 0) && getenv("rme0off"))
		return 0;
        if ((sc->sc_port == 1) && getenv("rme1off"))
		return 0;
        if ((sc->sc_port == 2) && getenv("rme2off"))
    		return 0;

	/* 
	 *  Process incoming packets if xdma reports
	 *  a receive interrupt.
	 */
	rme_rx(sc, icr, xicr);

	/* 
	 *  Transmit more packets if queue isn't empty 
	 */
	if (ifp->if_snd.ifq_head != NULL) {
			rme_start(ifp);
	}

#ifdef DEBUG_RME
	/* Dynamic debug verbosity check */
    	v = getenv("rmedebug");
    	if (v) {
        	rme_debug = atol(v);
    	}
    	v = getenv("rme0verbose");
    	if (v) {
  		rme0_verbose = atol(v);
	}
    	v = getenv("rme1verbose");
    	if (v) {
  		rme1_verbose = atol(v);
	}
    	v = getenv("rme2verbose");
    	if (v) {
  		rme2_verbose = atol(v);
	}
#endif

rx_norecurse = 0; 
}

	return(0);
}

static int
rme_rx(sc, status, xstatus)
        struct rme_softc *sc;
        uint32_t status;
        uint32_t xstatus;
{
	struct ifnet *ifp = &sc->arpcom.ac_if;
	struct mbuf *m;
	struct ether_header *eh;
	int nextRxDesc;
	RX_DESC *rx_desc;
	uint32_t cmdstat;
	uint16_t pkt_len;

	/* 
	 *  Process to current descriptor
	 */
	for (nextRxDesc = sc->rx_next_out;; nextRxDesc = (nextRxDesc + 1) % RX_RING_SIZE) {

		/* This is the only place where we touch rx descriptors */
		rx_desc = &sc->rx_ring[nextRxDesc];
		CACHESYNC(rx_desc, sizeof(RX_DESC), SYNC_R);
		cmdstat = (uint32_t)rx_desc->cmdstat;

		/* Bail if xdma owns the descriptor */
		if (cmdstat & RX_BUFFER_OWNED) {
			break;
		}

		/* 
		 *  Drop this packet if there are any errors
		 */
		pkt_len = rx_desc->cmdstat & 0x7fff;		/* low 15 bits of cmdstat */

		/* check packet error flag from packet processor */
		if (cmdstat & RX_PERR) {
			printf("%s: dropped packet, packet error, cmdstat %p, status %p\n",
			       sc->sc_dev.dv_xname, cmdstat, status);
			sc->rx_perr++;
			goto next;
		}

		/* check crc error flag from packet processor */
		if (cmdstat & RX_CRC_ERROR) {
			printf("%s: dropped packet, crc error, cmdstat %p, status %p\n",
			       sc->sc_dev.dv_xname, cmdstat, status);
			goto next;
		}

		/* if start of packet is not set it can't be for me */
		if (!(cmdstat & RX_STP)) {
			sc->rx_no_stp++;
			goto next;
		}

		/* check overflow error flag from packet processor */
		if (cmdstat & RX_OVERFLOW_ERROR) {
			printf("%s: dropped packet, overflow cmdstat %p, status %p\n",
			       sc->sc_dev.dv_xname, cmdstat, status);
			goto next;
		}

		/* packet too big check */
		if (pkt_len > MCLBYTES) {
			printf("%s: bad packet length, len %d\n",
				sc->sc_dev.dv_xname, pkt_len);
			goto next;
		}

		/* packet too small check */
		if (pkt_len < sizeof(struct ether_header)) {
			if (rme_debug > 0)
				printf("%s: short packet received, len %d\n",
		    			sc->sc_dev.dv_xname, pkt_len);
			goto next;
		}

		/* 
		 *  This is where the packet starts to get processed to send
		 *  to the upper layers of the protocol stack.  A new mbuf
		 *  'm' is allocated for this packet.  The function
		 *  rme_get_mbuf always has the flag M_EXT for external storage
		 *  set.  This adds another 2048 bytes of payload storage
		 *  to what is available in the mbuf.
		 */
                if (!rme_get_mbuf(sc, &m)) {

			/* 
			 *  Copy the packet into the mbuf with required
			 *  alignment for IP header because the ETH header
			 *  is 14 bytes long.  In this case shift
			 *  two bytes.
			 */
			m->m_data += RFA_ALIGNMENT_FUDGE;	/* input must be 64 bit aligned */
                	bcopy((char *)(rx_desc->cpu_buf_ptr | UNCACHED_MEMORY_ADDR2), 
				(char *)m->m_data, pkt_len);

			/* Set up packet header interface and length */
			m->m_pkthdr.rcvif = ifp;
			m->m_len = pkt_len - sizeof(struct ether_header);
			m->m_pkthdr.len = m->m_len;
			eh = mtod(m, struct ether_header *);
			m->m_data += sizeof(struct ether_header);

    			sc->rx_queued++;

			/* Debug output */
            		if (rme_debug==1 || rme_debug==2)
                		RX_NORECURSE(printf("rme%d: \t\t\t\t\trx pkt %d, desc %d, size %d\n", 
						    sc->sc_port, sc->rx_queued, nextRxDesc, pkt_len));
            		if (rme_debug == 2)
               			dump_pkt((int)m->m_data, (int)m->m_len);
            		if (rme_debug == 3)
                		dump_mbuf(m, DUMP_RX);

			ether_input(ifp, eh, m);
		}
		else {
			printf("%s: recycling rxbuf!\n", sc->sc_dev.dv_xname);
		}
next:
		/*  
		 *  An mbuf has been allocated for this packet.  Immediately
		 *  release ownership to xdma.
		 */
		rx_desc->cmdstat |= RX_BUFFER_OWNED;
		rx_desc->cmdstat &= ~(RX_STP);
		CACHESYNC(rx_desc, sizeof(RX_DESC), SYNC_W);

		/* Make xdma aware another descriptor is available */
		if (sc->sc_port == 0)
			RM9000_GE_WRITE(RM9000_XDMA_CHANNEL0_RX_DMA_STATUS, 1);
		else if (sc->sc_port == 1)
			RM9000_GE_WRITE(RM9000_XDMA_CHANNEL4_RX_DMA_STATUS, 1);
		else if (sc->sc_port == 2)
			RM9000_GE_WRITE(RM9000_XDMA_CHANNEL8_RX_DMA_STATUS, 1);
	}

	sc->rx_next_out = nextRxDesc;

	return 0;
}


/*
 * Get a receive buffer and return it's data address.
 * Return 0 if recycled. 
 */
int
rme_get_mbuf(sc, oldm)
	struct rme_softc *sc;
	struct mbuf **oldm;
{
	struct mbuf *m, *pm;

	pm = *oldm;

	MGETHDR(m, M_DONTWAIT, MT_DATA);
	if (m != NULL) {
		MCLGET(m, M_DONTWAIT);
		if ((m->m_flags & M_EXT) == 0) {
			printf("rme_get_mbuf: error, m_flags M_EXT not set\n");
			m_freem(m);
			if (pm == NULL) {
                                printf("rme_get_muf: error 1\n");
				return -1;
                        }
			/* Recycle */
			printf("recycle: m = pm 0x%08x\n", m);
			m = pm;
			m->m_data = m->m_ext.ext_buf;
		}
	}
	else { /* Recycle */
		if (pm == NULL) {
                        printf("rme_get_mbuf: error 2\n");
			return 1;
                }
		m = pm;
		printf("M NULL!, recycle: m = pm 0x%08x\n", m);
		m->m_data = m->m_ext.ext_buf;
	}

	/*
	 *  Move the data pointer up so that the incoming data packet
	 *  will be 64-bit aligned.  The ether header is not a multiple 
	 *  of 4 bytes but the upper layer assumes data to be aligned 
	 *  so we will have to adjust this later.
	 */
	m->m_data = (void *)ALIGN(m->m_data);
	CACHESYNC((void *)m->m_data, RX_BUF_SIZE, SYNC_R);

	*oldm = m;

	return (m == pm);
}


void 
rme_read_mib_counters (sc)
    struct rme_softc *sc;
{
#if 0
    uint32_t *mib_reg = (uint32_t *)&sc->mib;
    int i;

    for (i=0; i<sizeof(mib_counters_t)/sizeof(uint32_t); i++) {
        mib_reg[i] = GTETH_READ(sc, ETH0_MIB_COUNTER_BASE + i*sizeof(uint32_t));
    }
#endif
}


int
rme_mii_read_cfg (arg, phyaddr, regaddr)
	void *arg;
	int phyaddr;
	int regaddr;
{
        struct rme_softc *sc = arg;

	/*
	 *  Don't respond if not for the current
	 *  device being attached.  This satisfies
	 *  the mii probe code.
	 */ 
        if (sc->phy_addr != phyaddr) {
		return 0xffff;
	}

	return (rme_mii_read(phyaddr, regaddr));
}


int
rme_mii_read (phyaddr, regaddr)
	int phyaddr;
	int regaddr;
{
	uint16_t val;
	uint16_t data;

        /* Setup the PHY device */
	val = 0x4000 | (phyaddr << 8) | (regaddr & 0x001f);
        RM9000_MDIO_WRITE(RM9000_MDIO_1_DEVICE_PORT_ADDRESS, val);
	delay(5000);

        /* Issue the read command */
        val = READ_OPCODE;
        RM9000_MDIO_WRITE(RM9000_MDIO_1_COMMAND, val);
	delay(5000);

        if (rm9000_mdio_poll() != 0) {
		printf("mii read: timeout\n");
                return 0;
	}

	/* Check for error */
        data = RM9000_MDIO_READ(RM9000_MDIO_1_INTERRUPTS);
        if (data & 0x2) {
		printf("mii read: turn around bit error, mdio interrupts 0x%04x\n", data);
		return (-1);
	}

        data = RM9000_MDIO_READ(RM9000_MDIO_1_DATA);
	return data;
}


void
rme_mii_write_cfg (arg, phyaddr, regaddr, data)
	void *arg;
	int phyaddr;
	int regaddr;
	int data;
{
        struct rme_softc *sc = arg;

	/*
	 *  Don't respond if not for the current
	 *  device being attached.  This satisfies
	 *  the mii probe code.
	 */ 
        if (sc->phy_addr != phyaddr) {
		return;
	}

	rme_mii_write (phyaddr, regaddr, data);
	return;
}


void
rme_mii_write (phyaddr, regaddr, data)
	int phyaddr;
	int regaddr;
	int data;
{
        uint32_t val;

        if (rm9000_mdio_poll() != 0) {
		printf("mii write: device access timeout\n");
                return;
	}

        /* Setup the PHY device */
	val = 0x4000 | (phyaddr << 8) | (regaddr & 0x001f);
        RM9000_MDIO_WRITE(RM9000_MDIO_1_DEVICE_PORT_ADDRESS, val);
	delay(5000);

        /* Setup the data to write */
        RM9000_MDIO_WRITE(RM9000_MDIO_1_DATA, data);
	delay(5000);

        /* Issue the write command */
	val = WRITE_OPCODE;
        RM9000_MDIO_WRITE(RM9000_MDIO_1_COMMAND, val);
	delay(5000);

	/* Check for error */
        data = RM9000_MDIO_READ(RM9000_MDIO_1_INTERRUPTS);
        if (data & 0x2) {
		printf("mii write: turn around bit error, mdio interrupts 0x%04x\n", data);
	}

	return;
}


/*
 *  Function to poll the MDIO
 */
static int 
rm9000_mdio_poll(void)
{
	int i;
	uint32_t regdata;

        for (i = 0; i < MAX_MDIO_POLL; i++) {
                regdata = RM9000_MDIO_READ(RM9000_MDIO_1_COMMAND);

                if ((regdata & 0x8000) == 0)
                        return 0;
        }

        return (-1);
}

static void
rme_mii_statchg(struct device *us)
{
}


static int
rme_ifmedia_upd(struct ifnet *ifp)
{

	if (ifp->if_flags & IFF_UP) {
		printf("rme_ifmedia_upd!!!\n");
		rme_init(ifp->if_softc);
	}
	return (0);
}


static void
rme_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
{
	struct rme_softc *sc = ifp->if_softc;

	mii_pollstat(&sc->mii_data);
	ifmr->ifm_status = sc->mii_data.mii_media_status;
	ifmr->ifm_active = sc->mii_data.mii_media_active;
}


static void
dump_mbuf(mb, dir_flag)
	struct mbuf *mb;
	int dir_flag;
{
    	int argc;
    	char command[80];
    	char *commandp = command;
    	char *argv[MAX_AC];

    	printf("-------------\n");
    	printf("rme0: %s mbuf\n", (dir_flag==DUMP_TX)?"tx":"rx");
    	printf("-------------\n");
    	printf("mbuf: mb = %p\n",mb);
    	printf("\t m_hdr.mh_next    = %p\n",mb->m_hdr.mh_next);
    	printf("\t m_hdr.mh_nextpkt = %p\n",mb->m_hdr.mh_nextpkt);
    	printf("\t m_hdr.mh_data    = %p\n",mb->m_hdr.mh_data);
    	printf("\t m_hdr.mh_len     = %d\n",mb->m_hdr.mh_len);
    	printf("\t m_hdr.mh_type    = 0x%x\n",mb->m_hdr.mh_type);
    	printf("\t m_hdr.mh_flags   = 0x%x\n\n",mb->m_hdr.mh_flags);
	
    	commandp += sprintf(command, "d -h %p %d", mb->m_hdr.mh_data, mb->m_hdr.mh_len/16);
    	argc = argvize(argv, command);
    	cmd_dump (argc, argv);
}


static void 
dump_pkt(addr, size)
	int addr;
	int size;
{
    	int argc;
    	char command[80];
    	char *commandp = command;
    	char *argv[MAX_AC];

    	commandp += sprintf(command, "d -h %p %d", addr, size/16);
    	argc = argvize(argv, command);
    	cmd_dump (argc, argv);

	return;
}


/*
 *  Get a MSTATX counter 
 */
static int
getMstatxCounter(sc, mstatxReg, result)
        struct rme_softc *sc;
	int mstatxReg;
	unsigned long long *result;
{
	int portOffset;

#define HIGH_OFFSET	0x8
#define MID_OFFSET	0x4

	/* 
	 *  Determine offset to port or slice
	 */
	portOffset = 0x1000 * sc->sc_port;
	
	/* 
	 *  Form 40 bit counter value 
	 */

	/* High 8 bits (39:32) */
	*result = RM9000_GE_READ(mstatxReg + HIGH_OFFSET + portOffset) & 0xff;
	*result = (*result << 32);
	
//printf("high addr 0x%x, result %d\n", mstatxReg + HIGH_OFFSET + portOffset, *result);

	/* Middle 16 bits (31:16) */
	*result |= (RM9000_GE_READ(mstatxReg + MID_OFFSET + portOffset) & 0xffff) << 16;

//printf("mid addr 0x%x, result %d\n", mstatxReg + MID_OFFSET + portOffset, *result);

	/* Low 16 bits (15:0) */
	*result |= RM9000_GE_READ(mstatxReg + portOffset) & 0xffff;

//printf("low addr 0x%x, result %d\n", mstatxReg + portOffset, *result);

	return 0;
}


/*
 *  netstat command
 */
int
cmd_netstat (ac, av)
    	int	ac;
    	char	*av[];
{
	int c;
	int port;
	int portOffset;
	int verbose;
	uint32_t regdata;
	uint32_t regdata2;
	uint16_t tx_pkt_count;
	uint16_t rx_pkt_count;
	uint8_t tx_byte_count;
	uint8_t rx_byte_count;
        struct rme_softc *sc;
	unsigned long long ethstat;
	unsigned long long ethstat2;
	unsigned long long error;
extern int optind;
extern char *optarg; 

	port = 0;
	verbose = 0;
	optind = 0;

        while ((c = getopt (ac, av, "p:v:")) != EOF) {
        	switch (c) {

                case 'p':
                        if (!get_rsa ((long *) &port, optarg))
                           	return (-1);

                	if (port < 0 || port > 2)
                        	return(-1);
			break;

                case 'v':
                        if (!get_rsa ((long *) &verbose, optarg))
                           	return (-1);

                	if (verbose < 0 || verbose > 3)
                        	return(-1);
			break;

		default:
			port = 0;
			verbose = 0;
			break;
		}
	}

	/* 
	 *  Decide which softc to access and snap 
	 *  the statistics to the shadow registers.
	 *  The counters are not cleared.
	 */
	if (port == 0) {
		sc = sc_port0;
		RM9000_GE_WRITE(RM9000_GE_MSTATX_CONTROL_PORT_0, 0x11);
	}
	else if (port == 1) {
		sc = sc_port1;
		RM9000_GE_WRITE(RM9000_GE_MSTATX_CONTROL_PORT_1, 0x11);
	}
	else if (port == 2) {
		sc = sc_port2;
		RM9000_GE_WRITE(RM9000_GE_MSTATX_CONTROL_PORT_2, 0x11);
	}


	printf("---------------------------------------------------------------------\n");
	printf("                          Ingress\n");
	printf("---------------------------------------------------------------------\n");
	printf("	   	  	               _______\n");
	printf("             --> rmac0 --> pktproc -->|       | --> rxdma0\n");
	printf("line --> mux --> rmac1 --> pktproc -->|pktfifo| --> rxdma4 --> system\n");
	printf("             --> rmac2 --> pktproc -->|_______| --> rxdma8\n");
	printf("\n");

	printf("rx port %d:\n", sc->sc_port);
	printf("    ethernet statistics\n");

	/*
	 *  Rx frames
	 */
	getMstatxCounter(sc, RM9000_GE_RX_FRAMES_OK_LOW_PORT_0, &ethstat);
	getMstatxCounter(sc, RM9000_GE_RX_FRAMES_LOW_PORT_0, &ethstat2);

	error = ethstat2 - ethstat;
	printf("\tgood frames  ................. %d\n", ethstat);
	if (error || verbose) {
		printf("\terror frames ................. %d\n", error);
		printf("\ttotal frames ................. %d\n", ethstat2);
	}

	/*
	 *  Rx octets 
	 */
	getMstatxCounter(sc, RM9000_GE_RX_OCTETS_OK_LOW_PORT_0, &ethstat);
	getMstatxCounter(sc, RM9000_GE_RX_OCTETS_LOW_PORT_0, &ethstat2);

	printf("\tgood octets  ................. %d\n", ethstat);
	if (error || verbose) {
		printf("\terror octets ................. %d\n", error);
		printf("\ttotal octets ................. %d\n", ethstat2);
	}

	/*
	 *  Rx unicast frames
	 */
	getMstatxCounter(sc, RM9000_GE_RX_UNICAST_FRAMES_OK_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tunicast frames  .............. %d\n", ethstat);

	/*
	 *  Rx broadcast frames
	 */
	getMstatxCounter(sc, RM9000_GE_RX_BROADCAST_FRAMES_OK_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tbroadcast frames  ............ %d\n", ethstat);

	/*
	 *  Rx multicast frames
	 */
	getMstatxCounter(sc, RM9000_GE_RX_MULTICAST_FRAMES_OK_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tmulticast frames  ............ %d\n", ethstat);

	/*
	 *  Rx tagged frames
	 */
	getMstatxCounter(sc, RM9000_GE_RX_TAGGED_FRAMES_OK_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\ttagged frames  ............... %d\n", ethstat);

	/*
	 *  Rx pause frames
	 */
	getMstatxCounter(sc, RM9000_GE_RX_PAUSE_FRAMES_OK_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tpause frames  ................ %d\n", ethstat);

	/*
	 *  Rx control frames
	 */
	getMstatxCounter(sc, RM9000_GE_RX_MAC_CONTROL_FRAMES_OK_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tcontrol frames  .............. %d\n", ethstat);

	/*
	 *  Rx FCS errors
	 */
	getMstatxCounter(sc, RM9000_GE_RX_FCS_ERRORS_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tfcs errors  .................. %d\n", ethstat);

	/*
	 *  Rx alignment errors
	 */
	getMstatxCounter(sc, RM9000_GE_RX_ALIGNMENT_ERRORS_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\talignment errors  ............ %d\n", ethstat);

	/*
	 *  Rx symbol errors
	 */
	getMstatxCounter(sc, RM9000_GE_RX_SYMBOL_ERRORS_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tsymbol errors  ............... %d\n", ethstat);

	/*
	 *  Rx layer 1 errors
	 */
	getMstatxCounter(sc, RM9000_GE_RX_LAYER1_ERRORS_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tlayer 1 errors  .............. %d\n", ethstat);

	/*
	 *  Rx in range length errors
	 */
	getMstatxCounter(sc, RM9000_GE_RX_INRANGELENGTH_ERRORS_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tin range length errors  ...... %d\n", ethstat);

	/*
	 *  Rx long length errors
	 */
	getMstatxCounter(sc, RM9000_GE_RX_LONGLENGTH_ERRORS_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tlong length errors  .......... %d\n", ethstat);

	/*
	 *  Rx long length CRC errors
	 */
	getMstatxCounter(sc, RM9000_GE_RX_LONGLENGTHCRC_ERRORS_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tlong length CRC errors  ...... %d\n", ethstat);

	/*
	 *  Rx short length errors
	 */
	getMstatxCounter(sc, RM9000_GE_RX_SHORTLENGTH_ERRORS_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tshort length errors  ......... %d\n", ethstat);

	/*
	 *  Rx short length CRC errors
	 */
	getMstatxCounter(sc, RM9000_GE_RX_SHORTLENGTHCRC_ERRORS_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tshort length CRC errors  ..... %d\n", ethstat);

	/*
	 *  Rx frames 64 octets
	 */
	getMstatxCounter(sc, RM9000_GE_RX_FRAMES64OCTETS_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tframes 64 octets  ............ %d\n", ethstat);

	/*
	 *  Rx frames 65 to 127 octets
	 */
	getMstatxCounter(sc, RM9000_GE_RX_FRAMES65TO127OCTETS_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tframes 65 to 127 octets  ..... %d\n", ethstat);

	/*
	 *  Rx frames 128 to 255 octets
	 */
	getMstatxCounter(sc, RM9000_GE_RX_FRAMES128TO255OCTETS_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tframes 128 to 255 octets  .... %d\n", ethstat);

	/*
	 *  Rx frames 256 to 511 octets
	 */
	getMstatxCounter(sc, RM9000_GE_RX_FRAMES256TO511OCTETS_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tframes 256 to 511 octets  .... %d\n", ethstat);

	/*
	 *  Rx frames 512 to 1023 octets
	 */
	getMstatxCounter(sc, RM9000_GE_RX_FRAMES512TO1023OCTETS_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tframes 512 to 1023 octets  ... %d\n", ethstat);

	/*
	 *  Rx frames 1024 to 1518 octets
	 */
	getMstatxCounter(sc, RM9000_GE_RX_FRAMES1024TO1518OCTETS_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tframes 1024 to 1518 octets  .. %d\n", ethstat);

	/*
	 *  Rx frames 1519 to max octets
	 */
	getMstatxCounter(sc, RM9000_GE_RX_FRAMES1519TOMAXOCTETS_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tframes 1519 to max octets  ... %d\n", ethstat);

	/*
	 *  Rx frames filtered due to station address 
	 */
	getMstatxCounter(sc, RM9000_GE_RX_STATIONADDRESSFILTERED_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tframes address filtered  ..... %d\n", ethstat);


	/*
	 *  Driver counters
	 */
	printf("    software counters\n");
	printf("\tpackets queued  .............. %d\n", sc->rx_queued); 
	if (sc->rx_perr || verbose)
		printf("\terror packets  ............... %d\n", sc->rx_perr); 
	if (sc->rx_skipped || verbose)
		printf("\tdescriptors skipped  ......... %d\n", sc->rx_skipped); 
	if (sc->rx_no_stp || verbose)
		printf("\tpackets missing STP  ......... %d\n", sc->rx_no_stp); 

	/*
	 *  XDMA and descriptor info.  Latch the xdma data
	 *  once otherwise it clears the packet and byte
	 *  counters.
	 */
	RM9000_GE_WRITE(RM9000_XDMA_CHANNEL0_PACKET_COUNT, 0);
	delay(1000);
	if (sc->sc_port == 0) {
		printf("    xdma channel 0\n");
		printf("\txdma owned descriptors  ...... %d\n", 
			RM9000_GE_READ(RM9000_XDMA_CHANNEL0_RX_DMA_STATUS));
	}
	else if (sc->sc_port == 1) {
		printf("    xdma channel 4\n");
		printf("\txdma owned descriptors  ...... %d\n", 
			RM9000_GE_READ(RM9000_XDMA_CHANNEL4_RX_DMA_STATUS));
	}
	else if (sc->sc_port == 2) {
		printf("    xdma channel 8\n");
		printf("\txdma owned descriptors  ...... %d\n", 
			RM9000_GE_READ(RM9000_XDMA_CHANNEL8_RX_DMA_STATUS));
	}

	printf("\tdescriptor ring  ............. 0x%08x\n", sc->rx_ring);
	printf("\tcurrent descriptor  .......... 0x%08x (%d)\n", 
		&sc->rx_ring[sc->rx_next_out], sc->rx_next_out);

	/*
	 *  XDMA packet and byte count
	 */
	if (sc->sc_port == 0) {
		regdata = RM9000_GE_READ(RM9000_XDMA_CHANNEL0_PACKET_COUNT);
		regdata2 = RM9000_GE_READ(RM9000_XDMA_CHANNEL0_BYTE_COUNT);
	}
	else if (sc->sc_port == 1) {
		regdata = RM9000_GE_READ(RM9000_XDMA_CHANNEL4_PACKET_COUNT);
		regdata2 = RM9000_GE_READ(RM9000_XDMA_CHANNEL4_BYTE_COUNT);
	}
	else if (sc->sc_port == 2) {
		regdata = RM9000_GE_READ(RM9000_XDMA_CHANNEL8_PACKET_COUNT);
		regdata2 = RM9000_GE_READ(RM9000_XDMA_CHANNEL8_BYTE_COUNT);
	}
	tx_pkt_count = (uint16_t) (regdata >> 16);
	rx_pkt_count = (uint16_t) (regdata & 0xffff);
	tx_byte_count = (uint8_t) (regdata2 >> 16);
	rx_byte_count = (uint8_t) (regdata2 & 0xff);

	if (verbose) {
		printf("\tpacket count  ................ %d\n", rx_pkt_count);
		printf("\tbyte count  .................. %d\n", rx_byte_count);
	}

	printf("\n\n");
	printf("---------------------------------------------------------------------\n");
	printf("                          Egress\n");
	printf("---------------------------------------------------------------------\n");
	printf("	   		               _______\n");
	printf("             <-- tmac0 <-- pktproc <--|       | <-- txdma0\n");
	printf("line <-- mux <-- tmac1 <-- pktproc <--|pktfifo| <-- txdma4 <-- system\n");
	printf("             <-- tmac2 <-- pktproc <--|_______| <-- txdma8\n");
	printf("\n");

	printf("tx port %d:\n", sc->sc_port);
	printf("    ethernet statistics\n");

	/*
	 *  Tx frames
	 */
	getMstatxCounter(sc, RM9000_GE_TX_FRAMES_OK_LOW_PORT_0, &ethstat);
	
	printf("\tgood frames  ................. %d\n", ethstat);

	/*
	 *  Tx octets 
	 */
	getMstatxCounter(sc, RM9000_GE_TX_OCTETS_OK_LOW_PORT_0, &ethstat);
	getMstatxCounter(sc, RM9000_GE_TX_OCTETS_LOW_PORT_0, &ethstat2);

	printf("\tgood octets  ................. %d\n", ethstat);
	if (error || verbose) {
		printf("\terror octets ................. %d\n", error);
		printf("\ttotal octets ................. %d\n", ethstat2);
	}

	/*
	 *  Tx tagged frames
	 */
	getMstatxCounter(sc, RM9000_GE_TX_TAGGED_FRAMES_OK_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\ttagged frames  ............... %d\n", ethstat);

	/*
	 *  Tx pause frames
	 */
	getMstatxCounter(sc, RM9000_GE_TX_PAUSE_FRAMES_OK_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tpause frames  ................ %d\n", ethstat);

	/*
	 *  Tx FCS errors
	 */
	getMstatxCounter(sc, RM9000_GE_TX_FCS_ERRORS_LOW_PORT_0, &ethstat);

	if (ethstat || verbose)
		printf("\tfcs errors  .................. %d\n", ethstat);

	/*
	 *  Driver counters
	 */
	printf("    software counters\n");
	printf("\tpackets queued  .............. %d\n", sc->tx_queued); 
	if (sc->tx_full)
		printf("\ttx queue full\n");

	/*
	 *  XDMA and descriptor info
	 */
	if (sc->sc_port == 0) {
		printf("    xdma channel 0\n");
		printf("\txdma owned descriptors  ...... %d\n", 
			RM9000_GE_READ(RM9000_XDMA_CHANNEL0_TX_DMA_STATUS));
	}
	else if (sc->sc_port == 1) {
		printf("    xdma channel 4\n");
		printf("\txdma owned descriptors  ...... %d\n", 
			RM9000_GE_READ(RM9000_XDMA_CHANNEL4_TX_DMA_STATUS));
	}
	else if (sc->sc_port == 2) {
		printf("    xdma channel 8\n");
		printf("\txdma owned descriptors  ...... %d\n", 
			RM9000_GE_READ(RM9000_XDMA_CHANNEL8_TX_DMA_STATUS));
	}

	printf("\tdescriptor ring  ............. 0x%08x\n", sc->tx_ring);
	printf("\tcurrent descriptor  .......... 0x%08x (%d)\n", 
		&sc->tx_ring[sc->nextTxDesc], sc->nextTxDesc);

	/*
	 *  XDMA packet and byte count
	 */
	if (verbose) {
		printf("\tpacket count  ................ %d\n", tx_pkt_count);
		printf("\tbyte count  .................. %d\n", tx_byte_count);
	}


	if (verbose > 1) {
		portOffset = port * 0x1000;
		printf("\nGMII:\n");
		printf("rme%d: gmii config mode 0x%04x\n", port, 
			RM9000_GE_READ(RM9000_GE_GMII_CONFIG_MODE_PORT_0 + portOffset) & 0xffff);

		printf("rme%d: gmii config general 0x%04x\n", port, 
			RM9000_GE_READ(RM9000_GE_GMII_CONFIG_GENERAL_PORT_0 + portOffset) & 0xffff);

		printf("\nTMAC:\n");
		printf("rme%d: tmac config 1 0x%04x\n", port, 
			RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_1_PORT_0 + portOffset) & 0xffff);

		printf("rme%d: tmac config 2 0x%04x\n", port,
			RM9000_GE_READ(RM9000_GE_TMAC_CONFIG_2_PORT_0 + portOffset) & 0xffff);

		printf("\nRMAC:\n");
		printf("rme%d: rmac config 1 0x%04x\n", port,
			RM9000_GE_READ(RM9000_GE_RMAC_CONFIG_1_PORT_0 + portOffset) & 0xffff);

		printf("rme%d: rmac config 2 0x%04x\n", port,
			RM9000_GE_READ(RM9000_GE_RMAC_CONFIG_2_PORT_0 + portOffset) & 0xffff);

		printf("rme%d: link config 0x%04x\n", port,
			RM9000_GE_READ(RM9000_GE_RMAC_LINK_CONFIG_PORT_0 + portOffset) & 0xffff);

		printf("\nTRTG:\n");
		printf("rme%d: trtg config 0 0x%08x\n", port,
			RM9000_GE_READ(RM9000_GE_TRTG_CONFIG_PORT_0 + portOffset));

		printf("\nXDMA:\n");
		printf("rme: xdma configuration 0x%08x\n", 
			RM9000_GE_READ(RM9000_XDMA_CONFIG));

		if (sc->sc_port == 0) {
			printf("rme%d: xdma tx channel 0 start address 0x%08x\n", port,
				RM9000_GE_READ(RM9000_XDMA_CHANNEL0_TX_DESC));
			printf("rme%d: xdma rx channel 0 start address 0x%08x\n", port,
				RM9000_GE_READ(RM9000_XDMA_CHANNEL0_RX_DESC));
			printf("rme%d: xdma channel 0 enable 0x%08x\n", port,
				RM9000_GE_READ(RM9000_XDMA_CHANNEL0_CONFIG));
		}
		else if (sc->sc_port == 1) {
			printf("rme%d: xdma tx channel 4 start address 0x%08x\n", port,
				RM9000_GE_READ(RM9000_XDMA_CHANNEL4_TX_DESC));
			printf("rme%d: xdma rx channel 4 start address 0x%08x\n", port,
				RM9000_GE_READ(RM9000_XDMA_CHANNEL4_RX_DESC));
			printf("rme%d: xdma channel 4 enable 0x%08x\n", port,
				RM9000_GE_READ(RM9000_XDMA_CHANNEL4_CONFIG));
		}
		else if (sc->sc_port == 2) {
			printf("rme%d: xdma tx channel 8 start address 0x%08x\n", port,
				RM9000_GE_READ(RM9000_XDMA_CHANNEL8_TX_DESC));
			printf("rme%d: xdma rx channel 8 start address 0x%08x\n", port,
				RM9000_GE_READ(RM9000_XDMA_CHANNEL8_RX_DESC));
			printf("rme%d: xdma channel 8 enable 0x%08x\n", port,
				RM9000_GE_READ(RM9000_XDMA_CHANNEL8_CONFIG));
		}

		printf("rme%d: xdma tx/rx descriptor address prefix 0x%08x\n", port,
			RM9000_GE_READ(RM9000_XDMA_DESCRIPTOR_PREFIX));

		printf("rme%d: xdma tx/rx buffer address prefix 0x%08x\n", port,
			RM9000_GE_READ(RM9000_XDMA_BUFFER_PREFIX));


		printf("\nPacket FIFO:\n");
		printf("rme: tx fifo control 0x%08x\n",
			RM9000_GE_READ(RM9000_TX_FIFO_CONTROL));

		printf("rme: rx fifo control 0x%08x\n",
			RM9000_GE_READ(RM9000_RX_FIFO_CONTROL));

		printf("rme%d: enable rx fifo 0, config 0x%08x\n", port,
			RM9000_GE_READ(RM9000_RX_FIFO_0_CONFIG));

		printf("rme%d: rx fifo 0 threshold 0x%08x\n", port,
			RM9000_GE_READ(RM9000_RX_FIFO_0_THRESHOLD));

		printf("rme%d: rx fifo 0 count 0x%08x\n", port,
			RM9000_GE_READ(RM9000_RX_FIFO_0_COUNT));

		printf("rme%d: enable tx fifo 0, config 0x%08x\n", port,
			RM9000_GE_READ(RM9000_TX_FIFO_0_CONFIG));

		printf("rme%d: tx fifo 0 threshold 0x%08x\n", port,
			RM9000_GE_READ(RM9000_TX_FIFO_0_THRESHOLD));

		printf("rme%d: tx fifo 0 count 0x%08x\n\n", port,
			RM9000_GE_READ(RM9000_TX_FIFO_0_COUNT));
	}

	return (0);
}

const Optdesc	cmd_netstat_opts[] = {
	{"-p", "port"},
	{"-v", "verbose"},
    	{0}
};


/*
 *  MII bus command
 */
int
cmd_mii (ac, av)
	int	ac;
	char	*av[];
{
        unsigned int device;
        unsigned int reg;
        unsigned int value;
        unsigned int startdev, lastdev;
        int  c;
        extern int optind;

        optind = 0;
        c = getopt (ac, av, "wr");
        switch (c) {
                case 'w':
                        if (optind >= ac || 
			    !get_rsa ((long *) &device, av[optind++]) ||
                            !get_rsa ((long *) &reg   , av[optind++]) ||
                            !get_rsa ((long *) &value , av[optind]))
                           	return (-1);

                                rme_mii_write(device, reg, value);
                                break;

                case 'r':
                         if (optind >= ac || 
			     !get_rsa ((long *) &device, av[optind++]) ||
                             !get_rsa ((long *) &reg, av[optind]))
                                return (-1);

                                value = rme_mii_read(device, reg);
                                printf("dev %d  reg 0x%02x  data 0x%04x", device, reg, value);
                                if (reg == 0x0)
                                        printf("\tcontrol\n");
                                else if (reg == 0x1)
                                        printf("\tstatus\n");
                                else if (reg == 0x2)
                                       printf("\tphyid\n");
                                else if (reg == 0x3)
                                        printf("\tphyid\n");
                                else if (reg == 0x4)
                                        printf("\tauto-neg advertise\n");
                                else if (reg == 0x5)
                                        printf("\tlink partner ability\n");
                                else if (reg == 0x6)
                                        printf("\tauto-neg expansion\n");
                                else if (reg == 0x7)
                                        printf("\tnext page xmit\n");
                                else if (reg == 0x8)
                                        printf("\tlink partner next page\n");
                                else if (reg == 0x9)
                                        printf("\t1000Base-T control\n");
                                else if (reg == 0xa)
                                        printf("\t1000Base-T status\n");
                                else if (reg == 0xf)
                                        printf("\textended status\n");
                                else if (reg == 0x10)
                                        printf("\tphy specific control\n");
                                else if (reg == 0x12)
                                        printf("\tinterrupt enable\n");
                                else if (reg == 0x13)
                                        printf("\tinterrupt status\n");
                                else if (reg == 0x14)
                                        printf("\textended phy specific control\n");
                                break;

                default:
			if (ac > 1) {
                        	if (!get_rsa ((long *) &device , av[optind]))
					return (-1);
                                startdev = device;
                                lastdev = device;
			}
                        else {
                                startdev = 0;
                                lastdev = 2;
                        }

                        for (device=startdev; device<=lastdev; device++) {
                                printf("\n");
                                for (reg=0; reg<31; reg++) {
                                        if ((value = rme_mii_read(device, reg)) >= 0)
                                                printf("dev %d reg 0x%02x data 0x%04x\n",
                                                        device, reg, value);
                                }
                        }
        }

	return(0);
}

const Optdesc	mii_opts[] = {
    {"-r", "read device register"},
    {"-w", "write device register"},
    {0}
};


static const Cmd Cmds[] =
{
        {"Network"},
        {"netstat",	"[-p <port>][-v <1-3>]",
                   	cmd_netstat_opts,
                    	"network stats",
                    	cmd_netstat, 1, 5, 0},
        {"mii",         "[dev] [-r dev reg | -w dev reg data]",
                        mii_opts,
                        "mii bus cmd",
                        cmd_mii, 1, 99, 0},
        {0, 0}
};

static void init_cmd __P((void)) __attribute__ ((constructor));

static void
init_cmd()
{
        cmdlist_expand(Cmds, 1);
}
