Merge MoCA 2.10.4.6

Conflicts:
	bmoca-6802.c

Change-Id: I52f7015d37095c8fa450f52cf1ab760c96a9075f
diff --git a/3.3/bmoca.c b/3.3/bmoca.c
index b36c551..a35c479 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;
 		}
@@ -1565,10 +1577,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
@@ -1721,6 +1740,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) {
@@ -1730,6 +1753,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);
@@ -1825,8 +1849,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;
@@ -1849,8 +1875,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;
@@ -2073,12 +2101,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);
@@ -2116,6 +2142,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);
 
@@ -2363,6 +2392,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__);
@@ -2442,10 +2476,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);
@@ -2455,25 +2488,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);
@@ -2482,7 +2502,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;
@@ -2493,16 +2513,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;
@@ -2511,6 +2541,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;
@@ -2523,8 +2554,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);
@@ -2613,13 +2646,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..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
new file mode 100644
index 0000000..cdf7f3a
--- /dev/null
+++ b/bmoca-6802.c
@@ -0,0 +1,1220 @@
+/*
+    <:copyright-BRCM:2013:DUAL/GPL:standard
+    
+       Copyright (c) 2013 Broadcom Corporation
+       All Rights Reserved
+    
+    Unless you and Broadcom execute a separate written software license
+    agreement governing use of this software, this software is licensed
+    to you under the terms of the GNU General Public License version 2
+    (the "GPL"), available at http://www.broadcom.com/licenses/GPLv2.php,
+    with the following added to such license:
+    
+       As a special exception, the copyright holders of this software give
+       you permission to link this software with independent modules, and
+       to copy and distribute the resulting executable under terms of your
+       choice, provided that you also meet, for each linked independent
+       module, the terms and conditions of the license of that module.
+       An independent module is a module which is not derived from this
+       software.  The special exception does not apply to any modifications
+       of the software.
+    
+    Not withstanding the above, under no circumstances may you combine
+    this software in any way with any other Broadcom software provided
+    under a license other than the GPL, without Broadcom's express prior
+    written consent.
+    
+    :> 
+
+*/
+
+#include "bbsi.h"
+#include <linux/spi/spi.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
+#else
+typedef unsigned long uintptr_t;
+#endif // LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
+
+#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)->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)->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)->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)->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)->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)->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)
+#define I2C_WR(x, y)		MOCA_WR(x, y)
+
+#define MOCA_BPCM_NUM         5
+#define MOCA_BPCM_ZONES_NUM   8
+
+#define MOCA_CPU_CLOCK_NUM  1
+#define MOCA_PHY_CLOCK_NUM  2
+
+typedef enum _PMB_COMMAND_E_
+{
+   PMB_COMMAND_PHY1_ON=0,
+   PMB_COMMAND_PARTIAL_ON,
+   PMB_COMMAND_PHY1_OFF,
+   PMB_COMMAND_ALL_OFF,
+
+   PMB_COMMAND_LAST
+} PMB_COMMAND_E;
+
+typedef enum _PMB_GIVE_OWNERSHIP_E_
+{
+   PMB_GIVE_OWNERSHIP_2_HOST = 0,
+   PMB_GIVE_OWNERSHIP_2_FW,
+
+   PMB_GET_OWNERSHIP_LAST
+} PMB_GIVE_OWNERSHIP_E;
+
+struct moca_680x_clk
+{
+	struct device *dev;
+	uint32_t       clock_num;
+};
+
+static uint32_t zone_all_off_bitmask[MOCA_BPCM_NUM] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+static uint32_t zone_partial_on_bitmask[MOCA_BPCM_NUM]  = { 0x41, 0xFC, 0xFF, 0xFF, 0x00 };
+static uint32_t zone_phy1_bitmask[MOCA_BPCM_NUM]  = { 0x00, 0x00, 0x00, 0x00, 0xFF };
+
+
+static void bogus_release(struct device *dev)
+{
+}
+
+static struct moca_platform_data moca_lan_data = {
+	.macaddr_hi =		0x00000102,
+	.macaddr_lo =		0x03040000,
+
+	.bcm3450_i2c_base =  0x10406200,
+	.bcm3450_i2c_addr =  0x70,
+	.hw_rev  =     HWREV_MOCA_20_GEN22,
+	.rf_band =     MOCA_BAND_EXT_D,
+	.chip_id =     0,
+	.use_dma           = 0,
+	.use_spi           = 1,
+	.devId            = MOCA_DEVICE_ID_UNREGISTERED, // Filled in dynamically
+#ifdef CONFIG_SMP
+	.smp_processor_id = 1,
+#endif
+};
+
+static struct resource moca_lan_resources[] = {
+	[0] = {
+		.start = 0x10600000,
+		.end =   0x107ffd97,
+		.flags = IORESOURCE_MEM,
+	},
+	[1] = { /* Not used for 6802, define for bmoca */
+		.start = 0,
+		.end = 0,
+		.flags = IORESOURCE_IRQ,
+	}
+};
+
+static struct platform_device moca_lan_plat_dev = {
+	.name = "bmoca",
+	.id = 0,
+	.num_resources = ARRAY_SIZE(moca_lan_resources),
+	.resource = moca_lan_resources,
+	.dev = {
+		.platform_data = &moca_lan_data,
+		.release = bogus_release,
+	},
+};
+
+static struct moca_platform_data moca_wan_data = {
+	.macaddr_hi       = 0x00000102,
+	.macaddr_lo       = 0x03040000,
+
+	.bcm3450_i2c_base =  0x10406200,
+	.bcm3450_i2c_addr =  0x70,
+	.hw_rev  = HWREV_MOCA_20_GEN22,
+	.chip_id = 0,
+	
+	.rf_band = MOCA_BAND_EXT_D,
+
+	.use_dma           = 0,
+	.use_spi           = 1,
+	.devId            = MOCA_DEVICE_ID_UNREGISTERED, // Filled in dynamically
+
+#ifdef CONFIG_SMP
+	.smp_processor_id = 1,
+#endif
+};
+
+static struct resource moca_wan_resources[] = {
+	[0] = {
+		.start = 0x10600000,
+		.end =   0x107ffd97,
+		.flags = IORESOURCE_MEM,
+	},
+	[1] = { /* Not used for 6802, define for bmoca */
+		.start = 0,
+		.end = 0,
+		.flags = IORESOURCE_IRQ,
+	}
+};
+
+static struct platform_device moca_wan_plat_dev = {
+	.name          = "bmoca",
+	.id            = 1,
+	.num_resources = ARRAY_SIZE(moca_wan_resources),
+	.resource      = moca_wan_resources,
+	.dev           = {
+		.platform_data = &moca_wan_data,
+		.release       = bogus_release,
+	},
+};
+
+static void moca_enable_irq(struct moca_priv_data *priv)
+{
+	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)->spi);
+}
+
+static void moca_pmb_busy_wait(struct moca_priv_data *priv)
+{
+	#if 0
+	uint32_t data;
+
+	/* Possible time saver: The register access time over SPI may 
+	   always be enough to guarantee that the write will complete 
+	   in time without having to check the status. */
+	do
+	{
+		data = MOCA_RD(priv->base + priv->regs->pmb_master_status);
+	} while (data & 0x1);
+	#endif
+}
+
+void moca_pmb_delay(struct moca_priv_data *priv)
+{
+	unsigned int data;
+	int i, j;
+	
+	MOCA_WR(priv->base + priv->regs->pmb_master_wdata_offset, 0xFF444000);
+	
+	for (i = 0; i < MOCA_BPCM_NUM; i++)
+	{
+		for (j = 0; j < MOCA_BPCM_ZONES_NUM; j++)
+		{
+			data = 0x100012 + j*4 + i*0x1000; ;
+			MOCA_WR(priv->base + priv->regs->pmb_master_cmd_offset, data);
+			moca_pmb_busy_wait(priv);
+		}
+	}
+}
+
+static void moca_pmb_control(struct moca_priv_data *priv, PMB_COMMAND_E cmd)
+{
+	int i, j;
+	uint32_t * p_zone_control;
+	uint32_t data;
+
+	switch (cmd)
+	{
+		case PMB_COMMAND_ALL_OFF:
+			// Turn off zone command
+			MOCA_WR(priv->base + priv->regs->pmb_master_wdata_offset, 0xA00);
+			p_zone_control = &zone_all_off_bitmask[0];
+			break;
+
+		case PMB_COMMAND_PHY1_OFF:
+			// Turn off zone command
+			MOCA_WR(priv->base + priv->regs->pmb_master_wdata_offset, 0xA00);
+			p_zone_control = &zone_phy1_bitmask[0];
+			break;
+		 
+	 case PMB_COMMAND_PHY1_ON:
+			// Turn on zone command
+			MOCA_WR(priv->base + priv->regs->pmb_master_wdata_offset, 0xC00);
+			p_zone_control = &zone_phy1_bitmask[0];
+			break;
+		 
+	 case PMB_COMMAND_PARTIAL_ON:
+			// Turn on zone command
+			MOCA_WR(priv->base + priv->regs->pmb_master_wdata_offset, 0xC00);
+			p_zone_control = &zone_partial_on_bitmask[0];
+			break;
+		 
+		 
+		default:
+			printk(KERN_WARNING "%s: illegal cmd: %08x\n",
+				__FUNCTION__, cmd);
+			return;
+	}
+
+	for (i = 0; i < MOCA_BPCM_NUM; i++)
+	{
+		for (j = 0; j < MOCA_BPCM_ZONES_NUM; j++)
+		{
+			if (*p_zone_control & (1 << j))
+			{
+				// zone address in bpcms
+				data = (0x1 << 20) + 16 + (i * 4096) + (j * 4);
+				MOCA_WR(priv->base + priv->regs->pmb_master_cmd_offset, data);
+				moca_pmb_busy_wait(priv);
+			}
+		}
+		p_zone_control++;
+	}
+
+}
+
+static void moca_pmb_give_cntrl(struct moca_priv_data *priv, PMB_GIVE_OWNERSHIP_E cmd)
+{
+	int i;
+	uint32_t data;
+
+	/* Pass control over the memories to the FW */
+	MOCA_WR(priv->base + priv->regs->pmb_master_wdata_offset, cmd);
+	for (i = 0; i < 3; i++)
+	{
+		data = 0x100002 + i*0x1000;
+		MOCA_WR(priv->base + priv->regs->pmb_master_cmd_offset, data);   
+		moca_pmb_busy_wait(priv);
+	}
+	moca_pmb_busy_wait(priv);
+}
+
+static void moca_hw_reset(struct moca_priv_data *priv)
+{
+//	unsigned long flags;
+//   uint32_t chipid;
+  
+
+	/* disable and clear all interrupts */
+	MOCA_WR(priv->base + priv->regs->l2_mask_set_offset, 0xffffffff);
+	MOCA_RD(priv->base + priv->regs->l2_mask_set_offset);
+
+	/* assert resets */
+
+	/* reset CPU first, both CPUs for MoCA 20 HW */
+	if (priv->hw_rev == HWREV_MOCA_20_GEN22)
+		MOCA_SET(priv->base + priv->regs->sw_reset_offset, 5);
+	else
+		MOCA_SET(priv->base + priv->regs->sw_reset_offset, 1);
+
+	MOCA_RD(priv->base + priv->regs->sw_reset_offset);
+
+	udelay(20);
+
+	/* reset everything else except clocks */
+	MOCA_SET(priv->base + priv->regs->sw_reset_offset, 
+		~((1 << 3) | (1 << 7) | (1 << 15) | (1 << 16)));
+	MOCA_RD(priv->base + priv->regs->sw_reset_offset);
+
+	udelay(20);
+
+	/* disable clocks */
+	MOCA_SET(priv->base + priv->regs->sw_reset_offset, 
+		~((1 << 3) | (1 << 15) | (1 << 16)));
+	MOCA_RD(priv->base + priv->regs->sw_reset_offset);
+
+	MOCA_WR(priv->base + priv->regs->l2_clear_offset, 0xffffffff);
+	MOCA_RD(priv->base + priv->regs->l2_clear_offset);
+
+	/* Power down all zones */
+	//  The host can't give to itself permission.
+	moca_pmb_control(priv, PMB_COMMAND_ALL_OFF);
+
+	/* Power down all SYS_CTRL memories */
+	MOCA_WR(0x10100068, 1);   // CLKGEN_PLL_SYS1_PLL_PWRDN
+	MOCA_SET(0x1010000c, 1);  // CLKGEN_PLL_SYS0_PLL_CHANNEL_CTRL_CH_3
+
+}
+
+static unsigned int moca_get_phy_freq(struct moca_priv_data *priv)
+{
+	unsigned int x = MOCA_RD(0x10100044); // CLKGEN_PLL_SYS1_PLL_CHANNEL_CTRL_CH_2
+
+	x = (x >> 1) & 0xFF; // Get the MDIV_CH2 field
+
+	return( x ? 2400 / x : 0);
+}
+
+
+static void moca_ps_PowerCtrlPHY1(struct moca_priv_data *priv,  PMB_COMMAND_E cmd)
+{
+	uint32_t pll_ctrl_3, pll_ctrl_5, sw_reset; 
+	pll_ctrl_3 = MOCA_RD (0x10100048);
+	pll_ctrl_5 = MOCA_RD (0x10100050);
+	sw_reset = MOCA_RD (priv->base + priv->regs->sw_reset_offset);
+
+	// enable PLL 
+	MOCA_UNSET(0x10100048, 1);  // CLKGEN_PLL_SYS1_PLL_CHANNEL_CTRL_CH_3 
+	MOCA_UNSET(0x10100050, 1);  // CLKGEN_PLL_SYS1_PLL_CHANNEL_CTRL_CH_5 
+
+	udelay(1);
+
+	// de assert moca_phy1_disable_clk
+	MOCA_UNSET(priv->base + priv->regs->sw_reset_offset, (1 << 9));
+
+	moca_pmb_control(priv, cmd);
+
+	MOCA_WR (0x10100048, pll_ctrl_3);
+	MOCA_WR (0x10100050, pll_ctrl_5);
+
+	udelay(1);
+	
+	MOCA_WR (priv->base + priv->regs->sw_reset_offset, sw_reset);	
+}
+
+
+static void moca_gphy_init(struct moca_priv_data *priv)
+{
+	struct moca_platform_data * pMocaData = (struct moca_platform_data *)priv->pdev->dev.platform_data;
+	u32 port_mode;
+	u32 rgmii0_on;
+	u32 rgmii1_on;
+	u32 gphy_enabled = 0;
+
+	port_mode = MOCA_RD(0x10800000) & 0x3;
+	rgmii0_on = MOCA_RD(0x1080000c) & 0x1;
+	rgmii1_on = MOCA_RD(0x10800018) & 0x1;
+
+	if ((pMocaData->chip_id & 0xFFFEFFF0) == 0x680200C0)
+	{
+		if ((port_mode == 0) ||
+		    ((port_mode == 1) && rgmii0_on) ||
+		    ((port_mode == 2) && rgmii1_on))
+		{
+			gphy_enabled = 1;
+		}
+	}
+	else
+	{
+		if ((port_mode == 0) ||
+		    ((port_mode != 3) && rgmii1_on))
+		{
+			gphy_enabled = 1;
+		}
+	}
+
+	if (gphy_enabled)
+	{
+		MOCA_UNSET(0x10800004, 0xF);
+		msleep(10);
+		MOCA_WR(0x1040431c, 0xFFFFFFFF);
+	}
+}
+
+/* called any time we start/restart/stop MoCA */
+static void moca_hw_init(struct moca_priv_data *priv, int action)
+{
+	u32 mask;
+	u32 temp;
+	u32 data;
+	u32 count = 0;
+	struct moca_platform_data * pMocaData = (struct moca_platform_data *)priv->pdev->dev.platform_data;
+
+	if (action == MOCA_ENABLE && !priv->enabled) {
+		clk_enable(priv->clk);
+
+		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);
+   
+		priv->enabled = 1;
+	}
+
+	/* clock not enabled, register accesses will fail with bus error */
+	if (!priv->enabled)
+		return;
+
+	moca_hw_reset(priv);
+	udelay(1);
+
+	if (action == MOCA_ENABLE) {
+
+		/* Power up all zones */
+		moca_pmb_control(priv, PMB_COMMAND_PARTIAL_ON);
+
+		MOCA_UNSET(0x1010000c, 1);  // CLKGEN_PLL_SYS0_PLL_CHANNEL_CTRL_CH_3 
+
+		MOCA_WR(0x1010006C, 1);  // CLKGEN_PLL_SYS1_PLL_RESET 
+		MOCA_WR(0x10100068, 0);  // CLKGEN_PLL_SYS1_PLL_PWRDN 
+		data = 0;
+		while ((data & 0x1) == 0)
+		{
+			/* This typically is only read once */
+			data = MOCA_RD(0x10100060); // CLKGEN_PLL_SYS1_PLL_LOCK_STATUS
+
+			if (count++ > 10)
+				break;
+		}
+		MOCA_WR(0x1010006C, 0);  // CLKGEN_PLL_SYS1_PLL_RESET 
+
+		if (priv->bonded_mode) {
+			MOCA_UNSET(0x10100048, 1);  // CLKGEN_PLL_SYS1_PLL_CHANNEL_CTRL_CH_3 
+			MOCA_UNSET(0x10100050, 1);  // CLKGEN_PLL_SYS1_PLL_CHANNEL_CTRL_CH_5 
+		} else {
+			MOCA_SET(0x10100048, 1);  // CLKGEN_PLL_SYS1_PLL_CHANNEL_CTRL_CH_3 
+			MOCA_SET(0x10100050, 1);  // CLKGEN_PLL_SYS1_PLL_CHANNEL_CTRL_CH_5 
+		}
+		udelay(1);
+
+		/* deassert moca_sys_reset, system clock, phy0, phy0 clock */
+		mask = (1 << 1) | (1 << 7) | (1 << 4) | (1 << 8);
+
+		/* deassert phy1 and phy1 clock in bonded mode */
+		if (priv->bonded_mode)
+			mask |= (1 << 5) | (1 << 9);
+
+		MOCA_UNSET(priv->base + priv->regs->sw_reset_offset, mask);
+		MOCA_RD(priv->base + priv->regs->sw_reset_offset);
+		
+        // Before power off the memories, moca_phy1_disable_clk.    
+		if (priv->bonded_mode==0)
+			moca_ps_PowerCtrlPHY1(priv, PMB_COMMAND_PHY1_OFF);
+		else
+			moca_ps_PowerCtrlPHY1(priv, PMB_COMMAND_PHY1_ON);
+
+        
+		moca_pmb_give_cntrl(priv, PMB_GIVE_OWNERSHIP_2_FW);
+			
+		/* Check for 6802/6803 A0 chip only with Xtal mod */
+		if ((pMocaData->chip_id & 0xFFFEFFFF) == 0x680200A0)
+		{
+			data = MOCA_RD(0x1040401c);
+			if ((data & 0x7) == 0x2) {
+				/* 25MHz */
+				printk("MoCA running with 25MHz XTAL\n");
+				MOCA_WR(priv->base + priv->regs->host2moca_mmp_outbox_0_offset, 1);
+			} else {
+				printk("MoCA == 50MHz XTAL\n");
+				/* 50MHz clock change only */
+				MOCA_WR(priv->base + priv->regs->host2moca_mmp_outbox_0_offset, 0);
+				//Note: The re-configuration is in NDIV_INT, not PDIV.
+				//`CLKGEN_REG_START + `CLKGEN_PLL_SYS1_PLL_DIV (32'h10100058) [09:00] = 10’d48
+				temp = MOCA_RD(0x10100058);
+				temp = (temp & 0xFFFFFC00) + 48;
+				MOCA_WR(0x10100058, temp);
+
+				//`CLKGEN_REG_START + `CLKGEN_PLL_SYS0_PLL_DIV (32'h10100018) [09:00] = 10’d40
+				temp = MOCA_RD(0x10100018);
+				temp = (temp & 0xFFFFFC00) + 40;
+				MOCA_WR(0x10100018, temp);
+
+				//`CLKGEN_REG_START + `CLKGEN_PLL_SYS1_PLL_CHANNEL_CTRL_CH_4 (32'h1010004C) [08:01] = 8’d48
+				temp = MOCA_RD(0x1010004c);
+				temp = (temp & 0xFFFFFE01) + (48 << 1);
+				MOCA_WR(0x1010004c, temp);
+
+				//`CLKGEN_REG_START + `CLKGEN_PLL_SYS1_PLL_CHANNEL_CTRL_CH_5 (32'h10100050) [08:01] = 8’d48
+				temp = MOCA_RD(0x10100050);
+				temp = (temp & 0xFFFFFE01) + (48 << 1);
+				MOCA_WR(0x10100050, temp);
+
+				// Then Restart the PLL.
+
+				//`CLKGEN_REG_START + `CLKGEN_PLL_SYS0_PLL_RESET (32'h1010002C) [0] = 1’b1
+				MOCA_SET(0x1010002c, 1);
+				//`CLKGEN_REG_START + `CLKGEN_PLL_SYS1_PLL_RESET (32'h1010006C) [0] = 1’b1
+				MOCA_SET(0x1010006c, 1);
+
+				udelay(1);
+
+				//`CLKGEN_REG_START + `CLKGEN_PLL_SYS0_PLL_RESET (32'h1010002C) [0] = 1’b0
+				MOCA_UNSET(0x1010002c, 1);
+				//`CLKGEN_REG_START + `CLKGEN_PLL_SYS1_PLL_RESET (32'h1010006C) [0] = 1’b0
+				MOCA_UNSET(0x1010006c, 1);
+			}
+		}
+
+		// CLKGEN_PLL_SYS1_PLL_SSC_MODE_CONTROL_HIGH
+		data = MOCA_RD(0x10100070);
+		data = (data & 0xFFFF0000) | 0x7dd;
+		MOCA_WR(0x10100070, data);
+
+		// CLKGEN_PLL_SYS1_PLL_SSC_MODE_CONTROL_LOW
+		data = MOCA_RD(0x10100074);
+		data = (data & 0xffc00000) | 0x3d71;
+		MOCA_WR(0x10100074, data);
+
+		// CLKGEN_PLL_SYS1_PLL_SSC_MODE_CONTROL_LOW
+		MOCA_SET(0x10100074, (1 << 22));
+	}
+
+
+	if (priv->hw_rev <= HWREV_MOCA_20_GEN21) {
+	/* clear junk out of GP0/GP1 */
+		MOCA_WR(priv->base + priv->regs->gp0_offset, 0xffffffff);
+		MOCA_WR(priv->base + priv->regs->gp1_offset, 0x0);
+		/* set up activity LED for 50% duty cycle */
+		MOCA_WR(priv->base + priv->regs->led_ctrl_offset,
+			0x40004000);
+	}
+
+	/* enable DMA completion interrupts */
+	mask = M2H_REQ | M2H_RESP | M2H_ASSERT | M2H_WDT_CPU1 |
+		M2H_NEXTCHUNK | M2H_DMA;
+
+	if (priv->hw_rev >= HWREV_MOCA_20_GEN21)
+		mask |= M2H_WDT_CPU0 | M2H_NEXTCHUNK_CPU0 |
+			M2H_REQ_CPU0 | M2H_RESP_CPU0 | M2H_ASSERT_CPU0;
+
+	MOCA_WR(priv->base + priv->regs->ringbell_offset, 0);
+	MOCA_WR(priv->base + priv->regs->l2_mask_clear_offset, mask);
+	MOCA_RD(priv->base + priv->regs->l2_mask_clear_offset);
+
+
+	/* Set pinmuxing for MoCA interrupt and flow control */
+	MOCA_UNSET(0x10404110, 0xF00000FF);
+	MOCA_SET(0x10404110, 0x10000022);
+ 
+	/* Set pinmuxing for MoCA IIC control */
+	if (((pMocaData->chip_id & 0xFFFFFFF0) == 0x680200C0) || 
+	    ((pMocaData->chip_id & 0xFFFFFFF0) == 0x680300C0))
+	{
+		MOCA_UNSET(0x10404100, 0xFF);  // pin muxing
+		MOCA_SET(0x10404100, 0x22);  // pin muxing
+	}
+
+	MOCA_WR(0x100b0318, 2);
+
+	if (action == MOCA_DISABLE && priv->enabled) {
+		priv->enabled = 0;
+		clk_disable(priv->clk);
+	}
+}
+
+static void moca_ringbell(struct moca_priv_data *priv, uint32_t mask)
+{
+	MOCA_WR(priv->base + priv->regs->ringbell_offset, mask);
+}
+
+static uint32_t moca_start_mips(struct moca_priv_data *priv, unsigned int cpu)
+{
+	if (priv->hw_rev == HWREV_MOCA_20_GEN22) {
+		if (cpu == 1)
+			MOCA_UNSET(priv->base + priv->regs->sw_reset_offset,
+				(1 << 0));
+		else {
+			moca_mmp_init(priv, 1);
+			MOCA_UNSET(priv->base + priv->regs->sw_reset_offset,
+				(1 << 2));
+		}
+	} else
+		MOCA_UNSET(priv->base + priv->regs->sw_reset_offset, (1 << 0));
+	MOCA_RD(priv->base + priv->regs->sw_reset_offset);
+
+	return(0);
+}
+
+static void moca_m2m_xfer(struct moca_priv_data *priv,
+	uint32_t dst, uint32_t src, uint32_t ctl)
+{
+	uint32_t status;
+
+	MOCA_WR(priv->base + priv->regs->m2m_src_offset, src);
+	MOCA_WR(priv->base + priv->regs->m2m_dst_offset, dst);
+	MOCA_WR(priv->base + priv->regs->m2m_status_offset, 0);
+	MOCA_RD(priv->base + priv->regs->m2m_status_offset);
+	MOCA_WR(priv->base + priv->regs->m2m_cmd_offset, ctl);
+
+	do {
+		status = MOCA_RD(priv->base + priv->regs->m2m_status_offset);
+	} while(status == 0);
+
+}
+
+static void moca_write_mem(struct moca_priv_data *priv,
+	uint32_t dst_offset, void *src, unsigned int len)
+{
+	struct moca_platform_data *pd = priv->pdev->dev.platform_data;
+
+	if((dst_offset >= priv->regs->cntl_mem_offset+priv->regs->cntl_mem_size) ||
+		((dst_offset + len) > priv->regs->cntl_mem_offset+priv->regs->cntl_mem_size)) {
+		printk(KERN_WARNING "%s: copy past end of cntl memory: %08x\n",
+			__FUNCTION__, dst_offset);
+		return;
+	}
+
+	if ( 1 == pd->use_dma )
+	{
+		dma_addr_t pa;
+
+		pa = dma_map_single(&priv->pdev->dev, src, len, DMA_TO_DEVICE);
+		mutex_lock(&priv->copy_mutex);
+		moca_m2m_xfer(priv, dst_offset + priv->regs->data_mem_offset, (uint32_t)pa, len | M2M_WRITE);
+		mutex_unlock(&priv->copy_mutex);
+		dma_unmap_single(&priv->pdev->dev, pa, len, DMA_TO_DEVICE);
+	}
+	else
+	{
+		uintptr_t addr = (uintptr_t)priv->base + priv->regs->data_mem_offset + dst_offset;
+		uint32_t *data = src;
+		int i;
+
+		mutex_lock(&priv->copy_mutex);
+		if (((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 1)
+		{
+			src = data;
+			MOCA_WR_BLOCK(addr, src, len);
+		}
+		else
+		{
+			for(i = 0; i < len; i += 4, addr += 4, data++)
+				MOCA_WR(addr, *data);
+			MOCA_RD(addr - 4);	/* flush write */
+		}
+
+		mutex_unlock(&priv->copy_mutex);
+	}
+}
+
+static void moca_read_mem(struct moca_priv_data *priv,
+	void *dst, uint32_t src_offset, unsigned int len)
+{
+	struct moca_platform_data *pd = priv->pdev->dev.platform_data;
+    
+	if((src_offset >= priv->regs->cntl_mem_offset+priv->regs->cntl_mem_size) ||
+		((src_offset + len) > priv->regs->cntl_mem_offset+priv->regs->cntl_mem_size)) {
+		printk(KERN_WARNING "%s: copy past end of cntl memory: %08x\n",
+			__FUNCTION__, src_offset);
+		return;
+	}
+
+	if ( 1 == pd->use_dma )
+	{
+		dma_addr_t pa;
+
+		pa = dma_map_single(&priv->pdev->dev, dst, len, DMA_FROM_DEVICE);
+		mutex_lock(&priv->copy_mutex);
+		moca_m2m_xfer(priv, (uint32_t)pa, src_offset + priv->regs->data_mem_offset, len | M2M_READ);
+		mutex_unlock(&priv->copy_mutex);
+		dma_unmap_single(&priv->pdev->dev, pa, len, DMA_FROM_DEVICE);
+	}
+	else
+	{
+		uintptr_t addr = priv->regs->data_mem_offset + src_offset;
+		uint32_t *data = dst;
+		int i;
+
+		mutex_lock(&priv->copy_mutex);
+		if (((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 1)
+		{
+			MOCA_RD_BLOCK((uintptr_t)priv->base + addr, dst, len);
+		}
+		else
+		{
+			for(i = 0; i < len; i += 4, addr += 4, data++)
+				*data = MOCA_RD((uintptr_t)priv->base + addr);
+		}
+		mutex_unlock(&priv->copy_mutex);
+	}
+}
+
+static void moca_write_sg(struct moca_priv_data *priv,
+	uint32_t dst_offset, struct scatterlist *sg, int nents)
+{
+	int j;
+	uintptr_t addr = priv->regs->data_mem_offset + dst_offset;
+	struct moca_platform_data *pd = priv->pdev->dev.platform_data;
+
+	dma_map_sg(&priv->pdev->dev, sg, nents, DMA_TO_DEVICE);
+
+	mutex_lock(&priv->copy_mutex);
+	for(j = 0; j < nents; j++)
+	{
+		if ( 1 == pd->use_dma )
+		{
+		    // printk("XXX copying page %d, PA %08x\n", j, (int)sg[j].dma_address);
+			moca_m2m_xfer(priv, addr, (uint32_t)sg[j].dma_address, 
+				sg[j].length | M2M_WRITE);
+
+			addr += sg[j].length;
+		}
+		else
+		{
+			unsigned long *data = (void *)phys_to_virt(sg[j].dma_address);
+         //printk("%s: Writing 0x%lx to addr 0x%08lx (len = %d)\n", __FUNCTION__, *data, ((unsigned long)priv->base) + addr, sg[j].length);
+			MOCA_WR_BLOCK(((unsigned long)priv->base) + addr, data, sg[j].length);
+			addr += sg[j].length;
+		}
+	}
+	mutex_unlock(&priv->copy_mutex);
+
+	dma_unmap_sg(&priv->pdev->dev, sg, nents, DMA_TO_DEVICE);
+}
+
+/* NOTE: this function is not tested */
+#if 0
+static void moca_read_sg(struct moca_priv_data *priv,
+	uint32_t src_offset, struct scatterlist *sg, int nents)
+{
+	int j;
+	uintptr_t addr = priv->data_mem_offset + src_offset;
+
+	dma_map_sg(&priv->pdev->dev, sg, nents, DMA_FROM_DEVICE);
+
+	mutex_lock(&priv->copy_mutex);
+	for(j = 0; j < nents; j++) {
+#if 0 //USE_DMA
+		 printk("XXX copying page %d, PA %08x\n", j, (int)sg[j].dma_address);
+		moca_m2m_xfer(priv, addr, (uint32_t)sg[j].dma_address,
+			sg[j].length | M2M_READ);
+
+		addr += sg[j].length;
+#else
+		uint32_t *data = (void *)phys_to_virt(sg[j].dma_address);
+		unsigned int len = sg[j].length;
+		int i;
+
+		for(i = 0; i < len; i += 4, addr += 4, data++) {
+			*data = cpu_to_be32(
+				MOCA_RD((uintptr_t)priv->base + addr));
+			//printk("MoCA READ: AD 0x%x  = 0x%x (0x%x)\n", (priv->base + addr), MOCA_RD((uintptr_t)priv->base + addr), *data);
+		 }
+#endif
+	}
+	mutex_unlock(&priv->copy_mutex);
+
+	dma_unmap_sg(&priv->pdev->dev, sg, nents, DMA_FROM_DEVICE);
+}
+#endif
+
+static void moca_read_mac_addr(struct moca_priv_data *priv, uint32_t * hi, uint32_t * lo)
+{
+	struct net_device * pdev ;
+	char					 mocaName[7] ;
+
+	if (priv == NULL)
+		sprintf (mocaName, "moca%u", 0) ;
+	else
+		sprintf (mocaName, "moca%u", ((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId) ;
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 29)
+	pdev = dev_get_by_name ( &init_net, mocaName ) ;
+#else
+	pdev = dev_get_by_name ( mocaName ) ;
+#endif
+
+	if ((pdev != NULL) && (lo != NULL) && (hi != NULL)) {
+		mac_to_u32(hi, lo, pdev->dev_addr);
+	}
+}
+
+
+#if defined(DSL_MOCA)
+
+/*
+ * This helper function was added to allow the enet driver to compile in
+ * consumer environment for 68xx profiles.
+ */
+void moca_get_fc_bits(void * arg, unsigned long *moca_fc_reg)
+{
+	struct moca_priv_data *     priv;
+	struct moca_platform_data * pMocaData;
+	unsigned long               flags;
+
+	if (arg == NULL) {
+		return;
+	}
+
+	priv = (struct moca_priv_data *) arg;
+	pMocaData = (struct moca_platform_data *)priv->pdev->dev.platform_data;
+
+	*moca_fc_reg = 0;
+	if (priv != NULL)
+	{
+		/* We can't read moca core regs unless the core's clocks are on. */
+		spin_lock_irqsave(&priv->clock_lock, flags);
+		if (priv->running) {
+			*moca_fc_reg = MOCA_RD(priv->base+priv->regs->sideband_gmii_fc_offset);
+		}
+		spin_unlock_irqrestore(&priv->clock_lock, flags);
+	}
+}
+
+#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);
+
+static void moca_mem_init_680xC0( struct moca_priv_data *priv )
+{
+	// De-assert reset (all memories are OFF by default Force_SP_off =1, Force_Rf_off =1)
+	MOCA_UNSET(priv->base + priv->regs->sw_reset_offset, ((1 << 15) | (1 << 16)));
+
+	moca_pmb_delay(priv);
+	moca_pmb_control(priv, PMB_COMMAND_ALL_OFF);
+
+	//Write Force_SP_on =0, Force_SP_off =0, Force_RF_on =0, Force_RF_off =0
+	MOCA_UNSET(priv->base + 0x001ffd14, ((1 << 10) | (1 << 11)));
+	moca_pmb_control(priv, PMB_COMMAND_PARTIAL_ON);
+}
+
+static int  hw_specific_init( struct moca_priv_data *priv )
+{
+#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
+	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
+
+	if (((pMocaData->chip_id & 0xFFFFFFF0) == 0x680200C0) || ((pMocaData->chip_id & 0xFFFFFFF0) == 0x680300C0))
+	{
+		priv->i2c_base = NULL; 
+
+		/* Initialize 680x CO memory */
+		moca_mem_init_680xC0(priv);
+	}
+
+	pMocaData->hw_rev = HWREV_MOCA_20_GEN22;
+
+	/* Power down all LEAP memories */
+	MOCA_WR(0x101000e4, 0x6); // CLKGEN_LEAP_TOP_INST_DATA   
+	MOCA_WR(0x101000e8, 0x6); // CLKGEN_LEAP_TOP_INST_HAB 
+	MOCA_WR(0x101000ec, 0x6); // CLKGEN_LEAP_TOP_INST_PROG0
+	MOCA_WR(0x101000f0, 0x6); // CLKGEN_LEAP_TOP_INST_PROG1   
+	MOCA_WR(0x101000f4, 0x6); // CLKGEN_LEAP_TOP_INST_PROG2  
+	MOCA_WR(0x101000f8, 0x6); // CLKGEN_LEAP_TOP_INST_ROM
+	MOCA_WR(0x101000fc, 0x6); // CLKGEN_LEAP_TOP_INST_SHARED  
+	MOCA_WR(0x10100164, 0x3); // CLKGEN_SYS_CTRL_INST_POWER_SWITCH_MEMORY 
+
+//	bcmenet_register_moca_fc_bits_cb(
+//		moca_get_fc_bits, pMocaData->use_spi ? 1 : 0, (void *)priv);
+#endif
+
+	return 0;
+}
+
+static int moca_platform_dev_register(void)
+{
+	struct moca_platform_data *pMocaData;
+	struct platform_device *pPlatformDev;
+	BP_MOCA_INFO mocaInfo[BP_MOCA_MAX_NUM];
+	int mocaChipNum = BP_MOCA_MAX_NUM;
+	int i;
+	int ret = 0;   
+
+	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:
+				pMocaData = &moca_wan_data;
+				pPlatformDev = &moca_wan_plat_dev;
+				break;
+
+			case BP_MOCA_TYPE_LAN:
+				pMocaData = &moca_lan_data;
+				pPlatformDev = &moca_lan_plat_dev;
+				break;
+
+			default:
+				printk(KERN_ERR "bmoca: unrecognized MoCA type %d\n",
+					mocaInfo[i].type);
+				return(-1);
+				break;
+		}
+
+		ret = platform_device_register(pPlatformDev);
+		if (ret < 0) {
+			spi_unregister_driver(&bmoca_spi_driver);
+			return(ret);
+		}
+		else {
+			pMocaData->devId = i;
+
+			/* Map the board params RF Band to the bmoca.h value */
+			switch (mocaInfo[i].rfBand)
+			{
+				case BP_MOCA_RF_BAND_D_LOW:
+					pMocaData->rf_band = MOCA_BAND_D_LOW;
+					break;
+				case BP_MOCA_RF_BAND_D_HIGH:
+					pMocaData->rf_band = MOCA_BAND_D_HIGH;
+					break;
+				case BP_MOCA_RF_BAND_EXT_D:
+					pMocaData->rf_band = MOCA_BAND_EXT_D;
+					break;
+				case BP_MOCA_RF_BAND_E:
+					pMocaData->rf_band = MOCA_BAND_E;
+					break;
+				case BP_MOCA_RF_BAND_F:    
+					pMocaData->rf_band = MOCA_BAND_F;
+					break;
+				default:
+					/* Do nothing */
+					break;
+			}
+			printk(KERN_INFO "bmoca: Found MoCA device %d/%d  RF Band %d\n",
+				i, mocaChipNum, mocaInfo[i].rfBand);
+		}
+	}
+
+	return(ret);
+}
+
+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);
+
+	if (moca_wan_data.devId != MOCA_DEVICE_ID_UNREGISTERED)
+		platform_device_unregister(&moca_wan_plat_dev);
+}
+
+static void moca_3450_write(struct moca_priv_data *priv, u8 addr, u32 data)
+{
+	/* comment out for now. We don't use i2c on the 63268BHR board */
+#ifdef MOCA_3450_USE_I2C
+	if (((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0)
+		bcm3450_write_reg(addr, data);
+	else
+#endif
+	{
+		if (priv->i2c_base != NULL)
+			moca_3450_write_i2c(priv, addr, data);
+	}
+}
+
+static u32 moca_3450_read(struct moca_priv_data *priv, u8 addr)
+{
+	/* comment out for now. We don't use i2c on the 63268BHR board */
+#ifdef MOCA_3450_USE_I2C
+	if (((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0)
+		return(bcm3450_read_reg(addr));
+	else
+#endif
+	{
+		if (priv->i2c_base != NULL)
+			return(moca_3450_read_i2c(priv, addr));
+		else
+			return(0xffffffff);
+	}
+}
+
+/*
+ * PM STUBS
+ */
+
+struct clk *clk_get(struct device *dev, const char *id)
+{
+	// We're not actually using the "struct clk" for anything
+	// We'll use our own structure
+	struct moca_680x_clk * pclk = kzalloc(sizeof(struct moca_680x_clk), GFP_KERNEL);
+
+	pclk->dev = dev;
+
+	if (!strcmp(id, "moca-cpu"))
+		pclk->clock_num = MOCA_CPU_CLOCK_NUM;
+	else if (!strcmp(id, "moca-phy"))
+		pclk->clock_num = MOCA_PHY_CLOCK_NUM;
+	else
+	{
+		kfree(pclk);
+		return(NULL);
+	}
+
+	return((struct clk *)pclk);
+}
+
+int clk_enable(struct clk *clk)
+{
+	return 0;
+}
+
+void clk_disable(struct clk *clk)
+{
+}
+
+void clk_put(struct clk *clk)
+{
+	kfree((struct moca_680x_clk *)clk);
+}
+
+struct moca_6802c0_clock_params
+{
+	uint32_t        cpu_hz;
+	uint32_t        pdiv;
+	uint32_t        ndiv;
+	uint32_t        pll_mdivs[6];
+};
+
+#define NUM_6802C0_CLOCK_OPTIONS 2
+struct moca_6802c0_clock_params moca_6802c0_clock_params[NUM_6802C0_CLOCK_OPTIONS] =
+{
+	{  // VCO of 2100, default
+		420000000,             // cpu_hz
+		1,                     // pdiv
+		42,                    // ndiv
+		{5, 21, 7, 7, 42, 42}  // pll_mdivs[6]
+	},
+	{  // VCO of 2400
+		400000000,             // cpu_hz
+		1,                     // pdiv
+		48,                    // ndiv
+		{6, 24, 8, 8, 48, 48}  // pll_mdivs[6]
+	},
+};
+
+int clk_set_rate(struct clk *clk, unsigned long rate)
+{
+	// The MOCA_RD/MOCA_WR macros need a valid 'priv->pdev->dev'
+	static struct moca_priv_data dummy_priv; 
+	static struct platform_device dummy_pd;
+	struct moca_priv_data *priv = &dummy_priv;
+	struct moca_680x_clk * pclk = (struct moca_680x_clk *) clk;
+	struct moca_platform_data * pMocaData = (struct moca_platform_data *)pclk->dev->platform_data;
+	struct moca_6802c0_clock_params * p_clock_data = &moca_6802c0_clock_params[0];
+	uint32_t i;
+	uint32_t addr;
+	uint32_t data;
+	int ret = -1;
+
+	priv->pdev = &dummy_pd;
+	priv->pdev->dev = *pclk->dev;
+
+	if (pclk->clock_num == MOCA_CPU_CLOCK_NUM)
+	{
+		if ((pMocaData->chip_id & 0xFFFFFFF0) == 0x680200C0)
+		{
+			for (i = 0; i < NUM_6802C0_CLOCK_OPTIONS; i++)
+			{
+				if (moca_6802c0_clock_params[i].cpu_hz == rate)
+				{
+					p_clock_data = &moca_6802c0_clock_params[i];
+					ret = 0;
+				}
+			}
+
+			// 1. Set POST_DIVIDER_HOLD_CHx (bit [12] in each PLL_CHANNEL_CTRL_CH_x 
+			//    register)  // this will zero the output channels
+			for (addr = 0x1010003c; addr <= 0x10100050; addr += 4)
+			{
+				MOCA_SET(addr, (1 << 12));
+			}
+
+			//2. Program new PDIV/NDIV value, this will lose lock and 
+			//   trigger a new PLL lock process for a new VCO frequency
+			MOCA_WR(0x10100058, ((p_clock_data->pdiv << 10) | p_clock_data->ndiv));
+
+			//3. Wait >10 usec for lock time // max lock time per data sheet is 460/Fref, 
+			//   Or alternatively monitor CLKGEN_PLL_SYS*_PLL_LOCK_STATUS to check if PLL has locked
+			data = 0;
+			i = 0;
+			while ((data & 0x1) == 0)
+			{
+				/* This typically is only read once */
+				data = MOCA_RD(0x10100060); // CLKGEN_PLL_SYS1_PLL_LOCK_STATUS
+
+				if (i++ > 10)
+				{
+					printk("MoCA SYS1 PLL NOT LOCKED!\n");
+					break;
+				}
+			}
+
+			//4. Configure new MDIV value along with set POST_DIVIDER_LOAD_EN_CHx 
+			//   (bit [13]=1, while keep bit[12]=1) in each PLL_CHANNEL_CTRL_CH_x register
+			i = 0;
+			for (addr = 0x1010003c; addr <= 0x10100050; addr += 4)
+			{
+				data = MOCA_RD(addr);
+				data |= (1 << 13);
+				data &= ~(0xFF << 1);
+				data |= (p_clock_data->pll_mdivs[i] << 1);
+				MOCA_WR(addr, data);
+				i++;
+			}
+
+			//5. Clear bits [12] and bit [13] in each PLL_CHANNEL_CTRL_CH_x
+			for (addr = 0x1010003c; addr <= 0x10100050; addr += 4)
+			{
+				MOCA_UNSET(addr, ((1 << 13) | (1 << 12)));
+			}
+
+		}
+	}
+
+	return(ret);
+}
+
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 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