Merge MoCA 2.10.6.20
Conflicts:
3.3/bmoca.c
3.8/bmoca.c
bmoca-6802.c
Change-Id: I9b83acab299893ff0ab6ef1d05b6ef5337e4dd93
diff --git a/3.3/bmoca.c b/3.3/bmoca.c
index 86b59b6..266f17f 100644
--- a/3.3/bmoca.c
+++ b/3.3/bmoca.c
@@ -38,6 +38,8 @@
#include <linux/scatterlist.h>
#include <linux/clk.h>
#include <linux/io.h>
+#include <linux/slab.h>
+#include <net/net_namespace.h>
#define DRV_VERSION 0x00040000
#define DRV_BUILD_NUMBER 0x20110831
@@ -46,6 +48,8 @@
#if defined(CONFIG_BRCMSTB)
#define MOCA6816 0
+#undef DSL_MOCA
+#undef CONFIG_BCM_6802_MoCA
#include <linux/bmoca.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
@@ -58,14 +62,14 @@
#define MOCA6816 1
#include "bmoca.h"
-#include <boardparms.h>
-#include <bcm3450.h>
+//#include <boardparms.h>
+//#include <bcm3450.h>
/* board.h cannot declare spinlock, so do it here */
extern spinlock_t bcm_gpio_spinlock;
#include <linux/netdevice.h>
#if defined(CONFIG_BCM_6802_MoCA)
-#include <board.h>
+//#include <board.h>
#endif
#else
@@ -571,6 +575,11 @@
#define moca_clk_put clk_put
#define moca_clk_get clk_get
+static int hw_specific_init(struct moca_priv_data *priv)
+{
+ return 0;
+}
+
static void moca_hw_reset(struct moca_priv_data *priv)
{
/* disable and clear all interrupts */
@@ -672,6 +681,8 @@
static void moca_enable_irq(struct moca_priv_data *priv)
{
+ if (!priv->enabled) return;
+
/* unmask everything */
u32 mask = M2H_REQ | M2H_RESP | M2H_ASSERT | M2H_WDT_CPU1 |
M2H_NEXTCHUNK | M2H_DMA;
@@ -1174,6 +1185,7 @@
}
}
if (data & 3) {
+ printk("ie=%08X\n", data);
msg = "IE is not a multiple of 4 bytes";
goto bad;
}
@@ -1571,10 +1583,17 @@
static irqreturn_t moca_interrupt(int irq, void *arg)
{
struct moca_priv_data *priv = arg;
+ struct moca_platform_data *pd;
+
+ if (!priv || !priv->enabled || !priv->pdev ||
+ !priv->pdev->dev.platform_data) {
+ printk("moca_interrupt: can't go yet.\n");
+ moca_disable_irq(priv);
+ return IRQ_HANDLED;
+ }
#if MOCA6816
- struct moca_platform_data *pd =
- (struct moca_platform_data *)priv->pdev->dev.platform_data;
+ pd = (struct moca_platform_data *)priv->pdev->dev.platform_data;
/*
* If the driver is for an external chip then the work function needs
@@ -1617,7 +1636,6 @@
{
struct bsc_regs *bsc = priv->i2c_base;
long timeout = HZ / 1000; /* 1 ms */
- DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait);
int i = 0;
do {
@@ -1631,7 +1649,7 @@
__func__);
return -1;
}
- sleep_on_timeout(&wait, timeout ? timeout : 1);
+ schedule_timeout_interruptible(timeout ? timeout : 1);
} while (1);
}
@@ -1727,6 +1745,10 @@
{
u32 data;
+ /* some platforms connect the i2c directly to the MoCA core */
+ if (!priv->i2c_base)
+ return;
+
mutex_lock(&priv->moca_i2c_mutex);
if (action == MOCA_ENABLE) {
@@ -1736,6 +1758,7 @@
/* verify chip ID */
data = moca_3450_read(priv, BCM3450_CHIP_ID);
+ pr_info("bcm3450 chip id is: %08x\n", data);
if (data != 0x3450)
printk(KERN_WARNING "%s: invalid 3450 chip ID 0x%08x\n",
__func__, data);
@@ -1831,8 +1854,10 @@
if (copy_from_user(&x, (void __user *)xfer_uaddr, sizeof(x)))
return -EFAULT;
+#if !DSL_MOCA
if (moca_range_ok(priv, x.moca_addr, x.len) < 0)
return -EINVAL;
+#endif
src = (uintptr_t)priv->base + x.moca_addr;
dst = (void *)(unsigned long)x.buf;
@@ -1855,8 +1880,10 @@
if (copy_from_user(&x, (void __user *)xfer_uaddr, sizeof(x)))
return -EFAULT;
+#if !DSL_MOCA
if (moca_range_ok(priv, x.moca_addr, x.len) < 0)
return -EINVAL;
+#endif
dst = (uintptr_t)priv->base + x.moca_addr;
src = (void *)(unsigned long)x.buf;
@@ -2105,12 +2132,10 @@
ret = 0;
break;
case MOCA_IOCTL_READMEM:
- if (priv->running)
- ret = moca_ioctl_readmem(priv, arg);
+ ret = moca_ioctl_readmem(priv, arg);
break;
case MOCA_IOCTL_WRITEMEM:
- if (priv->running)
- ret = moca_ioctl_writemem(priv, arg);
+ ret = moca_ioctl_writemem(priv, arg);
break;
case MOCA_IOCTL_GET_DRV_INFO_V2:
ret = moca_ioctl_get_drv_info_v2(priv, arg);
@@ -2151,7 +2176,9 @@
case MOCA_IOCTL_CLK_SSC:
ret = moca_clk_ssc(priv, (unsigned int *)arg);
break;
-
+ default:
+ pr_warn("moca_ioctl: unrecognized cmd\n");
+ break;
}
mutex_unlock(&priv->dev_mutex);
@@ -2399,6 +2426,11 @@
int minor, err = 0;
struct moca_platform_data *pd = pdev->dev.platform_data;
+ if (pd->use_spi && !pd->spi) {
+ pr_err("moca: use_spi=1, but no bmoca SPI device found.\n");
+ return -EINVAL;
+ }
+
priv = kzalloc(sizeof(*priv), GFP_KERNEL);
if (!priv) {
printk(KERN_ERR "%s: out of memory\n", __func__);
@@ -2478,10 +2510,9 @@
goto bad;
}
-
#if defined(CONFIG_BCM_6802_MoCA)
priv->base = (void *)mres->start;
- priv->irq = ires->start;
+ priv->irq = pd->spi->irq;
priv->i2c_base = (void *)pd->bcm3450_i2c_base;
#else
priv->base = ioremap(mres->start, mres->end - mres->start + 1);
@@ -2491,25 +2522,12 @@
#if MOCA6816
moca_read_mac_addr(priv, &pd->macaddr_hi, &pd->macaddr_lo);
+#endif
if (hw_specific_init(priv))
goto bad;
-#endif
/* leave core in reset until we get an ioctl */
- moca_hw_reset(priv);
-
-#if defined(CONFIG_BCM_6802_MoCA)
- kerSysRegisterMocaHostIntrCallback(
- (MocaHostIntrCallback) moca_interrupt,
- (void *)priv, pd->devId);
-#else
- if (request_irq(priv->irq, moca_interrupt, 0, "moca", priv) < 0) {
- printk(KERN_WARNING "%s: can't request interrupt\n",
- __func__);
- err = -EIO;
- goto bad2;
- }
-#endif
+ //moca_hw_reset(priv); // moca_hw_init(MOCA_ENABLE) does this anyway
moca_hw_init(priv, MOCA_ENABLE);
moca_disable_irq(priv);
@@ -2518,7 +2536,7 @@
printk(KERN_INFO "bmoca: adding minor #%d at base 0x%08llx, IRQ %d, "
"I2C 0x%08llx/0x%02x\n", priv->minor,
- (unsigned long long)mres->start, ires->start,
+ (unsigned long long)mres->start, priv->irq,
(unsigned long long)pd->bcm3450_i2c_base, pd->bcm3450_i2c_addr);
minor_tbl[priv->minor] = priv;
@@ -2529,16 +2547,26 @@
priv->dev = NULL;
}
+ moca_enable_irq(priv);
+ if (request_irq(priv->irq, moca_interrupt, 0, "moca", priv) < 0) {
+ printk(KERN_WARNING "%s: can't request interrupt\n",
+ __func__);
+ err = -EIO;
+ goto bad2;
+ }
+
if (err)
goto bad2;
return 0;
bad2:
- if (priv->base)
- iounmap(priv->base);
- if (priv->i2c_base)
- iounmap(priv->i2c_base);
+ if (!pd->use_spi) {
+ if (priv->base)
+ iounmap(priv->base);
+ if (priv->i2c_base)
+ iounmap(priv->i2c_base);
+ }
bad:
kfree(priv);
return err;
@@ -2547,6 +2575,7 @@
static int moca_remove(struct platform_device *pdev)
{
struct moca_priv_data *priv = dev_get_drvdata(&pdev->dev);
+ struct moca_platform_data *pd = pdev->dev.platform_data;
struct clk *clk = priv->clk;
struct clk *phy_clk = priv->phy_clk;
struct clk *cpu_clk = priv->cpu_clk;
@@ -2559,8 +2588,10 @@
if (priv->irq)
free_irq(priv->irq, priv);
- iounmap(priv->i2c_base);
- iounmap(priv->base);
+ if (!pd->use_spi) {
+ iounmap(priv->i2c_base);
+ iounmap(priv->base);
+ }
kfree(priv);
moca_clk_put(clk);
@@ -2649,13 +2680,12 @@
static void moca_exit(void)
{
- class_destroy(moca_class);
unregister_chrdev(MOCA_MAJOR, MOCA_CLASS);
platform_driver_unregister(&moca_plat_drv);
#if MOCA6816
moca_platform_dev_unregister();
#endif
-
+ class_destroy(moca_class);
}
module_init(moca_init);
diff --git a/3.8/bmoca.c b/3.8/bmoca.c
index 471e4ed..700c626 100644
--- a/3.8/bmoca.c
+++ b/3.8/bmoca.c
@@ -1550,7 +1550,6 @@
{
struct bsc_regs *bsc = priv->i2c_base;
long timeout = HZ / 1000; /* 1ms */
- DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait);
int i = 0;
do {
@@ -1563,7 +1562,7 @@
dev_warn(priv->dev, "3450 I2C timed out\n");
return -1;
}
- sleep_on_timeout(&wait, timeout ? timeout : 1);
+ schedule_timeout_interruptible(timeout ? timeout : 1);
} while (1);
}
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..6538114
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,5 @@
+override EXTRA_CFLAGS += -I$M/../include -DCONFIG_BCM_6802_MoCA=1 -DDSL_MOCA=1
+
+all: modules
+
+obj-m += bmoca.o
diff --git a/bbsi.h b/bbsi.h
new file mode 100644
index 0000000..afd051f
--- /dev/null
+++ b/bbsi.h
@@ -0,0 +1,485 @@
+/*
+ * Copyright (C) 2013-2014 Google Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+#ifndef __BBSI_H
+#define __BBSI_H
+
+/*
+ * Resources:
+ * BCM6803 Data Sheet (Document ID: 6803-DS205-R), Chapter 5: Host Interface and CPU
+ * BBVLSI Serial Interface (BBSI) (Document ID: BBSI-UM200-R)
+ */
+
+#include <linux/netdevice.h>
+#include <linux/spi/spi.h>
+
+#ifndef KSEG1
+#define KSEG1 0 // just to appease non-MIPS CPUs. Not really used.
+#endif
+
+#define BP_MOCA_MAX_NUM 1
+
+#define SPI_FIFO_LEN 32
+
+#define BBSI_COMMAND_BYTE 0x80
+#define STATUS_REGISTER_ADDR 0x06
+#define CONFIG_REGISTER_ADDR 0x07
+#define DATA0_REGISTER_ADDR 0x0c
+
+#define READ_RBUS (1<<0)
+#define SPECULATIVE_READ_EN (1<<1)
+#define NO_RBUS_ADDR_INC (1<<2)
+
+#define STATUS_BUSY (1<<4)
+
+enum bbsi_operation_state {
+ START,
+ COMMAND_STATE,
+ DATA_STATE,
+ POLLSTATUS_STATE,
+ DONE,
+ FAIL,
+};
+
+struct bbsi_operation {
+ enum bbsi_operation_state state;
+ struct spi_device *spi;
+ struct spi_message sm;
+ struct spi_transfer st[2];
+ /* address on MoCA chip we want to read from/write to */
+ uint32_t addr;
+ /* len must be a multiple of 4 */
+ size_t len;
+ uint8_t *data;
+ /* number of bytes already read or written. data_len <= len */
+ size_t data_len;
+ uint8_t tx_buf[8];
+ uint8_t rx_buf[SPI_FIFO_LEN - 2];
+ /* See interpret_data() for a definition of rx_state */
+ int rx_state;
+ struct completion done;
+};
+
+/*
+ * The exact values here don't matter, as they're translated into "real"
+ * values before talking to mocad. This is just for the device registration
+ * tables.
+ */
+enum {
+ BP_MOCA_TYPE_WAN,
+ BP_MOCA_TYPE_LAN,
+};
+
+enum {
+ BP_MOCA_RF_BAND_D_LOW,
+ BP_MOCA_RF_BAND_D_HIGH,
+ BP_MOCA_RF_BAND_EXT_D,
+ BP_MOCA_RF_BAND_E,
+ BP_MOCA_RF_BAND_F,
+};
+
+typedef struct BpMocaInfo {
+ int type;
+ int rfBand;
+} BP_MOCA_INFO;
+
+static void BpGetMocaInfo(BP_MOCA_INFO *chips, int *nchips) {
+ if (*nchips >= 1) {
+ *nchips = 1;
+ chips[0].type = BP_MOCA_TYPE_LAN;
+ chips[0].rfBand = BP_MOCA_RF_BAND_E;
+ }
+}
+
+// TODO(apenwarr): don't make this global.
+// Or fix the driver to just only enable/disable interrupts at the right
+// times.
+static int irq_disabled = 0;
+
+static void kerSysMocaHostIntrEnable(struct spi_device *spi) {
+ if (irq_disabled == 1) {
+ irq_disabled = 0;
+ enable_irq(spi->irq);
+ }
+}
+
+static void kerSysMocaHostIntrDisable(struct spi_device *spi) {
+ if (irq_disabled == 0) {
+ disable_irq_nosync(spi->irq);
+ irq_disabled = 1;
+ }
+}
+
+static void state_machine_read(void *context);
+static void state_machine_write(void *context);
+
+static void bbsi_op_fail(struct bbsi_operation *t) {
+ t->state = FAIL;
+ complete(&t->done);
+}
+
+static int send_readcmd(struct bbsi_operation *t, int burst) {
+ int ret;
+ spi_message_init(&t->sm);
+ t->sm.complete = state_machine_read;
+ t->sm.context = t;
+ t->tx_buf[0] = BBSI_COMMAND_BYTE | 0x01;
+ t->tx_buf[1] = CONFIG_REGISTER_ADDR;
+ t->tx_buf[2] = READ_RBUS |
+ (burst ? SPECULATIVE_READ_EN : NO_RBUS_ADDR_INC);
+ writel(cpu_to_be32(t->addr), t->tx_buf + 3);
+ t->st[0] = ((struct spi_transfer) { .tx_buf = t->tx_buf, .len = 7 });
+ spi_message_add_tail(&t->st[0], &t->sm);
+ ret = spi_async_locked(t->spi, &t->sm);
+ if (ret) {
+ pr_err("%s: spi_async failed with %d\n", __func__, ret);
+ bbsi_op_fail(t);
+ }
+ return ret;
+}
+
+static int send_writecmd(struct bbsi_operation *t) {
+ int ret;
+ spi_message_init(&t->sm);
+ t->sm.complete = state_machine_write;
+ t->sm.context = t;
+ t->tx_buf[0] = BBSI_COMMAND_BYTE | 0x01;
+ t->tx_buf[1] = CONFIG_REGISTER_ADDR;
+ t->tx_buf[2] = 0;
+ writel(cpu_to_be32(t->addr), t->tx_buf + 3);
+ t->st[0] = ((struct spi_transfer) { .tx_buf = t->tx_buf, .len = 7 });
+ spi_message_add_tail(&t->st[0], &t->sm);
+ ret = spi_async_locked(t->spi, &t->sm);
+ if (ret) {
+ pr_err("%s: spi_async failed with %d\n", __func__, ret);
+ bbsi_op_fail(t);
+ }
+ return ret;
+}
+
+static int send_pollstatuscmd(struct bbsi_operation *t, void (*complete)(void *context)) {
+ int ret;
+ spi_message_init(&t->sm);
+ t->sm.complete = complete;
+ t->sm.context = t;
+ t->tx_buf[0] = BBSI_COMMAND_BYTE;
+ t->tx_buf[1] = STATUS_REGISTER_ADDR;
+ t->st[0] = ((struct spi_transfer) { .tx_buf = t->tx_buf, .len = 2 });
+ t->st[1] = ((struct spi_transfer) { .rx_buf = t->rx_buf, .len = 1 });
+ spi_message_add_tail(&t->st[0], &t->sm);
+ spi_message_add_tail(&t->st[1], &t->sm);
+ ret = spi_async_locked(t->spi, &t->sm);
+ if (ret) {
+ pr_err("%s: spi_async failed with %d\n", __func__, ret);
+ bbsi_op_fail(t);
+ }
+ return ret;
+}
+
+static int read_data(struct bbsi_operation *t) {
+ int ret;
+ spi_message_init(&t->sm);
+ t->sm.complete = state_machine_read;
+ t->sm.context = t;
+ t->tx_buf[0] = BBSI_COMMAND_BYTE;
+ /* We want to continue reading where we left off. See interpret_data()
+ * for the definition of t->rx_state */
+ if (t->rx_state == 0) {
+ t->tx_buf[1] = STATUS_REGISTER_ADDR;
+ } else {
+ t->tx_buf[1] = DATA0_REGISTER_ADDR + t->rx_state - 1;
+ }
+ t->st[0] = ((struct spi_transfer) { .tx_buf = t->tx_buf, .len = 2 });
+ t->st[1] = ((struct spi_transfer) { .rx_buf = t->rx_buf,
+ .len = sizeof(t->rx_buf)});
+ spi_message_add_tail(&t->st[0], &t->sm);
+ spi_message_add_tail(&t->st[1], &t->sm);
+ ret = spi_async_locked(t->spi, &t->sm);
+ if (ret) {
+ pr_err("%s: spi_async failed with %d\n", __func__, ret);
+ bbsi_op_fail(t);
+ }
+ return ret;
+}
+
+static int write_data(struct bbsi_operation *t) {
+ int ret;
+ size_t len;
+ len = min(t->len - t->data_len, sizeof(t->rx_buf));
+ spi_message_init(&t->sm);
+ t->sm.complete = state_machine_write;
+ t->sm.context = t;
+ t->tx_buf[0] = BBSI_COMMAND_BYTE | 0x01;
+ t->tx_buf[1] = DATA0_REGISTER_ADDR + (t->data_len & 3);
+ t->st[0] = ((struct spi_transfer) { .tx_buf = t->tx_buf, .len = 2 });
+ t->st[1] = ((struct spi_transfer) { .tx_buf = t->data + t->data_len,
+ .len = len});
+ spi_message_add_tail(&t->st[0], &t->sm);
+ spi_message_add_tail(&t->st[1], &t->sm);
+ ret = spi_async_locked(t->spi, &t->sm);
+ if (ret) {
+ pr_err("%s: spi_async failed with %d\n", __func__, ret);
+ bbsi_op_fail(t);
+ }
+ t->data_len += len;
+ return ret;
+}
+
+static int interpret_data(struct bbsi_operation *t) {
+ /* The bytes we are receiving over the SPI bus are the values of
+ * registers in the BBSI interface on the BCM6803. The register csrAddr
+ * points to the register that we are going to read next. It cycles
+ * through the following registers:
+ * - STATUS (t->rx_state==0)
+ * - DATA0 (t->rx_state==1)
+ * - DATA1 (t->rx_state==2)
+ * - DATA2 (t->rx_state==3)
+ * - DATA3 (t->rx_state==4)
+ *
+ * If csrAddr points to the status register, csrAddr is not incremented
+ * if the busy bit in the status register is set. As a result, we might
+ * read the status register multiple times before we get to DATA0. We
+ * therefore might have to skip over multiple status bytes.
+ *
+ * t->rx_state defines the register that we are going to read next: 0
+ * stands for STATUS, 1 stands for DATA0, 2 for DATA1 and so on.
+ */
+ uint8_t *r;
+ r = t->rx_buf;
+ for(r = t->rx_buf;r < t->rx_buf + sizeof(t->rx_buf);r++) {
+ if (t->rx_state == 0) {
+ if (*r & STATUS_BUSY)
+ /* t->rx_state stays at 0 because the next byte
+ * will be another status byte */
+ continue;
+ else if (*r) {
+ pr_err("rbus error 0x%02x while trying to read %u "
+ "bytes from 0x%08x\n",
+ (unsigned) *r, t->len, t->addr);
+ bbsi_op_fail(t);
+ return -1;
+ } else
+ /* The busy bit is not set which means that the next byte will be DATA0. */
+ t->rx_state = 1;
+ } else {
+ BUG_ON(t->data_len >= t->len);
+ t->data[t->data_len++] = *r;
+ /* t->rx_state==4 means that we just read DATA3. The
+ * next byte will be a status byte (t->rx_state==0) */
+ t->rx_state++;
+ t->rx_state%=5;
+ if (t->data_len == t->len) return 0;
+ }
+
+ }
+ return 0;
+}
+
+static void state_machine_read(void *context) {
+ struct bbsi_operation *t = (struct bbsi_operation *) context;
+ int ret;
+
+ BUG_ON(t->len&3);
+
+ switch (t->state) {
+ case START:
+ t->state = COMMAND_STATE;
+ ret = send_readcmd(t, t->len>4);
+ if (ret) return;
+ break;
+ case COMMAND_STATE:
+ if (t->sm.status) {
+ pr_err("readcmd returned bad status %d\n", t->sm.status);
+ bbsi_op_fail(t);
+ return;
+ }
+ t->state = DATA_STATE;
+ ret = read_data(t);
+ if (ret) return;
+ break;
+ case DATA_STATE:
+ if (t->sm.status) {
+ pr_err("read_data returned bad status %d\n", t->sm.status);
+ bbsi_op_fail(t);
+ return;
+ }
+ if (interpret_data(t)) return;
+ if (t->data_len == t->len) {
+ t->state = DONE;
+ complete(&t->done);
+ } else {
+ /* Stay in this state */
+ ret = read_data(t);
+ if (ret) return;
+ }
+ break;
+ default:
+ BUG();
+ break;
+ }
+}
+
+static void state_machine_write(void *context) {
+ struct bbsi_operation *t = (struct bbsi_operation *) context;
+ int ret;
+
+ BUG_ON(t->len&3);
+
+ switch (t->state) {
+ case START:
+ t->state = COMMAND_STATE;
+ ret = send_writecmd(t);
+ if (ret) return;
+ break;
+ case COMMAND_STATE:
+ if (t->sm.status) {
+ pr_err("writecmd returned bad status %d\n", t->sm.status);
+ bbsi_op_fail(t);
+ return;
+ }
+ t->state = DATA_STATE;
+ ret = write_data(t);
+ if (ret) return;
+ break;
+ case DATA_STATE:
+ if (t->sm.status) {
+ pr_err("write_data returned bad status %d\n", t->sm.status);
+ bbsi_op_fail(t);
+ return;
+ }
+ if (t->data_len == t->len) {
+ t->state = POLLSTATUS_STATE;
+ ret = send_pollstatuscmd(t, state_machine_write);
+ if (ret) return;
+ } else {
+ /* Stay in this state */
+ ret = write_data(t);
+ if (ret) return;
+ }
+ break;
+ case POLLSTATUS_STATE:
+ if (t->sm.status) {
+ pr_err("pollstatuscmd returned bad status %d\n", t->sm.status);
+ bbsi_op_fail(t);
+ return;
+ }
+ if (*t->rx_buf & STATUS_BUSY) {
+ /* Stay in this state */
+ ret = send_pollstatuscmd(t, state_machine_write);
+ if (ret) return;
+ } else if (*t->rx_buf) {
+ pr_err("rbus error 0x%02x while trying to write %u "
+ "bytes to 0x%08x\n",
+ (unsigned) *t->rx_buf, t->len, t->addr);
+ bbsi_op_fail(t);
+ } else {
+ t->state = DONE;
+ complete(&t->done);
+ }
+ break;
+ default:
+ BUG();
+ break;
+ }
+}
+
+static uint32_t bbsi_read(struct spi_device *spi, uint32_t addr, void *dst, size_t len) {
+ struct bbsi_operation *t;
+ t = kmalloc(sizeof(*t), GFP_KERNEL);
+ t->state = START;
+ t->spi = spi;
+ t->addr = addr;
+ t->len = len;
+ t->data = dst;
+ t->data_len = 0;
+ t->rx_state = 0;
+ init_completion(&t->done);
+ state_machine_read((void*) t);
+ wait_for_completion(&t->done);
+ kfree(t);
+ return 0;
+}
+
+static uint32_t bbsi_write(struct spi_device *spi, uint32_t addr, const void *src, size_t len) {
+ struct bbsi_operation *t;
+ t = kmalloc(sizeof(*t), GFP_KERNEL);
+ t->state = START;
+ t->spi = spi;
+ t->addr = addr;
+ t->len = len;
+ t->data = (void*) src;
+ t->data_len = 0;
+ init_completion(&t->done);
+ state_machine_write((void*) t);
+ wait_for_completion(&t->done);
+ kfree(t);
+ return 0;
+}
+
+static uint32_t _spi_read32(struct spi_device *spi, uint32_t addr) {
+ uint32_t retval = 0;
+
+ spi_bus_lock(spi->master);
+ bbsi_read(spi, addr, &retval, sizeof(retval));
+ spi_bus_unlock(spi->master);
+ return be32_to_cpu(retval);
+}
+
+static void _spi_write32(struct spi_device *spi, uint32_t addr, uint32_t value) {
+ spi_bus_lock(spi->master);
+ value = cpu_to_be32(value);
+ bbsi_write(spi, addr, &value, sizeof(value));
+ spi_bus_unlock(spi->master);
+}
+
+static uint32_t kerSysBcmSpiSlaveReadReg32(struct spi_device *spi, uint32_t addr) {
+ return _spi_read32(spi, addr);
+}
+
+static void kerSysBcmSpiSlaveWriteReg32(struct spi_device *spi, uint32_t addr, uint32_t value) {
+ _spi_write32(spi, addr, value);
+}
+
+static void kerSysBcmSpiSlaveReadBuf(struct spi_device *spi, uint32_t addr, void *dst, int len, int wordsize) {
+ if (len == 0) {
+ pr_warn("spi readbuf: buffer size 0 invalid\n");
+ return;
+ }
+ if (wordsize != 4) {
+ pr_info("SPI readbuf: only word size == 4 bytes is supported!\n");
+ return;
+ }
+ spi_bus_lock(spi->master);
+ bbsi_read(spi, addr, dst, len);
+ spi_bus_unlock(spi->master);
+}
+
+static void kerSysBcmSpiSlaveWriteBuf(struct spi_device *spi, uint32_t addr, const void *src, int len, int wordsize) {
+ if (len > 8192) {
+ pr_warn("spi writebuf: buffer size %d is too large\n", len);
+ return;
+ }
+ if (wordsize != 4 || len&3) {
+ pr_err("SPI writebuf: only word size == 4 bytes is supported!\n");
+ return;
+ }
+
+ spi_bus_lock(spi->master);
+ bbsi_write(spi, addr, src, len);
+ spi_bus_unlock(spi->master);
+}
+
+#endif // __BBSI_H
diff --git a/bmoca-6802.c b/bmoca-6802.c
index ecd69e8..f641fa8 100644
--- a/bmoca-6802.c
+++ b/bmoca-6802.c
@@ -29,6 +29,7 @@
*/
#include "bbsi.h"
+#include <linux/spi/spi.h>
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)
#else
@@ -37,26 +38,26 @@
#define MOCA_RD(x) ((((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0) ? \
(*((volatile uint32_t *)((unsigned long)(x)))) : \
- ((uint32_t)kerSysBcmSpiSlaveReadReg32(((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId, (uint32_t)(x))))
+ ((uint32_t)kerSysBcmSpiSlaveReadReg32(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, (uint32_t)(x))))
#define MOCA_RD8(x, y) ((((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0) ? \
(*(y) = *((volatile unsigned char *)((unsigned long)(x)))) : \
- (kerSysBcmSpiSlaveRead(((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId, (unsigned long)(x), y, 1)))
+ (kerSysBcmSpiSlaveRead(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, (unsigned long)(x), y, 1)))
#define MOCA_WR(x,y) do { ((((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0) ? \
(*((volatile uint32_t *)((unsigned long)(x)))) = (y) : \
- kerSysBcmSpiSlaveWriteReg32(((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId, (uint32_t)(x), (y))); } while(0)
+ kerSysBcmSpiSlaveWriteReg32(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, (uint32_t)(x), (y))); } while(0)
#define MOCA_WR8(x,y) do { ((((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0) ? \
(*((volatile unsigned char *)((unsigned long)(x)))) = (unsigned char)(y) : \
- kerSysBcmSpiSlaveWrite(((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId, (unsigned long)(x), (y), 1)); } while(0)
+ kerSysBcmSpiSlaveWrite(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, (unsigned long)(x), (y), 1)); } while(0)
#define MOCA_WR16(x,y) do { ((((struct moca_platform_data *)priv->pdev->dev.platform_data)->use_spi == 0) ? \
(*((volatile unsigned short *)((unsigned long)(x)))) = (unsigned short)(y) : \
- kerSysBcmSpiSlaveWrite(((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId, (unsigned long)(x), (y), 2)); } while(0)
+ kerSysBcmSpiSlaveWrite(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, (unsigned long)(x), (y), 2)); } while(0)
-#define MOCA_WR_BLOCK(addr, src, len) do { kerSysBcmSpiSlaveWriteBuf(((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId, addr, src, len, 4); } while(0)
-#define MOCA_RD_BLOCK(addr, dst, len) do { kerSysBcmSpiSlaveReadBuf(((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId, addr, dst, len, 4); } while(0)
+#define MOCA_WR_BLOCK(addr, src, len) do { kerSysBcmSpiSlaveWriteBuf(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, addr, src, len, 4); } while(0)
+#define MOCA_RD_BLOCK(addr, dst, len) do { kerSysBcmSpiSlaveReadBuf(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi, addr, dst, len, 4); } while(0)
#define I2C_RD(x) MOCA_RD(x)
@@ -186,7 +187,6 @@
},
};
-
/* MoCA Clock Functions */
struct clk *moca_clk_get(struct device *dev, const char *id)
{
@@ -335,15 +335,14 @@
return(ret);
}
-
static void moca_enable_irq(struct moca_priv_data *priv)
{
- kerSysMocaHostIntrEnable(((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId);
+ kerSysMocaHostIntrEnable(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi);
}
static void moca_disable_irq(struct moca_priv_data *priv)
{
- kerSysMocaHostIntrDisable(((struct moca_platform_data *)priv->pdev->dev.platform_data)->devId);
+ kerSysMocaHostIntrDisable(((struct moca_platform_data *)priv->pdev->dev.platform_data)->spi);
}
static void moca_pmb_busy_wait(struct moca_priv_data *priv)
@@ -1007,6 +1006,36 @@
#endif /* DSL_MOCA */
+static int __devinit bmoca_spi_probe(struct spi_device *spi) {
+ // TODO(apenwarr): match one spi device to one moca device struct.
+ // I happen to know that right now the system only registers one of
+ // moca_lan or moca_wan, never both, and there is never more than
+ // one moca chip present on our systems, so this is okay for now.
+ uint32_t val = kerSysBcmSpiSlaveReadReg32(spi, 0x10404000);
+ pr_info("bmoca_spi_probe bus=%d chip_select=%d: id=%08x %s\n",
+ spi->master->bus_num, spi->chip_select, val,
+ val != 0 ? "yes" : "no");
+ if (val == 0) return -ENODEV;
+ moca_lan_data.spi = spi;
+ moca_wan_data.spi = spi;
+ return 0; // success
+}
+
+static int __devexit bmoca_spi_remove(struct spi_device *spi) {
+ pr_info("bmoca_spi_remove\n");
+ if (moca_lan_data.spi == spi) moca_lan_data.spi = NULL;
+ if (moca_wan_data.spi == spi) moca_wan_data.spi = NULL;
+ return 0; // success
+}
+
+static struct spi_driver bmoca_spi_driver = {
+ .driver = {
+ .name = "bmoca",
+ .owner = THIS_MODULE,
+ },
+ .probe = bmoca_spi_probe,
+ .remove = __devexit_p(bmoca_spi_remove),
+};
//extern void bcmenet_register_moca_fc_bits_cb(void cb(void *, unsigned long *), int isWan, void * arg);
@@ -1027,16 +1056,41 @@
{
#ifdef DSL_MOCA
struct moca_platform_data *pMocaData;
+ u32 port_mode;
pMocaData = (struct moca_platform_data *)priv->pdev->dev.platform_data;
/* fill in the hw_rev field */
pMocaData->chip_id = MOCA_RD(0x10404004) + 0xA0;
+ pr_info("read moca chip id: %08x\n", pMocaData->chip_id);
if ((pMocaData->chip_id & 0xFFFE0000) != 0x68020000) { /* 6802 or 6803 */
printk(KERN_ERR "bmoca: No MoCA chip found\n");
return -EFAULT;
}
+ MOCA_WR(0x1040431c, 0x0FFFFFFF); // SUN_TOP_CTRL_SW_INIT_0_CLEAR
+ MOCA_WR(0x104040a4, 0x01); // GENERAL_CTRL_NO_SCAN_0
+ /* Power down GPHY LDO regulator to save power */
+ MOCA_WR(0x104040a8, 0x03); // GENERAL_CTRL_NO_SCAN_1
+ MOCA_WR(0x10404100, 0x11110011); // PIN_MUX_CTRL_0
+ MOCA_WR(0x10404104, 0x11111111); // PIN_MUX_CTRL_1
+
+ /* The definition of PORT_MODE has changed from chip revision B0 to C0
+ * */
+ if ((pMocaData->chip_id & 0xFFFEFFF0) == 0x680200C0)
+ port_mode = 2; /* RGMII_1 <-> GPHY, RGMII_0 <-> MoCA */
+ else
+ port_mode = 3; /* RGMII_0 <-> MoCA */
+
+ MOCA_WR(0x10800000, port_mode); // EMUX_CNTRL
+ MOCA_WR(0x1080000c, 0x11); // RGMII_0_CNTRL
+ MOCA_WR(0x10800014, 0xc0); // RGMII_0_RX_CLK_DELAY_CNTRL
+
+ /* Shutdown GPHY to save power */
+ MOCA_WR(0x10800004, MOCA_RD(0x10800004) | 0xF); // EPORT_REG_GPHY_CNTRL
+ /* Disable UARTs to save power */
+ MOCA_WR(0x10406180, 0x4000); // PM_CONFIG
+
if (((pMocaData->chip_id & 0xFFFFFFF0) == 0x680200C0) || ((pMocaData->chip_id & 0xFFFFFFF0) == 0x680300C0))
{
priv->i2c_base = NULL;
@@ -1057,6 +1111,11 @@
MOCA_WR(0x101000fc, 0x6); // CLKGEN_LEAP_TOP_INST_SHARED
MOCA_WR(0x10100164, 0x3); // CLKGEN_SYS_CTRL_INST_POWER_SWITCH_MEMORY
+ /* Disable clkobsv output pin to save power */
+ MOCA_WR(0x1010013C, 0x1); // CLKGEN_PAD_CLOCK_DISABLE
+ /* Disable LEAP clocks to save power */
+ MOCA_WR(0x101000D4, 0x7); // CLKGEN_LEAP_TOP_INST_CLOCK_DISABLE
+
// bcmenet_register_moca_fc_bits_cb(
// moca_get_fc_bits, pMocaData->use_spi ? 1 : 0, (void *)priv);
#endif
@@ -1075,6 +1134,9 @@
BpGetMocaInfo(mocaInfo, &mocaChipNum);
+ ret = spi_register_driver(&bmoca_spi_driver);
+ if (ret < 0) return ret;
+
for (i = 0; i < mocaChipNum; i++) {
switch (mocaInfo[i].type) {
case BP_MOCA_TYPE_WAN:
@@ -1096,6 +1158,7 @@
ret = platform_device_register(pPlatformDev);
if (ret < 0) {
+ spi_unregister_driver(&bmoca_spi_driver);
return(ret);
}
else {
@@ -1133,6 +1196,8 @@
static void moca_platform_dev_unregister(void)
{
+ spi_unregister_driver(&bmoca_spi_driver);
+
if (moca_lan_data.devId != MOCA_DEVICE_ID_UNREGISTERED)
platform_device_unregister(&moca_lan_plat_dev);
@@ -1169,4 +1234,3 @@
return(0xffffffff);
}
}
-
diff --git a/bmoca.c b/bmoca.c
new file mode 120000
index 0000000..621a8ab
--- /dev/null
+++ b/bmoca.c
@@ -0,0 +1 @@
+3.3/bmoca.c
\ No newline at end of file
diff --git a/bmoca.h b/bmoca.h
index a0d34ee..348b866 100644
--- a/bmoca.h
+++ b/bmoca.h
@@ -180,6 +180,8 @@
int use_dma;
int use_spi;
int devId;
+ struct spi_device *spi;
+
u32 chip_id;
#ifdef CONFIG_SMP
diff --git a/linux b/linux
new file mode 120000
index 0000000..945c9b4
--- /dev/null
+++ b/linux
@@ -0,0 +1 @@
+.
\ No newline at end of file