Merge branch 'broadcom_drops' into HEAD

Merge 2.10.3.3
Conflicts:
    bmoca-6802.c

Change-Id: I84a772d205a4fc59276bbbf6f59dfb186e17da3e
diff --git a/3.3/bmoca.c b/3.3/bmoca.c
index 0e55484..e8f1829 100644
--- a/3.3/bmoca.c
+++ b/3.3/bmoca.c
@@ -38,6 +38,8 @@
 #include <linux/scatterlist.h>
 #include <linux/clk.h>
 #include <linux/io.h>
+#include <linux/slab.h>
+#include <net/net_namespace.h>
 
 #define DRV_VERSION		0x00040000
 #define DRV_BUILD_NUMBER	0x20110831
@@ -46,6 +48,8 @@
 #if defined(CONFIG_BRCMSTB)
 
 #define MOCA6816		0
+#undef DSL_MOCA
+#undef CONFIG_BCM_6802_MoCA
 #include <linux/bmoca.h>
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
@@ -58,14 +62,14 @@
 
 #define MOCA6816		1
 #include "bmoca.h"
-#include <boardparms.h>
-#include <bcm3450.h>
+//#include <boardparms.h>
+//#include <bcm3450.h>
 /* board.h cannot declare spinlock, so do it here */
 extern spinlock_t bcm_gpio_spinlock;
 #include <linux/netdevice.h>
 
 #if defined(CONFIG_BCM_6802_MoCA)
-#include <board.h>
+//#include <board.h>
 #endif
 
 #else
@@ -565,6 +569,11 @@
 #define I2C_RD(x)		MOCA_RD(x)
 #define I2C_WR(x, y)		MOCA_WR(x, y)
 
+static int hw_specific_init(struct moca_priv_data *priv)
+{
+	return 0;
+}
+
 static void moca_hw_reset(struct moca_priv_data *priv)
 {
 	/* disable and clear all interrupts */
@@ -666,6 +675,8 @@
 
 static void moca_enable_irq(struct moca_priv_data *priv)
 {
+	if (!priv->enabled) return;
+
 	/* unmask everything */
 	u32 mask = M2H_REQ | M2H_RESP | M2H_ASSERT | M2H_WDT_CPU1 |
 		M2H_NEXTCHUNK | M2H_DMA;
@@ -1168,6 +1179,7 @@
 			}
 		}
 		if (data & 3) {
+			printk("ie=%08X\n", data);
 			msg = "IE is not a multiple of 4 bytes";
 			goto bad;
 		}
@@ -1563,10 +1575,17 @@
 static irqreturn_t moca_interrupt(int irq, void *arg)
 {
 	struct moca_priv_data *priv = arg;
+	struct moca_platform_data *pd;
+
+	if (!priv || !priv->enabled || !priv->pdev ||
+			!priv->pdev->dev.platform_data) {
+		printk("moca_interrupt: can't go yet.\n");
+		moca_disable_irq(priv);
+		return IRQ_HANDLED;
+	}
 
 #if MOCA6816
-	struct moca_platform_data *pd =
-		(struct moca_platform_data *)priv->pdev->dev.platform_data;
+	pd = (struct moca_platform_data *)priv->pdev->dev.platform_data;
 
 	/*
 	 * If the driver is for an external chip then the work function needs
@@ -1728,6 +1747,7 @@
 
 		/* verify chip ID */
 		data = moca_3450_read(priv, BCM3450_CHIP_ID);
+		pr_info("bcm3450 chip id is: %08x\n", data);
 		if (data != 0x3450)
 			printk(KERN_WARNING "%s: invalid 3450 chip ID 0x%08x\n",
 				__func__, data);
@@ -1823,8 +1843,10 @@
 	if (copy_from_user(&x, (void __user *)xfer_uaddr, sizeof(x)))
 		return -EFAULT;
 
+#if !DSL_MOCA
 	if (moca_range_ok(priv, x.moca_addr, x.len) < 0)
 		return -EINVAL;
+#endif
 
 	src = (uintptr_t)priv->base + x.moca_addr;
 	dst = (void *)(unsigned long)x.buf;
@@ -1847,8 +1869,10 @@
 	if (copy_from_user(&x, (void __user *)xfer_uaddr, sizeof(x)))
 		return -EFAULT;
 
+#if !DSL_MOCA
 	if (moca_range_ok(priv, x.moca_addr, x.len) < 0)
 		return -EINVAL;
+#endif
 
 	dst = (uintptr_t)priv->base + x.moca_addr;
 	src = (void *)(unsigned long)x.buf;
@@ -2071,12 +2095,10 @@
 		ret = 0;
 		break;
 	case MOCA_IOCTL_READMEM:
-		if (priv->running)
-			ret = moca_ioctl_readmem(priv, arg);
+		ret = moca_ioctl_readmem(priv, arg);
 		break;
 	case MOCA_IOCTL_WRITEMEM:
-		if (priv->running)
-			ret = moca_ioctl_writemem(priv, arg);
+		ret = moca_ioctl_writemem(priv, arg);
 		break;
 	case MOCA_IOCTL_GET_DRV_INFO_V2:
 		ret = moca_ioctl_get_drv_info_v2(priv, arg);
@@ -2114,6 +2136,9 @@
 	case MOCA_IOCTL_SET_3450_REG:
 		ret = moca_3450_set_reg(priv, (unsigned int *)arg);
 		break;
+	default:
+		pr_warn("moca_ioctl: unrecognized cmd\n");
+		break;
 	}
 	mutex_unlock(&priv->dev_mutex);
 
@@ -2361,6 +2386,11 @@
 	int minor, err = 0;
 	struct moca_platform_data *pd = pdev->dev.platform_data;
 
+	if (pd->use_spi && !pd->spi) {
+		pr_err("moca: use_spi=1, but no bmoca SPI device found.\n");
+		return -EINVAL;
+	}
+
 	priv = kzalloc(sizeof(*priv), GFP_KERNEL);
 	if (!priv) {
 		printk(KERN_ERR "%s: out of memory\n", __func__);
@@ -2440,10 +2470,9 @@
 		goto bad;
 	}
 
-
 #if defined(CONFIG_BCM_6802_MoCA)
 	priv->base = (void *)mres->start;
-	priv->irq = ires->start;
+	priv->irq = pd->spi->irq;
 	priv->i2c_base = (void *)pd->bcm3450_i2c_base;
 #else
 	priv->base = ioremap(mres->start, mres->end - mres->start + 1);
@@ -2453,25 +2482,12 @@
 
 #if MOCA6816
 	moca_read_mac_addr(priv, &pd->macaddr_hi, &pd->macaddr_lo);
+#endif
 	if (hw_specific_init(priv))
 		goto bad;
-#endif
 
 	/* leave core in reset until we get an ioctl */
-	moca_hw_reset(priv);
-
-#if defined(CONFIG_BCM_6802_MoCA)
-	kerSysRegisterMocaHostIntrCallback(
-		(MocaHostIntrCallback) moca_interrupt,
-		(void *)priv, pd->devId);
-#else
-	if (request_irq(priv->irq, moca_interrupt, 0, "moca", priv) < 0) {
-		printk(KERN_WARNING  "%s: can't request interrupt\n",
-			__func__);
-		err = -EIO;
-		goto bad2;
-	}
-#endif
+	//moca_hw_reset(priv); // moca_hw_init(MOCA_ENABLE) does this anyway
 
 	moca_hw_init(priv, MOCA_ENABLE);
 	moca_disable_irq(priv);
@@ -2480,7 +2496,7 @@
 
 	printk(KERN_INFO "bmoca: adding minor #%d at base 0x%08llx, IRQ %d, "
 		"I2C 0x%08llx/0x%02x\n", priv->minor,
-		(unsigned long long)mres->start, ires->start,
+		(unsigned long long)mres->start, priv->irq,
 		(unsigned long long)pd->bcm3450_i2c_base, pd->bcm3450_i2c_addr);
 
 	minor_tbl[priv->minor] = priv;
@@ -2491,16 +2507,26 @@
 		priv->dev = NULL;
 	}
 
+	moca_enable_irq(priv);
+	if (request_irq(priv->irq, moca_interrupt, 0, "moca", priv) < 0) {
+		printk(KERN_WARNING  "%s: can't request interrupt\n",
+			__func__);
+		err = -EIO;
+		goto bad2;
+	}
+
 	if (err)
 		goto bad2;
 
 	return 0;
 
 bad2:
-	if (priv->base)
-		iounmap(priv->base);
-	if (priv->i2c_base)
-		iounmap(priv->i2c_base);
+	if (!pd->use_spi) {
+		if (priv->base)
+			iounmap(priv->base);
+		if (priv->i2c_base)
+			iounmap(priv->i2c_base);
+	}
 bad:
 	kfree(priv);
 	return err;
@@ -2509,6 +2535,7 @@
 static int moca_remove(struct platform_device *pdev)
 {
 	struct moca_priv_data *priv = dev_get_drvdata(&pdev->dev);
+	struct moca_platform_data *pd = pdev->dev.platform_data;
 	struct clk *clk = priv->clk;
 	struct clk *phy_clk = priv->phy_clk;
 	struct clk *cpu_clk = priv->cpu_clk;
@@ -2521,8 +2548,10 @@
 	if (priv->irq)
 		free_irq(priv->irq, priv);
 
-	iounmap(priv->i2c_base);
-	iounmap(priv->base);
+	if (!pd->use_spi) {
+		iounmap(priv->i2c_base);
+		iounmap(priv->base);
+	}
 	kfree(priv);
 
 	clk_put(clk);
@@ -2611,13 +2640,12 @@
 
 static void moca_exit(void)
 {
-	class_destroy(moca_class);
 	unregister_chrdev(MOCA_MAJOR, MOCA_CLASS);
 	platform_driver_unregister(&moca_plat_drv);
 #if MOCA6816
 	moca_platform_dev_unregister();
 #endif
-
+	class_destroy(moca_class);
 }
 
 module_init(moca_init);
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..6538114
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,5 @@
+override EXTRA_CFLAGS += -I$M/../include -DCONFIG_BCM_6802_MoCA=1 -DDSL_MOCA=1
+
+all: modules
+
+obj-m += bmoca.o
diff --git a/bbsi.h b/bbsi.h
new file mode 100644
index 0000000..7907864
--- /dev/null
+++ b/bbsi.h
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2013 Google Inc.
+ *
+ * This program 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.
+ *
+ * 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 __BBSI_H
+#define __BBSI_H
+
+#include <linux/netdevice.h>
+#include <linux/spi/spi.h>
+#include <linux/vmalloc.h>
+
+#ifndef KSEG1
+#define KSEG1 0  // just to appease non-MIPS CPUs. Not really used.
+#endif
+
+#define BP_MOCA_MAX_NUM 1
+
+/*
+ * The exact values here don't matter, as they're translated into "real"
+ * values before talking to mocad.  This is just for the device registration
+ * tables.
+ */
+enum {
+	BP_MOCA_TYPE_WAN,
+	BP_MOCA_TYPE_LAN,
+};
+
+enum {
+	BP_MOCA_RF_BAND_D_LOW,
+	BP_MOCA_RF_BAND_D_HIGH,
+	BP_MOCA_RF_BAND_EXT_D,
+	BP_MOCA_RF_BAND_E,
+	BP_MOCA_RF_BAND_F,
+};
+
+typedef struct BpMocaInfo {
+	int type;
+	int rfBand;
+} BP_MOCA_INFO;
+
+static void BpGetMocaInfo(BP_MOCA_INFO *chips, int *nchips) {
+	if (*nchips >= 1) {
+		*nchips = 1;
+		chips[0].type = BP_MOCA_TYPE_LAN;
+		chips[0].rfBand = BP_MOCA_RF_BAND_E;
+	}
+}
+
+static uint32_t _spi_read32(struct spi_device *spi, uint32_t addr);
+
+
+// TODO(apenwarr): don't make this global.
+//   Or fix the driver to just only enable/disable interrupts at the right
+//   times.
+static int irq_disabled = 0;
+
+static void kerSysMocaHostIntrEnable(struct spi_device *spi) {
+	if (irq_disabled == 1) {
+		irq_disabled = 0;
+		enable_irq(spi->irq);
+	}
+}
+
+static void kerSysMocaHostIntrDisable(struct spi_device *spi) {
+	if (irq_disabled == 0) {
+		disable_irq_nosync(spi->irq);
+		irq_disabled = 1;
+	}
+}
+
+static uint8_t __pollstatus(struct spi_device *spi) {
+	uint8_t wclear[] = { 0x80, 0x06 };
+	uint8_t rdata[1] = { 0 };
+	struct spi_transfer t[2] = {
+		{ .tx_buf = wclear, .len = sizeof(wclear) },
+		{ .rx_buf = rdata, .len = sizeof(rdata) },
+	};
+	struct spi_message m;
+	int i;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t[0], &m);
+	spi_message_add_tail(&t[1], &m);
+
+	for (i = 0; i < 10; i++) {
+		if (spi_sync_locked(spi, &m) < 0) {
+			pr_warn("spi _pollstatus: SPI error\n");
+			return 0x01; // error code
+		}
+		if (rdata[0] & 0x01) {
+			pr_warn("spi _pollstatus: rbus error: %02X\n", rdata[0]);
+			return 0x01; // error result; stop polling now
+		}
+		if (!(rdata[0] & 0x10)) return 0;   // transaction finished
+	}
+	// if we get here, the transaction still isn't finished: weird
+	pr_warn("spi _pollstatus: still busy: %02X\n", rdata[0]);
+	return rdata[0];
+}
+
+static uint32_t __spi_read32a(struct spi_device *spi, uint32_t addr,
+				int speculative) {
+	uint8_t waddr[] = {
+		0x81, 0x07,
+		0x01 | (speculative ? 0x02 : 0),
+		0, 0, 0, 0 };
+	struct spi_transfer addrt[1] = {
+		{ .tx_buf = waddr, .len = sizeof(waddr) },
+	};
+	struct spi_message addrm;
+	int j, st;
+
+	spi_message_init(&addrm);
+	spi_message_add_tail(&addrt[0], &addrm);
+
+	__pollstatus(spi);
+	for (j = 0; j < 10; j++) {
+		// write address reg, which triggers the read
+		writel(cpu_to_be32(addr), waddr + sizeof(waddr) - 4);
+		if (spi_sync_locked(spi, &addrm) < 0) {
+			pr_warn("spi_read_addr: error\n");
+		}
+		st = __pollstatus(spi);
+		if (!st) break;
+	}
+	return st;
+}
+
+static uint32_t __spi_read32d_noswap(struct spi_device *spi) {
+	uint8_t wdata[] = { 0x80, 0x0c };
+	uint8_t rdata[4];
+	struct spi_transfer datat[2] = {
+		{ .tx_buf = wdata, .len = sizeof(wdata) },
+		{ .rx_buf = rdata, .len = sizeof(rdata) },
+	};
+	struct spi_message datam;
+
+	spi_message_init(&datam);
+	spi_message_add_tail(&datat[0], &datam);
+	spi_message_add_tail(&datat[1], &datam);
+
+	// retrieve actual data bits
+	if (spi_sync_locked(spi, &datam) < 0) {
+		pr_warn("spi_read_data: error\n");
+	}
+	return readl(rdata);
+}
+
+static uint32_t _spi_read32(struct spi_device *spi, uint32_t addr) {
+	int st;
+	uint32_t retval;
+
+	spi_bus_lock(spi->master);
+
+	st = __spi_read32a(spi, addr, 0);
+	if (st) {
+		retval = 0x00000000; // error
+	} else {
+		retval = be32_to_cpu(__spi_read32d_noswap(spi));
+	}
+	spi_bus_unlock(spi->master);
+	return retval;
+}
+
+static void __spi_write32a(struct spi_device *spi, uint32_t addr) {
+	uint8_t waddr[] = { 0x81, 0x07, 0x00, 0, 0, 0, 0  };
+	struct spi_transfer t[1] = {
+		{ .tx_buf = waddr, .len = sizeof(waddr) },
+	};
+	struct spi_message m;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t[0], &m);
+
+	// write address reg
+	writel(cpu_to_be32(addr), waddr + sizeof(waddr) - 4);
+	if (spi_sync_locked(spi, &m) < 0) {
+		pr_warn("spi_write: error\n");
+	}
+}
+
+static void __spi_write32d_noswap(struct spi_device *spi, uint32_t value) {
+	uint8_t wdata[] = { 0x81, 0x0c, 0, 0, 0, 0 };
+	struct spi_transfer t[1] = {
+		{ .tx_buf = wdata, .len = sizeof(wdata) },
+	};
+	struct spi_message m;
+
+	spi_message_init(&m);
+	spi_message_add_tail(&t[0], &m);
+
+	// write data reg
+	writel(value, wdata + sizeof(wdata) - 4);
+	if (spi_sync_locked(spi, &m) < 0) {
+		pr_warn("spi_write: error\n");
+	}
+}
+
+
+static void _spi_write32(struct spi_device *spi, uint32_t addr, uint32_t value) {
+	spi_bus_lock(spi->master);
+	__pollstatus(spi);
+	__spi_write32a(spi, addr);
+	__spi_write32d_noswap(spi, cpu_to_be32(value));
+	__pollstatus(spi);
+	spi_bus_unlock(spi->master);
+}
+
+static uint32_t kerSysBcmSpiSlaveReadReg32(struct spi_device *spi, uint32_t addr) {
+	return _spi_read32(spi, addr);
+}
+
+static void kerSysBcmSpiSlaveWriteReg32(struct spi_device *spi, uint32_t addr, uint32_t value) {
+	_spi_write32(spi, addr, value);
+}
+
+static void kerSysBcmSpiSlaveReadBuf(struct spi_device *spi, uint32_t addr, void *dst, int len, int wordsize) {
+	int i;
+	uint32_t *buf = dst;
+
+	spi_bus_lock(spi->master);
+
+	if (wordsize != 4) {
+		pr_info("SPI readbuf: only word size == 4 bytes is supported!\n");
+		return;
+	}
+	__spi_read32a(spi, addr, 1);
+	for (i = 0; i < len; i += wordsize) {
+		buf[i/4] = __spi_read32d_noswap(spi);
+		__pollstatus(spi);
+	}
+
+	spi_bus_unlock(spi->master);
+}
+
+static void kerSysBcmSpiSlaveWriteBuf(struct spi_device *spi, uint32_t addr, const void *src, int len, int wordsize) {
+	int i, nelems = len/4;
+	const uint32_t *buf = src;
+	uint8_t wdata[] = { 0x81, 0x0c };
+	struct spi_transfer *t, *tp;
+	struct spi_message m;
+
+	if (len > 8192) {
+		pr_warn("spi writebuf: buffer size %d is too large\n", len);
+		return;
+	}
+	if (wordsize != 4) {
+		pr_err("SPI writebuf: only word size == 4 bytes is supported!\n");
+		return;
+	}
+
+	t = vmalloc(nelems * sizeof(struct spi_transfer) * 2);
+	if (!t) {
+		pr_warn("spi writebuf: out of memory\n");
+		return;
+	}
+
+	memset(t, 0, nelems * sizeof(struct spi_transfer) * 2);
+	spi_message_init(&m);
+
+	for (i = 0, tp = t; i < nelems; i++) {
+		tp->tx_buf = wdata;
+		tp->len = sizeof(wdata);
+		spi_message_add_tail(tp, &m);
+		tp++;
+
+		tp->tx_buf = &buf[i];
+		tp->len = 4;
+		tp->cs_change = 1;
+		spi_message_add_tail(tp, &m);
+		tp++;
+	}
+
+	spi_bus_lock(spi->master);
+
+	__pollstatus(spi);
+	writel(cpu_to_be32(addr), wdata + 2);
+	__spi_write32a(spi, addr);
+	spi_sync_locked(spi, &m);
+	__pollstatus(spi);
+
+	spi_bus_unlock(spi->master);
+	vfree(t);
+}
+
+#endif // __BBSI_H
diff --git a/bmoca-6802.c b/bmoca-6802.c
index c46e323..a06ad5b 100644
--- a/bmoca-6802.c
+++ b/bmoca-6802.c
@@ -29,6 +29,7 @@
 */
 
 #include "bbsi.h"
+#include <linux/spi/spi.h>
 
 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
 #else
@@ -37,26 +38,26 @@
 
 #define MOCA_RD(x)    ((((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0) ? \
                        (*((volatile uint32_t *)((unsigned long)(x)))) : \
-                       ((uint32_t)kerSysBcmSpiSlaveReadReg32(((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId, (uint32_t)(x))))
+                       ((uint32_t)kerSysBcmSpiSlaveReadReg32(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, (uint32_t)(x))))
 
 #define MOCA_RD8(x, y) ((((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0) ? \
                         (*(y) = *((volatile unsigned char *)((unsigned long)(x)))) : \
-                        (kerSysBcmSpiSlaveRead(((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId, (unsigned long)(x), y, 1)))
+                        (kerSysBcmSpiSlaveRead(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, (unsigned long)(x), y, 1)))
 
 #define MOCA_WR(x,y)   do { ((((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0) ? \
                             (*((volatile uint32_t *)((unsigned long)(x)))) = (y) : \
-                            kerSysBcmSpiSlaveWriteReg32(((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId, (uint32_t)(x), (y))); } while(0)
+                            kerSysBcmSpiSlaveWriteReg32(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, (uint32_t)(x), (y))); } while(0)
 
 #define MOCA_WR8(x,y)    do { ((((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0) ? \
                                (*((volatile unsigned char *)((unsigned long)(x)))) = (unsigned char)(y) : \
-                               kerSysBcmSpiSlaveWrite(((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId, (unsigned long)(x), (y), 1)); } while(0)
+                               kerSysBcmSpiSlaveWrite(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, (unsigned long)(x), (y), 1)); } while(0)
 
 #define MOCA_WR16(x,y)   do { ((((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0) ? \
                                (*((volatile unsigned short *)((unsigned long)(x)))) = (unsigned short)(y) : \
-                               kerSysBcmSpiSlaveWrite(((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId, (unsigned long)(x), (y), 2)); } while(0)
+                               kerSysBcmSpiSlaveWrite(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, (unsigned long)(x), (y), 2)); } while(0)
 
-#define MOCA_WR_BLOCK(addr, src, len) do { kerSysBcmSpiSlaveWriteBuf(((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId, addr, src, len, 4); } while(0)
-#define MOCA_RD_BLOCK(addr, dst, len) do { kerSysBcmSpiSlaveReadBuf(((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId, addr, dst, len, 4); } while(0)
+#define MOCA_WR_BLOCK(addr, src, len) do { kerSysBcmSpiSlaveWriteBuf(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, addr, src, len, 4); } while(0)
+#define MOCA_RD_BLOCK(addr, dst, len) do { kerSysBcmSpiSlaveReadBuf(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, addr, dst, len, 4); } while(0)
 
 
 #define I2C_RD(x)		MOCA_RD(x)
@@ -188,12 +189,12 @@
 
 static void moca_enable_irq(struct moca_priv_data *priv)
 {
-	kerSysMocaHostIntrEnable(((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId);
+	kerSysMocaHostIntrEnable(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi);
 }
 
 static void moca_disable_irq(struct moca_priv_data *priv)
 {
-	kerSysMocaHostIntrDisable(((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId);
+	kerSysMocaHostIntrDisable(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi);
 }
 
 static void moca_pmb_busy_wait(struct moca_priv_data *priv)
@@ -354,7 +355,7 @@
 
 	x = (x >> 1) & 0xFF; // Get the MDIV_CH2 field
 
-	return( 2400 / x); 
+	return( x ? 2400 / x : 0);
 }
 
 
@@ -435,6 +436,8 @@
 	if (action == MOCA_ENABLE && !priv->enabled) {
 		clk_enable(priv->clk);
 
+		MOCA_WR(0x10404318, 0xfffffffd); // SUN_TOP_CTRL_SW_INIT_0_SET
+		udelay(20);
 		MOCA_WR(0x1040431c, ~(1 << 26)); // SUN_TOP_CTRL_SW_INIT_0_CLEAR --> Do this at start of sequence, don't touch gphy_sw_init
 		udelay(20);
 		moca_gphy_init(priv);
@@ -449,6 +452,14 @@
 	moca_hw_reset(priv);
 	udelay(1);
 
+	MOCA_WR(0x10800000, 0x03);       // EMUX_CNTRL
+	MOCA_WR(0x1080000c, 0x11);       // RGMII_0_CNTRL
+	MOCA_WR(0x10800014, 0xc0);       // RGMII_0_RX_CLK_DELAY_CNTRL
+
+	MOCA_WR(0x104040a4, 0x01);       // GENERAL_CTRL_NO_SCAN_0
+	MOCA_WR(0x10404100, 0x11110011); // PIN_MUX_CTRL_0
+	MOCA_WR(0x10404104, 0x11111111); // PIN_MUX_CTRL_1
+
 	if (action == MOCA_ENABLE) {
 
 		/* Power up all zones */
@@ -854,6 +865,36 @@
 
 #endif /* DSL_MOCA */
 
+static int __devinit bmoca_spi_probe(struct spi_device *spi) {
+	// TODO(apenwarr): match one spi device to one moca device struct.
+	// I happen to know that right now the system only registers one of
+	// moca_lan or moca_wan, never both, and there is never more than
+	// one moca chip present on our systems, so this is okay for now.
+	uint32_t val = kerSysBcmSpiSlaveReadReg32(spi, 0x10404000);
+	pr_info("bmoca_spi_probe bus=%d chip_select=%d: id=%08x %s\n",
+		spi->master->bus_num, spi->chip_select, val,
+		val != 0 ? "yes" : "no");
+	if (val == 0) return -ENODEV;
+	moca_lan_data.spi = spi;
+	moca_wan_data.spi = spi;
+	return 0; // success
+}
+
+static int __devexit bmoca_spi_remove(struct spi_device *spi) {
+	pr_info("bmoca_spi_remove\n");
+	if (moca_lan_data.spi == spi) moca_lan_data.spi = NULL;
+	if (moca_wan_data.spi == spi) moca_wan_data.spi = NULL;
+	return 0; // success
+}
+
+static struct spi_driver bmoca_spi_driver = {
+  .driver = {
+    .name = "bmoca",
+    .owner = THIS_MODULE,
+  },
+  .probe = bmoca_spi_probe,
+  .remove = __devexit_p(bmoca_spi_remove),
+};
 
 //extern void bcmenet_register_moca_fc_bits_cb(void cb(void *, unsigned long *), int isWan, void * arg);
 
@@ -879,6 +920,7 @@
 
 	/* fill in the hw_rev field */
 	pMocaData->chip_id = MOCA_RD(0x10404004) + 0xA0;
+        pr_info("read moca chip id: %08x\n", pMocaData->chip_id);
 	if ((pMocaData->chip_id & 0xFFFE0000) != 0x68020000) { /* 6802 or 6803 */
 		printk(KERN_ERR "bmoca: No MoCA chip found\n");
 		return -EFAULT;
@@ -922,6 +964,9 @@
 
 	BpGetMocaInfo(mocaInfo, &mocaChipNum);
 
+	ret = spi_register_driver(&bmoca_spi_driver);
+	if (ret < 0) return ret;
+
 	for (i = 0; i < mocaChipNum; i++) {
 		switch (mocaInfo[i].type) {
 			case BP_MOCA_TYPE_WAN:
@@ -943,6 +988,7 @@
 
 		ret = platform_device_register(pPlatformDev);
 		if (ret < 0) {
+			spi_unregister_driver(&bmoca_spi_driver);
 			return(ret);
 		}
 		else {
@@ -980,6 +1026,8 @@
 
 static void moca_platform_dev_unregister(void)
 {
+	spi_unregister_driver(&bmoca_spi_driver);
+
 	if (moca_lan_data.devId != MOCA_DEVICE_ID_UNREGISTERED)
 		platform_device_unregister(&moca_lan_plat_dev);
 
diff --git a/bmoca.h b/bmoca.h
index f7d7d69..57eeb27 100644
--- a/bmoca.h
+++ b/bmoca.h
@@ -166,6 +166,8 @@
 	int			use_dma;
 	int			use_spi;
 	int			devId;
+	struct spi_device	*spi;
+
 	u32			chip_id;
 
 #ifdef CONFIG_SMP
diff --git a/linux b/linux
new file mode 120000
index 0000000..945c9b4
--- /dev/null
+++ b/linux
@@ -0,0 +1 @@
+.
\ No newline at end of file