diff -Nur linux-2.6.14/arch/arm/mach-s3c2410/mach-smdk2410.c linux-2.6.14_cs8900/arch/arm/mach-s3c2410/mach-smdk2410.c
--- linux-2.6.14/arch/arm/mach-s3c2410/mach-smdk2410.c	2005-10-28 08:02:08.000000000 +0800
+++ linux-2.6.14_cs8900/arch/arm/mach-s3c2410/mach-smdk2410.c	2008-02-03 23:08:55.000000000 +0800
@@ -52,9 +52,12 @@
 
 #include "devs.h"
 #include "cpu.h"
+#include <asm/arch-s3c2410/smdk2410.h>
 
 static struct map_desc smdk2410_iodesc[] __initdata = {
   /* nothing here yet */
+  /* Map the ethernet controller CS8900A */
+{vSMDK2410_ETH_IO,pSMDK2410_ETH_IO, SZ_1M, MT_DEVICE}
 };
 
 #define UCON S3C2410_UCON_DEFAULT
diff -Nur linux-2.6.14/drivers/net/arm/cs8900.c linux-2.6.14_cs8900/drivers/net/arm/cs8900.c
--- linux-2.6.14/drivers/net/arm/cs8900.c	1970-01-01 08:00:00.000000000 +0800
+++ linux-2.6.14_cs8900/drivers/net/arm/cs8900.c	2008-02-03 23:15:49.000000000 +0800
@@ -0,0 +1,923 @@
+
+/*
+ * linux/drivers/net/cs8900.c
+ *
+ * Author: Abraham van der Merwe <abraham at 2d3d.co.za>
+ *
+ * A Cirrus Logic CS8900A driver for Linux
+ * based on the cs89x0 driver written by Russell Nelson,
+ * Donald Becker, and others.
+ *
+ * This source code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * History:
+ *    22-May-2002  Initial version (Abraham vd Merwe)
+ *    30-May-2002  Added char device support for eeprom (Frank Becker)
+ *    24-Jan-2004  Fixups for 2.6 (Frank Becker)
+ *	  15-July-2004 Modified for SMDK2410 (Roc Wu pwu at jadechip.com)
+ */
+ 
+#define VERSION_STRING "Cirrus Logic CS8900A driver for Linux (Modified for SMDK2410)"
+
+/*
+ * At the moment the driver does not support memory mode operation.
+ * It is trivial to implement this, but not worth the effort.
+ */
+
+/*
+ * TODO:
+ *
+ *   1. Sort out ethernet checksum
+ *   2. If !ready in send_start(), queue buffer and send it in interrupt handler
+ *      when we receive a BufEvent with Rdy4Tx, send it again. dangerous!
+ *   3. how do we prevent interrupt handler destroying integrity of get_stats()?
+ *   4. Change reset code to check status.
+ *   5. Implement set_mac_address and remove fake mac address
+ *   7. Link status detection stuff
+ *   8. Write utility to write EEPROM, do self testing, etc.
+ *   9. Implement DMA routines (I need a board w/ DMA support for that)
+ *  10. Power management
+ *  11. Add support for multiple ethernet chips
+ */
+
+// added BSt
+#include <linux/config.h>
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+// Added BSt
+#include <asm/mach-types.h>
+
+#ifdef CONFIG_SA1100_CERF
+#include "asm/arch/cerf.h"
+#endif
+
+#ifdef CONFIG_ARCH_SMDK2410
+#include "asm/arch-s3c2410/smdk2410.h"
+#endif
+
+#include "cs8900.h"
+
+//#define FULL_DUPLEX
+//#define DEBUG
+
+typedef struct {
+	struct net_device_stats stats;
+	u16 txlen;
+	int char_devnum;
+
+        spinlock_t lock;
+} cs8900_t;
+
+int cs8900_probe (struct net_device *dev);
+static struct net_device cs8900_dev =
+{
+        init: cs8900_probe
+};
+
+/* 
+ * There seems to be no way to determine the exact size of the eeprom, 
+ * so we use the largest size.
+ * FIXME: Verify it's safe to read/write past the end of a 64/128
+ *        byte eeprom. 
+ *
+ * Possible eeprom sizes: 
+ * Cx46 -  64 bytes
+ * Cx56 - 128 bytes
+ * Cx66 - 256 bytes
+ */
+#define MAX_EEPROM_SIZE		256
+
+static int cs8900_eeprom_fopen(struct inode *inode, struct file *file);
+static int cs8900_eeprom_frelease(struct inode *inode, struct file *file);
+static loff_t cs8900_eeprom_fllseek(struct file * file,loff_t offset, int flags);
+static ssize_t cs8900_eeprom_fread(struct file *file, char *buf, size_t count, loff_t *f_pos);
+static ssize_t cs8900_eeprom_fwrite(struct file *file, const char *buf, size_t count, loff_t *f_pos);
+static struct file_operations cs8900_eeprom_fops = {
+        owner:          THIS_MODULE,
+        open:           cs8900_eeprom_fopen,
+        release:        cs8900_eeprom_frelease,
+        llseek:         cs8900_eeprom_fllseek,
+        read:           cs8900_eeprom_fread,
+        write:          cs8900_eeprom_fwrite,
+};      
+
+static u16 cs8900_eeprom_cache[MAX_EEPROM_SIZE/2];
+
+/*
+ * I/O routines
+ */
+
+static inline u16 cs8900_read (struct net_device *dev,u16 reg)
+{
+	outw (reg,dev->base_addr + PP_Address);
+	return (inw (dev->base_addr + PP_Data));
+}
+
+static inline void cs8900_write (struct net_device *dev,u16 reg,u16 value)
+{
+	outw (reg,dev->base_addr + PP_Address);
+	outw (value,dev->base_addr + PP_Data);
+}
+
+static inline void cs8900_set (struct net_device *dev,u16 reg,u16 value)
+{
+	cs8900_write (dev,reg,cs8900_read (dev,reg) | value);
+}
+
+static inline void cs8900_clear (struct net_device *dev,u16 reg,u16 value)
+{
+	cs8900_write (dev,reg,cs8900_read (dev,reg) & ~value);
+}
+
+static inline void cs8900_frame_read (struct net_device *dev,struct sk_buff *skb,u16 length)
+{
+	insw (dev->base_addr,skb_put (skb,length),(length + 1) / 2);
+}
+
+static inline void cs8900_frame_write (struct net_device *dev,struct sk_buff *skb)
+{
+	outsw (dev->base_addr,skb->data,(skb->len + 1) / 2);
+}
+
+/*
+ * EEPROM I/O routines
+ */
+
+static int cs8900_eeprom_wait (struct net_device *dev)
+{
+	int i;
+
+	for (i = 0; i < 3000; i++) {
+		if (!(cs8900_read (dev,PP_SelfST) & SIBUSY))
+			return (0);
+		udelay (1);
+	}
+
+	return (-1);
+}
+
+static int cs8900_eeprom_read (struct net_device *dev,u16 *value,u16 offset)
+{
+	if (cs8900_eeprom_wait (dev) < 0)
+		return (-1);
+
+	cs8900_write (dev,PP_EEPROMCommand,offset | EEReadRegister);
+
+	if (cs8900_eeprom_wait (dev) < 0)
+		return (-1);
+
+	*value = cs8900_read (dev,PP_EEPROMData);
+
+	return (0);
+}
+
+static int cs8900_eeprom_write (struct net_device *dev,u16 *value,u16 offset)
+{
+	cs8900_eeprom_wait(dev);
+        cs8900_write(dev, PP_EEPROMCommand, (EEWriteEnable));
+	cs8900_eeprom_wait(dev);
+        cs8900_write(dev, PP_EEPROMData, *value);
+	cs8900_eeprom_wait(dev);
+        cs8900_write(dev, PP_EEPROMCommand, (offset | EEWriteRegister));
+	cs8900_eeprom_wait(dev);
+        cs8900_write(dev, PP_EEPROMCommand, (EEWriteDisable));
+	cs8900_eeprom_wait(dev);
+
+        return 0;
+}
+
+/*
+ * Debugging functions
+ */
+
+#ifdef DEBUG
+static inline int printable (int c)
+{
+	return ((c >= 32 && c <= 126) ||
+			(c >= 174 && c <= 223) ||
+			(c >= 242 && c <= 243) ||
+			(c >= 252 && c <= 253));
+}
+
+static void dump16 (struct net_device *dev,const u8 *s,size_t len)
+{
+	int i;
+	char str[128];
+
+	if (!len) return;
+
+	*str = '\0';
+
+	for (i = 0; i < len; i++) {
+		if (i && !(i % 4)) strcat (str," ");
+		sprintf (str,"%s%.2x ",str,s[i]);
+	}
+
+	for ( ; i < 16; i++) {
+		if (i && !(i % 4)) strcat (str," ");
+		strcat (str,"   ");
+	}
+
+	strcat (str," ");
+	for (i = 0; i < len; i++) sprintf (str,"%s%c",str,printable (s[i]) ? s[i] : '.');
+
+	printk (KERN_DEBUG "%s:     %s\n",dev->name,str);
+}
+
+static void hexdump (struct net_device *dev,const void *ptr,size_t size)
+{
+	const u8 *s = (u8 *) ptr;
+	int i;
+	for (i = 0; i < size / 16; i++, s += 16) dump16 (dev,s,16);
+	dump16 (dev,s,size % 16);
+}
+
+static void dump_packet (struct net_device *dev,struct sk_buff *skb,const char *type)
+{
+	printk (KERN_INFO "%s: %s %d byte frame %.2x:%.2x:%.2x:%.2x:%.2x:%.2x to %.2x:%.2x:%.2x:%.2x:%.2x:%.2x type %.4x\n",
+			dev->name,
+			type,
+			skb->len,
+			skb->data[0],skb->data[1],skb->data[2],skb->data[3],skb->data[4],skb->data[5],
+			skb->data[6],skb->data[7],skb->data[8],skb->data[9],skb->data[10],skb->data[11],
+			(skb->data[12] << 8) | skb->data[13]);
+	if (skb->len < 0x100) hexdump (dev,skb->data,skb->len);
+}
+
+static void eepromdump( struct net_device *dev)
+{
+	u16 buf[0x80];
+	u16 i;
+	int count;
+	int total;
+
+	if( cs8900_read( dev, PP_SelfST) & EEPROMpresent)
+	{
+		printk (KERN_INFO "%s: EEPROM present\n",dev->name);
+	}
+	else
+	{
+		printk (KERN_INFO "%s: NO EEPROM present\n",dev->name);
+		return;
+	}
+
+	if( cs8900_read( dev, PP_SelfST) & EEPROMOK)
+	{
+		printk (KERN_INFO "%s: EEPROM OK\n",dev->name);
+	}
+	else
+	{
+		printk (KERN_INFO "%s: EEPROM checksum mismatch - fixing...\n",dev->name);
+	}
+
+	printk (KERN_INFO "%s: Hexdump\n",dev->name);
+	for( i=0; i<0x80; i++)
+	{
+		cs8900_eeprom_read( dev, &buf[i], i);
+	}
+	hexdump( dev, buf, 0x100);
+
+	if( buf[0] & 0x0100)
+	{
+		printk (KERN_INFO "%s: non-sequential EEPROM\n",dev->name);
+	}
+	else
+	{
+		printk (KERN_INFO "%s: sequential EEPROM\n",dev->name);
+	}
+
+	if( (buf[0] & 0xe000) == 0xa000)
+	{
+		printk (KERN_INFO "%s: Found reset configuration block\n",dev->name);
+	}
+	else
+	{
+		printk (KERN_INFO "%s: Reset configuration block not found\n",dev->name);
+		return;
+	}
+
+	count = 2;
+	total = buf[0] & 0xff;
+	printk (KERN_INFO "%s: Reset configuration block size = %d bytes\n",dev->name, total);
+
+	while( count < total)
+	{
+		int groupsize = (buf[count/2] >> 12) + 1;
+		int basereg = (buf[count/2] &0x1ff);
+		printk (KERN_INFO "%s: Group size = %d words\n",dev->name, groupsize);
+		printk (KERN_INFO "%s:  Base register = %x\n",dev->name, basereg);
+		count += (groupsize + 1)*2;
+	}
+}
+
+#endif	/* #ifdef DEBUG */
+
+/*
+ * Driver functions
+ */
+
+static void cs8900_receive (struct net_device *dev)
+{
+	cs8900_t *priv = (cs8900_t *) dev->priv;
+	struct sk_buff *skb;
+	u16 status,length;
+
+	status = cs8900_read (dev,PP_RxStatus);
+	length = cs8900_read (dev,PP_RxLength);
+
+	if (!(status & RxOK)) {
+		priv->stats.rx_errors++;
+		if ((status & (Runt | Extradata))) priv->stats.rx_length_errors++;
+		if ((status & CRCerror)) priv->stats.rx_crc_errors++;
+		return;
+	}
+
+	if ((skb = dev_alloc_skb (length + 4)) == NULL) {
+		priv->stats.rx_dropped++;
+		return;
+	}
+
+	skb->dev = dev;
+	skb_reserve (skb,2);
+
+	cs8900_frame_read (dev,skb,length);
+
+#ifdef FULL_DUPLEX
+	dump_packet (dev,skb,"recv");
+#endif	/* #ifdef FULL_DUPLEX */
+
+	skb->protocol = eth_type_trans (skb,dev);
+
+	netif_rx (skb);
+	dev->last_rx = jiffies;
+
+	priv->stats.rx_packets++;
+	priv->stats.rx_bytes += length;
+}
+
+static int cs8900_send_start (struct sk_buff *skb,struct net_device *dev)
+{
+	cs8900_t *priv = (cs8900_t *) dev->priv;
+	u16 status;
+
+	spin_lock_irq(&priv->lock);
+	netif_stop_queue (dev);
+
+	cs8900_write (dev,PP_TxCMD,TxStart (After5));
+	cs8900_write (dev,PP_TxLength,skb->len);
+
+	status = cs8900_read (dev,PP_BusST);
+
+	if ((status & TxBidErr)) {
+		spin_unlock_irq(&priv->lock);
+		printk (KERN_WARNING "%s: Invalid frame size %d!\n",dev->name,skb->len);
+		priv->stats.tx_errors++;
+		priv->stats.tx_aborted_errors++;
+		priv->txlen = 0;
+		return (1);
+	}
+
+	if (!(status & Rdy4TxNOW)) {
+		spin_unlock_irq(&priv->lock);
+		printk (KERN_WARNING "%s: Transmit buffer not free!\n",dev->name);
+		priv->stats.tx_errors++;
+		priv->txlen = 0;
+		/* FIXME: store skb and send it in interrupt handler */
+		return (1);
+	}
+
+	cs8900_frame_write (dev,skb);
+	spin_unlock_irq(&priv->lock);
+
+#ifdef DEBUG
+	dump_packet (dev,skb,"send");
+#endif	/* #ifdef DEBUG */
+
+	dev->trans_start = jiffies;
+
+	dev_kfree_skb (skb);
+
+	priv->txlen = skb->len;
+
+	return (0);
+}
+
+static irqreturn_t cs8900_interrupt (int irq,void *id,struct pt_regs *regs)
+{
+	struct net_device *dev = (struct net_device *) id;
+	cs8900_t *priv;
+	volatile u16 status;
+	irqreturn_t handled = 0;
+		
+	if (dev->priv == NULL) {
+		printk (KERN_WARNING "%s: irq %d for unknown device.\n",dev->name,irq);
+		return 0;
+	}
+
+	priv = (cs8900_t *) dev->priv;
+	
+	while ((status = cs8900_read (dev, PP_ISQ))) {
+		handled = 1;
+
+		switch (RegNum (status)) {
+		case RxEvent:
+			cs8900_receive (dev);
+			break;
+
+		case TxEvent:
+			priv->stats.collisions += ColCount (cs8900_read (dev,PP_TxCOL));
+			if (!(RegContent (status) & TxOK)) {
+				priv->stats.tx_errors++;
+				if ((RegContent (status) & Out_of_window)) priv->stats.tx_window_errors++;
+				if ((RegContent (status) & Jabber)) priv->stats.tx_aborted_errors++;
+				break;
+			} else if (priv->txlen) {
+				priv->stats.tx_packets++;
+				priv->stats.tx_bytes += priv->txlen;
+			}
+			priv->txlen = 0;
+			netif_wake_queue (dev);
+			break;
+
+		case BufEvent:
+			if ((RegContent (status) & RxMiss)) {
+				u16 missed = MissCount (cs8900_read (dev,PP_RxMISS));
+				priv->stats.rx_errors += missed;
+				priv->stats.rx_missed_errors += missed;
+			}
+			if ((RegContent (status) & TxUnderrun)) {
+				priv->stats.tx_errors++;
+				priv->stats.tx_fifo_errors++;
+
+				priv->txlen = 0;
+				netif_wake_queue (dev);
+			}
+			/* FIXME: if Rdy4Tx, transmit last sent packet (if any) */
+			break;
+
+		case TxCOL:
+			priv->stats.collisions += ColCount (cs8900_read (dev,PP_TxCOL));
+			break;
+
+		case RxMISS:
+			status = MissCount (cs8900_read (dev,PP_RxMISS));
+			priv->stats.rx_errors += status;
+			priv->stats.rx_missed_errors += status;
+			break;
+		}
+	}
+	return IRQ_RETVAL(handled);
+}
+
+static void cs8900_transmit_timeout (struct net_device *dev)
+{
+	cs8900_t *priv = (cs8900_t *) dev->priv;
+	priv->stats.tx_errors++;
+	priv->stats.tx_heartbeat_errors++;
+	priv->txlen = 0;
+	netif_wake_queue (dev);
+}
+
+static int cs8900_start (struct net_device *dev)
+{
+	int result;
+
+#if defined(CONFIG_ARCH_SMDK2410)
+	set_irq_type(dev->irq, IRQT_RISING);
+
+	/* enable the ethernet controller */
+	cs8900_set (dev,PP_RxCFG,RxOKiE | BufferCRC | CRCerroriE | RuntiE | ExtradataiE);
+	cs8900_set (dev,PP_RxCTL,RxOKA | IndividualA | BroadcastA);
+	cs8900_set (dev,PP_TxCFG,TxOKiE | Out_of_windowiE | JabberiE);
+	cs8900_set (dev,PP_BufCFG,Rdy4TxiE | RxMissiE | TxUnderruniE | TxColOvfiE | MissOvfloiE);
+	cs8900_set (dev,PP_LineCTL,SerRxON | SerTxON);
+	cs8900_set (dev,PP_BusCTL,EnableRQ);
+
+#ifdef FULL_DUPLEX
+	cs8900_set (dev,PP_TestCTL,FDX);
+#endif	/* #ifdef FULL_DUPLEX */
+	udelay(200);	
+	/* install interrupt handler */
+	if ((result = request_irq (dev->irq, &cs8900_interrupt, 0, dev->name, dev)) < 0) {
+		printk ("%s: could not register interrupt %d\n",dev->name, dev->irq);
+		return (result);
+	}
+#else
+	
+	/* install interrupt handler */
+	if ((result = request_irq (dev->irq, &cs8900_interrupt, 0, dev->name, dev)) < 0) {
+		printk ("%s: could not register interrupt %d\n",dev->name, dev->irq);
+		return (result);
+	}
+
+	set_irq_type(dev->irq, IRQT_RISING);
+
+	/* enable the ethernet controller */
+	cs8900_set (dev,PP_RxCFG,RxOKiE | BufferCRC | CRCerroriE | RuntiE | ExtradataiE);
+	cs8900_set (dev,PP_RxCTL,RxOKA | IndividualA | BroadcastA);
+	cs8900_set (dev,PP_TxCFG,TxOKiE | Out_of_windowiE | JabberiE);
+	cs8900_set (dev,PP_BufCFG,Rdy4TxiE | RxMissiE | TxUnderruniE | TxColOvfiE | MissOvfloiE);
+	cs8900_set (dev,PP_LineCTL,SerRxON | SerTxON);
+	cs8900_set (dev,PP_BusCTL,EnableRQ);
+
+#ifdef FULL_DUPLEX
+	cs8900_set (dev,PP_TestCTL,FDX);
+#endif	/* #ifdef FULL_DUPLEX */
+	
+#endif /* #if defined(CONFIG_ARCH_SMDK2410) */
+
+	/* start the queue */
+	netif_start_queue (dev);
+
+	return (0);
+}
+
+static int cs8900_stop (struct net_device *dev)
+{
+	/* disable ethernet controller */
+	cs8900_write (dev,PP_BusCTL,0);
+	cs8900_write (dev,PP_TestCTL,0);
+	cs8900_write (dev,PP_SelfCTL,0);
+	cs8900_write (dev,PP_LineCTL,0);
+	cs8900_write (dev,PP_BufCFG,0);
+	cs8900_write (dev,PP_TxCFG,0);
+	cs8900_write (dev,PP_RxCTL,0);
+	cs8900_write (dev,PP_RxCFG,0);
+
+	/* uninstall interrupt handler */
+	free_irq (dev->irq,dev);
+
+	/* stop the queue */
+	netif_stop_queue (dev);
+
+	return (0);
+}
+
+static struct net_device_stats *cs8900_get_stats (struct net_device *dev)
+{
+	cs8900_t *priv = (cs8900_t *) dev->priv;
+	return (&priv->stats);
+}
+
+static void cs8900_set_receive_mode (struct net_device *dev)
+{
+	if ((dev->flags & IFF_PROMISC))
+		cs8900_set (dev,PP_RxCTL,PromiscuousA);
+	else
+		cs8900_clear (dev,PP_RxCTL,PromiscuousA);
+
+	if ((dev->flags & IFF_ALLMULTI) && dev->mc_list)
+		cs8900_set (dev,PP_RxCTL,MulticastA);
+	else
+		cs8900_clear (dev,PP_RxCTL,MulticastA);
+}
+
+static int cs8900_eeprom (struct net_device *dev)
+{
+	cs8900_t *priv = (cs8900_t *) dev->priv;
+	int i;
+
+	/* SMDK2410 CS8900A without EEPROM at all */
+#if defined(CONFIG_ARCH_SMDK2410)
+	return (-ENODEV);
+#endif 
+
+#ifdef DEBUG
+	eepromdump (dev);
+#endif
+
+	if( (cs8900_read( dev, PP_SelfST) & EEPROMpresent) == 0)
+	{
+		/* no eeprom */
+		return (-ENODEV);
+	}
+
+	/* add character device for easy eeprom programming */
+	if( (priv->char_devnum=register_chrdev(0,"cs8900_eeprom",&cs8900_eeprom_fops)) != 0)
+		printk (KERN_INFO "%s: Registered cs8900_eeprom char device (major #%d)\n",
+			dev->name, priv->char_devnum);
+	else
+		printk (KERN_WARNING "%s: Failed to register char device cs8900_eeprom\n",dev->name);
+
+	if( (cs8900_read( dev, PP_SelfST) & EEPROMOK) == 0) 
+	{
+		/* bad checksum, invalid config block */
+		return (-EFAULT);
+	}
+
+	/* If we get here, the chip will have initialized the registers
+	 * that were specified in the eeprom configuration block
+	 * We assume this is at least the mac address.
+	 */
+	for (i = 0; i < ETH_ALEN; i += 2)
+	{
+		u16 mac = cs8900_read (dev,PP_IA + i);
+		dev->dev_addr[i] = mac & 0xff;
+		dev->dev_addr[i+1] = (mac>>8) & 0xff;
+	}
+
+	return (0);
+}
+
+/*
+ * EEPROM Charater device
+ */
+
+static int cs8900_eeprom_fopen(struct inode *inode, struct file *file)
+{
+	u16 i;
+	for( i=0; i<MAX_EEPROM_SIZE/2; i++)
+	{
+		cs8900_eeprom_read( &cs8900_dev, &cs8900_eeprom_cache[i],i);
+	}
+
+	return 0;
+}
+
+static int cs8900_eeprom_frelease(struct inode *inode, struct file *file)
+{
+	return 0;
+}
+
+static loff_t cs8900_eeprom_fllseek(struct file * file,loff_t offset, int whence)
+{
+	long newpos;
+
+	switch(whence)
+	{
+		case 0: /* SEEK_SET */
+			newpos = offset;
+			break;
+		case 1: /* SEEK_CUR */
+			newpos = file->f_pos + offset;
+			break;
+		case 2: /* SEEK_END */
+			newpos = (MAX_EEPROM_SIZE-1) - offset;
+			break;
+		default: /* can't happen */
+			return -EINVAL;
+
+	}
+
+	if( (newpos<0) || (newpos>=MAX_EEPROM_SIZE)) return -EINVAL;
+
+	file->f_pos = newpos;
+	return newpos;
+}
+
+static ssize_t cs8900_eeprom_fread(struct file *file, char *buf, size_t count, loff_t *f_pos)
+{
+	unsigned char *temp = (unsigned char *)cs8900_eeprom_cache;
+
+        if (*f_pos >= MAX_EEPROM_SIZE)
+                return 0;
+
+        if (*f_pos + count > MAX_EEPROM_SIZE)
+            count = MAX_EEPROM_SIZE - *f_pos;
+
+        if (count<1)
+                return 0;
+
+        if (copy_to_user(buf, &temp[*f_pos], count)){
+                return -EFAULT;
+        }
+        *f_pos += count;
+        return count;
+}
+
+static ssize_t cs8900_eeprom_fwrite(struct file *file, const char *buf, size_t count, loff_t *f_pos)
+{
+	u16 i;
+	unsigned char *temp = (unsigned char *)cs8900_eeprom_cache;
+
+        if (*f_pos >= MAX_EEPROM_SIZE)
+                return 0;
+
+        if (*f_pos + count > MAX_EEPROM_SIZE)
+            count = MAX_EEPROM_SIZE - *f_pos;
+
+        if (count<1)
+                return 0;
+
+	/* FIXME: lock critical section */
+
+	/* update the cache */
+        if (copy_from_user(&temp[*f_pos], buf, count)){
+                return -EFAULT;
+        }
+
+	/* not concerned about performance, so write the entire thing */
+	for( i=0; i<MAX_EEPROM_SIZE/2; i++)
+	{
+		cs8900_eeprom_write( &cs8900_dev, &cs8900_eeprom_cache[i],i);
+	}
+
+        *f_pos += count;
+        return count;
+}
+
+/*
+ * Architecture dependant code
+ */
+
+#ifdef CONFIG_SA1100_FRODO
+static void frodo_reset (struct net_device *dev)
+{
+	int i;
+	volatile u16 value;
+
+	/* reset ethernet controller */
+	FRODO_CPLD_ETHERNET |= FRODO_ETH_RESET;
+	mdelay (50);
+	FRODO_CPLD_ETHERNET &= ~FRODO_ETH_RESET;
+	mdelay (50);
+
+	/* we tied SBHE to CHIPSEL, so each memory access ensure the chip is in 16-bit mode */
+	for (i = 0; i < 3; i++) value = cs8900_read (dev,0);
+
+	/* FIXME: poll status bit */
+}
+#endif	/* #ifdef CONFIG_SA1100_FRODO */
+
+/*
+ * Driver initialization routines
+ */
+
+int __init cs8900_probe (struct net_device *dev)
+{
+	static cs8900_t priv;
+	int i,result;
+	u16 value;
+
+	printk (VERSION_STRING"\n");
+
+	memset (&priv,0,sizeof (cs8900_t));
+
+	ether_setup (dev);
+
+	dev->open               = cs8900_start;
+	dev->stop               = cs8900_stop;
+	dev->hard_start_xmit    = cs8900_send_start;
+	dev->get_stats          = cs8900_get_stats;
+	dev->set_multicast_list = cs8900_set_receive_mode;
+	dev->tx_timeout         = cs8900_transmit_timeout;
+	dev->watchdog_timeo     = HZ;
+
+#if defined(CONFIG_ARCH_SMDK2410)
+	dev->dev_addr[0] = 0x08;
+	dev->dev_addr[1] = 0x00;
+	dev->dev_addr[2] = 0x3e;
+	dev->dev_addr[3] = 0x26;
+	dev->dev_addr[4] = 0x0a;
+	dev->dev_addr[5] = 0x5b;
+#else
+	dev->dev_addr[0] = 0x00;
+    dev->dev_addr[1] = 0x12;
+    dev->dev_addr[2] = 0x34;
+    dev->dev_addr[3] = 0x56;
+    dev->dev_addr[4] = 0x78;
+    dev->dev_addr[5] = 0x9a;
+#endif
+
+	dev->if_port   = IF_PORT_10BASET;
+	dev->priv      = (void *) &priv;
+
+	spin_lock_init(&priv.lock);
+
+	SET_MODULE_OWNER (dev);
+
+#ifdef CONFIG_SA1100_FRODO
+	dev->base_addr = FRODO_ETH_IO + 0x300;
+	dev->irq = FRODO_ETH_IRQ;
+	frodo_reset (dev);
+#endif	/* #ifdef CONFIG_SA1100_FRODO */
+
+#if defined(CONFIG_SA1100_CERF)
+	dev->base_addr = CERF_ETH_IO + 0x300;
+	dev->irq = CERF_ETH_IRQ;
+#endif /* #if defined(CONFIG_SA1100_CERF) */
+
+#if defined(CONFIG_ARCH_SMDK2410)
+	dev->base_addr = vSMDK2410_ETH_IO + 0x300;
+	dev->irq = SMDK2410_ETH_IRQ;
+#endif /* #if defined(CONFIG_ARCH_SMDK2410) */ 
+
+	if ((result = check_mem_region (dev->base_addr, 16))) {
+		printk (KERN_ERR "%s: can't get I/O port address 0x%lx\n",dev->name,dev->base_addr);
+		return (result);
+	}
+	request_mem_region (dev->base_addr, 16, dev->name);
+	
+	/* verify EISA registration number for Cirrus Logic */
+	if ((value = cs8900_read (dev,PP_ProductID)) != EISA_REG_CODE) {
+		printk (KERN_ERR "%s: incorrect signature 0x%.4x\n",dev->name,value);
+		return (-ENXIO);
+	}
+
+	/* verify chip version */
+	value = cs8900_read (dev,PP_ProductID + 2);
+	if (VERSION (value) != CS8900A) {
+		printk (KERN_ERR "%s: unknown chip version 0x%.8x\n",dev->name,VERSION (value));
+		return (-ENXIO);
+	}
+	/* setup interrupt number */
+	cs8900_write (dev,PP_IntNum,0);
+
+	/* If an EEPROM is present, use it's MAC address. A valid EEPROM will 
+	 * initialize the registers automatically.
+	 */
+	result = cs8900_eeprom (dev);
+
+	printk (KERN_INFO "%s: CS8900A rev %c at %#lx irq=%d",
+		dev->name,'B' + REVISION (value) - REV_B, dev->base_addr, dev->irq);
+	if (result == -ENODEV) {
+		/* no eeprom or invalid config block, configure MAC address by hand */
+		for (i = 0; i < ETH_ALEN; i += 2)
+			cs8900_write (dev,PP_IA + i,dev->dev_addr[i] | (dev->dev_addr[i + 1] << 8));
+		printk (", no eeprom ");
+	}
+	else if( result == -EFAULT)
+	{
+#if defined(CONFIG_SA1100_CERF)
+	    /* The default eeprom layout doesn't follow the cs8900 layout 
+		 * that enables automatic cs8900 initialization. Doh!
+		 * Read the mac address manually.
+		 */
+		u16 MAC_addr[3] = {0, 0, 0};
+
+		if (cs8900_eeprom_read(dev, &MAC_addr[0], 0x1c) == -1)
+			printk("\ncs8900: [CERF] EEPROM[0] read failed\n");
+		if (cs8900_eeprom_read(dev, &MAC_addr[1], 0x1d) == -1)
+			printk("\ncs8900: [CERF] EEPROM[1] read failed\n");
+		if (cs8900_eeprom_read(dev, &MAC_addr[2], 0x1e) == -1)
+			printk("\ncs8900: [CERF] EEPROM[2] read failed\n");
+
+		for (i = 0; i < ETH_ALEN / 2; i++)
+		{
+			dev->dev_addr[i*2]	= MAC_addr[i] & 0xff;
+			dev->dev_addr[i*2+1]	= (MAC_addr[i] >> 8) & 0xff;
+
+			cs8900_write (dev,PP_IA + i*2,dev->dev_addr[i*2] | (dev->dev_addr[i*2 + 1] << 8));
+		}
+		printk (", eeprom (smdk2410 layout)");
+#else
+		printk (", eeprom (invalid config block)");
+#endif /* #if defined(CONFIG_SA1100_CERF) */
+	}
+	else
+	{
+		printk (", eeprom ok");
+	}
+
+	printk (", addr:");
+	for (i = 0; i < ETH_ALEN; i += 2)
+	{
+		u16 mac = cs8900_read (dev,PP_IA + i);
+		printk ("%c%02X:%2X", (i==0)?' ':':', mac & 0xff, (mac >> 8));
+	}
+	printk ("\n");
+
+	return (0);
+}
+
+static int __init cs8900_init (void)
+{
+	strcpy(cs8900_dev.name, "eth%d");
+
+	return (register_netdev (&cs8900_dev));
+}
+
+static void __exit cs8900_cleanup (void)
+{
+	cs8900_t *priv = (cs8900_t *) cs8900_dev.priv;
+	if( priv->char_devnum)
+	{
+		unregister_chrdev(priv->char_devnum,"cs8900_eeprom");
+	}
+	release_mem_region (cs8900_dev.base_addr,16);
+	unregister_netdev (&cs8900_dev);
+}
+
+MODULE_AUTHOR ("Abraham van der Merwe <abraham at 2d3d.co.za>");
+MODULE_DESCRIPTION (VERSION_STRING);
+MODULE_LICENSE ("GPL");
+
+module_init (cs8900_init);
+module_exit (cs8900_cleanup);
diff -Nur linux-2.6.14/drivers/net/arm/cs8900.h linux-2.6.14_cs8900/drivers/net/arm/cs8900.h
--- linux-2.6.14/drivers/net/arm/cs8900.h	1970-01-01 08:00:00.000000000 +0800
+++ linux-2.6.14_cs8900/drivers/net/arm/cs8900.h	2008-02-03 23:15:49.000000000 +0800
@@ -0,0 +1,237 @@
+#ifndef CS8900_H
+#define CS8900_H
+
+/*
+ * linux/drivers/net/cs8900.h
+ *
+ * Author: Abraham van der Merwe <abraham at 2d3d.co.za>
+ *
+ * A Cirrus Logic CS8900A driver for Linux
+ * based on the cs89x0 driver written by Russell Nelson,
+ * Donald Becker, and others.
+ *
+ * This source code is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+/*
+ * Ports
+ */
+
+#define PP_Address		0x0a	/* PacketPage Pointer Port (Section 4.10.10) */
+#define PP_Data			0x0c	/* PacketPage Data Port (Section 4.10.10) */
+
+/*
+ * Registers
+ */
+
+#define PP_ProductID		0x0000	/* Section 4.3.1   Product Identification Code */
+#define PP_MemBase			0x002c	/* Section 4.9.2   Memory Base Address Register */
+#define PP_IntNum			0x0022	/* Section 3.2.3   Interrupt Number */
+#define PP_EEPROMCommand	0x0040	/* Section 4.3.11  EEPROM Command */
+#define PP_EEPROMData		0x0042	/* Section 4.3.12  EEPROM Data */
+#define PP_RxCFG			0x0102	/* Section 4.4.6   Receiver Configuration */
+#define PP_RxCTL			0x0104	/* Section 4.4.8   Receiver Control */
+#define PP_TxCFG			0x0106	/* Section 4.4.9   Transmit Configuration */
+#define PP_BufCFG			0x010a	/* Section 4.4.12  Buffer Configuration */
+#define PP_LineCTL			0x0112	/* Section 4.4.16  Line Control */
+#define PP_SelfCTL			0x0114	/* Section 4.4.18  Self Control */
+#define PP_BusCTL			0x0116	/* Section 4.4.20  Bus Control */
+#define PP_TestCTL			0x0118	/* Section 4.4.22  Test Control */
+#define PP_ISQ				0x0120	/* Section 4.4.5   Interrupt Status Queue */
+#define PP_TxEvent			0x0128	/* Section 4.4.10  Transmitter Event */
+#define PP_BufEvent			0x012c	/* Section 4.4.13  Buffer Event */
+#define PP_RxMISS			0x0130	/* Section 4.4.14  Receiver Miss Counter */
+#define PP_TxCOL			0x0132	/* Section 4.4.15  Transmit Collision Counter */
+#define PP_SelfST			0x0136	/* Section 4.4.19  Self Status */
+#define PP_BusST			0x0138	/* Section 4.4.21  Bus Status */
+#define PP_TxCMD			0x0144	/* Section 4.4.11  Transmit Command */
+#define PP_TxLength			0x0146	/* Section 4.5.2   Transmit Length */
+#define PP_IA				0x0158	/* Section 4.6.2   Individual Address (IEEE Address) */
+#define PP_RxStatus			0x0400	/* Section 4.7.1   Receive Status */
+#define PP_RxLength			0x0402	/* Section 4.7.1   Receive Length (in bytes) */
+#define PP_RxFrame			0x0404	/* Section 4.7.2   Receive Frame Location */
+#define PP_TxFrame			0x0a00	/* Section 4.7.2   Transmit Frame Location */
+
+/*
+ * Values
+ */
+
+/* PP_IntNum */
+#define INTRQ0			0x0000
+#define INTRQ1			0x0001
+#define INTRQ2			0x0002
+#define INTRQ3			0x0003
+
+/* PP_ProductID */
+#define EISA_REG_CODE	0x630e
+#define REVISION(x)		(((x) & 0x1f00) >> 8)
+#define VERSION(x)		((x) & ~0x1f00)
+
+#define CS8900A			0x0000
+#define REV_B			7
+#define REV_C			8
+#define REV_D			9
+
+/* PP_RxCFG */
+#define Skip_1			0x0040
+#define StreamE			0x0080
+#define RxOKiE			0x0100
+#define RxDMAonly		0x0200
+#define AutoRxDMAE		0x0400
+#define BufferCRC		0x0800
+#define CRCerroriE		0x1000
+#define RuntiE			0x2000
+#define ExtradataiE		0x4000
+
+/* PP_RxCTL */
+#define IAHashA			0x0040
+#define PromiscuousA	0x0080
+#define RxOKA			0x0100
+#define MulticastA		0x0200
+#define IndividualA		0x0400
+#define BroadcastA		0x0800
+#define CRCerrorA		0x1000
+#define RuntA			0x2000
+#define ExtradataA		0x4000
+
+/* PP_TxCFG */
+#define Loss_of_CRSiE	0x0040
+#define SQErroriE		0x0080
+#define TxOKiE			0x0100
+#define Out_of_windowiE	0x0200
+#define JabberiE		0x0400
+#define AnycolliE		0x0800
+#define T16colliE		0x8000
+
+/* PP_BufCFG */
+#define SWint_X			0x0040
+#define RxDMAiE			0x0080
+#define Rdy4TxiE		0x0100
+#define TxUnderruniE	0x0200
+#define RxMissiE		0x0400
+#define Rx128iE			0x0800
+#define TxColOvfiE		0x1000
+#define MissOvfloiE		0x2000
+#define RxDestiE		0x8000
+
+/* PP_LineCTL */
+#define SerRxON			0x0040
+#define SerTxON			0x0080
+#define AUIonly			0x0100
+#define AutoAUI_10BT	0x0200
+#define ModBackoffE		0x0800
+#define PolarityDis		0x1000
+#define L2_partDefDis	0x2000
+#define LoRxSquelch		0x4000
+
+/* PP_SelfCTL */
+#define RESET			0x0040
+#define SWSuspend		0x0100
+#define HWSleepE		0x0200
+#define HWStandbyE		0x0400
+#define HC0E			0x1000
+#define HC1E			0x2000
+#define HCB0			0x4000
+#define HCB1			0x8000
+
+/* PP_BusCTL */
+#define ResetRxDMA		0x0040
+#define DMAextend		0x0100
+#define UseSA			0x0200
+#define MemoryE			0x0400
+#define DMABurst		0x0800
+#define IOCHRDYE		0x1000
+#define RxDMAsize		0x2000
+#define EnableRQ		0x8000
+
+/* PP_TestCTL */
+#define DisableLT		0x0080
+#define ENDECloop		0x0200
+#define AUIloop			0x0400
+#define DisableBackoff	0x0800
+#define FDX				0x4000
+
+/* PP_ISQ */
+#define RegNum(x) ((x) & 0x3f)
+#define RegContent(x) ((x) & ~0x3d)
+
+#define RxEvent			0x0004
+#define TxEvent			0x0008
+#define BufEvent		0x000c
+#define RxMISS			0x0010
+#define TxCOL			0x0012
+
+/* PP_RxStatus */
+#define IAHash			0x0040
+#define Dribblebits		0x0080
+#define RxOK			0x0100
+#define Hashed			0x0200
+#define IndividualAdr	0x0400
+#define Broadcast		0x0800
+#define CRCerror		0x1000
+#define Runt			0x2000
+#define Extradata		0x4000
+
+#define HashTableIndex(x) ((x) >> 0xa)
+
+/* PP_TxCMD */
+#define After5			0
+#define After381		1
+#define After1021		2
+#define AfterAll		3
+#define TxStart(x) ((x) << 6)
+
+#define Force			0x0100
+#define Onecoll			0x0200
+#define InhibitCRC		0x1000
+#define TxPadDis		0x2000
+
+/* PP_BusST */
+#define TxBidErr		0x0080
+#define Rdy4TxNOW		0x0100
+
+/* PP_TxEvent */
+#define Loss_of_CRS		0x0040
+#define SQEerror		0x0080
+#define TxOK			0x0100
+#define Out_of_window	0x0200
+#define Jabber			0x0400
+#define T16coll			0x8000
+
+#define TX_collisions(x) (((x) >> 0xb) & ~0x8000)
+
+/* PP_BufEvent */
+#define SWint			0x0040
+#define RxDMAFrame		0x0080
+#define Rdy4Tx			0x0100
+#define TxUnderrun		0x0200
+#define RxMiss			0x0400
+#define Rx128			0x0800
+#define RxDest			0x8000
+
+/* PP_RxMISS */
+#define MissCount(x) ((x) >> 6)
+
+/* PP_TxCOL */
+#define ColCount(x) ((x) >> 6)
+
+/* PP_SelfST */
+#define T3VActive		0x0040
+#define INITD			0x0080
+#define SIBUSY			0x0100
+#define EEPROMpresent	0x0200
+#define EEPROMOK		0x0400
+#define ELpresent		0x0800
+#define EEsize			0x1000
+
+/* PP_EEPROMCommand */
+#define EEWriteEnable   0x00F0
+#define EEWriteDisable  0x0000
+#define EEWriteRegister	0x0100
+#define EEReadRegister	0x0200
+#define EEEraseRegister	0x0300
+#define ELSEL			0x0400
+
+#endif	/* #ifndef CS8900_H */
diff -Nur linux-2.6.14/drivers/net/arm/Kconfig linux-2.6.14_cs8900/drivers/net/arm/Kconfig
--- linux-2.6.14/drivers/net/arm/Kconfig	2005-10-28 08:02:08.000000000 +0800
+++ linux-2.6.14_cs8900/drivers/net/arm/Kconfig	2008-02-03 23:16:28.000000000 +0800
@@ -44,3 +44,11 @@
 	  will generate a suitable hw address based on the board serial
 	  number (MTD support is required for this). Otherwise you will
 	  need to set a suitable hw address using ifconfig.
+config ARM_CS8900
+        tristate "CS8900 support"
+        depends on NET_ETHERNET && ARM && ARCH_SMDK2410
+        help
+          Support for CS8900A chipset based Ethernet cards. If you have a network
+          (Ethernet) card of this type, say Y and read the Ethernet-HOWTO, available
+          from as well as .To compile this driver as a module, choose M here and read.
+          The module will be called cs8900.o.
diff -Nur linux-2.6.14/drivers/net/arm/Makefile linux-2.6.14_cs8900/drivers/net/arm/Makefile
--- linux-2.6.14/drivers/net/arm/Makefile	2005-10-28 08:02:08.000000000 +0800
+++ linux-2.6.14_cs8900/drivers/net/arm/Makefile	2008-02-03 23:44:15.000000000 +0800
@@ -8,3 +8,4 @@
 obj-$(CONFIG_ARM_ETHERH)	+= etherh.o
 obj-$(CONFIG_ARM_ETHER3)	+= ether3.o
 obj-$(CONFIG_ARM_ETHER1)	+= ether1.o
+obj-$(CONFIG_ARM_CS8900) 	+= cs8900.o
diff -Nur linux-2.6.14/include/asm-arm/arch-s3c2410/smdk2410.h linux-2.6.14_cs8900/include/asm-arm/arch-s3c2410/smdk2410.h
--- linux-2.6.14/include/asm-arm/arch-s3c2410/smdk2410.h	1970-01-01 08:00:00.000000000 +0800
+++ linux-2.6.14_cs8900/include/asm-arm/arch-s3c2410/smdk2410.h	2008-02-03 23:05:36.000000000 +0800
@@ -0,0 +1,7 @@
+#ifndef _INCLUDE_SMDK2410_H_
+#define _INCLUDE_SMDK2410_H_
+#include <asm/arch/irqs.h>
+#define pSMDK2410_ETH_IO 0x19000000
+#define vSMDK2410_ETH_IO 0xE0000000
+#define SMDK2410_ETH_IRQ IRQ_EINT9
+#endif // _INCLUDE_SMDK2410_H_
