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] = 10d48
+ temp = MOCA_RD(0x10100058);
+ temp = (temp & 0xFFFFFC00) + 48;
+ MOCA_WR(0x10100058, temp);
+
+ //`CLKGEN_REG_START + `CLKGEN_PLL_SYS0_PLL_DIV (32'h10100018) [09:00] = 10d40
+ 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] = 8d48
+ 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] = 8d48
+ 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] = 1b1
+ MOCA_SET(0x1010002c, 1);
+ //`CLKGEN_REG_START + `CLKGEN_PLL_SYS1_PLL_RESET (32'h1010006C) [0] = 1b1
+ MOCA_SET(0x1010006c, 1);
+
+ udelay(1);
+
+ //`CLKGEN_REG_START + `CLKGEN_PLL_SYS0_PLL_RESET (32'h1010002C) [0] = 1b0
+ MOCA_UNSET(0x1010002c, 1);
+ //`CLKGEN_REG_START + `CLKGEN_PLL_SYS1_PLL_RESET (32'h1010006C) [0] = 1b0
+ 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