Add port for the lpc2292sodimm evaluation board from EmbeddedArtists
diff --git a/board/lpc2292sodimm/Makefile b/board/lpc2292sodimm/Makefile
new file mode 100644
index 0000000..5a30198
--- /dev/null
+++ b/board/lpc2292sodimm/Makefile
@@ -0,0 +1,58 @@
+#
+# (C) Copyright 2002
+# Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+# Marius Groeger <mgroeger@sysgo.de>
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+include $(TOPDIR)/config.mk
+
+LIB	= lib$(BOARD).a
+
+OBJS	:= lpc2292sodimm.o flash.o mmc.o spi.o mmc_hw.o eth.o
+SOBJS	:= lowlevel_init.o iap_entry.o
+
+$(LIB):	$(OBJS) $(SOBJS)
+	$(AR) crv $@ $(OBJS) $(SOBJS)
+
+# this MUST be compiled as thumb code!
+iap_entry.o:
+	arm-linux-gcc  -D__ASSEMBLY__ -g  -Os   -fno-strict-aliasing  \
+	-fno-common -ffixed-r8 -msoft-float  -D__KERNEL__ \
+	-DTEXT_BASE=0x81500000  -I/home/garyj/proj/LPC/u-boot/include \
+	-fno-builtin -ffreestanding -nostdinc -isystem \
+	/opt/eldk/arm/usr/bin/../lib/gcc/arm-linux/4.0.0/include -pipe  \
+	-DCONFIG_ARM -D__ARM__ -march=armv4t -mtune=arm7tdmi -mabi=apcs-gnu \
+	-c -o iap_entry.o iap_entry.S
+
+clean:
+	rm -f $(SOBJS) $(OBJS)
+
+distclean:	clean
+	rm -f $(LIB) core *.bak .depend
+
+#########################################################################
+
+.depend:	Makefile $(SOBJS:.o=.S) $(OBJS:.o=.c)
+		$(CC) -M $(CPPFLAGS) $(SOBJS:.o=.S) $(OBJS:.o=.c) > $@
+
+-include .depend
+
+#########################################################################
diff --git a/board/lpc2292sodimm/config.mk b/board/lpc2292sodimm/config.mk
new file mode 100644
index 0000000..4146dac
--- /dev/null
+++ b/board/lpc2292sodimm/config.mk
@@ -0,0 +1,31 @@
+#
+# (C) Copyright 2000
+# Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+# Marius Groeger <mgroeger@sysgo.de>
+#
+# (C) Copyright 2000
+# Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+#
+# See file CREDITS for list of people who contributed to this
+# project.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of
+# the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+# MA 02111-1307 USA
+#
+
+#address where u-boot will be relocated
+#TEXT_BASE = 0x0
+TEXT_BASE = 0x81500000
+
diff --git a/board/lpc2292sodimm/eth.c b/board/lpc2292sodimm/eth.c
new file mode 100644
index 0000000..14bec19
--- /dev/null
+++ b/board/lpc2292sodimm/eth.c
@@ -0,0 +1,884 @@
+/*
+ * 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
+ */
+
+#include <config.h>
+#include <common.h>
+#include <net.h>
+#include <asm/arch/hardware.h>
+#include "spi.h"
+
+/*
+ * Control Registers in Bank 0
+ */
+
+#define CTL_REG_ERDPTL   0x00
+#define CTL_REG_ERDPTH   0x01
+#define CTL_REG_EWRPTL   0x02
+#define CTL_REG_EWRPTH   0x03
+#define CTL_REG_ETXSTL   0x04
+#define CTL_REG_ETXSTH   0x05
+#define CTL_REG_ETXNDL   0x06
+#define CTL_REG_ETXNDH   0x07
+#define CTL_REG_ERXSTL   0x08
+#define CTL_REG_ERXSTH   0x09
+#define CTL_REG_ERXNDL   0x0A
+#define CTL_REG_ERXNDA   0x0B
+#define CTL_REG_ERXRDPTL 0x0C
+#define CTL_REG_ERXRDPTH 0x0D
+#define CTL_REG_ERXWRPTL 0x0E
+#define CTL_REG_ERXWRPTH 0x0F
+#define CTL_REG_EDMASTL  0x10
+#define CTL_REG_EDMASTH  0x11
+#define CTL_REG_EDMANDL  0x12
+#define CTL_REG_EDMANDH  0x13
+#define CTL_REG_EDMADSTL 0x14
+#define CTL_REG_EDMADSTH 0x15
+#define CTL_REG_EDMACSL  0x16
+#define CTL_REG_EDMACSH  0x17
+/* these are common in all banks */
+#define CTL_REG_EIE      0x1B
+#define CTL_REG_EIR      0x1C
+#define CTL_REG_ESTAT    0x1D
+#define CTL_REG_ECON2    0x1E
+#define CTL_REG_ECON1    0x1F
+
+/*
+ * Control Registers in Bank 1
+ */
+
+#define CTL_REG_EHT0    0x00
+#define CTL_REG_EHT1    0x01
+#define CTL_REG_EHT2    0x02
+#define CTL_REG_EHT3    0x03
+#define CTL_REG_EHT4    0x04
+#define CTL_REG_EHT5    0x05
+#define CTL_REG_EHT6    0x06
+#define CTL_REG_EHT7    0x07
+#define CTL_REG_EPMM0   0x08
+#define CTL_REG_EPMM1   0x09
+#define CTL_REG_EPMM2   0x0A
+#define CTL_REG_EPMM3   0x0B
+#define CTL_REG_EPMM4   0x0C
+#define CTL_REG_EPMM5   0x0D
+#define CTL_REG_EPMM6   0x0E
+#define CTL_REG_EPMM7   0x0F
+#define CTL_REG_EPMCSL  0x10
+#define CTL_REG_EPMCSH  0x11
+#define CTL_REG_EPMOL   0x14
+#define CTL_REG_EPMOH   0x15
+#define CTL_REG_EWOLIE  0x16
+#define CTL_REG_EWOLIR  0x17
+#define CTL_REG_ERXFCON 0x18
+#define CTL_REG_EPKTCNT 0x19
+
+/*
+ * Control Registers in Bank 2
+ */
+
+#define CTL_REG_MACON1   0x00
+#define CTL_REG_MACON2   0x01
+#define CTL_REG_MACON3   0x02
+#define CTL_REG_MACON4   0x03
+#define CTL_REG_MABBIPG  0x04
+#define CTL_REG_MAIPGL   0x06
+#define CTL_REG_MAIPGH   0x07
+#define CTL_REG_MACLCON1 0x08
+#define CTL_REG_MACLCON2 0x09
+#define CTL_REG_MAMXFLL  0x0A
+#define CTL_REG_MAMXFLH  0x0B
+#define CTL_REG_MAPHSUP  0x0D
+#define CTL_REG_MICON    0x11
+#define CTL_REG_MICMD    0x12
+#define CTL_REG_MIREGADR 0x14
+#define CTL_REG_MIWRL    0x16
+#define CTL_REG_MIWRH    0x17
+#define CTL_REG_MIRDL    0x18
+#define CTL_REG_MIRDH    0x19
+
+/*
+ * Control Registers in Bank 3
+ */
+
+#define CTL_REG_MAADR1  0x00
+#define CTL_REG_MAADR0  0x01
+#define CTL_REG_MAADR3  0x02
+#define CTL_REG_MAADR2  0x03
+#define CTL_REG_MAADR5  0x04
+#define CTL_REG_MAADR4  0x05
+#define CTL_REG_EBSTSD  0x06
+#define CTL_REG_EBSTCON 0x07
+#define CTL_REG_EBSTCSL 0x08
+#define CTL_REG_EBSTCSH 0x09
+#define CTL_REG_MISTAT  0x0A
+#define CTL_REG_EREVID  0x12
+#define CTL_REG_ECOCON  0x15
+#define CTL_REG_EFLOCON 0x17
+#define CTL_REG_EPAUSL  0x18
+#define CTL_REG_EPAUSH  0x19
+
+
+/*
+ * PHY Register
+ */
+
+#define PHY_REG_PHID1 0x02
+#define PHY_REG_PHID2 0x03
+
+
+/*
+ * Receive Filter Register (ERXFCON) bits
+ */
+
+#define ENC_RFR_UCEN  0x80
+#define ENC_RFR_ANDOR 0x40
+#define ENC_RFR_CRCEN 0x20
+#define ENC_RFR_PMEN  0x10
+#define ENC_RFR_MPEN  0x08
+#define ENC_RFR_HTEN  0x04
+#define ENC_RFR_MCEN  0x02
+#define ENC_RFR_BCEN  0x01
+
+/*
+ * ECON1 Register Bits
+ */
+
+#define ENC_ECON1_TXRST  0x80
+#define ENC_ECON1_RXRST  0x40
+#define ENC_ECON1_DMAST  0x20
+#define ENC_ECON1_CSUMEN 0x10
+#define ENC_ECON1_TXRTS  0x08
+#define ENC_ECON1_RXEN   0x04
+#define ENC_ECON1_BSEL1  0x02
+#define ENC_ECON1_BSEL0  0x01
+
+/*
+ * ECON2 Register Bits
+ */
+#define ENC_ECON2_AUTOINC 0x80
+#define ENC_ECON2_PKTDEC  0x40
+#define ENC_ECON2_PWRSV   0x20
+#define ENC_ECON2_VRPS    0x08
+
+/*
+ * EIR Register Bits
+ */
+#define ENC_EIR_PKTIF  0x40
+#define ENC_EIR_DMAIF  0x20
+#define ENC_EIR_LINKIF 0x10
+#define ENC_EIR_TXIF   0x08
+#define ENC_EIR_WOLIF  0x04
+#define ENC_EIR_TXERIF 0x02
+#define ENC_EIR_RXERIF 0x01
+
+/*
+ * ESTAT Register Bits
+ */
+
+#define ENC_ESTAT_INT     0x80
+#define ENC_ESTAT_LATECOL 0x10
+#define ENC_ESTAT_RXBUSY  0x04
+#define ENC_ESTAT_TXABRT  0x02
+#define ENC_ESTAT_CLKRDY  0x01
+
+/*
+ * EIE Register Bits
+ */
+
+#define ENC_EIE_INTIE  0x80
+#define ENC_EIE_PKTIE  0x40
+#define ENC_EIE_DMAIE  0x20
+#define ENC_EIE_LINKIE 0x10
+#define ENC_EIE_TXIE   0x08
+#define ENC_EIE_WOLIE  0x04
+#define ENC_EIE_TXERIE 0x02
+#define ENC_EIE_RXERIE 0x01
+
+/*
+ * MACON1 Register Bits
+ */
+#define ENC_MACON1_LOOPBK  0x10
+#define ENC_MACON1_TXPAUS  0x08
+#define ENC_MACON1_RXPAUS  0x04
+#define ENC_MACON1_PASSALL 0x02
+#define ENC_MACON1_MARXEN  0x01
+
+
+/*
+ * MACON2 Register Bits
+ */
+#define ENC_MACON2_MARST   0x80
+#define ENC_MACON2_RNDRST  0x40
+#define ENC_MACON2_MARXRST 0x08
+#define ENC_MACON2_RFUNRST 0x04
+#define ENC_MACON2_MATXRST 0x02
+#define ENC_MACON2_TFUNRST 0x01
+
+/*
+ * MACON3 Register Bits
+ */
+#define ENC_MACON3_PADCFG2 0x80
+#define ENC_MACON3_PADCFG1 0x40
+#define ENC_MACON3_PADCFG0 0x20
+#define ENC_MACON3_TXCRCEN 0x10
+#define ENC_MACON3_PHDRLEN 0x08
+#define ENC_MACON3_HFRMEN  0x04
+#define ENC_MACON3_FRMLNEN 0x02
+#define ENC_MACON3_FULDPX  0x01
+
+/*
+ * MICMD Register Bits
+ */
+#define ENC_MICMD_MIISCAN 0x02
+#define ENC_MICMD_MIIRD   0x01
+
+/*
+ * MISTAT Register Bits
+ */
+#define ENC_MISTAT_NVALID 0x04
+#define ENC_MISTAT_SCAN   0x02
+#define ENC_MISTAT_BUSY   0x01
+
+/*
+ * PHID1 and PHID2 values
+ */
+#define ENC_PHID1_VALUE 0x0083
+#define ENC_PHID2_VALUE 0x1400
+#define ENC_PHID2_MASK  0xFC00
+
+
+#define ENC_SPI_SLAVE_CS 0x00010000  /* pin P1.16 */
+#define ENC_RESET        0x00020000  /* pin P1.17 */
+
+#define FAILSAFE_VALUE 5000
+
+/*
+ * Controller memory layout:
+ *
+ * 0x0000 - 0x17ff  6k bytes receive buffer
+ * 0x1800 - 0x1fff  2k bytes transmit buffer
+ */
+/* Use the lower memory for receiver buffer. See errata pt. 5 */
+#define ENC_RX_BUF_START 0x0000
+#define ENC_TX_BUF_START 0x1800
+
+/* maximum frame length */
+#define ENC_MAX_FRM_LEN 1518
+
+#define enc_enable() PUT32(IO1CLR, ENC_SPI_SLAVE_CS)
+#define enc_disable() PUT32(IO1SET, ENC_SPI_SLAVE_CS)
+#define enc_cfg_spi() spi_set_cfg(0, 0, 0); spi_set_clock(8); 
+
+
+static unsigned char encReadReg(unsigned char regNo);
+static void encWriteReg(unsigned char regNo, unsigned char data);
+static void encWriteRegRetry(unsigned char regNo, unsigned char data, int c);
+static void encReadBuff(unsigned short length, unsigned char *pBuff);
+static void encWriteBuff(unsigned short length, unsigned char *pBuff);
+static void encBitSet(unsigned char regNo, unsigned char data);
+static void encBitClr(unsigned char regNo, unsigned char data);
+static void encReset(void);
+static void encInit(unsigned char *pEthAddr);
+static unsigned short phyRead(unsigned char addr);
+static void encPoll(void);
+static void encRx(void);
+
+#define m_nic_read(reg) encReadReg(reg)
+#define m_nic_write(reg, data) encWriteReg(reg, data)
+#define m_nic_write_retry(reg, data, count) encWriteRegRetry(reg, data, count)
+#define m_nic_read_data(len, buf) encReadBuff((len), (buf))
+#define m_nic_write_data(len, buf) encWriteBuff((len), (buf))
+
+/* bit field set */
+#define m_nic_bfs(reg, data) encBitSet(reg, data)
+
+/* bit field clear */
+#define m_nic_bfc(reg, data) encBitClr(reg, data)
+
+static unsigned char bank = 0;  /* current bank in enc28j60 */
+static unsigned char next_pointer_lsb;
+static unsigned char next_pointer_msb;
+
+static unsigned char buffer[ENC_MAX_FRM_LEN];
+static int rxResetCounter = 0;
+#define RX_RESET_COUNTER 1000;
+
+/*-----------------------------------------------------------------------------
+ * Returns 0 when failes otherwize 1
+ */
+int eth_init(bd_t *bis)
+{
+	/* configure GPIO */
+	(*((volatile unsigned long *) IO1DIR)) |= ENC_SPI_SLAVE_CS;
+	(*((volatile unsigned long *) IO1DIR)) |= ENC_RESET;
+
+	/* CS and RESET active low */
+	PUT32(IO1SET, ENC_SPI_SLAVE_CS);
+	PUT32(IO1SET, ENC_RESET);
+
+	spi_init();
+
+	/* initialize controller */
+	encReset();
+	encInit(bis->bi_enetaddr);
+
+	m_nic_bfs(CTL_REG_ECON1, ENC_ECON1_RXEN); /* enable receive */  
+
+	return 0;
+}
+
+int eth_send(volatile void *packet, int length)
+{
+	/* check frame length, etc. */
+	/* TODO: */
+
+	/* switch to bank 0 */
+	m_nic_bfc(CTL_REG_ECON1, (ENC_ECON1_BSEL1 | ENC_ECON1_BSEL0));  
+
+	/* set EWRPT */
+	m_nic_write(CTL_REG_EWRPTL, (ENC_TX_BUF_START & 0xff));
+	m_nic_write(CTL_REG_EWRPTH, (ENC_TX_BUF_START >> 8));
+
+	/* set ETXST */
+	m_nic_write(CTL_REG_ETXSTL, ENC_TX_BUF_START & 0xFF);
+	m_nic_write(CTL_REG_ETXSTH, ENC_TX_BUF_START >> 8);
+
+	/* write packet */
+	m_nic_write_data(length, (unsigned char*)packet);
+
+	/* set ETXND */
+	m_nic_write(CTL_REG_ETXNDL, (length + ENC_TX_BUF_START) & 0xFF);
+	m_nic_write(CTL_REG_ETXNDH, (length + ENC_TX_BUF_START) >> 8);
+
+	/* set ECON1.TXRTS */
+	m_nic_bfs(CTL_REG_ECON1, ENC_ECON1_TXRTS);
+
+	return 0;
+}
+
+
+/*****************************************************************************
+ * This function resets the receiver only. This function may be called from
+ * interrupt-context.
+ */
+static void encReceiverReset(void)
+{
+	unsigned char econ1;
+
+	econ1 = m_nic_read(CTL_REG_ECON1);
+	if((econ1 & ENC_ECON1_RXRST) == 0) {
+		m_nic_bfs(CTL_REG_ECON1, ENC_ECON1_RXRST);
+		rxResetCounter = RX_RESET_COUNTER;
+	}
+}
+
+/*****************************************************************************
+ * receiver reset timer
+ */
+static void encReceiverResetCallback(void)
+{
+	m_nic_bfc(CTL_REG_ECON1, ENC_ECON1_RXRST);
+	m_nic_bfs(CTL_REG_ECON1, ENC_ECON1_RXEN); /* enable receive */
+}
+
+/*-----------------------------------------------------------------------------
+ * Check for received packets. Call NetReceive for each packet. The return
+ * value is ignored by the caller.
+ */
+int eth_rx(void)
+{
+	if(rxResetCounter > 0 && --rxResetCounter == 0)
+	{
+		encReceiverResetCallback();
+	}
+
+	encPoll();
+
+	return 0;
+}
+
+void eth_halt(void)
+{
+	m_nic_bfc(CTL_REG_ECON1, ENC_ECON1_RXEN); /* disable receive */
+}
+
+/*****************************************************************************/
+
+static void encPoll(void) 
+{
+	unsigned char eir_reg;
+	volatile unsigned char estat_reg;
+	unsigned char pkt_cnt;
+
+	/* clear global interrupt enable bit in enc28j60 */
+	m_nic_bfc(CTL_REG_EIE, ENC_EIE_INTIE);
+	estat_reg = m_nic_read(CTL_REG_ESTAT);
+
+	eir_reg = m_nic_read(CTL_REG_EIR);
+
+	if (eir_reg & ENC_EIR_TXIF){
+		/* clear TXIF bit in EIR */
+		m_nic_bfc(CTL_REG_EIR, ENC_EIR_TXIF);
+	}
+
+	/* We have to use pktcnt and not pktif bit, see errata pt. 6 */
+
+	/* move to bank 1 */
+	m_nic_bfc(CTL_REG_ECON1, ENC_ECON1_BSEL1);
+	m_nic_bfs(CTL_REG_ECON1, ENC_ECON1_BSEL0);
+
+	/* read pktcnt */
+	pkt_cnt = m_nic_read(CTL_REG_EPKTCNT);
+
+	if (pkt_cnt > 0) {
+		if ((eir_reg & ENC_EIR_PKTIF) == 0) {
+		  /*printf("encPoll: pkt cnt > 0, but pktif not set\n"); */
+		}
+		encRx();
+		/* clear PKTIF bit in EIR, this should not need to be done but it
+		   seems like we get problems if we do not */
+		m_nic_bfc(CTL_REG_EIR, ENC_EIR_PKTIF);
+	}
+
+	if (eir_reg & ENC_EIR_RXERIF) {
+		printf("encPoll: rx error\n");
+		m_nic_bfc(CTL_REG_EIR, ENC_EIR_RXERIF);
+	}
+	if (eir_reg & ENC_EIR_TXERIF) {
+		printf("encPoll: tx error\n");
+		m_nic_bfc(CTL_REG_EIR, ENC_EIR_TXERIF);
+	}
+
+	/* set global interrupt enable bit in enc28j60 */
+	m_nic_bfs(CTL_REG_EIE, ENC_EIE_INTIE);
+}
+
+static void encRx(void)
+{
+	unsigned short pkt_len;
+	unsigned short copy_len;
+	unsigned short status;
+	unsigned char eir_reg;
+	unsigned char pkt_cnt = 0;
+
+	/* switch to bank 0 */
+	m_nic_bfc(CTL_REG_ECON1, (ENC_ECON1_BSEL1 | ENC_ECON1_BSEL0));
+
+	m_nic_write(CTL_REG_ERDPTL, next_pointer_lsb);
+	m_nic_write(CTL_REG_ERDPTH, next_pointer_msb);
+
+	do {
+		m_nic_read_data(6, buffer);
+		next_pointer_lsb = buffer[0];
+		next_pointer_msb = buffer[1];
+		pkt_len = buffer[2];
+		pkt_len |= (unsigned short)buffer[3] << 8;
+		status = buffer[4];
+		status |= (unsigned short)buffer[5] << 8;
+
+		if (pkt_len <= ENC_MAX_FRM_LEN) {
+			copy_len = pkt_len;
+		} else {
+			copy_len = 0;
+			/*      p_priv->stats.rx_dropped++; */
+			/* we will drop this packet */
+		}
+
+		if ((status & (1L << 7)) == 0) { /* check Received Ok bit */
+			copy_len = 0;
+			/*      p_priv->stats.rx_errors++; */
+		}
+
+		if (copy_len > 0) { 
+			m_nic_read_data(copy_len, buffer);
+		}    
+
+		/* advance read pointer to next pointer */
+		m_nic_write(CTL_REG_ERDPTL, next_pointer_lsb);
+		m_nic_write(CTL_REG_ERDPTH, next_pointer_msb);
+
+		/* decrease packet counter */
+		m_nic_bfs(CTL_REG_ECON2, ENC_ECON2_PKTDEC);
+
+		/* move to bank 1 */
+		m_nic_bfc(CTL_REG_ECON1, ENC_ECON1_BSEL1);
+		m_nic_bfs(CTL_REG_ECON1, ENC_ECON1_BSEL0);
+
+		/* read pktcnt */
+		pkt_cnt = m_nic_read(CTL_REG_EPKTCNT);
+
+		/* switch to bank 0 */
+		m_nic_bfc(CTL_REG_ECON1, (ENC_ECON1_BSEL1 | ENC_ECON1_BSEL0));    
+
+		if (copy_len == 0) {
+			eir_reg = m_nic_read(CTL_REG_EIR);
+			encReceiverReset();
+			printf("eth_rx: copy_len=0\n");
+			continue;
+		}
+
+		NetReceive((unsigned char *)buffer, pkt_len);
+
+		eir_reg = m_nic_read(CTL_REG_EIR);
+	} while (pkt_cnt); /* Use EPKTCNT not EIR.PKTIF flag, see errata pt. 6 */
+	m_nic_write(CTL_REG_ERXRDPTL, next_pointer_lsb);
+	m_nic_write(CTL_REG_ERXRDPTH, next_pointer_msb);
+}
+
+static void encWriteReg(unsigned char regNo, unsigned char data)
+{
+	spi_lock();
+	enc_cfg_spi();
+	enc_enable();
+
+	spi_write(0x40 | regNo);  /* write in regNo */
+	spi_write(data);
+
+	enc_disable();
+	enc_enable();
+
+	spi_write(0x1f);      /* write reg 0x1f */
+
+	enc_disable();
+	spi_unlock();
+}
+
+static void encWriteRegRetry(unsigned char regNo, unsigned char data, int c)
+{
+	unsigned char readback;
+	int i;
+
+	spi_lock();
+
+	for (i = 0; i < c; i++) {
+		enc_cfg_spi();
+		enc_enable();
+
+		spi_write(0x40 | regNo);    /* write in regNo */
+		spi_write(data);
+
+		enc_disable();
+		enc_enable();
+
+		spi_write(0x1f);        /* write reg 0x1f */
+
+		enc_disable();
+
+		spi_unlock();       /* we must unlock spi first */
+
+		readback = encReadReg(regNo);
+
+		spi_lock();
+
+		if (readback == data)
+			break;
+	}
+	spi_unlock();
+
+	if (i == c) {
+		printf("enc28j60: write reg %d failed\n", regNo);
+	}
+}
+
+static unsigned char encReadReg(unsigned char regNo)
+{
+	unsigned char rxByte;
+
+	spi_lock();
+	enc_cfg_spi();
+	enc_enable();
+
+	spi_write(0x1f);      /* read reg 0x1f */
+
+	bank = spi_read() & 0x3;
+
+	enc_disable();
+	enc_enable();
+
+	spi_write(regNo);
+	rxByte = spi_read();
+
+	/* check if MAC or MII register */
+	if (((bank == 2) && (regNo <= 0x1a)) ||
+			((bank == 3) && (regNo <= 0x05 || regNo == 0x0a))) {
+		/* ignore first byte and read another byte */
+		rxByte = spi_read();
+	}
+
+	enc_disable();
+	spi_unlock();
+
+	return rxByte;
+}
+
+static void encReadBuff(unsigned short length, unsigned char *pBuff)
+{
+	spi_lock();
+	enc_cfg_spi();
+	enc_enable();
+
+	spi_write(0x20 | 0x1a);         /* read buffer memory */
+
+	while(length--) {
+		if(pBuff != NULL)
+			*pBuff++ = spi_read();
+		else
+			spi_write(0);
+	}
+
+	enc_disable();
+	spi_unlock();
+}
+
+static void encWriteBuff(unsigned short length, 
+             unsigned char *pBuff)
+{
+	spi_lock();
+	enc_cfg_spi();
+	enc_enable();
+
+	spi_write(0x60 | 0x1a);         /* write buffer memory */
+
+	spi_write(0x00);      /* control byte */
+
+	while(length--)
+		spi_write(*pBuff++);
+
+	enc_disable();
+	spi_unlock();
+}
+
+static void encBitSet(unsigned char regNo, unsigned char data)
+{
+	spi_lock();
+	enc_cfg_spi();
+	enc_enable();
+
+	spi_write(0x80 | regNo);  /* bit field set */
+	spi_write(data);
+
+	enc_disable();
+	spi_unlock();
+}
+
+static void encBitClr(unsigned char regNo, unsigned char data)
+{
+	spi_lock();
+	enc_cfg_spi();
+	enc_enable();
+
+	spi_write(0xA0 | regNo);  /* bit field clear */
+	spi_write(data);
+
+	enc_disable();
+	spi_unlock();
+}
+
+static void encReset(void)
+{
+	spi_lock();
+	enc_cfg_spi();
+	enc_enable();
+
+	spi_write(0xff);      /* soft reset */
+
+	enc_disable();
+	spi_unlock();
+
+	/* sleep 1 ms. See errata pt. 2 */
+	udelay(1000);
+
+#if 0
+	(*((volatile unsigned long*)IO1CLR)) &= ENC_RESET;
+	mdelay(5);
+	(*((volatile unsigned long*)IO1SET)) &= ENC_RESET;
+#endif
+}
+
+static void encInit(unsigned char *pEthAddr)
+{
+	unsigned short phid1 = 0;
+	unsigned short phid2 = 0;
+
+	/* switch to bank 0 */
+	m_nic_bfc(CTL_REG_ECON1, (ENC_ECON1_BSEL1 | ENC_ECON1_BSEL0));
+
+	/* 
+	* Setup the buffer space. The reset values are valid for the
+	* other pointers. 
+	*/  
+#if 0
+	/* We shall not write to ERXST, see errata pt. 5. Instead we 
+	 have to make sure that ENC_RX_BUS_START is 0. */
+	m_nic_write_retry(CTL_REG_ERXSTL, (ENC_RX_BUF_START & 0xFF), 1);
+	m_nic_write_retry(CTL_REG_ERXSTH, (ENC_RX_BUF_START >> 8), 1);
+#endif
+	m_nic_write_retry(CTL_REG_ERDPTL, (ENC_RX_BUF_START & 0xFF), 1);
+	m_nic_write_retry(CTL_REG_ERDPTH, (ENC_RX_BUF_START >> 8), 1);
+
+	next_pointer_lsb = (ENC_RX_BUF_START & 0xFF);
+	next_pointer_msb = (ENC_RX_BUF_START >> 8);
+
+	/*
+	* For tracking purposes, the ERXRDPT registers should be programmed with 
+	* the same value. This is the read pointer.
+	*/
+	m_nic_write(CTL_REG_ERXRDPTL, (ENC_RX_BUF_START & 0xFF));
+	m_nic_write_retry(CTL_REG_ERXRDPTH, (ENC_RX_BUF_START >> 8), 1);
+
+	/* Setup receive filters. */
+
+	/* move to bank 1 */
+	m_nic_bfc(CTL_REG_ECON1, ENC_ECON1_BSEL1);
+	m_nic_bfs(CTL_REG_ECON1, ENC_ECON1_BSEL0);
+
+	/* OR-filtering, Unicast, CRC-check and broadcast */
+	m_nic_write_retry(CTL_REG_ERXFCON, 
+			(ENC_RFR_UCEN|ENC_RFR_CRCEN|ENC_RFR_BCEN), 1);
+
+	/* Wait for Oscillator Start-up Timer (OST). */
+	while((m_nic_read(CTL_REG_ESTAT) & ENC_ESTAT_CLKRDY) == 0) {
+		static int cnt = 0;
+		if(cnt++ >= 1000){
+		  cnt = 0;
+		}
+	}
+
+	/* verify identification */
+	phid1 = phyRead(PHY_REG_PHID1);
+	phid2 = phyRead(PHY_REG_PHID2);
+
+	if(phid1 != ENC_PHID1_VALUE 
+	 || (phid2 & ENC_PHID2_MASK) != ENC_PHID2_VALUE) {
+		printf("ERROR: failed to identify controller\n");
+		printf("phid1 = %x, phid2 = %x\n", 
+			phid1, (phid2&ENC_PHID2_MASK));
+		printf("should be phid1 = %x, phid2 = %x\n", 
+			ENC_PHID1_VALUE, ENC_PHID2_VALUE);
+	}
+
+	/*
+	* --- MAC Initialization ---
+	*/
+
+	/* Pull MAC out of Reset */
+
+	/* switch to bank 2 */
+	m_nic_bfc(CTL_REG_ECON1, ENC_ECON1_BSEL0);
+	m_nic_bfs(CTL_REG_ECON1, ENC_ECON1_BSEL1);
+	/* clear MAC reset bits */
+	m_nic_write_retry(CTL_REG_MACON2, 0, 1);
+
+	/* enable MAC to receive frames */
+	m_nic_write_retry(CTL_REG_MACON1, ENC_MACON1_MARXEN, 10);
+
+	/* configure pad, tx-crc and duplex */ 
+	/* TODO maybe enable FRMLNEN */
+	m_nic_write_retry(CTL_REG_MACON3, (ENC_MACON3_PADCFG0|ENC_MACON3_TXCRCEN), 
+			10);
+
+	/* set maximum frame length */
+	m_nic_write_retry(CTL_REG_MAMXFLL, (ENC_MAX_FRM_LEN & 0xff), 10);
+	m_nic_write_retry(CTL_REG_MAMXFLH, (ENC_MAX_FRM_LEN >> 8), 10);
+
+	/* 
+	* Set MAC back-to-back inter-packet gap. Recommended 0x12 for half duplex
+	* and 0x15 for full duplex.
+	*/
+	m_nic_write_retry(CTL_REG_MABBIPG, 0x12, 10);
+
+	/* Set (low byte) Non-Back-to_Back Inter-Packet Gap. Recommended 0x12 */
+	m_nic_write_retry(CTL_REG_MAIPGL, 0x12, 10);
+
+	/* 
+	* Set (high byte) Non-Back-to_Back Inter-Packet Gap. Recommended 
+	* 0x0c for half-duplex. Nothing for full-duplex
+	*/
+	m_nic_write_retry(CTL_REG_MAIPGH, 0x0C, 10);
+
+	/* set MAC address */
+
+	/* switch to bank 3 */
+	m_nic_bfs(CTL_REG_ECON1, (ENC_ECON1_BSEL0|ENC_ECON1_BSEL1));
+
+	m_nic_write_retry(CTL_REG_MAADR0, pEthAddr[5], 1);
+	m_nic_write_retry(CTL_REG_MAADR1, pEthAddr[4], 1);
+	m_nic_write_retry(CTL_REG_MAADR2, pEthAddr[3], 1);
+	m_nic_write_retry(CTL_REG_MAADR3, pEthAddr[2], 1);
+	m_nic_write_retry(CTL_REG_MAADR4, pEthAddr[1], 1);
+	m_nic_write_retry(CTL_REG_MAADR5, pEthAddr[0], 1);
+
+	/*
+	* Receive settings
+	*/
+
+	/* auto-increment RX-pointer when reading a received packet */
+	m_nic_bfs(CTL_REG_ECON2, ENC_ECON2_AUTOINC);
+
+	/* enable interrupts */
+	m_nic_bfs(CTL_REG_EIE, ENC_EIE_PKTIE);
+	m_nic_bfs(CTL_REG_EIE, ENC_EIE_TXIE);
+	m_nic_bfs(CTL_REG_EIE, ENC_EIE_RXERIE);
+	m_nic_bfs(CTL_REG_EIE, ENC_EIE_TXERIE);
+	m_nic_bfs(CTL_REG_EIE, ENC_EIE_INTIE);
+}
+
+/*****************************************************************************
+ *
+ * Description:
+ *    Read PHY registers.
+ *
+ *    NOTE! This function will change to Bank 2.
+ *
+ * Params:
+ *    [in] addr address of the register to read
+ *
+ * Returns:
+ *    The value in the register
+ */
+static unsigned short phyRead(unsigned char addr)
+{
+	unsigned short ret = 0;
+
+	/* move to bank 2 */
+	m_nic_bfc(CTL_REG_ECON1, ENC_ECON1_BSEL0);
+	m_nic_bfs(CTL_REG_ECON1, ENC_ECON1_BSEL1);
+
+	/* write address to MIREGADR */
+	m_nic_write(CTL_REG_MIREGADR, addr);
+
+	/* set MICMD.MIIRD */
+	m_nic_write(CTL_REG_MICMD, ENC_MICMD_MIIRD);
+
+	/* poll MISTAT.BUSY bit until operation is complete */
+	while((m_nic_read(CTL_REG_MISTAT) & ENC_MISTAT_BUSY) != 0) {
+		static int cnt = 0;
+
+		if(cnt++ >= 1000) {
+			/* GJ - this seems extremely dangerous! */
+			/* printf("#"); */
+			cnt = 0;
+		}
+	}
+
+	/* clear MICMD.MIIRD */
+	m_nic_write(CTL_REG_MICMD, 0);
+
+	ret  = (m_nic_read(CTL_REG_MIRDH) << 8);
+	ret |= (m_nic_read(CTL_REG_MIRDL) & 0xFF);
+
+	return ret;
+}
diff --git a/board/lpc2292sodimm/flash.c b/board/lpc2292sodimm/flash.c
new file mode 100644
index 0000000..dcef7dd
--- /dev/null
+++ b/board/lpc2292sodimm/flash.c
@@ -0,0 +1,477 @@
+/*
+ * (C) Copyright 2006 Embedded Artists AB <www.embeddedartists.com>
+ *
+ * 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
+ */
+
+#include <common.h>
+#include <asm/arch/hardware.h>
+
+/* IAP commands use 32 bytes at the top of CPU internal sram, we
+   use 512 bytes below that */
+#define COPY_BUFFER_LOCATION 0x40003de0
+
+#define IAP_LOCATION 0x7ffffff1
+#define IAP_CMD_PREPARE 50
+#define IAP_CMD_COPY 51
+#define IAP_CMD_ERASE 52
+#define IAP_CMD_CHECK 53
+#define IAP_CMD_ID 54
+#define IAP_CMD_VERSION 55
+#define IAP_CMD_COMPARE 56
+
+#define IAP_RET_CMD_SUCCESS 0
+
+#define SST_BASEADDR 0x80000000
+#define SST_ADDR1 ((volatile ushort*)(SST_BASEADDR + (0x5555 << 1)))
+#define SST_ADDR2 ((volatile ushort*)(SST_BASEADDR + (0x2AAA << 1)))
+
+
+static unsigned long command[5];
+static unsigned long result[2];
+
+flash_info_t flash_info[CFG_MAX_FLASH_BANKS];
+
+extern void iap_entry(unsigned long * command, unsigned long * result);
+
+/*-----------------------------------------------------------------------
+ *
+ */
+int get_flash_sector(flash_info_t * info, ulong flash_addr)
+{
+	int i;
+
+	for(i=1; i < (info->sector_count); i++) {
+		if (flash_addr < (info->start[i]))
+			break;
+	}
+
+	return (i-1);
+}
+
+/*-----------------------------------------------------------------------
+ * This function assumes that flash_addr is aligned on 512 bytes boundary
+ * in flash. This function also assumes that prepare have been called 
+ * for the sector in question.
+ */
+int copy_buffer_to_flash(flash_info_t * info, ulong flash_addr)
+{
+	int first_sector;
+	int last_sector;
+
+	first_sector = get_flash_sector(info, flash_addr);
+	last_sector = get_flash_sector(info, flash_addr + 512 - 1);
+
+	/* prepare sectors for write */
+	command[0] = IAP_CMD_PREPARE;
+	command[1] = first_sector;
+	command[2] = last_sector;
+	iap_entry(command, result);
+	if (result[0] != IAP_RET_CMD_SUCCESS) {
+		printf("IAP prepare failed\n");
+		return ERR_PROG_ERROR;
+	}
+
+	command[0] = IAP_CMD_COPY;
+	command[1] = flash_addr;
+	command[2] = COPY_BUFFER_LOCATION;
+	command[3] = 512;
+	command[4] = CFG_SYS_CLK_FREQ >> 10;
+	iap_entry(command, result);
+	if (result[0] != IAP_RET_CMD_SUCCESS) {
+		printf("IAP copy failed\n");
+		return 1;
+	}
+
+	return 0;
+}
+
+/*-----------------------------------------------------------------------
+ *
+ */
+void write_word_sst(ulong addr, ushort data)
+{
+	ushort tmp;
+
+	*SST_ADDR1 = 0x00AA;
+	*SST_ADDR2 = 0x0055;
+	*SST_ADDR1 = 0x00A0;
+	*((volatile ushort*)addr) = data;
+	/* do data polling */
+	do {
+		tmp = *((volatile ushort*)addr);
+	} while (tmp != data);
+}
+
+/*-----------------------------------------------------------------------
+ */
+
+ulong flash_init (void)
+{
+	int j, k;
+	ulong size = 0;
+	ulong flashbase = 0;
+
+	flash_info[0].flash_id = (PHILIPS_LPC2292 & FLASH_VENDMASK);
+	flash_info[0].size = 0x003E000;	/* 256 - 8 KB */
+	flash_info[0].sector_count = 17;
+	memset (flash_info[0].protect, 0, 17);
+	flashbase = 0x00000000;
+	for (j = 0, k = 0; j < 8; j++, k++) {
+		flash_info[0].start[k] = flashbase;
+		flashbase += 0x00002000;
+	}
+	for (j = 0; j < 2; j++, k++) {
+		flash_info[0].start[k] = flashbase;
+		flashbase += 0x00010000;
+	}
+	for (j = 0; j < 7; j++, k++) {
+		flash_info[0].start[k] = flashbase;
+		flashbase += 0x00002000;
+	}		
+	size += flash_info[0].size;
+
+	flash_info[1].flash_id = (SST_MANUFACT & FLASH_VENDMASK);
+	flash_info[1].size = 0x00200000; /* 2 MB */
+	flash_info[1].sector_count = 512;
+	memset (flash_info[1].protect, 0, 512);
+	flashbase = SST_BASEADDR;
+	for (j=0; j<512; j++) {
+		flash_info[1].start[j] = flashbase;
+		flashbase += 0x1000;	/* 4 KB sectors */
+	}
+	size += flash_info[1].size;
+
+	/* Protect monitor and environment sectors */
+	flash_protect (FLAG_PROTECT_SET,
+		 0x0,
+		 0x0 + monitor_flash_len - 1,
+		 &flash_info[0]);
+
+	flash_protect (FLAG_PROTECT_SET,
+		 CFG_ENV_ADDR,
+		 CFG_ENV_ADDR + CFG_ENV_SIZE - 1, 
+		 &flash_info[0]);
+
+	return size;
+}
+
+/*-----------------------------------------------------------------------
+ */
+void flash_print_info (flash_info_t * info)
+{
+	int i;
+	int erased = 0;
+	unsigned long j;
+	unsigned long count;
+	unsigned char *p;
+
+	switch (info->flash_id & FLASH_VENDMASK) {
+	case (SST_MANUFACT & FLASH_VENDMASK):
+		printf("SST: ");
+		break;
+	case (PHILIPS_LPC2292 & FLASH_VENDMASK):
+		printf("Philips: ");
+		break;
+	default:
+		printf ("Unknown Vendor ");
+		break;
+	}
+
+	printf ("  Size: %ld KB in %d Sectors\n",
+	  info->size >> 10, info->sector_count);
+
+	printf ("  Sector Start Addresses:");
+	for (i = 0; i < info->sector_count; i++) {
+		if ((i % 5) == 0) {
+			printf ("\n   ");
+		}
+		if (i < (info->sector_count - 1)) {
+			count = info->start[i+1] - info->start[i];
+		}
+		else {
+			count = info->start[0] + info->size - info->start[i];
+		}
+		p = (unsigned char*)(info->start[i]);
+		erased = 1;
+		for (j = 0; j < count; j++) {
+			if (*p != 0xFF) {
+				erased = 0;
+				break;
+			}
+			p++;
+		}
+		printf (" %08lX%s%s", info->start[i], info->protect[i] ? " RO" : "   ",
+			erased ? " E" : "  ");
+	}
+	printf ("\n");
+}
+
+/*-----------------------------------------------------------------------
+ */
+
+int flash_erase_philips (flash_info_t * info, int s_first, int s_last)
+{
+	int flag;
+	int prot;
+	int sect;
+
+	prot = 0;
+	for (sect = s_first; sect <= s_last; ++sect) {
+		if (info->protect[sect]) {
+			prot++;
+		}
+	}
+	if (prot)
+		return ERR_PROTECTED;
+
+
+	flag = disable_interrupts();
+
+	printf ("Erasing %d sectors starting at sector %2d.\n"
+	"This make take some time ... ",
+	s_last - s_first + 1, s_first);
+
+	command[0] = IAP_CMD_PREPARE;
+	command[1] = s_first;
+	command[2] = s_last;
+	iap_entry(command, result);
+	if (result[0] != IAP_RET_CMD_SUCCESS) {
+		printf("IAP prepare failed\n");
+		return ERR_PROTECTED;
+	}
+
+	command[0] = IAP_CMD_ERASE;
+	command[1] = s_first;
+	command[2] = s_last;
+	command[3] = CFG_SYS_CLK_FREQ >> 10;
+	iap_entry(command, result);
+	if (result[0] != IAP_RET_CMD_SUCCESS) {
+		printf("IAP erase failed\n");
+		return ERR_PROTECTED;
+	}
+
+	if (flag)
+		enable_interrupts();
+
+	return ERR_OK;
+}
+
+int flash_erase_sst (flash_info_t * info, int s_first, int s_last)
+{
+	int i;
+
+	for (i = s_first; i <= s_last; i++) {
+		*SST_ADDR1 = 0x00AA;
+		*SST_ADDR2 = 0x0055;
+		*SST_ADDR1 = 0x0080;
+		*SST_ADDR1 = 0x00AA;
+		*SST_ADDR2 = 0x0055;
+		*((volatile ushort*)(info->start[i])) = 0x0030;
+		/* wait for erase to finish */
+		udelay(25000);
+	}
+
+	return ERR_OK;
+}
+
+int flash_erase (flash_info_t * info, int s_first, int s_last)
+{
+	switch (info->flash_id & FLASH_VENDMASK) {
+		case (SST_MANUFACT & FLASH_VENDMASK):
+			return flash_erase_sst(info, s_first, s_last);  
+		case (PHILIPS_LPC2292 & FLASH_VENDMASK):
+			return flash_erase_philips(info, s_first, s_last);  
+		default:
+			return ERR_PROTECTED;
+	}
+	return ERR_PROTECTED;
+}
+
+/*-----------------------------------------------------------------------
+ * Copy memory to flash.
+ *
+ * cnt is in bytes
+ */
+
+int write_buff_sst (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
+{
+	ushort tmp;
+	ulong i;
+	uchar* src_org;
+	uchar* dst_org;
+	ulong cnt_org = cnt;
+	int ret = ERR_OK;
+
+	src_org = src;
+	dst_org = (uchar*)addr;
+
+	if (addr & 1) {		/* if odd address */
+		tmp = *((uchar*)(addr - 1)); /* little endian */
+		tmp |= (*src << 8);
+		write_word_sst(addr - 1, tmp);
+		addr += 1;
+		cnt -= 1;
+		src++;
+	}
+	while (cnt > 1) {
+		tmp = ((*(src+1)) << 8) + (*src); /* little endian */
+		write_word_sst(addr, tmp);
+		addr += 2;
+		src += 2;
+		cnt -= 2;
+	}
+	if (cnt > 0) {
+		tmp = (*((uchar*)(addr + 1))) << 8;
+		tmp |= *src;
+		write_word_sst(addr, tmp);
+	}
+
+	for (i = 0; i < cnt_org; i++) {
+		if (*dst_org != *src_org) {
+			printf("Write failed. Byte %lX differs\n", i);
+			ret = ERR_PROG_ERROR;
+			break;
+		}
+		dst_org++;
+		src_org++;
+	}
+
+
+	return ret;
+}
+
+int write_buff_philips (flash_info_t * info, 
+			uchar * src, 
+			ulong addr, 
+			ulong cnt)
+{
+	int first_copy_size;
+	int last_copy_size;
+	int first_block;
+	int last_block;
+	int nbr_mid_blocks;
+	uchar memmap_value;
+	ulong i;
+	uchar* src_org;
+	uchar* dst_org;
+	int ret = ERR_OK;
+
+	src_org = src;
+	dst_org = (uchar*)addr;
+
+	first_block = addr / 512;
+	last_block = (addr + cnt) / 512;
+	nbr_mid_blocks = last_block - first_block - 1;
+
+	first_copy_size = 512 - (addr % 512);
+	last_copy_size = (addr + cnt) % 512;
+
+#if 0
+	printf("\ncopy first block: (1) %lX -> %lX 0x200 bytes, "
+		"(2) %lX -> %lX 0x%X bytes, (3) %lX -> %lX 0x200 bytes\n",
+	(ulong)(first_block * 512),
+	(ulong)COPY_BUFFER_LOCATION,
+	(ulong)src,
+	(ulong)(COPY_BUFFER_LOCATION + 512 - first_copy_size),
+	first_copy_size,
+	(ulong)COPY_BUFFER_LOCATION,
+	(ulong)(first_block * 512));
+#endif
+
+	/* copy first block */
+	memcpy((void*)COPY_BUFFER_LOCATION, 
+		(void*)(first_block * 512), 512);
+	memcpy((void*)(COPY_BUFFER_LOCATION + 512 - first_copy_size), 
+		src, first_copy_size);
+	copy_buffer_to_flash(info, first_block * 512);
+	src += first_copy_size;
+	addr += first_copy_size;
+
+	/* copy middle blocks */
+	for (i = 0; i < nbr_mid_blocks; i++) {
+#if 0
+		printf("copy middle block: %lX -> %lX 512 bytes, "
+		"%lX -> %lX 512 bytes\n",
+		(ulong)src,
+		(ulong)COPY_BUFFER_LOCATION,
+		(ulong)COPY_BUFFER_LOCATION,
+		(ulong)addr);
+#endif
+		memcpy((void*)COPY_BUFFER_LOCATION, src, 512);
+		copy_buffer_to_flash(info, addr);
+		src += 512;
+		addr += 512;    
+	}
+
+
+	if (last_copy_size > 0) {
+#if 0
+		printf("copy last block: (1) %lX -> %lX 0x200 bytes, "
+		"(2) %lX -> %lX 0x%X bytes, (3) %lX -> %lX x200 bytes\n",
+		(ulong)(last_block * 512),
+		(ulong)COPY_BUFFER_LOCATION,
+		(ulong)src,
+		(ulong)(COPY_BUFFER_LOCATION),
+		last_copy_size,
+		(ulong)COPY_BUFFER_LOCATION,
+		(ulong)addr);
+#endif
+		/* copy last block */
+		memcpy((void*)COPY_BUFFER_LOCATION, 
+			(void*)(last_block * 512), 512);
+		memcpy((void*)COPY_BUFFER_LOCATION, 
+			src, last_copy_size);
+		copy_buffer_to_flash(info, addr);
+	}
+
+	/* verify write */
+	memmap_value = GET8(MEMMAP);
+
+	disable_interrupts();
+
+	PUT8(MEMMAP, 01);		/* we must make sure that initial 64
+							   bytes are taken from flash when we
+							   do the compare */
+
+	for (i = 0; i < cnt; i++) {
+		if (*dst_org != *src_org){
+			printf("Write failed. Byte %lX differs\n", i);
+			ret = ERR_PROG_ERROR;
+			break;
+		}
+		dst_org++;
+		src_org++;
+	}
+
+	PUT8(MEMMAP, memmap_value);
+	enable_interrupts();
+
+	return ret;
+}
+
+int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
+{
+	switch (info->flash_id & FLASH_VENDMASK) {
+		case (SST_MANUFACT & FLASH_VENDMASK):
+			return write_buff_sst(info, src, addr, cnt);  
+		case (PHILIPS_LPC2292 & FLASH_VENDMASK):
+			return write_buff_philips(info, src, addr, cnt);  
+		default:
+			return ERR_PROG_ERROR;
+	}
+	return ERR_PROG_ERROR;
+}
diff --git a/board/lpc2292sodimm/iap_entry.S b/board/lpc2292sodimm/iap_entry.S
new file mode 100644
index 0000000..28ed805
--- /dev/null
+++ b/board/lpc2292sodimm/iap_entry.S
@@ -0,0 +1,7 @@
+IAP_ADDRESS:	.word	0x7FFFFFF1
+	
+.globl iap_entry
+iap_entry:
+	ldr	r2, IAP_ADDRESS
+	bx	r2
+	mov	pc, lr
diff --git a/board/lpc2292sodimm/lowlevel_init.S b/board/lpc2292sodimm/lowlevel_init.S
new file mode 100644
index 0000000..a91e678
--- /dev/null
+++ b/board/lpc2292sodimm/lowlevel_init.S
@@ -0,0 +1,87 @@
+/*
+ * (C) Copyright 2006 Embedded Artists AB <www.embeddedartists.com>
+ *
+ * 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
+ */
+
+#include <config.h>
+#include <version.h>
+#include <asm/arch/hardware.h>
+
+/* some parameters for the board */
+/* setting up the memory */
+#define 	SRAM_START 	0x40000000
+#define 	SRAM_SIZE	0x00004000
+#define   BCFG0_VALUE 0x1000ffef
+#define   BCFG1_VALUE 0x10001C61
+	
+_TEXT_BASE:
+	.word	TEXT_BASE
+MEMMAP_ADR:	
+	.word	MEMMAP
+BCFG0_ADR:
+  .word BCFG0
+_BCFG0_VALUE:
+  .word BCFG0_VALUE
+BCFG1_ADR:
+	.word	BCFG1
+_BCFG1_VALUE:
+	.word	BCFG1_VALUE
+PINSEL2_ADR:
+	.word	PINSEL2
+PINSEL2_MASK:
+	.word	0x00000000
+PINSEL2_VALUE:
+	.word	0x0F800914
+
+.extern _start
+
+.globl lowlevel_init
+lowlevel_init:
+	/* set up memory control register for bank 0 */
+	ldr r0, _BCFG0_VALUE
+	ldr r1, BCFG0_ADR
+	str r0, [r1]
+
+	/* set up memory control register for bank 1 */
+	ldr	r0, _BCFG1_VALUE
+	ldr	r1, BCFG1_ADR
+	str	r0, [r1]
+
+	/* set up PINSEL2 for bus-pins */
+	ldr	r0, PINSEL2_ADR
+	ldr	r1, [r0]
+	ldr	r2, PINSEL2_MASK
+	ldr	r3, PINSEL2_VALUE
+	and	r1, r1, r2
+	orr	r1, r1, r3
+	str	r1, [r0]
+		
+	/* move vectors to beginning of SRAM */
+	mov	r2, #SRAM_START		
+	mov	r0, #0 /*_start*/
+	ldmneia r0!, {r3-r10}		
+	stmneia r2!, {r3-r10}		
+	ldmneia r0, {r3-r9}
+	stmneia r2, {r3-r9}
+
+        /* Set-up MEMMAP register, so vectors are taken from SRAM */
+	ldr	r0, MEMMAP_ADR
+	mov	r1, #0x02	/* vectors re-mapped to static RAM */
+	str	r1, [r0]
+			
+	/* everything is fine now */
+	mov	pc, lr
diff --git a/board/lpc2292sodimm/lpc2292sodimm.c b/board/lpc2292sodimm/lpc2292sodimm.c
new file mode 100644
index 0000000..de04c66
--- /dev/null
+++ b/board/lpc2292sodimm/lpc2292sodimm.c
@@ -0,0 +1,62 @@
+/*
+ * (C) Copyright 2002
+ * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
+ * Marius Groeger <mgroeger@sysgo.de>
+ *
+ * (C) Copyright 2005 Rowel Atienza <rowel@diwalabs.com>
+ * Armadillo board HT1070
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+#include <common.h>
+#include <clps7111.h>
+
+/* ------------------------------------------------------------------------- */
+
+
+/*
+ * Miscelaneous platform dependent initialisations
+ */
+
+int board_init (void)
+{
+	DECLARE_GLOBAL_DATA_PTR;
+
+	/* Activate LED flasher */
+	IO_LEDFLSH = 0x40;
+
+	/* arch number MACH_TYPE_ARMADILLO - not official*/
+	gd->bd->bi_arch_number = 83;
+
+	/* location of boot parameters */
+	gd->bd->bi_boot_params = 0xc0000100;
+
+	return 0;
+}
+
+int dram_init (void)
+{
+	DECLARE_GLOBAL_DATA_PTR;
+
+	gd->bd->bi_dram[0].start = PHYS_SDRAM_1;
+	gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;
+
+	return (0);
+}
diff --git a/board/lpc2292sodimm/mmc.c b/board/lpc2292sodimm/mmc.c
new file mode 100644
index 0000000..e361db1
--- /dev/null
+++ b/board/lpc2292sodimm/mmc.c
@@ -0,0 +1,154 @@
+/*
+ * 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
+ */
+
+#include <config.h>
+#include <common.h>
+#include <mmc.h>
+#include <asm/errno.h>
+#include <asm/arch/hardware.h>
+#include <part.h>
+#include <fat.h>
+#include "mmc_hw.h"
+#include "spi.h"
+
+#ifdef CONFIG_MMC
+
+#undef MMC_DEBUG
+
+static block_dev_desc_t mmc_dev;
+
+/* these are filled out by a call to mmc_hw_get_parameters */
+static int hw_size;		/* in kbytes */
+static int hw_nr_sects;
+static int hw_sect_size;	/* in bytes */
+
+block_dev_desc_t * mmc_get_dev(int dev)
+{
+	return (block_dev_desc_t *)(&mmc_dev);
+}
+
+unsigned long mmc_block_read(int dev, 
+			     unsigned long start, 
+			     lbaint_t blkcnt,
+			     unsigned long *buffer)
+{
+	unsigned long rc = 0;
+	unsigned char *p = (unsigned char *)buffer;
+	unsigned long i;
+	unsigned long addr = start;
+
+#ifdef MMC_DEBUG
+	printf("mmc_block_read: start=%lu, blkcnt=%lu\n", start, 
+		 (unsigned long)blkcnt);
+#endif
+
+	for(i = 0; i < (unsigned long)blkcnt; i++) {
+#ifdef MMC_DEBUG
+		printf("mmc_read_sector: addr=%lu, buffer=%p\n", addr, p);
+#endif
+		(void)mmc_read_sector(addr, p);
+		rc++;
+		addr++;
+		p += hw_sect_size;
+	} 
+
+	return rc;
+}
+
+/*-----------------------------------------------------------------------------
+ * Read hardware paramterers (sector size, size, number of sectors)
+ */
+static int mmc_hw_get_parameters(void)
+{
+	unsigned char csddata[16];
+	unsigned int sizemult;
+	unsigned int size;
+
+	mmc_read_csd(csddata);
+	hw_sect_size = 1<<(csddata[5] & 0x0f);
+	size = ((csddata[6]&0x03)<<10)+(csddata[7]<<2)+(csddata[8]&0xc0);
+	sizemult = ((csddata[10] & 0x80)>>7)+((csddata[9] & 0x03)<<1);
+	hw_nr_sects = (size+1)*(1<<(sizemult+2));
+	hw_size = hw_nr_sects*hw_sect_size/1024;
+
+#ifdef MMC_DEBUG
+	printf("mmc_hw_get_parameters: hw_sect_size=%d, hw_nr_sects=%d, "
+		 "hw_size=%d\n", hw_sect_size, hw_nr_sects, hw_size);
+#endif
+
+	return 0;
+}
+
+int mmc_init(int verbose)
+{
+	int ret = -ENODEV;
+
+	if (verbose)
+		printf("mmc_init\n");
+
+	spi_init();
+	mmc_hw_init();
+
+	mmc_hw_get_parameters();
+
+	mmc_dev.if_type = IF_TYPE_MMC;
+	mmc_dev.part_type = PART_TYPE_DOS;
+	mmc_dev.dev = 0;
+	mmc_dev.lun = 0;
+	mmc_dev.type = 0;
+	mmc_dev.blksz = hw_sect_size;
+	mmc_dev.lba = hw_nr_sects;
+	sprintf((char*)mmc_dev.vendor, "Unknown vendor");
+	sprintf((char*)mmc_dev.product, "Unknown product");
+	sprintf((char*)mmc_dev.revision, "N/A");
+	mmc_dev.removable = 0;	/* should be true??? */
+	mmc_dev.block_read = mmc_block_read;
+
+	fat_register_device(&mmc_dev, 1);
+
+	ret = 0;
+
+	return ret;
+}
+
+int mmc_write(uchar * src, ulong dst, int size)
+{
+#ifdef MMC_DEBUG
+	printf("mmc_write: src=%p, dst=%lu, size=%u\n", src, dst, size);
+#endif
+	/* Since mmc2info always returns 0 this function will never be called */
+	return 0;
+}
+
+int mmc_read(ulong src, uchar * dst, int size)
+{
+#ifdef MMC_DEBUG
+	printf("mmc_read: src=%lu, dst=%p, size=%u\n", src, dst, size);
+#endif
+	/* Since mmc2info always returns 0 this function will never be called */
+	return 0;
+}
+
+int mmc2info(ulong addr)
+{
+	/* This function is used by cmd_cp to determine if source or destination
+	 address resides on MMC-card or not. We do not support copy to and from
+	 MMC-card so we always return 0. */
+	return 0;
+}
+
+#endif /* CONFIG_MMC */
diff --git a/board/lpc2292sodimm/mmc_hw.c b/board/lpc2292sodimm/mmc_hw.c
new file mode 100644
index 0000000..0b5ddf9
--- /dev/null
+++ b/board/lpc2292sodimm/mmc_hw.c
@@ -0,0 +1,233 @@
+/*
+    This code was original written by Ulrich Radig and modified by
+    Embedded Artists AB (www.embeddedartists.com).
+
+    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
+*/
+
+#include <config.h>
+#include <common.h>
+#include <asm/arch/hardware.h>
+#include "spi.h"
+
+#define MMC_Enable() PUT32(IO1CLR, 1l << 22)
+#define MMC_Disable() PUT32(IO1SET, 1l << 22)
+#define mmc_spi_cfg() spi_set_clock(8); spi_set_cfg(0, 1, 0); 
+
+static unsigned char Write_Command_MMC (unsigned char *CMD);
+static void MMC_Read_Block(unsigned char *CMD, unsigned char *Buffer,
+		    unsigned short int Bytes);
+
+/* initialize the hardware */
+int mmc_hw_init(void)
+{
+	unsigned long a;
+	unsigned short int Timeout = 0;
+	unsigned char b;
+	unsigned char CMD[] = {0x40, 0x00, 0x00, 0x00, 0x00, 0x95};
+
+	/* set-up GPIO and SPI */
+	(*((volatile unsigned long *)PINSEL2)) &= ~(1l << 3); /* clear bit 3 */
+	(*((volatile unsigned long *)IO1DIR)) |= (1l << 22); /* set bit 22 (output) */
+
+	MMC_Disable();
+
+	spi_lock();
+	spi_set_clock(248);
+	spi_set_cfg(0, 1, 0);
+	MMC_Enable();
+
+	/* waste some time */
+	for(a=0; a < 20000; a++)
+		asm("nop");
+
+	/* Put the MMC/SD-card into SPI-mode */
+	for (b = 0; b < 10; b++) /* Sends min 74+ clocks to the MMC/SD-card */
+		spi_write(0xff);
+
+	/* Sends command CMD0 to MMC/SD-card */
+	while (Write_Command_MMC(CMD) != 1) {
+		if (Timeout++ > 200) {
+			MMC_Disable();
+			spi_unlock();
+			return(1); /* Abort with command 1 (return 1) */
+		}
+	}
+	/* Sends Command CMD1 an MMC/SD-card */
+	Timeout = 0;
+	CMD[0] = 0x41;/* Command 1 */
+	CMD[5] = 0xFF;
+
+	while (Write_Command_MMC(CMD) != 0) {
+		if (Timeout++ > 200) {
+			MMC_Disable();
+			spi_unlock();
+			return (2); /* Abort with command 2 (return 2) */
+		}
+	}
+
+	MMC_Disable();
+	spi_unlock();
+
+	return 0;
+}
+
+/* ############################################################################
+   Sends a command to the MMC/SD-card
+   ######################################################################### */
+static unsigned char Write_Command_MMC (unsigned char *CMD)
+{
+	unsigned char a, tmp = 0xff;
+	unsigned short int Timeout = 0;
+
+	MMC_Disable();
+	spi_write(0xFF);
+	MMC_Enable();
+
+	for (a = 0; a < 0x06; a++)
+		spi_write(*CMD++);
+
+	while (tmp == 0xff) {
+		tmp = spi_read();
+		if (Timeout++ > 5000)
+		  break;
+	}
+
+	return (tmp);
+}
+
+/* ############################################################################
+   Routine to read the CID register from the MMC/SD-card (16 bytes)
+   ######################################################################### */
+void MMC_Read_Block(unsigned char *CMD, unsigned char *Buffer, unsigned short
+	int Bytes)
+{
+	unsigned short int a;
+
+	spi_lock();
+	mmc_spi_cfg();
+	MMC_Enable();
+
+	if (Write_Command_MMC(CMD) != 0) {
+		MMC_Disable();
+		spi_unlock();
+		return;
+	}	
+
+	while (spi_read() != 0xfe) {};
+	for (a = 0; a < Bytes; a++)
+		*Buffer++ = spi_read();
+
+	/* Read the CRC-byte */
+	spi_read(); /* CRC - byte is discarded */
+	spi_read(); /* CRC - byte is discarded */
+	/* set MMC_Chip_Select to high (MMC/SD-card Inaktiv) */
+	MMC_Disable();
+	spi_unlock();
+
+	return;
+}
+
+/* ############################################################################
+   Routine to read a block (512 bytes) from the MMC/SD-card
+   ######################################################################### */
+unsigned char mmc_read_sector (unsigned long addr,unsigned char *Buffer)
+{
+	/* Command 16 to read aBlocks from the MMC/SD - caed */
+	unsigned char CMD[] = {0x51,0x00,0x00,0x00,0x00,0xFF}; 
+
+	/* The addres on the MMC/SD-card is in bytes,
+	addr is transformed from blocks to bytes and the result is
+	placed into the command */
+
+	addr = addr << 9; /* addr = addr * 512 */
+
+	CMD[1] = ((addr & 0xFF000000) >> 24);
+	CMD[2] = ((addr & 0x00FF0000) >> 16);
+	CMD[3] = ((addr & 0x0000FF00) >> 8 );
+
+	MMC_Read_Block(CMD, Buffer, 512);
+
+	return (0);
+}
+
+/* ############################################################################
+   Routine to write a block (512 byte) to the MMC/SD-card
+   ######################################################################### */
+unsigned char mmc_write_sector (unsigned long addr,unsigned char *Buffer)
+{
+	unsigned char tmp, a;
+	unsigned short int b;
+	/* Command 24 to write a block to the MMC/SD - card */
+	unsigned char CMD[] = {0x58, 0x00, 0x00, 0x00, 0x00, 0xFF}; 
+
+	/* The addres on the MMC/SD-card is in bytes,
+	addr is transformed from blocks to bytes and the result is
+	placed into the command */
+
+	addr = addr << 9; /* addr = addr * 512 */
+
+	CMD[1] = ((addr & 0xFF000000) >> 24);
+	CMD[2] = ((addr & 0x00FF0000) >> 16);
+	CMD[3] = ((addr & 0x0000FF00) >> 8 );
+
+	spi_lock();
+	mmc_spi_cfg();
+	MMC_Enable();
+
+	/* Send command CMD24 to the MMC/SD-card (Write 1 Block/512 Bytes) */
+	tmp = Write_Command_MMC(CMD);
+	if (tmp != 0) {
+		MMC_Disable();
+		spi_unlock();
+		return(tmp);
+	}	
+
+	/* Do a short delay and send a clock-pulse to the MMC/SD-card */
+	for (a = 0; a < 100; a++)
+		spi_read();
+
+	/* Send a start byte to the MMC/SD-card */
+	spi_write(0xFE);	
+
+	/* Write the block (512 bytes) to the MMC/SD-card */
+	for (b = 0; b < 512; b++)
+		spi_write(*Buffer++);
+
+	/* write the CRC-Byte */
+	spi_write(0xFF); /* write a dummy CRC */
+	spi_write(0xFF); /* CRC code is not used */
+
+	/* Wait for MMC/SD-card busy */
+	while (spi_read() != 0xff) {};
+
+	/* set MMC_Chip_Select to high (MMC/SD-card inactive) */
+	MMC_Disable();
+	spi_unlock();
+	return (0);
+}
+
+/* #########################################################################
+   Routine to read the CSD register from the MMC/SD-card (16 bytes)
+   ######################################################################### */
+unsigned char mmc_read_csd (unsigned char *Buffer)
+{
+	/* Command to read the CSD register */
+	unsigned char CMD[] = {0x49, 0x00, 0x00, 0x00, 0x00, 0xFF};
+
+	MMC_Read_Block(CMD, Buffer, 16);
+
+	return (0);
+}
diff --git a/board/lpc2292sodimm/mmc_hw.h b/board/lpc2292sodimm/mmc_hw.h
new file mode 100644
index 0000000..3687dbf
--- /dev/null
+++ b/board/lpc2292sodimm/mmc_hw.h
@@ -0,0 +1,29 @@
+/*
+    This module implements a linux character device driver for the 24c256 chip.
+    Copyright (C) 2006  Embedded Artists AB (www.embeddedartists.com)
+
+    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
+*/
+
+#ifndef _MMC_HW_
+#define _MMC_HW_
+
+unsigned char mmc_read_csd(unsigned char *Buffer);
+unsigned char mmc_read_sector (unsigned long addr,
+			       unsigned char *Buffer);
+unsigned char mmc_write_sector (unsigned long addr,unsigned char *Buffer);
+int mmc_hw_init(void);
+
+#endif /* _MMC_HW_ */
diff --git a/board/lpc2292sodimm/spi.c b/board/lpc2292sodimm/spi.c
new file mode 100644
index 0000000..4ba1468
--- /dev/null
+++ b/board/lpc2292sodimm/spi.c
@@ -0,0 +1,40 @@
+/*
+    This module implements an interface to the SPI on the lpc22xx.
+    Copyright (C) 2006  Embedded Artists AB (www.embeddedartists.com)
+
+    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
+*/
+
+#include <config.h>
+#include <common.h>
+#include <asm/errno.h>
+#include <asm/arch/hardware.h>
+#include "spi.h"
+
+unsigned long spi_flags;
+unsigned char spi_idle = 0x00;
+
+int spi_init(void)
+{
+	unsigned long pinsel0_value;
+
+	/* activate spi pins */
+	pinsel0_value = GET32(PINSEL0);
+	pinsel0_value &= ~(0xFFl << 8);
+	pinsel0_value |= (0x55l << 8);
+	PUT32(PINSEL0, pinsel0_value);
+
+	return 0;
+}
diff --git a/board/lpc2292sodimm/spi.h b/board/lpc2292sodimm/spi.h
new file mode 100644
index 0000000..64f088c
--- /dev/null
+++ b/board/lpc2292sodimm/spi.h
@@ -0,0 +1,84 @@
+/*
+    This file defines the interface to the lpc22xx SPI module.
+    Copyright (C) 2006  Embedded Artists AB (www.embeddedartists.com)
+
+    This file may be included in software not adhering to the GPL.
+
+    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
+*/
+
+#ifndef SPI_H
+#define SPI_H
+
+#include <config.h>
+#include <common.h>
+#include <asm/errno.h>
+#include <asm/arch/hardware.h>
+
+#define SPIF 0x80
+
+#define spi_lock() disable_interrupts();
+#define spi_unlock() enable_interrupts();
+
+extern unsigned long spi_flags;
+extern unsigned char spi_idle;
+
+int spi_init(void);
+
+static inline unsigned char spi_read(void)
+{
+	unsigned char b;
+
+	PUT8(S0SPDR, spi_idle);
+	while (!(GET8(S0SPSR) & SPIF));
+	b = GET8(S0SPDR);
+
+	return b;
+}
+
+static inline void spi_write(unsigned char b)
+{
+	PUT8(S0SPDR, b);
+	while (!(GET8(S0SPSR) & SPIF));
+	GET8(S0SPDR);		/* this will clear the SPIF bit */
+}
+
+static inline void spi_set_clock(unsigned char clk_value)
+{
+	PUT8(S0SPCCR, clk_value);
+}
+
+static inline void spi_set_cfg(unsigned char phase,
+			       unsigned char polarity,
+			       unsigned char lsbf)
+{
+	unsigned char v = 0x20;	/* master bit set */
+
+	if (phase)
+		v |= 0x08;			/* set phase bit */
+	if (polarity) {
+		v |= 0x10;			/* set polarity bit */
+		spi_idle = 0xFF;
+	} else {
+		spi_idle = 0x00;
+	}
+	if (lsbf)
+		v |= 0x40;			/* set lsbf bit */
+
+	PUT8(S0SPCR, v);
+}
+
+#endif
+
diff --git a/board/lpc2292sodimm/u-boot.lds b/board/lpc2292sodimm/u-boot.lds
new file mode 100644
index 0000000..64d946c
--- /dev/null
+++ b/board/lpc2292sodimm/u-boot.lds
@@ -0,0 +1,55 @@
+/*
+ * (C) Copyright 2000
+ * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+
+OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
+OUTPUT_ARCH(arm)
+ENTRY(_start)
+SECTIONS
+{
+	. = 0x00000000;
+
+	. = ALIGN(4);
+	.text      :
+	{
+	  cpu/arm720t/start.o	(.text)
+	  *(.text)
+	}
+
+	. = ALIGN(4);
+	.rodata : { *(.rodata) }
+
+	. = ALIGN(4);
+	.data : { *(.data) }
+
+	. = ALIGN(4);
+	.got : { *(.got) }
+
+	__u_boot_cmd_start = .;
+	.u_boot_cmd : { *(.u_boot_cmd) }
+	__u_boot_cmd_end = .;
+
+	. = ALIGN(4);
+	__bss_start = .;
+	.bss : { *(.bss) }
+	_end = .;
+}