From f27bb38420d5224d5a88c32c089b290106caa090 Mon Sep 17 00:00:00 2001
From: Dirk Behme <dirk.behme@googlemail.com>
Date: Tue, 11 May 2010 20:17:53 +0200
Subject: [PATCH 37/37] ARM: beagle: add enc28j60 driver

---
 board/ti/beagle/beagle.c       |   14 ++
 drivers/net/enc28j60.c         |  156 +++++++++++++----------
 drivers/spi/Makefile           |    1 +
 drivers/spi/omap3_spi.c        |  272 ++++++++++++++++++++++++++++++++++++++++
 drivers/spi/omap3_spi.h        |  115 +++++++++++++++++
 include/configs/omap3_beagle.h |   10 ++
 include/netdev.h               |    3 +-
 7 files changed, 503 insertions(+), 68 deletions(-)
 create mode 100644 drivers/spi/omap3_spi.c
 create mode 100644 drivers/spi/omap3_spi.h

diff --git a/board/ti/beagle/beagle.c b/board/ti/beagle/beagle.c
index 556e995..5ed6e18 100644
--- a/board/ti/beagle/beagle.c
+++ b/board/ti/beagle/beagle.c
@@ -30,6 +30,8 @@
  * MA 02111-1307 USA
  */
 #include <common.h>
+#include <netdev.h>
+#include <spi.h>
 #include <twl4030.h>
 #include <asm/io.h>
 #include <asm/arch/mux.h>
@@ -278,3 +280,15 @@ void set_muxconf_regs(void)
 {
 	MUX_BEAGLE();
 }
+
+int board_eth_init(bd_t *bis)
+{
+	int rc = 0;
+#ifdef CONFIG_ENC28J60
+	rc = enc28j60_initialize(bis, CONFIG_ENC28J60_SPI_BUS,
+				 CONFIG_ENC28J60_SPI_CS,
+				 CONFIG_ENC28J60_SPI_CLK,
+				 SPI_MODE_3);
+#endif
+	return rc;
+}
diff --git a/drivers/net/enc28j60.c b/drivers/net/enc28j60.c
index 3238a50..cc24d44 100644
--- a/drivers/net/enc28j60.c
+++ b/drivers/net/enc28j60.c
@@ -17,9 +17,9 @@
 
 #include <config.h>
 #include <common.h>
+#include <malloc.h>
 #include <net.h>
-#include <asm/arch/hardware.h>
-#include <asm/arch/spi.h>
+#include <spi.h>
 
 /*
  * Control Registers in Bank 0
@@ -284,10 +284,14 @@
 /* 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);
-
+#define enc_enable(x) spi_cs_activate(x);
+#define enc_disable(x) spi_cs_deactivate(x);
+#define spi_write(x) spi_w8r8(slave, x)
+#define spi_read() spi_w8r8(slave, 0)
+#define spi_lock() spi_claim_bus(slave)
+#define spi_unlock() spi_release_bus(slave)
+/* Use spi_setup_slave() instead of enc_cfg_spi() */
+#define enc_cfg_spi()
 
 static unsigned char encReadReg (unsigned char regNo);
 static void encWriteReg (unsigned char regNo, unsigned char data);
@@ -322,45 +326,11 @@ static unsigned char next_pointer_msb;
 static unsigned char buffer[ENC_MAX_FRM_LEN];
 static int rxResetCounter = 0;
 
-#define RX_RESET_COUNTER 1000;
-
-/*-----------------------------------------------------------------------------
- * Always returns 0
- */
-int eth_init (bd_t * bis)
-{
-	unsigned char estatVal;
-	uchar enetaddr[6];
-
-	/* 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 ();
-
-	/* taken from the Linux driver - dangerous stuff here! */
-	/* Wait for CLKRDY to become set (i.e., check that we can communicate with
-	   the ENC) */
-	do
-	{
-		estatVal = m_nic_read(CTL_REG_ESTAT);
-	} while ((estatVal & 0x08) || (~estatVal & ENC_ESTAT_CLKRDY));
-
-	/* initialize controller */
-	encReset ();
-	eth_getenv_enetaddr("ethaddr", enetaddr);
-	encInit (enetaddr);
-
-	m_nic_bfs (CTL_REG_ECON1, ENC_ECON1_RXEN);	/* enable receive */
+static struct spi_slave *slave;
 
-	return 0;
-}
+#define RX_RESET_COUNTER 1000;
 
-int eth_send (volatile void *packet, int length)
+int enc28j60_send (struct eth_device *dev, volatile void *packet, int length)
 {
 	/* check frame length, etc. */
 	/* TODO: */
@@ -428,7 +398,7 @@ static void encReceiverResetCallback (void)
  * Check for received packets. Call NetReceive for each packet. The return
  * value is ignored by the caller.
  */
-int eth_rx (void)
+int enc28j60_recv (struct eth_device *dev)
 {
 	if (rxResetCounter > 0 && --rxResetCounter == 0) {
 		encReceiverResetCallback ();
@@ -439,7 +409,7 @@ int eth_rx (void)
 	return 0;
 }
 
-void eth_halt (void)
+void enc28j60_halt (struct eth_device *dev)
 {
 	m_nic_bfc (CTL_REG_ECON1, ENC_ECON1_RXEN);	/* disable receive */
 }
@@ -592,17 +562,17 @@ static void encWriteReg (unsigned char regNo, unsigned char data)
 {
 	spi_lock ();
 	enc_cfg_spi ();
-	enc_enable ();
+	enc_enable (slave);
 
 	spi_write (0x40 | regNo);	/* write in regNo */
 	spi_write (data);
 
-	enc_disable ();
-	enc_enable ();
+	enc_disable (slave);
+	enc_enable (slave);
 
 	spi_write (0x1f);	/* write reg 0x1f */
 
-	enc_disable ();
+	enc_disable (slave);
 	spi_unlock ();
 }
 
@@ -615,17 +585,17 @@ static void encWriteRegRetry (unsigned char regNo, unsigned char data, int c)
 
 	for (i = 0; i < c; i++) {
 		enc_cfg_spi ();
-		enc_enable ();
+		enc_enable (slave);
 
 		spi_write (0x40 | regNo);	/* write in regNo */
 		spi_write (data);
 
-		enc_disable ();
-		enc_enable ();
+		enc_disable (slave);
+		enc_enable (slave);
 
 		spi_write (0x1f);	/* write reg 0x1f */
 
-		enc_disable ();
+		enc_disable (slave);
 
 		spi_unlock ();	/* we must unlock spi first */
 
@@ -649,14 +619,14 @@ static unsigned char encReadReg (unsigned char regNo)
 
 	spi_lock ();
 	enc_cfg_spi ();
-	enc_enable ();
+	enc_enable (slave);
 
 	spi_write (0x1f);	/* read reg 0x1f */
 
 	bank = spi_read () & 0x3;
 
-	enc_disable ();
-	enc_enable ();
+	enc_disable (slave);
+	enc_enable (slave);
 
 	spi_write (regNo);
 	rxByte = spi_read ();
@@ -668,7 +638,7 @@ static unsigned char encReadReg (unsigned char regNo)
 		rxByte = spi_read ();
 	}
 
-	enc_disable ();
+	enc_disable (slave);
 	spi_unlock ();
 
 	return rxByte;
@@ -678,7 +648,7 @@ static void encReadBuff (unsigned short length, unsigned char *pBuff)
 {
 	spi_lock ();
 	enc_cfg_spi ();
-	enc_enable ();
+	enc_enable (slave);
 
 	spi_write (0x20 | 0x1a);	/* read buffer memory */
 
@@ -689,7 +659,7 @@ static void encReadBuff (unsigned short length, unsigned char *pBuff)
 			spi_write (0);
 	}
 
-	enc_disable ();
+	enc_disable (slave);
 	spi_unlock ();
 }
 
@@ -697,7 +667,7 @@ static void encWriteBuff (unsigned short length, unsigned char *pBuff)
 {
 	spi_lock ();
 	enc_cfg_spi ();
-	enc_enable ();
+	enc_enable (slave);
 
 	spi_write (0x60 | 0x1a);	/* write buffer memory */
 
@@ -706,7 +676,7 @@ static void encWriteBuff (unsigned short length, unsigned char *pBuff)
 	while (length--)
 		spi_write (*pBuff++);
 
-	enc_disable ();
+	enc_disable (slave);
 	spi_unlock ();
 }
 
@@ -714,12 +684,12 @@ static void encBitSet (unsigned char regNo, unsigned char data)
 {
 	spi_lock ();
 	enc_cfg_spi ();
-	enc_enable ();
+	enc_enable (slave);
 
 	spi_write (0x80 | regNo);	/* bit field set */
 	spi_write (data);
 
-	enc_disable ();
+	enc_disable (slave);
 	spi_unlock ();
 }
 
@@ -727,12 +697,12 @@ static void encBitClr (unsigned char regNo, unsigned char data)
 {
 	spi_lock ();
 	enc_cfg_spi ();
-	enc_enable ();
+	enc_enable (slave);
 
 	spi_write (0xA0 | regNo);	/* bit field clear */
 	spi_write (data);
 
-	enc_disable ();
+	enc_disable (slave);
 	spi_unlock ();
 }
 
@@ -740,11 +710,11 @@ static void encReset (void)
 {
 	spi_lock ();
 	enc_cfg_spi ();
-	enc_enable ();
+	enc_enable (slave);
 
 	spi_write (0xff);	/* soft reset */
 
-	enc_disable ();
+	enc_disable (slave);
 	spi_unlock ();
 
 	/* sleep 1 ms. See errata pt. 2 */
@@ -980,3 +950,55 @@ static void phyWrite(unsigned char addr, unsigned short data)
 		}
 	}
 }
+
+/*-----------------------------------------------------------------------------
+ */
+int enc28j60_initialize(bd_t *bis, unsigned int spi_bus, unsigned int spi_cs,
+			unsigned int max_hz, unsigned int mode)
+{
+	unsigned char estatVal;
+	struct eth_device *dev;
+	uchar enetaddr[6];
+
+	dev = malloc(sizeof(*dev));
+	if (dev == NULL) {
+		return 1;
+	}
+
+	spi_init ();
+	if (!slave) {
+		slave = spi_setup_slave(spi_bus, spi_cs, max_hz, mode);
+		if (!slave)
+			return -1;
+	}
+	spi_claim_bus(slave);
+	printf("Test 3\n");
+
+	/* taken from the Linux driver - dangerous stuff here! */
+	/* Wait for CLKRDY to become set (i.e., check that we can communicate with
+	   the ENC) */
+	do
+	{
+		estatVal = m_nic_read(CTL_REG_ESTAT);
+	} while ((estatVal & 0x08) || (~estatVal & ENC_ESTAT_CLKRDY));
+	printf("Test 4\n");
+
+	/* initialize controller */
+	encReset ();
+	eth_getenv_enetaddr("ethaddr", enetaddr);
+	encInit (enetaddr);
+
+	m_nic_bfs (CTL_REG_ECON1, ENC_ECON1_RXEN);	/* enable receive */
+
+	memset(dev, 0, sizeof(*dev));
+	sprintf(dev->name, "ENC29J60");
+
+	dev->halt = enc28j60_halt;
+	dev->send = enc28j60_send;
+	dev->recv = enc28j60_recv;
+
+	eth_register(dev);
+
+	return 0;
+}
+
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index f112ed0..b24c094 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -34,6 +34,7 @@ COBJS-$(CONFIG_KIRKWOOD_SPI) += kirkwood_spi.o
 COBJS-$(CONFIG_MPC52XX_SPI) += mpc52xx_spi.o
 COBJS-$(CONFIG_MPC8XXX_SPI) += mpc8xxx_spi.o
 COBJS-$(CONFIG_MXC_SPI) += mxc_spi.o
+COBJS-$(CONFIG_OMAP3_SPI) += omap3_spi.o
 COBJS-$(CONFIG_SOFT_SPI) += soft_spi.o
 
 COBJS	:= $(COBJS-y)
diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c
new file mode 100644
index 0000000..282382c
--- /dev/null
+++ b/drivers/spi/omap3_spi.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2010 Dirk Behme <dirk.behme@googlemail.com>
+ *
+ * Driver for McSPI controller on OMAP3. Based on davinci_spi.c
+ * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * Copyright (C) 2007 Atmel Corporation
+ *
+ * Parts taken from linux/drivers/spi/omap2_mcspi.c
+ * Copyright (C) 2005, 2006 Nokia Corporation
+ *
+ * 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 <spi.h>
+#include <malloc.h>
+#include <asm/io.h>
+#include "omap3_spi.h"
+
+#define WORD_LEN	8
+
+static void spi_reset(struct omap3_spi_slave *ds)
+{
+	unsigned int tmp;
+
+	writel(OMAP3_MCSPI_SYSCONFIG_SOFTRESET, &ds->regs->sysconfig);
+	do {
+		tmp = readl(&ds->regs->sysstatus);
+	} while (!(tmp & OMAP3_MCSPI_SYSSTATUS_RESETDONE));
+
+	writel(OMAP3_MCSPI_SYSCONFIG_AUTOIDLE |
+	       OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP |
+	       OMAP3_MCSPI_SYSCONFIG_SMARTIDLE,
+	       &ds->regs->sysconfig);
+
+	writel(OMAP3_MCSPI_WAKEUPENABLE_WKEN, &ds->regs->wakeupenable);
+}
+
+void spi_init()
+{
+	/* do nothing */
+}
+
+struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
+				  unsigned int max_hz, unsigned int mode)
+{
+	struct omap3_spi_slave	*ds;
+
+	ds = malloc(sizeof(struct omap3_spi_slave));
+	if (!ds) {
+		printf("SPI error: malloc of SPI structure failed\n");
+		return NULL;
+	}
+
+	/*
+	 * OMAP3 McSPI (MultiChannel SPI) has 4 busses (modules) with different number of
+	 * chip selects (CS, channels):
+	 * McSPI1 has 4 CS (bus 0, cs 0 - 3)
+	 * McSPI2 has 2 CS (bus 1, cs 0 - 1)
+	 * McSPI3 has 2 CS (bus 2, cs 0 - 1)
+	 * McSPI4 has 1 CS (bus 3, cs 0)
+	 */
+
+	switch (bus) {
+	case 0:
+		ds->regs = (struct mcspi *)OMAP3_MCSPI1_BASE;
+		break;
+	case 1:
+		ds->regs = (struct mcspi *)OMAP3_MCSPI2_BASE;
+		break;
+	case 2:
+		ds->regs = (struct mcspi *)OMAP3_MCSPI3_BASE;
+		break;
+	case 3:
+		ds->regs = (struct mcspi *)OMAP3_MCSPI4_BASE;
+		break;
+	default:
+		printf("SPI error: unsupported bus %i. Supported busses 0 - 3\n", bus);
+		return NULL;
+	}
+	ds->slave.bus = bus;
+
+	if (((bus == 0) && (cs > 3)) ||
+	    ((bus == 1) && (cs > 1)) ||
+	    ((bus == 2) && (cs > 1)) ||
+	    ((bus == 3) && (cs > 0))) {
+		printf("SPI error: unsupported chip select %i on bus %i\n", cs, bus);
+		return NULL;
+	}
+	ds->slave.cs = cs;
+
+	if (max_hz > OMAP3_MCSPI_MAX_FREQ) {
+		printf("SPI error: unsupported frequency %i Hz. Max frequency is 48 Mhz\n",
+		       max_hz);
+		return NULL;
+	}
+	ds->freq = max_hz;
+
+	if (mode > SPI_MODE_3) {
+		printf("SPI error: unsupported SPI mode %i\n", mode);
+		return NULL;
+	}
+	ds->mode = mode;
+
+	return &ds->slave;
+}
+
+void spi_free_slave(struct spi_slave *slave)
+{
+	struct omap3_spi_slave *ds = to_omap3_spi(slave);
+
+	free(ds);
+}
+
+int spi_claim_bus(struct spi_slave *slave)
+{
+	struct omap3_spi_slave *ds = to_omap3_spi(slave);
+	unsigned int conf, div = 0;
+
+	/* McSPI global module configuration */
+
+	/*
+	 * setup when switching from (reset default) slave mode
+	 * to single-channel master mode
+	 */
+	spi_reset(ds);
+	conf = readl(&ds->regs->modulctrl);
+	conf &= ~(OMAP3_MCSPI_MODULCTRL_STEST | OMAP3_MCSPI_MODULCTRL_MS);
+	conf |= OMAP3_MCSPI_MODULCTRL_SINGLE;
+	writel(conf, &ds->regs->modulctrl);
+
+	/* McSPI individual channel configuration */
+
+	/* Calculate clock divisor. Valid range: 0x0 - 0xC ( /1 - /4096 ) */
+	if (ds->freq) {
+		while (div <= 0xC && (OMAP3_MCSPI_MAX_FREQ / (1 << div))
+					> ds->freq)
+			div++;
+	} else
+		div = 0xC;
+
+	conf = readl(&ds->regs->channel[ds->slave.cs].chconf);
+
+	/* standard 4-wire master mode:  SCK, MOSI/out, MISO/in, nCS
+	 * REVISIT: this controller could support SPI_3WIRE mode.
+	 */
+	conf &= ~(OMAP3_MCSPI_CHCONF_IS|OMAP3_MCSPI_CHCONF_DPE1);
+	conf |= OMAP3_MCSPI_CHCONF_DPE0;
+
+	/* wordlength */
+	conf &= ~OMAP3_MCSPI_CHCONF_WL_MASK;
+	conf |= (WORD_LEN - 1) << 7;
+
+	/* set chipselect polarity; manage with FORCE */
+	if (!(ds->mode & SPI_CS_HIGH))
+		conf |= OMAP3_MCSPI_CHCONF_EPOL;	/* active-low; normal */
+	else
+		conf &= ~OMAP3_MCSPI_CHCONF_EPOL;
+
+	/* set clock divisor */
+	conf &= ~OMAP3_MCSPI_CHCONF_CLKD_MASK;
+	conf |= div << 2;
+
+	/* set SPI mode 0..3 */
+	if (ds->mode & SPI_CPOL)
+		conf |= OMAP3_MCSPI_CHCONF_POL;
+	else
+		conf &= ~OMAP3_MCSPI_CHCONF_POL;
+	if (ds->mode & SPI_CPHA)
+		conf |= OMAP3_MCSPI_CHCONF_PHA;
+	else
+		conf &= ~OMAP3_MCSPI_CHCONF_PHA;
+
+	/* Transmit & receive mode */
+	conf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
+
+	writel(conf, &ds->regs->channel[ds->slave.cs].chconf);
+
+	return 0;
+}
+
+void spi_release_bus(struct spi_slave *slave)
+{
+	struct omap3_spi_slave *ds = to_omap3_spi(slave);
+
+	/* Reset the SPI hardware */
+	spi_reset(ds);
+}
+
+int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
+		const void *dout, void *din, unsigned long flags)
+{
+	struct omap3_spi_slave *ds = to_omap3_spi(slave);
+	unsigned int	len, i;
+	const u8	*txp = dout;
+	u8		*rxp = din;
+
+	if (bitlen == 0)
+		/* Finish any previously submitted transfers */
+		goto out;
+
+	/*
+	 * It's not clear how non-8-bit-aligned transfers are supposed to be
+	 * represented as a stream of bytes...this is a limitation of
+	 * the current SPI interface - here we terminate on receiving such a
+	 * transfer request.
+	 */
+	if (bitlen % 8) {
+		/* Errors always terminate an ongoing transfer */
+		flags |= SPI_XFER_END;
+		goto out;
+	}
+
+	len = bitlen / 8;
+
+	/* enable McSPI channel */
+	writel(OMAP3_MCSPI_CHCTRL_EN, &ds->regs->channel[ds->slave.cs].chctrl);
+
+	/* Keep writing and reading 1 byte until done */
+	for (i = 0; i < len; i++) {
+
+		/* wait till TX register is empty (TXS == 1) */
+		while (!readl(&ds->regs->channel[ds->slave.cs].chstat) & OMAP3_MCSPI_CHSTAT_TXS);
+
+		/* Write the data */
+		writel(*txp++, &ds->regs->channel[ds->slave.cs].tx);
+
+		/* Wait till RX register contains data (RXS == 1) */
+		while (!readl(&ds->regs->channel[ds->slave.cs].chstat) & OMAP3_MCSPI_CHSTAT_RXS);
+
+		/* Read the data */
+		*rxp++ = readl(&ds->regs->channel[ds->slave.cs].rx);
+	}
+
+out:
+	/* Disable McSPI channel */
+	writel(0, &ds->regs->channel[ds->slave.cs].chctrl);
+
+	return 0;
+}
+
+int spi_cs_is_valid(unsigned int bus, unsigned int cs)
+{
+	return 1;
+}
+
+void spi_cs_activate(struct spi_slave *slave)
+{
+	/* nothing to do */
+}
+
+void spi_cs_deactivate(struct spi_slave *slave)
+{
+	/* nothing to do */
+}
+
diff --git a/drivers/spi/omap3_spi.h b/drivers/spi/omap3_spi.h
new file mode 100644
index 0000000..c9e91ea
--- /dev/null
+++ b/drivers/spi/omap3_spi.h
@@ -0,0 +1,115 @@
+/*
+ * Register definitions for the OMAP3 McSPI Controller
+ *
+ * Copyright (C) 2010 Dirk Behme <dirk.behme@googlemail.com>
+ *
+ * Parts taken from linux/drivers/spi/omap2_mcspi.c
+ * Copyright (C) 2005, 2006 Nokia Corporation
+ *
+ * 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
+ */
+
+#define OMAP3_MCSPI1_BASE		0x48098000
+#define OMAP3_MCSPI2_BASE		0x4809A000
+#define OMAP3_MCSPI3_BASE		0x480B8000
+#define OMAP3_MCSPI4_BASE		0x480BA000
+
+#define OMAP3_MCSPI_MAX_FREQ		48000000
+
+/* OMAP3 McSPI registers */
+struct mcspi_channel {
+	unsigned int chconf;		/* 0x2C, 0x40, 0x54, 0x68 */
+	unsigned int chstat;		/* 0x30, 0x44, 0x58, 0x6C */
+	unsigned int chctrl;		/* 0x34, 0x48, 0x5C, 0x70 */
+	unsigned int tx;		/* 0x38, 0x4C, 0x60, 0x74 */
+	unsigned int rx;		/* 0x3C, 0x50, 0x64, 0x78 */
+};
+
+struct mcspi {
+	unsigned char res1[0x10];
+	unsigned int sysconfig;		/* 0x10 */
+	unsigned int sysstatus;		/* 0x14 */
+	unsigned int irqstatus;		/* 0x18 */
+	unsigned int irqenable;		/* 0x1C */
+	unsigned int wakeupenable;	/* 0x20 */
+	unsigned int syst;		/* 0x24 */
+	unsigned int modulctrl;		/* 0x28 */
+	struct mcspi_channel channel[4]; /* channel0: 0x2C - 0x3C, bus 0 & 1 & 2 & 3 */
+					/* channel1: 0x40 - 0x50, bus 0 & 1 */
+					/* channel2: 0x54 - 0x64, bus 0 & 1 */
+					/* channel3: 0x68 - 0x78, bus 0 */
+};
+
+/* per-register bitmasks */
+#define OMAP3_MCSPI_SYSCONFIG_SMARTIDLE	(2 << 3)
+#define OMAP3_MCSPI_SYSCONFIG_ENAWAKEUP	(1 << 2)
+#define OMAP3_MCSPI_SYSCONFIG_AUTOIDLE	(1 << 0)
+#define OMAP3_MCSPI_SYSCONFIG_SOFTRESET	(1 << 1)
+
+#define OMAP3_MCSPI_SYSSTATUS_RESETDONE	(1 << 0)
+
+#define OMAP3_MCSPI_MODULCTRL_SINGLE	(1 << 0)
+#define OMAP3_MCSPI_MODULCTRL_MS	(1 << 2)
+#define OMAP3_MCSPI_MODULCTRL_STEST	(1 << 3)
+
+#define OMAP3_MCSPI_CHCONF_PHA		(1 << 0)
+#define OMAP3_MCSPI_CHCONF_POL		(1 << 1)
+#define OMAP3_MCSPI_CHCONF_CLKD_MASK	(0x0f << 2)
+#define OMAP3_MCSPI_CHCONF_EPOL		(1 << 6)
+#define OMAP3_MCSPI_CHCONF_WL_MASK	(0x1f << 7)
+#define OMAP3_MCSPI_CHCONF_TRM_RX_ONLY	(0x01 << 12)
+#define OMAP3_MCSPI_CHCONF_TRM_TX_ONLY	(0x02 << 12)
+#define OMAP3_MCSPI_CHCONF_TRM_MASK	(0x03 << 12)
+#define OMAP3_MCSPI_CHCONF_DMAW		(1 << 14)
+#define OMAP3_MCSPI_CHCONF_DMAR		(1 << 15)
+#define OMAP3_MCSPI_CHCONF_DPE0		(1 << 16)
+#define OMAP3_MCSPI_CHCONF_DPE1		(1 << 17)
+#define OMAP3_MCSPI_CHCONF_IS		(1 << 18)
+#define OMAP3_MCSPI_CHCONF_TURBO	(1 << 19)
+#define OMAP3_MCSPI_CHCONF_FORCE	(1 << 20)
+
+#define OMAP3_MCSPI_CHSTAT_RXS		(1 << 0)
+#define OMAP3_MCSPI_CHSTAT_TXS		(1 << 1)
+#define OMAP3_MCSPI_CHSTAT_EOT		(1 << 2)
+
+#define OMAP3_MCSPI_CHCTRL_EN		(1 << 0)
+
+#define OMAP3_MCSPI_WAKEUPENABLE_WKEN	(1 << 0)
+
+struct omap3_spi_slave {
+	struct spi_slave	slave;
+	struct mcspi		*regs;
+	unsigned int		freq;
+	unsigned int		mode;
+};
+
+static inline struct omap3_spi_slave *to_omap3_spi(struct spi_slave *slave)
+{
+	return container_of(slave, struct omap3_spi_slave, slave);
+}
+
+#if 0
+
+#define spi_readl(ds, reg)					\
+	readl(ds->regs + DAVINCI_SPI_##reg)
+#define spi_writel(ds, reg, value)				\
+	writel(value, ds->regs + DAVINCI_SPI_##reg)
+
+#endif
+
diff --git a/include/configs/omap3_beagle.h b/include/configs/omap3_beagle.h
index acca697..6083680 100644
--- a/include/configs/omap3_beagle.h
+++ b/include/configs/omap3_beagle.h
@@ -341,4 +341,14 @@ extern unsigned int boot_flash_sec;
 extern unsigned int boot_flash_type;
 #endif
 
+/*-----------------------------------------------------------------------
+ * SPI based ethernet on optional TinCanTools Zippy board
+ */
+#define CONFIG_OMAP3_SPI
+#define CONFIG_NET_MULTI
+#define CONFIG_ENC28J60
+#define CONFIG_ENC28J60_SPI_BUS		3
+#define CONFIG_ENC28J60_SPI_CS		0
+#define CONFIG_ENC28J60_SPI_CLK		20000000
+#define CONFIG_CMD_NET			/* bootp, tftpboot, rarpboot	*/
 #endif /* __CONFIG_H */
diff --git a/include/netdev.h b/include/netdev.h
index 1dd80f0..b038322 100644
--- a/include/netdev.h
+++ b/include/netdev.h
@@ -50,7 +50,8 @@ int davinci_emac_initialize(void);
 int dnet_eth_initialize(int id, void *regs, unsigned int phy_addr);
 int e1000_initialize(bd_t *bis);
 int eepro100_initialize(bd_t *bis);
-int ep93xx_eth_initialize(u8 dev_num, int base_addr);
+int enc28j60_initialize(bd_t *bis, unsigned int spi_bus, unsigned int spi_cs,
+			unsigned int max_hz, unsigned int mode);int ep93xx_eth_initialize(u8 dev_num, int base_addr);
 int eth_3com_initialize (bd_t * bis);
 int fec_initialize (bd_t *bis);
 int fecmxc_initialize (bd_t *bis);
-- 
1.6.6.1

