Patches by Murray Jensen, 17 Jun 2003:
- Hymod board database mods: add "who" field and new xilinx chip types
- provide new "init_cmd_timeout()" function so code external to
  "common/main.c" can use the "reset_cmd_timeout()" function before
  entering the main loop
- add DTT support for adm1021 (new file dtt/adm1021.c; config
  slightly different. see include/configs/hymod.h for an example
  (requires CONFIG_DTT_ADM1021, CONFIG_DTT_SENSORS, and
  CFG_DTT_ADM1021 defined)
- add new "eeprom_probe()" function which has similar args and
  behaves in a similar way to "eeprom_read()" etc.
- add 8260 FCC ethernet loopback code (new "eth_loopback_test()"
  function which is enabled by defining CONFIG_ETHER_LOOPBACK_TEST)
- gdbtools copyright update
- ensure that set_msr() executes the "sync" and "isync" instructions
  after the "mtmsr" instruction in cpu/mpc8260/interrupts.c
- 8260 I/O ports fix: Open Drain should be set last when configuring
- add SIU IRQ defines for 8260
- allow LDSCRIPT override and OBJCFLAGS initialization: change to
  config.mk to allow board configurations to override the GNU
  linker script, selected via the LDSCRIPT, make variable, and to
  give an initial value to the OBJCFLAGS make variable
- 8260 i2c enhancement:
  o correctly extends the timeout depending on the size of all
    queued messages for both transmit and receive
  o will not continue with receive if transmit times out
  o ensures that the error callback is done for all queued tx
    and rx messages
  o correctly detects both tx and rx timeouts, only delivers one to
    the callback, and does not overwrite an earlier error
  o logic in i2c_probe now correct
- add "vprintf()" function so that "panic()" function can be
  technically correct
- many Hymod board changes
diff --git a/cpu/mpc8260/ether_fcc.c b/cpu/mpc8260/ether_fcc.c
index bd2fcc9..45dca73 100644
--- a/cpu/mpc8260/ether_fcc.c
+++ b/cpu/mpc8260/ether_fcc.c
@@ -391,4 +391,761 @@
 	return 1;
 }
 
+#ifdef CONFIG_ETHER_LOOPBACK_TEST
+
+#define ELBT_BUFSZ	1024	/* must be multiple of 32 */
+
+#define ELBT_CRCSZ	4
+
+#define ELBT_NRXBD	4	/* must be at least 2 */
+#define ELBT_NTXBD	4
+
+#define ELBT_MAXRXERR	32
+#define ELBT_MAXTXERR	32
+
+#define ELBT_CLSWAIT	1000	/* msec to wait for further input frames */
+
+typedef
+	struct {
+		uint off;
+		char *lab;
+	}
+elbt_prdesc;
+
+typedef
+	struct {
+		uint _l, _f, m, bc, mc, lg, no, sh, cr, ov, cl;
+		uint badsrc, badtyp, badlen, badbit;
+	}
+elbt_rxeacc;
+
+static elbt_prdesc rxeacc_descs[] = {
+	{ offsetof(elbt_rxeacc, _l),		"Not Last in Frame"	},
+	{ offsetof(elbt_rxeacc, _f),		"Not First in Frame"	},
+	{ offsetof(elbt_rxeacc, m),		"Address Miss"		},
+	{ offsetof(elbt_rxeacc, bc),		"Broadcast Address"	},
+	{ offsetof(elbt_rxeacc, mc),		"Multicast Address"	},
+	{ offsetof(elbt_rxeacc, lg),		"Frame Length Violation"},
+	{ offsetof(elbt_rxeacc, no),		"Non-Octet Alignment"	},
+	{ offsetof(elbt_rxeacc, sh),		"Short Frame"		},
+	{ offsetof(elbt_rxeacc, cr),		"CRC Error"		},
+	{ offsetof(elbt_rxeacc, ov),		"Overrun"		},
+	{ offsetof(elbt_rxeacc, cl),		"Collision"		},
+	{ offsetof(elbt_rxeacc, badsrc),	"Bad Src Address"	},
+	{ offsetof(elbt_rxeacc, badtyp),	"Bad Frame Type"	},
+	{ offsetof(elbt_rxeacc, badlen),	"Bad Frame Length"	},
+	{ offsetof(elbt_rxeacc, badbit),	"Data Compare Errors"	},
+};
+static int rxeacc_ndesc = sizeof (rxeacc_descs) / sizeof (rxeacc_descs[0]);
+
+typedef
+	struct {
+		uint def, hb, lc, rl, rc, un, csl;
+	}
+elbt_txeacc;
+
+static elbt_prdesc txeacc_descs[] = {
+	{ offsetof(elbt_txeacc, def),		"Defer Indication"	},
+	{ offsetof(elbt_txeacc, hb),		"Heartbeat"		},
+	{ offsetof(elbt_txeacc, lc),		"Late Collision"	},
+	{ offsetof(elbt_txeacc, rl),		"Retransmission Limit"	},
+	{ offsetof(elbt_txeacc, rc),		"Retry Count"		},
+	{ offsetof(elbt_txeacc, un),		"Underrun"		},
+	{ offsetof(elbt_txeacc, csl),		"Carrier Sense Lost"	},
+};
+static int txeacc_ndesc = sizeof (txeacc_descs) / sizeof (txeacc_descs[0]);
+
+typedef
+	struct {
+		uchar rxbufs[ELBT_NRXBD][ELBT_BUFSZ];
+		uchar txbufs[ELBT_NTXBD][ELBT_BUFSZ];
+		cbd_t rxbd[ELBT_NRXBD];
+		cbd_t txbd[ELBT_NTXBD];
+		enum { Idle, Running, Closing, Closed } state;
+		int proff, page, sblock;
+		uint clstime, nsent, ntxerr, nrcvd, nrxerr;
+		ushort rxerrs[ELBT_MAXRXERR], txerrs[ELBT_MAXTXERR];
+		elbt_rxeacc rxeacc;
+		elbt_txeacc txeacc;
+	} __attribute__ ((aligned(8)))
+elbt_chan;
+
+static uchar patbytes[ELBT_NTXBD] = {
+	0xff, 0xaa, 0x55, 0x00
+};
+static uint patwords[ELBT_NTXBD] = {
+	0xffffffff, 0xaaaaaaaa, 0x55555555, 0x00000000
+};
+
+#ifdef __GNUC__
+static elbt_chan elbt_chans[3] __attribute__ ((aligned(8)));
+#else
+#error "elbt_chans must be 64-bit aligned"
+#endif
+
+#define CPM_CR_GRACEFUL_STOP_TX	((ushort)0x0005)
+
+static elbt_prdesc epram_descs[] = {
+	{ offsetof(fcc_enet_t, fen_crcec),	"CRC Errors"		},
+	{ offsetof(fcc_enet_t, fen_alec),	"Alignment Errors"	},
+	{ offsetof(fcc_enet_t, fen_disfc),	"Discarded Frames"	},
+	{ offsetof(fcc_enet_t, fen_octc),	"Octets"		},
+	{ offsetof(fcc_enet_t, fen_colc),	"Collisions"		},
+	{ offsetof(fcc_enet_t, fen_broc),	"Broadcast Frames"	},
+	{ offsetof(fcc_enet_t, fen_mulc),	"Multicast Frames"	},
+	{ offsetof(fcc_enet_t, fen_uspc),	"Undersize Frames"	},
+	{ offsetof(fcc_enet_t, fen_frgc),	"Fragments"		},
+	{ offsetof(fcc_enet_t, fen_ospc),	"Oversize Frames"	},
+	{ offsetof(fcc_enet_t, fen_jbrc),	"Jabbers"		},
+	{ offsetof(fcc_enet_t, fen_p64c),	"64 Octet Frames"	},
+	{ offsetof(fcc_enet_t, fen_p65c),	"65-127 Octet Frames"	},
+	{ offsetof(fcc_enet_t, fen_p128c),	"128-255 Octet Frames"	},
+	{ offsetof(fcc_enet_t, fen_p256c),	"256-511 Octet Frames"	},
+	{ offsetof(fcc_enet_t, fen_p512c),	"512-1023 Octet Frames"	},
+	{ offsetof(fcc_enet_t, fen_p1024c),	"1024-1518 Octet Frames"},
+};
+static int epram_ndesc = sizeof (epram_descs) / sizeof (epram_descs[0]);
+
+/*
+ * given an elbt_prdesc array and an array of base addresses, print
+ * each prdesc down the screen with the values fetched from each
+ * base address across the screen
+ */
+static void
+print_desc (elbt_prdesc descs[], int ndesc, uchar *bases[], int nbase)
+{
+	elbt_prdesc *dp = descs, *edp = dp + ndesc;
+	int i;
+
+	printf ("%32s", "");
+
+	for (i = 0; i < nbase; i++)
+		printf ("  Channel %d", i);
+
+	puts ("\n");
+
+	while (dp < edp) {
+
+		printf ("%-32s", dp->lab);
+
+		for (i = 0; i < nbase; i++) {
+			uint val = *(uint *)(bases[i] + dp->off);
+
+			printf (" %10u", val);
+		}
+
+		puts ("\n");
+
+		dp++;
+	}
+}
+
+/*
+ * return number of bits that are set in a value; value contains
+ * nbits (right-justified) bits.
+ */
+static uint __inline__
+nbs (uint value, uint nbits)
+{
+	uint cnt = 0;
+#if 1
+	uint pos = sizeof (uint) * 8;
+
+	__asm__ __volatile__ ("\
+	mtctr	%2\n\
+1:	rlwnm.	%2,%1,%4,31,31\n\
+	beq	2f\n\
+	addi	%0,%0,1\n\
+2:	subi	%4,%4,1\n\
+	bdnz	1b"
+	: "=r"(cnt)
+	: "r"(value), "r"(nbits), "r"(cnt), "r"(pos)
+	: "ctr", "cc" );
+#else
+	uint mask = 1;
+
+	do {
+		if (value & mask)
+			cnt++;
+		mask <<= 1;
+	} while (--nbits);
+#endif
+
+	return (cnt);
+}
+
+static ulong
+badbits (uchar *bp, int n, ulong pat)
+{
+	ulong *lp, cnt = 0;
+	int nl;
+
+	while (n > 0 && ((ulong)bp & (sizeof (ulong) - 1)) != 0) {
+		uchar diff;
+
+		diff = *bp++ ^ (uchar)pat;
+
+		if (diff)
+			cnt += nbs ((ulong)diff, 8);
+
+		n--;
+	}
+
+	lp = (ulong *)bp;
+	nl = n / sizeof (ulong);
+	n -= nl * sizeof (ulong);
+
+	while (nl > 0) {
+		ulong diff;
+
+		diff = *lp++ ^ pat;
+
+		if (diff)
+			cnt += nbs (diff, 32);
+
+		nl--;
+	}
+
+	bp = (uchar *)lp;
+
+	while (n > 0) {
+		uchar diff;
+
+		diff = *bp++ ^ (uchar)pat;
+
+		if (diff)
+			cnt += nbs ((ulong)diff, 8);
+
+		n--;
+	}
+
+	return (cnt);
+}
+
+static inline unsigned short
+swap16 (unsigned short x)
+{
+	return (((x & 0xff) << 8) | ((x & 0xff00) >> 8));
+}
+
+void
+eth_loopback_test (void)
+{
+	DECLARE_GLOBAL_DATA_PTR;
+
+	volatile immap_t *immr = (immap_t *)CFG_IMMR;
+	volatile cpm8260_t *cp = &(immr->im_cpm);
+	int c, nclosed;
+	ulong runtime, nmsec;
+	uchar *bases[3];
+
+	puts ("FCC Ethernet External loopback test\n");
+
+	memcpy (NetOurEther, gd->bd->bi_enetaddr, 6);
+
+	/*
+	 * global initialisations for all FCC channels
+	 */
+
+	/* 28.9 - (1-2): ioports have been set up already */
+
+#if defined(CONFIG_HYMOD)
+	/*
+	 * Attention: this is board-specific
+	 * - FCC1 Rx-CLK is CLK10
+	 * - FCC1 Tx-CLK is CLK11
+	 * - FCC2 Rx-CLK is CLK13
+	 * - FCC2 Tx-CLK is CLK14
+	 * - FCC3 Rx-CLK is CLK15
+	 * - FCC3 Tx-CLK is CLK16
+	 */
+
+	/* 28.9 - (3): connect FCC's tx and rx clocks */
+	immr->im_cpmux.cmx_uar = 0;
+	immr->im_cpmux.cmx_fcr = CMXFCR_RF1CS_CLK10|CMXFCR_TF1CS_CLK11|\
+	    CMXFCR_RF2CS_CLK13|CMXFCR_TF2CS_CLK14|\
+	    CMXFCR_RF3CS_CLK15|CMXFCR_TF3CS_CLK16;
+#else
+#error "eth_loopback_test not supported on your board"
+#endif
+
+	puts ("Initialise FCC channels:");
+
+	for (c = 0; c < 3; c++) {
+		elbt_chan *ecp = &elbt_chans[c];
+		volatile fcc_t *fcp = &immr->im_fcc[c];
+		volatile fcc_enet_t *fpp;
+		int i;
+		ulong addr;
+
+		/*
+		 * initialise channel data
+		 */
+
+		printf (" %d", c);
+
+		memset ((void *)ecp, 0, sizeof (*ecp));
+
+		ecp->state = Idle;
+
+		switch (c) {
+
+		case 0: /* FCC1 */
+			ecp->proff = PROFF_FCC1;
+			ecp->page = CPM_CR_FCC1_PAGE;
+			ecp->sblock = CPM_CR_FCC1_SBLOCK;
+			break;
+
+		case 1: /* FCC2 */
+			ecp->proff = PROFF_FCC2;
+			ecp->page = CPM_CR_FCC2_PAGE;
+			ecp->sblock = CPM_CR_FCC2_SBLOCK;
+			break;
+
+		case 2: /* FCC3 */
+			ecp->proff = PROFF_FCC3;
+			ecp->page = CPM_CR_FCC3_PAGE;
+			ecp->sblock = CPM_CR_FCC3_SBLOCK;
+			break;
+		}
+
+		/*
+		 * set up tx buffers and bds
+		 */
+
+		for (i = 0; i < ELBT_NTXBD; i++) {
+			cbd_t *bdp = &ecp->txbd[i];
+			uchar *bp = &ecp->txbufs[i][0];
+
+			bdp->cbd_bufaddr = (uint)bp;
+			/* room for crc */
+			bdp->cbd_datlen = ELBT_BUFSZ - ELBT_CRCSZ;
+			bdp->cbd_sc = BD_ENET_TX_READY | BD_ENET_TX_PAD | \
+				BD_ENET_TX_LAST | BD_ENET_TX_TC;
+
+			memset ((void *)bp, patbytes[i], ELBT_BUFSZ);
+			NetSetEther (bp, NetBcastAddr, 0x8000);
+		}
+		ecp->txbd[ELBT_NTXBD - 1].cbd_sc |= BD_ENET_TX_WRAP;
+
+		/*
+		 * set up rx buffers and bds
+		 */
+
+		for (i = 0; i < ELBT_NRXBD; i++) {
+		    cbd_t *bdp = &ecp->rxbd[i];
+		    uchar *bp = &ecp->rxbufs[i][0];
+
+		    bdp->cbd_bufaddr = (uint)bp;
+		    bdp->cbd_datlen = 0;
+		    bdp->cbd_sc = BD_ENET_RX_EMPTY;
+
+		    memset ((void *)bp, 0, ELBT_BUFSZ);
+		}
+		ecp->rxbd[ELBT_NRXBD - 1].cbd_sc |= BD_ENET_RX_WRAP;
+
+		/*
+		 * set up the FCC channel hardware
+		 */
+
+		/* 28.9 - (4): GFMR: disable tx/rx, CCITT CRC, Mode Ethernet */
+		fcp->fcc_gfmr = FCC_GFMR_MODE_ENET | FCC_GFMR_TCRC_32;
+
+		/* 28.9 - (5): FPSMR: fd, enet CRC, Promis, RMON, Rx SHort */
+		fcp->fcc_fpsmr = FCC_PSMR_FDE | FCC_PSMR_LPB | \
+			FCC_PSMR_ENCRC | FCC_PSMR_PRO | \
+			FCC_PSMR_MON | FCC_PSMR_RSH;
+
+		/* 28.9 - (6): FDSR: Ethernet Syn */
+		fcp->fcc_fdsr = 0xD555;
+
+		/* 29.9 - (7): initialise parameter ram */
+		fpp = (fcc_enet_t *)&(immr->im_dprambase[ecp->proff]);
+
+		/* clear whole struct to make sure all resv fields are zero */
+		memset ((void *)fpp, 0, sizeof (fcc_enet_t));
+
+		/*
+		 * common Parameter RAM area
+		 *
+		 * Allocate space in the reserved FCC area of DPRAM for the
+		 * internal buffers.  No one uses this space (yet), so we
+		 * can do this.  Later, we will add resource management for
+		 * this area.
+		 */
+		addr = CPM_FCC_SPECIAL_BASE + (c * 64);
+		fpp->fen_genfcc.fcc_riptr = addr;
+		fpp->fen_genfcc.fcc_tiptr = addr + 32;
+
+		/*
+		 * Set maximum bytes per receive buffer.
+		 * It must be a multiple of 32.
+		 * buffers are in 60x bus memory.
+		 */
+		fpp->fen_genfcc.fcc_mrblr = PKT_MAXBLR_SIZE;
+		fpp->fen_genfcc.fcc_rstate = (CPMFCR_GBL | CPMFCR_EB) << 24;
+		fpp->fen_genfcc.fcc_rbase = (unsigned int)(&ecp->rxbd[0]);
+		fpp->fen_genfcc.fcc_tstate = (CPMFCR_GBL | CPMFCR_EB) << 24;
+		fpp->fen_genfcc.fcc_tbase = (unsigned int)(&ecp->txbd[0]);
+
+		/* protocol-specific area */
+		fpp->fen_cmask = 0xdebb20e3;	/* CRC mask */
+		fpp->fen_cpres = 0xffffffff;	/* CRC preset */
+		fpp->fen_retlim = 15;		/* Retry limit threshold */
+		fpp->fen_mflr = PKT_MAXBUF_SIZE;/* max frame length register */
+
+		/*
+		 * Set Ethernet station address.
+		 *
+		 * This is supplied in the board information structure, so we
+		 * copy that into the controller.
+		 * So, far we have only been given one Ethernet address. We use
+		 * the same address for all channels
+		 */
+#define ea gd->bd->bi_enetaddr
+		fpp->fen_paddrh = (ea[5] << 8) + ea[4];
+		fpp->fen_paddrm = (ea[3] << 8) + ea[2];
+		fpp->fen_paddrl = (ea[1] << 8) + ea[0];
+#undef ea
+
+		fpp->fen_minflr = PKT_MINBUF_SIZE; /* min frame len register */
+		/*
+		 * pad pointer. use tiptr since we don't need
+		 * a specific padding char
+		 */
+		fpp->fen_padptr = fpp->fen_genfcc.fcc_tiptr;
+		fpp->fen_maxd1 = PKT_MAXDMA_SIZE;	/* max DMA1 length */
+		fpp->fen_maxd2 = PKT_MAXDMA_SIZE;	/* max DMA2 length */
+		fpp->fen_rfthr = 1;
+		fpp->fen_rfcnt = 1;
+
+		/* 28.9 - (8): clear out events in FCCE */
+		fcp->fcc_fcce = ~0x0;
+
+		/* 28.9 - (9): FCCM: mask all events */
+		fcp->fcc_fccm = 0;
+
+		/* 28.9 - (10-12): we don't use ethernet interrupts */
+
+		/* 28.9 - (13)
+		 *
+		 * Let's re-initialize the channel now.  We have to do it later
+		 * than the manual describes because we have just now finished
+		 * the BD initialization.
+		 */
+		cp->cp_cpcr = mk_cr_cmd (ecp->page, ecp->sblock, \
+			0x0c, CPM_CR_INIT_TRX) | CPM_CR_FLG;
+		do {
+			__asm__ __volatile__ ("eieio");
+		} while (cp->cp_cpcr & CPM_CR_FLG);
+	}
+
+	puts (" done\nStarting test... (Ctrl-C to Finish)\n");
+
+	/*
+	 * Note: don't want serial output from here until the end of the
+	 * test - the delays would probably stuff things up.
+	 */
+
+	clear_ctrlc ();
+	runtime = get_timer (0);
+
+	do {
+		nclosed = 0;
+
+		for (c = 0; c < 3; c++) {
+			volatile fcc_t *fcp = &immr->im_fcc[c];
+			elbt_chan *ecp = &elbt_chans[c];
+			int i;
+
+			switch (ecp->state) {
+
+			case Idle:
+				/*
+				 * set the channel Running ...
+				 */
+
+				/* 28.9 - (14): enable tx/rx in gfmr */
+				fcp->fcc_gfmr |= FCC_GFMR_ENT | FCC_GFMR_ENR;
+
+				ecp->state = Running;
+				break;
+
+			case Running:
+				/*
+				 * (while Running only) check for
+				 * termination of the test
+				 */
+
+				(void)ctrlc ();
+
+				if (had_ctrlc ()) {
+					/*
+					 * initiate a "graceful stop transmit"
+					 * on the channel
+					 */
+					cp->cp_cpcr = mk_cr_cmd (ecp->page, \
+						ecp->sblock, 0x0c, \
+						CPM_CR_GRACEFUL_STOP_TX) | \
+						CPM_CR_FLG;
+					do {
+						__asm__ __volatile__ ("eieio");
+					} while (cp->cp_cpcr & CPM_CR_FLG);
+
+					ecp->clstime = get_timer (0);
+					ecp->state = Closing;
+				}
+				/* fall through ... */
+
+			case Closing:
+				/*
+				 * (while Running or Closing) poll the channel:
+				 * - check for any non-READY tx buffers and
+				 *   make them ready
+				 * - check for any non-EMPTY rx buffers and
+				 *   check that they were received correctly,
+				 *   adjust counters etc, then make empty
+				 */
+
+				for (i = 0; i < ELBT_NTXBD; i++) {
+					cbd_t *bdp = &ecp->txbd[i];
+					ushort sc = bdp->cbd_sc;
+
+					if ((sc & BD_ENET_TX_READY) != 0)
+						continue;
+
+					/*
+					 * this frame has finished
+					 * transmitting
+					 */
+					ecp->nsent++;
+
+					if (sc & BD_ENET_TX_STATS) {
+						ulong n;
+
+						/*
+						 * we had an error on
+						 * the transmission
+						 */
+						n = ecp->ntxerr++;
+						if (n < ELBT_MAXTXERR)
+							ecp->txerrs[n] = sc;
+
+						if (sc & BD_ENET_TX_DEF)
+							ecp->txeacc.def++;
+						if (sc & BD_ENET_TX_HB)
+							ecp->txeacc.hb++;
+						if (sc & BD_ENET_TX_LC)
+							ecp->txeacc.lc++;
+						if (sc & BD_ENET_TX_RL)
+							ecp->txeacc.rl++;
+						if (sc & BD_ENET_TX_RCMASK)
+							ecp->txeacc.rc++;
+						if (sc & BD_ENET_TX_UN)
+							ecp->txeacc.un++;
+						if (sc & BD_ENET_TX_CSL)
+							ecp->txeacc.csl++;
+
+						bdp->cbd_sc &= \
+							~BD_ENET_TX_STATS;
+					}
+
+					if (ecp->state == Closing)
+						ecp->clstime = get_timer (0);
+
+					/* make it ready again */
+					bdp->cbd_sc |= BD_ENET_TX_READY;
+				}
+
+				for (i = 0; i < ELBT_NRXBD; i++) {
+					cbd_t *bdp = &ecp->rxbd[i];
+					ushort sc = bdp->cbd_sc, mask;
+
+					if ((sc & BD_ENET_RX_EMPTY) != 0)
+						continue;
+
+					/* we have a new frame in this buffer */
+					ecp->nrcvd++;
+
+					mask = BD_ENET_RX_LAST|BD_ENET_RX_FIRST;
+					if ((sc & mask) != mask) {
+						/* somethings wrong here ... */
+						if (!(sc & BD_ENET_RX_LAST))
+							ecp->rxeacc._l++;
+						if (!(sc & BD_ENET_RX_FIRST))
+							ecp->rxeacc._f++;
+					}
+
+					if (sc & BD_ENET_RX_STATS) {
+						ulong n;
+
+						/*
+						 * we had some sort of error
+						 * on the frame
+						 */
+						n = ecp->nrxerr++;
+						if (n < ELBT_MAXRXERR)
+							ecp->rxerrs[n] = sc;
+
+						if (sc & BD_ENET_RX_MISS)
+							ecp->rxeacc.m++;
+						if (sc & BD_ENET_RX_BC)
+							ecp->rxeacc.bc++;
+						if (sc & BD_ENET_RX_MC)
+							ecp->rxeacc.mc++;
+						if (sc & BD_ENET_RX_LG)
+							ecp->rxeacc.lg++;
+						if (sc & BD_ENET_RX_NO)
+							ecp->rxeacc.no++;
+						if (sc & BD_ENET_RX_SH)
+							ecp->rxeacc.sh++;
+						if (sc & BD_ENET_RX_CR)
+							ecp->rxeacc.cr++;
+						if (sc & BD_ENET_RX_OV)
+							ecp->rxeacc.ov++;
+						if (sc & BD_ENET_RX_CL)
+							ecp->rxeacc.cl++;
+
+						bdp->cbd_sc &= \
+							~BD_ENET_RX_STATS;
+					}
+					else {
+						ushort datlen = bdp->cbd_datlen;
+						Ethernet_t *ehp;
+						ushort prot;
+						int ours, tb, n, nbytes;
+
+						ehp = (Ethernet_t *) \
+							&ecp->rxbufs[i][0];
+
+						ours = memcmp (ehp->et_src, \
+							NetOurEther, 6);
+
+						prot = swap16 (ehp->et_protlen);
+						tb = prot & 0x8000;
+						n = prot & 0x7fff;
+
+						nbytes = ELBT_BUFSZ - \
+							offsetof (Ethernet_t, \
+								et_dsap) - \
+							ELBT_CRCSZ;
+
+						/* check the frame is correct */
+						if (datlen != ELBT_BUFSZ)
+							ecp->rxeacc.badlen++;
+						else if (!ours)
+							ecp->rxeacc.badsrc++;
+						else if (!tb || n >= ELBT_NTXBD)
+							ecp->rxeacc.badtyp++;
+						else {
+							ulong patword = \
+								patwords[n];
+							uint nbb;
+
+							nbb = badbits ( \
+								&ehp->et_dsap, \
+								nbytes, \
+								patword);
+
+							ecp->rxeacc.badbit += \
+								nbb;
+						}
+					}
+
+					if (ecp->state == Closing)
+					    ecp->clstime = get_timer (0);
+
+					/* make it empty again */
+					bdp->cbd_sc |= BD_ENET_RX_EMPTY;
+				}
+
+				if (ecp->state != Closing)
+					break;
+
+				/*
+				 * (while Closing) check to see if
+				 * waited long enough
+				 */
+
+				if (get_timer (ecp->clstime) >= ELBT_CLSWAIT) {
+					/* write GFMR: disable tx/rx */
+					fcp->fcc_gfmr &= \
+						~(FCC_GFMR_ENT | FCC_GFMR_ENR);
+					ecp->state = Closed;
+				}
+
+				break;
+
+			case Closed:
+				nclosed++;
+				break;
+			}
+		}
+
+	} while (nclosed < 3);
+
+	runtime = get_timer (runtime);
+	if (runtime <= ELBT_CLSWAIT) {
+		printf ("Whoops! somehow elapsed time (%ld) is wrong (<= %d)\n",
+			runtime, ELBT_CLSWAIT);
+		return;
+	}
+	nmsec = runtime - ELBT_CLSWAIT;
+
+	printf ("Test Finished in %ldms (plus %dms close wait period)!\n\n",
+		nmsec, ELBT_CLSWAIT);
+
+	/*
+	 * now print stats
+	 */
+
+	for (c = 0; c < 3; c++) {
+		elbt_chan *ecp = &elbt_chans[c];
+		uint rxpps, txpps, nerr;
+
+		rxpps = (ecp->nrcvd * 1000) / nmsec;
+		txpps = (ecp->nsent * 1000) / nmsec;
+
+		printf ("Channel %d: %d rcvd (%d pps, %d rxerrs), "
+			"%d sent (%d pps, %d txerrs)\n\n", c,
+			ecp->nrcvd, rxpps, ecp->nrxerr,
+			ecp->nsent, txpps, ecp->ntxerr);
+
+		if ((nerr = ecp->nrxerr) > 0) {
+			ulong i;
+
+			printf ("\tFirst %d rx errs:", nerr);
+			for (i = 0; i < nerr; i++)
+				printf (" %04x", ecp->rxerrs[i]);
+			puts ("\n");
+		}
+
+		if ((nerr = ecp->ntxerr) > 0) {
+			ulong i;
+
+			printf ("\tFirst %d tx errs:", nerr);
+			for (i = 0; i < nerr; i++)
+				printf (" %04x", ecp->txerrs[i]);
+			puts ("\n");
+		}
+	}
+
+	puts ("Receive Error Counts:\n");
+	for (c = 0; c < 3; c++)
+		bases[c] = (uchar *)&elbt_chans[c].rxeacc;
+	print_desc (rxeacc_descs, rxeacc_ndesc, bases, 3);
+
+	puts ("\nTransmit Error Counts:\n");
+	for (c = 0; c < 3; c++)
+		bases[c] = (uchar *)&elbt_chans[c].txeacc;
+	print_desc (txeacc_descs, txeacc_ndesc, bases, 3);
+
+	puts ("\nRMON(-like) Counters:\n");
+	for (c = 0; c < 3; c++)
+		bases[c] = (uchar *)&immr->im_dprambase[elbt_chans[c].proff];
+	print_desc (epram_descs, epram_ndesc, bases, 3);
+}
+
+#endif /* CONFIG_ETHER_LOOPBACK_TEST */
+
 #endif	/* CONFIG_ETHER_ON_FCC && CFG_CMD_NET && CONFIG_NET_MULTI */