Merge MoCA 2.10.6.20

Conflicts:
      3.3/bmoca.c
      3.8/bmoca.c
      bmoca-6802.c

Change-Id: I9b83acab299893ff0ab6ef1d05b6ef5337e4dd93
diff --git a/3.3/bmoca.c b/3.3/bmoca.c
index 86b59b6..266f17f 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
@@ -571,6 +575,11 @@
 #define moca_clk_put      clk_put
 #define moca_clk_get      clk_get
 
+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 */
@@ -672,6 +681,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;
@@ -1174,6 +1185,7 @@
 			}
 		}
 		if (data & 3) {
+			printk("ie=%08X\n", data);
 			msg = "IE is not a multiple of 4 bytes";
 			goto bad;
 		}
@@ -1571,10 +1583,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
@@ -1617,7 +1636,6 @@
 {
 	struct bsc_regs *bsc = priv->i2c_base;
 	long timeout = HZ / 1000; /* 1 ms */
-	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait);
 	int i = 0;
 
 	do {
@@ -1631,7 +1649,7 @@
 				__func__);
 			return -1;
 		}
-		sleep_on_timeout(&wait, timeout ? timeout : 1);
+		schedule_timeout_interruptible(timeout ? timeout : 1);
 	} while (1);
 }
 
@@ -1727,6 +1745,10 @@
 {
 	u32 data;
 
+	/* some platforms connect the i2c directly to the MoCA core */
+	if (!priv->i2c_base)
+		return;
+
 	mutex_lock(&priv->moca_i2c_mutex);
 
 	if (action == MOCA_ENABLE) {
@@ -1736,6 +1758,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);
@@ -1831,8 +1854,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;
@@ -1855,8 +1880,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;
@@ -2105,12 +2132,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);
@@ -2151,7 +2176,9 @@
 	case MOCA_IOCTL_CLK_SSC:
 		ret = moca_clk_ssc(priv, (unsigned int *)arg);
 		break;
-
+	default:
+		pr_warn("moca_ioctl: unrecognized cmd\n");
+		break;
 	}
 	mutex_unlock(&priv->dev_mutex);
 
@@ -2399,6 +2426,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__);
@@ -2478,10 +2510,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);
@@ -2491,25 +2522,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);
@@ -2518,7 +2536,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;
@@ -2529,16 +2547,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;
@@ -2547,6 +2575,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;
@@ -2559,8 +2588,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);
 
 	moca_clk_put(clk);
@@ -2649,13 +2680,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/3.8/bmoca.c b/3.8/bmoca.c
index 471e4ed..700c626 100644
--- a/3.8/bmoca.c
+++ b/3.8/bmoca.c
@@ -1550,7 +1550,6 @@
 {
 	struct bsc_regs *bsc = priv->i2c_base;
 	long timeout = HZ / 1000; /* 1ms */
-	DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait);
 	int i = 0;
 
 	do {
@@ -1563,7 +1562,7 @@
 			dev_warn(priv->dev, "3450 I2C timed out\n");
 			return -1;
 		}
-		sleep_on_timeout(&wait, timeout ? timeout : 1);
+		schedule_timeout_interruptible(timeout ? timeout : 1);
 	} while (1);
 }
 
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..afd051f
--- /dev/null
+++ b/bbsi.h
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2013-2014 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
+
+/*
+ * Resources:
+ * BCM6803 Data Sheet (Document ID: 6803-DS205-R), Chapter 5: Host Interface and CPU
+ * BBVLSI Serial Interface (BBSI) (Document ID: BBSI-UM200-R)
+ */
+
+#include <linux/netdevice.h>
+#include <linux/spi/spi.h>
+
+#ifndef KSEG1
+#define KSEG1 0  // just to appease non-MIPS CPUs. Not really used.
+#endif
+
+#define BP_MOCA_MAX_NUM 1
+
+#define SPI_FIFO_LEN 32
+
+#define BBSI_COMMAND_BYTE 0x80
+#define STATUS_REGISTER_ADDR 0x06
+#define CONFIG_REGISTER_ADDR 0x07
+#define DATA0_REGISTER_ADDR 0x0c
+
+#define READ_RBUS (1<<0)
+#define SPECULATIVE_READ_EN (1<<1)
+#define NO_RBUS_ADDR_INC (1<<2)
+
+#define STATUS_BUSY (1<<4)
+
+enum bbsi_operation_state {
+	START,
+	COMMAND_STATE,
+	DATA_STATE,
+	POLLSTATUS_STATE,
+	DONE,
+	FAIL,
+};
+
+struct bbsi_operation {
+	enum bbsi_operation_state state;
+	struct spi_device *spi;
+	struct spi_message sm;
+	struct spi_transfer st[2];
+	/* address on MoCA chip we want to read from/write to */
+	uint32_t addr;
+	/* len must be a multiple of 4 */
+	size_t len;
+	uint8_t *data;
+	/* number of bytes already read or written. data_len <= len */
+	size_t data_len;
+	uint8_t tx_buf[8];
+	uint8_t rx_buf[SPI_FIFO_LEN - 2];
+	/* See interpret_data() for a definition of rx_state */
+	int rx_state;
+	struct completion done;
+};
+
+/*
+ * 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;
+	}
+}
+
+// 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 void state_machine_read(void *context);
+static void state_machine_write(void *context);
+
+static void bbsi_op_fail(struct bbsi_operation *t) {
+	t->state = FAIL;
+	complete(&t->done);
+}
+
+static int send_readcmd(struct bbsi_operation *t, int burst) {
+	int ret;
+	spi_message_init(&t->sm);
+	t->sm.complete = state_machine_read;
+	t->sm.context = t;
+	t->tx_buf[0] = BBSI_COMMAND_BYTE | 0x01;
+	t->tx_buf[1] = CONFIG_REGISTER_ADDR;
+	t->tx_buf[2] = READ_RBUS |
+		(burst ? SPECULATIVE_READ_EN : NO_RBUS_ADDR_INC);
+	writel(cpu_to_be32(t->addr), t->tx_buf + 3);
+	t->st[0] = ((struct spi_transfer) { .tx_buf = t->tx_buf, .len = 7 });
+	spi_message_add_tail(&t->st[0], &t->sm);
+	ret = spi_async_locked(t->spi, &t->sm);
+	if (ret) {
+		pr_err("%s: spi_async failed with %d\n", __func__, ret);
+		bbsi_op_fail(t);
+	}
+	return ret;
+}
+
+static int send_writecmd(struct bbsi_operation *t) {
+	int ret;
+	spi_message_init(&t->sm);
+	t->sm.complete = state_machine_write;
+	t->sm.context = t;
+	t->tx_buf[0] = BBSI_COMMAND_BYTE | 0x01;
+	t->tx_buf[1] = CONFIG_REGISTER_ADDR;
+	t->tx_buf[2] = 0;
+	writel(cpu_to_be32(t->addr), t->tx_buf + 3);
+	t->st[0] = ((struct spi_transfer) { .tx_buf = t->tx_buf, .len = 7 });
+	spi_message_add_tail(&t->st[0], &t->sm);
+	ret = spi_async_locked(t->spi, &t->sm);
+	if (ret) {
+		pr_err("%s: spi_async failed with %d\n", __func__, ret);
+		bbsi_op_fail(t);
+	}
+	return ret;
+}
+
+static int send_pollstatuscmd(struct bbsi_operation *t, void (*complete)(void *context)) {
+	int ret;
+	spi_message_init(&t->sm);
+	t->sm.complete = complete;
+	t->sm.context = t;
+	t->tx_buf[0] = BBSI_COMMAND_BYTE;
+	t->tx_buf[1] = STATUS_REGISTER_ADDR;
+	t->st[0] = ((struct spi_transfer) { .tx_buf = t->tx_buf, .len = 2 });
+	t->st[1] = ((struct spi_transfer) { .rx_buf = t->rx_buf, .len = 1 });
+	spi_message_add_tail(&t->st[0], &t->sm);
+	spi_message_add_tail(&t->st[1], &t->sm);
+	ret = spi_async_locked(t->spi, &t->sm);
+	if (ret) {
+		pr_err("%s: spi_async failed with %d\n", __func__, ret);
+		bbsi_op_fail(t);
+	}
+	return ret;
+}
+
+static int read_data(struct bbsi_operation *t) {
+	int ret;
+	spi_message_init(&t->sm);
+	t->sm.complete = state_machine_read;
+	t->sm.context = t;
+	t->tx_buf[0] = BBSI_COMMAND_BYTE;
+	/* We want to continue reading where we left off. See interpret_data()
+	 * for the definition of t->rx_state */
+	if (t->rx_state == 0) {
+		t->tx_buf[1] = STATUS_REGISTER_ADDR;
+	} else {
+		t->tx_buf[1] = DATA0_REGISTER_ADDR + t->rx_state - 1;
+	}
+	t->st[0] = ((struct spi_transfer) { .tx_buf = t->tx_buf, .len = 2 });
+	t->st[1] = ((struct spi_transfer) { .rx_buf = t->rx_buf,
+			.len = sizeof(t->rx_buf)});
+	spi_message_add_tail(&t->st[0], &t->sm);
+	spi_message_add_tail(&t->st[1], &t->sm);
+	ret = spi_async_locked(t->spi, &t->sm);
+	if (ret) {
+		pr_err("%s: spi_async failed with %d\n", __func__, ret);
+		bbsi_op_fail(t);
+	}
+	return ret;
+}
+
+static int write_data(struct bbsi_operation *t) {
+	int ret;
+	size_t len;
+	len = min(t->len - t->data_len, sizeof(t->rx_buf));
+	spi_message_init(&t->sm);
+	t->sm.complete = state_machine_write;
+	t->sm.context = t;
+	t->tx_buf[0] = BBSI_COMMAND_BYTE | 0x01;
+	t->tx_buf[1] = DATA0_REGISTER_ADDR + (t->data_len & 3);
+	t->st[0] = ((struct spi_transfer) { .tx_buf = t->tx_buf, .len = 2 });
+	t->st[1] = ((struct spi_transfer) { .tx_buf = t->data + t->data_len,
+			.len = len});
+	spi_message_add_tail(&t->st[0], &t->sm);
+	spi_message_add_tail(&t->st[1], &t->sm);
+	ret = spi_async_locked(t->spi, &t->sm);
+	if (ret) {
+		pr_err("%s: spi_async failed with %d\n", __func__, ret);
+		bbsi_op_fail(t);
+	}
+	t->data_len += len;
+	return ret;
+}
+
+static int interpret_data(struct bbsi_operation *t) {
+	/* The bytes we are receiving over the SPI bus are the values of
+	 * registers in the BBSI interface on the BCM6803. The register csrAddr
+	 * points to the register that we are going to read next. It cycles
+	 * through the following registers:
+	 *  - STATUS (t->rx_state==0)
+	 *  - DATA0 (t->rx_state==1)
+	 *  - DATA1 (t->rx_state==2)
+	 *  - DATA2 (t->rx_state==3)
+	 *  - DATA3 (t->rx_state==4)
+	 *
+	 * If csrAddr points to the status register, csrAddr is not incremented
+	 * if the busy bit in the status register is set. As a result, we might
+	 * read the status register multiple times before we get to DATA0. We
+	 * therefore might have to skip over multiple status bytes.
+	 *
+	 * t->rx_state defines the register that we are going to read next: 0
+	 * stands for STATUS, 1 stands for DATA0, 2 for DATA1 and so on.
+	*/
+	uint8_t *r;
+	r = t->rx_buf;
+	for(r = t->rx_buf;r < t->rx_buf + sizeof(t->rx_buf);r++) {
+		if (t->rx_state == 0) {
+			if (*r & STATUS_BUSY)
+				/* t->rx_state stays at 0 because the next byte
+				 * will be another status byte */
+				continue;
+			else if (*r) {
+				pr_err("rbus error 0x%02x while trying to read %u "
+						"bytes from 0x%08x\n",
+						(unsigned) *r, t->len, t->addr);
+				bbsi_op_fail(t);
+				return -1;
+			} else
+				/* The busy bit is not set which means that the next byte will be DATA0. */
+				t->rx_state = 1;
+		} else {
+			BUG_ON(t->data_len >= t->len);
+			t->data[t->data_len++] = *r;
+			/* t->rx_state==4 means that we just read DATA3. The
+			 * next byte will be a status byte (t->rx_state==0) */
+			t->rx_state++;
+			t->rx_state%=5;
+			if (t->data_len == t->len) return 0;
+		}
+
+	}
+	return 0;
+}
+
+static void state_machine_read(void *context) {
+	struct bbsi_operation *t = (struct bbsi_operation *) context;
+	int ret;
+
+	BUG_ON(t->len&3);
+
+	switch (t->state) {
+		case START:
+			t->state = COMMAND_STATE;
+			ret = send_readcmd(t, t->len>4);
+			if (ret) return;
+			break;
+		case COMMAND_STATE:
+			if (t->sm.status) {
+				pr_err("readcmd returned bad status %d\n", t->sm.status);
+				bbsi_op_fail(t);
+				return;
+			}
+			t->state = DATA_STATE;
+			ret = read_data(t);
+			if (ret) return;
+			break;
+		case DATA_STATE:
+			if (t->sm.status) {
+				pr_err("read_data returned bad status %d\n", t->sm.status);
+				bbsi_op_fail(t);
+				return;
+			}
+			if (interpret_data(t)) return;
+			if (t->data_len == t->len) {
+				t->state = DONE;
+				complete(&t->done);
+			} else {
+				/* Stay in this state */
+				ret = read_data(t);
+				if (ret) return;
+			}
+			break;
+		default:
+			BUG();
+			break;
+	}
+}
+
+static void state_machine_write(void *context) {
+	struct bbsi_operation *t = (struct bbsi_operation *) context;
+	int ret;
+
+	BUG_ON(t->len&3);
+
+	switch (t->state) {
+		case START:
+			t->state = COMMAND_STATE;
+			ret = send_writecmd(t);
+			if (ret) return;
+			break;
+		case COMMAND_STATE:
+			if (t->sm.status) {
+				pr_err("writecmd returned bad status %d\n", t->sm.status);
+				bbsi_op_fail(t);
+				return;
+			}
+			t->state = DATA_STATE;
+			ret = write_data(t);
+			if (ret) return;
+			break;
+		case DATA_STATE:
+			if (t->sm.status) {
+				pr_err("write_data returned bad status %d\n", t->sm.status);
+				bbsi_op_fail(t);
+				return;
+			}
+			if (t->data_len == t->len) {
+				t->state = POLLSTATUS_STATE;
+				ret = send_pollstatuscmd(t, state_machine_write);
+				if (ret) return;
+			} else {
+				/* Stay in this state */
+				ret = write_data(t);
+				if (ret) return;
+			}
+			break;
+		case POLLSTATUS_STATE:
+			if (t->sm.status) {
+				pr_err("pollstatuscmd returned bad status %d\n", t->sm.status);
+				bbsi_op_fail(t);
+				return;
+			}
+			if (*t->rx_buf & STATUS_BUSY) {
+				/* Stay in this state */
+				ret = send_pollstatuscmd(t, state_machine_write);
+				if (ret) return;
+			} else if (*t->rx_buf) {
+				pr_err("rbus error 0x%02x while trying to write %u "
+						"bytes to 0x%08x\n",
+						(unsigned) *t->rx_buf, t->len, t->addr);
+				bbsi_op_fail(t);
+			} else {
+				t->state = DONE;
+				complete(&t->done);
+			}
+			break;
+		default:
+			BUG();
+			break;
+	}
+}
+
+static uint32_t bbsi_read(struct spi_device *spi, uint32_t addr, void *dst, size_t len) {
+	struct bbsi_operation *t;
+	t = kmalloc(sizeof(*t), GFP_KERNEL);
+	t->state = START;
+	t->spi = spi;
+	t->addr = addr;
+	t->len = len;
+	t->data = dst;
+	t->data_len = 0;
+	t->rx_state = 0;
+	init_completion(&t->done);
+	state_machine_read((void*) t);
+	wait_for_completion(&t->done);
+	kfree(t);
+	return 0;
+}
+
+static uint32_t bbsi_write(struct spi_device *spi, uint32_t addr, const void *src, size_t len) {
+	struct bbsi_operation *t;
+	t = kmalloc(sizeof(*t), GFP_KERNEL);
+	t->state = START;
+	t->spi = spi;
+	t->addr = addr;
+	t->len = len;
+	t->data = (void*) src;
+	t->data_len = 0;
+	init_completion(&t->done);
+	state_machine_write((void*) t);
+	wait_for_completion(&t->done);
+	kfree(t);
+	return 0;
+}
+
+static uint32_t _spi_read32(struct spi_device *spi, uint32_t addr) {
+	uint32_t retval = 0;
+
+	spi_bus_lock(spi->master);
+	bbsi_read(spi, addr, &retval, sizeof(retval));
+	spi_bus_unlock(spi->master);
+	return be32_to_cpu(retval);
+}
+
+static void _spi_write32(struct spi_device *spi, uint32_t addr, uint32_t value) {
+	spi_bus_lock(spi->master);
+	value = cpu_to_be32(value);
+	bbsi_write(spi, addr, &value, sizeof(value));
+	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) {
+	if (len == 0) {
+		pr_warn("spi readbuf: buffer size 0 invalid\n");
+		return;
+	}
+	if (wordsize != 4) {
+		pr_info("SPI readbuf: only word size == 4 bytes is supported!\n");
+		return;
+	}
+	spi_bus_lock(spi->master);
+	bbsi_read(spi, addr, dst, len);
+	spi_bus_unlock(spi->master);
+}
+
+static void kerSysBcmSpiSlaveWriteBuf(struct spi_device *spi, uint32_t addr, const void *src, int len, int wordsize) {
+	if (len > 8192) {
+		pr_warn("spi writebuf: buffer size %d is too large\n", len);
+		return;
+	}
+	if (wordsize != 4 || len&3) {
+		pr_err("SPI writebuf: only word size == 4 bytes is supported!\n");
+		return;
+	}
+
+	spi_bus_lock(spi->master);
+	bbsi_write(spi, addr, src, len);
+	spi_bus_unlock(spi->master);
+}
+
+#endif // __BBSI_H
diff --git a/bmoca-6802.c b/bmoca-6802.c
index ecd69e8..f641fa8 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)
@@ -186,7 +187,6 @@
 	},
 };
 
-
 /* MoCA Clock Functions */
 struct clk *moca_clk_get(struct device *dev, const char *id)
 {
@@ -335,15 +335,14 @@
 	return(ret);
 }
 
-
 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)
@@ -1007,6 +1006,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);
 
@@ -1027,16 +1056,41 @@
 {
 #ifdef DSL_MOCA
 	struct moca_platform_data *pMocaData;
+	u32 port_mode;
 
 	pMocaData = (struct moca_platform_data *)priv->pdev->dev.platform_data;
 
 	/* 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;
 	}
 
+	MOCA_WR(0x1040431c, 0x0FFFFFFF); // SUN_TOP_CTRL_SW_INIT_0_CLEAR
+	MOCA_WR(0x104040a4, 0x01);       // GENERAL_CTRL_NO_SCAN_0
+	/* Power down GPHY LDO regulator to save power */
+	MOCA_WR(0x104040a8, 0x03);       // GENERAL_CTRL_NO_SCAN_1
+	MOCA_WR(0x10404100, 0x11110011); // PIN_MUX_CTRL_0
+	MOCA_WR(0x10404104, 0x11111111); // PIN_MUX_CTRL_1
+
+	/* The definition of PORT_MODE has changed from chip revision B0 to C0
+	 * */
+	if ((pMocaData->chip_id & 0xFFFEFFF0) == 0x680200C0)
+		port_mode = 2; /* RGMII_1 <-> GPHY, RGMII_0 <-> MoCA */
+	else
+		port_mode = 3; /* RGMII_0 <-> MoCA */
+
+	MOCA_WR(0x10800000, port_mode);  // EMUX_CNTRL
+	MOCA_WR(0x1080000c, 0x11);       // RGMII_0_CNTRL
+	MOCA_WR(0x10800014, 0xc0);       // RGMII_0_RX_CLK_DELAY_CNTRL
+
+	/* Shutdown GPHY to save power */
+	MOCA_WR(0x10800004, MOCA_RD(0x10800004) | 0xF); // EPORT_REG_GPHY_CNTRL
+	/* Disable UARTs to save power */
+	MOCA_WR(0x10406180, 0x4000); // PM_CONFIG
+
 	if (((pMocaData->chip_id & 0xFFFFFFF0) == 0x680200C0) || ((pMocaData->chip_id & 0xFFFFFFF0) == 0x680300C0))
 	{
 		priv->i2c_base = NULL; 
@@ -1057,6 +1111,11 @@
 	MOCA_WR(0x101000fc, 0x6); // CLKGEN_LEAP_TOP_INST_SHARED  
 	MOCA_WR(0x10100164, 0x3); // CLKGEN_SYS_CTRL_INST_POWER_SWITCH_MEMORY 
 
+	/* Disable clkobsv output pin to save power */
+	MOCA_WR(0x1010013C, 0x1); // CLKGEN_PAD_CLOCK_DISABLE
+	/* Disable LEAP clocks to save power */
+	MOCA_WR(0x101000D4, 0x7); // CLKGEN_LEAP_TOP_INST_CLOCK_DISABLE
+
 //	bcmenet_register_moca_fc_bits_cb(
 //		moca_get_fc_bits, pMocaData->use_spi ? 1 : 0, (void *)priv);
 #endif
@@ -1075,6 +1134,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:
@@ -1096,6 +1158,7 @@
 
 		ret = platform_device_register(pPlatformDev);
 		if (ret < 0) {
+			spi_unregister_driver(&bmoca_spi_driver);
 			return(ret);
 		}
 		else {
@@ -1133,6 +1196,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);
 
@@ -1169,4 +1234,3 @@
 			return(0xffffffff);
 	}
 }
-
diff --git a/bmoca.c b/bmoca.c
new file mode 120000
index 0000000..621a8ab
--- /dev/null
+++ b/bmoca.c
@@ -0,0 +1 @@
+3.3/bmoca.c
\ No newline at end of file
diff --git a/bmoca.h b/bmoca.h
index a0d34ee..348b866 100644
--- a/bmoca.h
+++ b/bmoca.h
@@ -180,6 +180,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