* Patch by Thomas Elste, 10 Feb 2004:
  Add support for NET+50 CPU and ModNET50 board

* Patch by Sam Song, 10 Feb 2004:
  Fix typos in cfi_flash.c

* Patch by Leon Kukovec, 10 Feb 2004
  Fixed long dir entry slot id calculation in get_vfatname

* Patch by Robin Gilks, 10 Feb 2004:
  add "itest" command (operators: -eq, -ne, -lt, -gt, -le, -ge, ==,
  !=, <>, <, >, <=, >=)
diff --git a/drivers/Makefile b/drivers/Makefile
index 2999eb4..b333aed 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -32,24 +32,16 @@
 	  cs8900.o ct69000.o dataflash.o dc2114x.o          \
 	  e1000.o eepro100.o \
 	  i8042.o i82365.o inca-ip_sw.o \
-	  lan91c96.o             natsemi.o \
+	  lan91c96.o natsemi.o netarm_eth.o \
 	  ns16550.o ns8382x.o ns87308.o \
 	  pci.o pci_auto.o pci_indirect.o \
 	  pcnet.o plb2800_eth.o \
 	  ps2ser.o ps2mult.o pc_keyb.o keyboard.o \
 	  rtl8019.o rtl8139.o \
-	  s3c24x0_i2c.o sed13806.o serial.o \
-	  serial_max3100.o \
-	  smc91111.o smiLynxEM.o              sym53c8xx.o \
-	  status_led.o \
+	  s3c24x0_i2c.o sed13806.o serial.o serial_max3100.o \
+	  smc91111.o smiLynxEM.o status_led.o sym53c8xx.o \
 	  ti_pci1410a.o tigon3.o w83c553f.o
 
-## Disabled for now:
-##	  cs8900.o ct69000.o dataflash.o dc2114x.o ds1722.o \
-##	  lan91c96.o mw_eeprom.o natsemi.o \
-##	  smc91111.o smiLynxEM.o spi_eeprom.o sym53c8xx.o \
-##
-
 all:	$(LIB)
 
 $(LIB):	$(OBJS)
diff --git a/drivers/cfi_flash.c b/drivers/cfi_flash.c
index c6c2751..95179ae 100644
--- a/drivers/cfi_flash.c
+++ b/drivers/cfi_flash.c
@@ -36,13 +36,14 @@
  *
  * Tested Architectures
  * Port Width  Chip Width    # of banks	   Flash Chip  Board
- * 32	       16	     1		   23F128J3    seranoa/eagle
- * 64	       16	     1		   23F128J3    seranoa/falcon
+ * 32	       16	     1		   28F128J3    seranoa/eagle
+ * 64	       16	     1		   28F128J3    seranoa/falcon
  *
  */
 
 /* The DEBUG define must be before common to enable debugging */
-#undef	DEBUG
+/* #define DEBUG	*/
+
 #include <common.h>
 #include <asm/processor.h>
 #ifdef	CFG_FLASH_CFI_DRIVER
diff --git a/drivers/netarm_eth.c b/drivers/netarm_eth.c
new file mode 100644
index 0000000..a15b653
--- /dev/null
+++ b/drivers/netarm_eth.c
@@ -0,0 +1,359 @@
+/*
+ * Copyright (C) 2004 IMMS gGmbH <www.imms.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * author(s): Thomas Elste, <info@elste.org>
+ *            (some parts derived from uCLinux Netarm Ethernet Driver)
+ */
+
+
+#include <common.h>
+#include <command.h>
+#include <net.h>
+#include "netarm_eth.h"
+#include <asm/arch/netarm_registers.h>
+
+#ifdef CONFIG_DRIVER_NETARMETH
+
+#if (CONFIG_COMMANDS & CFG_CMD_NET)
+
+static int na_mii_poll_busy (void);
+
+static void na_get_mac_addr (void)
+{
+	unsigned short p[3];
+	char *m_addr;
+	char ethaddr[20];
+
+	m_addr = (char *) p;
+
+	p[0] = (unsigned short) GET_EADDR (NETARM_ETH_SAL_STATION_ADDR_1);
+	p[1] = (unsigned short) GET_EADDR (NETARM_ETH_SAL_STATION_ADDR_2);
+	p[2] = (unsigned short) GET_EADDR (NETARM_ETH_SAL_STATION_ADDR_3);
+
+	sprintf (ethaddr, "%02X:%02X:%02X:%02X:%02X:%02X",
+		 m_addr[0], m_addr[1],
+		 m_addr[2], m_addr[3], m_addr[4], m_addr[5]);
+
+	printf ("HW-MAC Address:  %s\n", ethaddr);
+
+	/* set env, todo: check if already an adress is set */
+	setenv ("ethaddr", ethaddr);
+}
+
+
+static void na_mii_write (int reg, int value)
+{
+	int mii_addr;
+
+	/* Select register */
+	mii_addr = CFG_ETH_PHY_ADDR + reg;
+	SET_EADDR (NETARM_ETH_MII_ADDR, mii_addr);
+	/* Write value */
+	SET_EADDR (NETARM_ETH_MII_WRITE, value);
+	na_mii_poll_busy ();
+}
+
+static unsigned int na_mii_read (int reg)
+{
+	int mii_addr, val;
+
+	/* Select register */
+	mii_addr = CFG_ETH_PHY_ADDR + reg;
+	SET_EADDR (NETARM_ETH_MII_ADDR, mii_addr);
+	/* do one management cycle */
+	SET_EADDR (NETARM_ETH_MII_CMD,
+		   GET_EADDR (NETARM_ETH_MII_CMD) | NETARM_ETH_MIIC_RSTAT);
+	na_mii_poll_busy ();
+	/* Return read value */
+	val = GET_EADDR (NETARM_ETH_MII_READ);
+	return val;
+}
+
+static int na_mii_poll_busy (void)
+{
+	/* arm simple, non interrupt dependent timer */
+	reset_timer_masked ();
+	while (get_timer_masked () < NA_MII_POLL_BUSY_DELAY) {
+		if (!(GET_EADDR (NETARM_ETH_MII_IND) & NETARM_ETH_MIII_BUSY)) {
+			return 1;
+		}
+	}
+	printf ("na_mii_busy timeout\n");
+	return (0);
+}
+
+static int na_mii_identify_phy (void)
+{
+	int id_reg_a = 0;
+
+	/* get phy id register */
+	id_reg_a = na_mii_read (MII_PHY_ID);
+
+	if (id_reg_a == 0x0043) {
+		/* This must be an Enable or a Lucent LU3X31 PHY chip */
+		return 1;
+	} else if (id_reg_a == 0x0013) {
+		/* it is an Intel LXT971A */
+		return 1;
+	}
+	return (0);
+}
+
+static int na_mii_negotiate (void)
+{
+	int i = 0;
+
+	/* Enable auto-negotiation */
+	na_mii_write (MII_PHY_AUTONEGADV, 0x01e1);
+	/* FIXME: 0x01E1 is 100Mb half and full duplex, 0x0061 is 10Mb only */
+	/* Restart auto-negotiation */
+	na_mii_write (MII_PHY_CONTROL, 0x1200);
+
+	/* status register is 0xffff after setting the autoneg restart bit */
+	while (na_mii_read (MII_PHY_STATUS) == 0xffff) {
+		i++;
+	}
+
+	/* na_mii_read uses the timer already, so we can't use it again for
+	   timeout checking.
+	   Instead we just try some times.
+	 */
+	for (i = 0; i < 40000; i++) {
+		if ((na_mii_read (MII_PHY_STATUS) & 0x0024) == 0x0024) {
+			return 0;
+		}
+	}
+	/*
+	   printf("*Warning* autonegotiation timeout, status: 0x%x\n",na_mii_read(MII_PHY_STATUS));
+	 */
+	return (1);
+}
+
+static unsigned int na_mii_check_speed (void)
+{
+	unsigned int status;
+
+	/* Read Status register */
+	status = na_mii_read (MII_PHY_STATUS);
+	/* Check link status.  If 0, default to 100 Mbps. */
+	if ((status & 0x0004) == 0) {
+		printf ("*Warning* no link detected, set default speed to 100Mbs\n");
+		return 1;
+	} else {
+		if ((na_mii_read (17) & 0x4000) != 0) {
+			printf ("100Mbs link detected\n");
+			return 1;
+		} else {
+			printf ("10Mbs link detected\n");
+			return 0;
+		}
+	}
+	return 0;
+}
+
+static int reset_eth (void)
+{
+	int pt;
+
+	na_get_mac_addr ();
+	pt = na_mii_identify_phy ();
+
+	/* reset the phy */
+	na_mii_write (MII_PHY_CONTROL, 0x8000);
+	reset_timer_masked ();
+	while (get_timer_masked () < NA_MII_NEGOTIATE_DELAY) {
+		if ((na_mii_read (MII_PHY_STATUS) & 0x8000) == 0) {
+			break;
+		}
+	}
+	if (get_timer_masked () >= NA_MII_NEGOTIATE_DELAY)
+		printf ("phy reset timeout\n");
+
+	/* set the PCS reg */
+	SET_EADDR (NETARM_ETH_PCS_CFG, NETARM_ETH_PCSC_CLKS_25M |
+		   NETARM_ETH_PCSC_ENJAB | NETARM_ETH_PCSC_NOCFR);
+
+	na_mii_negotiate ();
+	na_mii_check_speed ();
+
+	/* Delay 10 millisecond.  (Maybe this should be 1 second.) */
+	udelay (10000);
+
+	/* Turn receive on.
+	   Enable statistics register autozero on read.
+	   Do not insert MAC address on transmit.
+	   Do not enable special test modes.  */
+	SET_EADDR (NETARM_ETH_STL_CFG,
+		   (NETARM_ETH_STLC_AUTOZ | NETARM_ETH_STLC_RXEN));
+
+	/* Set the inter-packet gap delay to 0.96us for MII.
+	   The NET+ARM H/W Reference Guide indicates that the Back-to-back IPG
+	   Gap Timer Register should be set to 0x15 and the Non Back-to-back IPG
+	   Gap Timer Register should be set to 0x00000C12 for the MII PHY. */
+	SET_EADDR (NETARM_ETH_B2B_IPG_GAP_TMR, 0x15);
+	SET_EADDR (NETARM_ETH_NB2B_IPG_GAP_TMR, 0x00000C12);
+
+	/* Add CRC to end of packets.
+	   Pad packets to minimum length of 64 bytes.
+	   Allow unlimited length transmit packets.
+	   Receive all broadcast packets.
+	   NOTE:  Multicast addressing is NOT enabled here currently. */
+	SET_EADDR (NETARM_ETH_MAC_CFG,
+		   (NETARM_ETH_MACC_CRCEN |
+		    NETARM_ETH_MACC_PADEN | NETARM_ETH_MACC_HUGEN));
+	SET_EADDR (NETARM_ETH_SAL_FILTER, NETARM_ETH_SALF_BROAD);
+
+	/* enable fifos */
+	SET_EADDR (NETARM_ETH_GEN_CTRL,
+		   (NETARM_ETH_GCR_ERX | NETARM_ETH_GCR_ETX));
+
+	return (0);
+}
+
+
+extern int eth_init (bd_t * bd)
+{
+	reset_eth ();
+	return 0;
+}
+
+extern void eth_halt (void)
+{
+	SET_EADDR (NETARM_ETH_GEN_CTRL, 0);
+}
+
+/* Get a data block via Ethernet */
+extern int eth_rx (void)
+{
+	int i;
+	unsigned short rxlen;
+	unsigned int *addr;
+	unsigned int rxstatus, lastrxlen;
+	char *pa;
+
+	/* RXBR is 1, data block was received */
+	if ((GET_EADDR (NETARM_ETH_GEN_STAT) & NETARM_ETH_GST_RXBR) == 0)
+		return 0;
+
+	/* get status register and the length of received block */
+	rxstatus = GET_EADDR (NETARM_ETH_RX_STAT);
+	rxlen = (rxstatus & NETARM_ETH_RXSTAT_SIZE) >> 16;
+
+	if (rxlen == 0)
+		return 0;
+
+	/* clear RXBR to make fifo available */
+	SET_EADDR (NETARM_ETH_GEN_STAT,
+		   GET_EADDR (NETARM_ETH_GEN_STAT) & ~NETARM_ETH_GST_RXBR);
+
+	/* clear TXBC to make fifo available */
+	/* According to NETARM50 data manual you just have to clear
+	   RXBR but that has no effect. Only after clearing TXBC the
+	   Fifo becomes readable. */
+	SET_EADDR (NETARM_ETH_GEN_STAT,
+		   GET_EADDR (NETARM_ETH_GEN_STAT) & ~NETARM_ETH_GST_TXBC);
+
+	addr = (unsigned int *) NetRxPackets[0];
+	pa = (char *) NetRxPackets[0];
+
+	/* read the fifo */
+	for (i = 0; i < rxlen / 4; i++) {
+		*addr = GET_EADDR (NETARM_ETH_FIFO_DAT1);
+		addr++;
+	}
+
+	if (GET_EADDR (NETARM_ETH_GEN_STAT) & NETARM_ETH_GST_RXREGR) {
+		/* RXFDB indicates wether the last word is 1,2,3 or 4 bytes long */
+		lastrxlen =
+			(GET_EADDR (NETARM_ETH_GEN_STAT) &
+			 NETARM_ETH_GST_RXFDB) >> 28;
+		*addr = GET_EADDR (NETARM_ETH_FIFO_DAT1);
+		switch (lastrxlen) {
+		case 1:
+			*addr &= 0xff000000;
+			break;
+		case 2:
+			*addr &= 0xffff0000;
+			break;
+		case 3:
+			*addr &= 0xffffff00;
+			break;
+		}
+	}
+
+	/* Pass the packet up to the protocol layers. */
+	NetReceive (NetRxPackets[0], rxlen);
+
+	return rxlen;
+}
+
+/* Send a data block via Ethernet. */
+extern int eth_send (volatile void *packet, int length)
+{
+	int i, length32;
+	char *pa;
+	unsigned int *pa32, lastp = 0, rest;
+
+	pa = (char *) packet;
+	pa32 = (unsigned int *) packet;
+	length32 = length / 4;
+	rest = length % 4;
+
+	/* make sure there's no garbage in the last word */
+	switch (rest) {
+	case 0:
+		lastp = pa32[length32];
+		length32--;
+		break;
+	case 1:
+		lastp = pa32[length32] & 0x000000ff;
+		break;
+	case 2:
+		lastp = pa32[length32] & 0x0000ffff;
+		break;
+	case 3:
+		lastp = pa32[length32] & 0x00ffffff;
+		break;
+	}
+
+	/* write to the fifo */
+	for (i = 0; i < length32; i++)
+		SET_EADDR (NETARM_ETH_FIFO_DAT1, pa32[i]);
+
+	/* the last word is written to an extra register, this
+	   starts the transmission */
+	SET_EADDR (NETARM_ETH_FIFO_DAT2, lastp);
+
+	/* NETARM_ETH_TXSTAT_TXOK should be checked, to know if the transmission
+	   went fine. But we can't use the timer for a timeout loop because
+	   of it is used already in upper layers. So we just try some times. */
+	i = 0;
+	while (i < 50000) {
+		if ((GET_EADDR (NETARM_ETH_TX_STAT) & NETARM_ETH_TXSTAT_TXOK)
+		    == NETARM_ETH_TXSTAT_TXOK)
+			return 0;
+		i++;
+	}
+
+	printf ("eth_send timeout\n");
+	return 1;
+}
+
+#endif /* COMMANDS & CFG_NET */
+
+#endif /* CONFIG_DRIVER_NETARMETH */
diff --git a/drivers/netarm_eth.h b/drivers/netarm_eth.h
new file mode 100644
index 0000000..8edab82
--- /dev/null
+++ b/drivers/netarm_eth.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2003 IMMS gGmbH <www.imms.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ * author(s): Thomas Elste, <info@elste.org>
+ */
+
+#include <asm/types.h>
+#include <config.h>
+
+#ifdef CONFIG_DRIVER_NETARMETH
+
+#define SET_EADDR(ad,val) *(volatile unsigned int*)(ad + NETARM_ETH_MODULE_BASE) = val
+#define GET_EADDR(ad) (*(volatile unsigned int*)(ad + NETARM_ETH_MODULE_BASE))
+
+#define NA_MII_POLL_BUSY_DELAY 900
+
+/* MII negotiation timeout value
+   500 jiffies = 5 seconds */
+#define NA_MII_NEGOTIATE_DELAY 30
+
+/* Registers in the physical layer chip */
+#define MII_PHY_CONTROL		0
+#define MII_PHY_STATUS		1
+#define MII_PHY_ID              2
+#define MII_PHY_AUTONEGADV	4
+
+#endif /* CONFIG_DRIVER_NETARMETH */