Merge remote-tracking branch 'gfiber-internal/master' into newkernel_dev

Change-Id: I99d608fe04627af10d4cc5cbe5b3dfa117d07ca2
diff --git a/bbsi.h b/bbsi.h
index 7907864..afd051f 100644
--- a/bbsi.h
+++ b/bbsi.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013 Google Inc.
+ * 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
@@ -17,9 +17,14 @@
 #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>
-#include <linux/vmalloc.h>
 
 #ifndef KSEG1
 #define KSEG1 0  // just to appease non-MIPS CPUs. Not really used.
@@ -27,6 +32,47 @@
 
 #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
@@ -58,9 +104,6 @@
 	}
 }
 
-static uint32_t _spi_read32(struct spi_device *spi, uint32_t addr);
-
-
 // TODO(apenwarr): don't make this global.
 //   Or fix the driver to just only enable/disable interrupts at the right
 //   times.
@@ -80,141 +123,325 @@
 	}
 }
 
-static uint8_t __pollstatus(struct spi_device *spi) {
-	uint8_t wclear[] = { 0x80, 0x06 };
-	uint8_t rdata[1] = { 0 };
-	struct spi_transfer t[2] = {
-		{ .tx_buf = wclear, .len = sizeof(wclear) },
-		{ .rx_buf = rdata, .len = sizeof(rdata) },
-	};
-	struct spi_message m;
-	int i;
+static void state_machine_read(void *context);
+static void state_machine_write(void *context);
 
-	spi_message_init(&m);
-	spi_message_add_tail(&t[0], &m);
-	spi_message_add_tail(&t[1], &m);
-
-	for (i = 0; i < 10; i++) {
-		if (spi_sync_locked(spi, &m) < 0) {
-			pr_warn("spi _pollstatus: SPI error\n");
-			return 0x01; // error code
-		}
-		if (rdata[0] & 0x01) {
-			pr_warn("spi _pollstatus: rbus error: %02X\n", rdata[0]);
-			return 0x01; // error result; stop polling now
-		}
-		if (!(rdata[0] & 0x10)) return 0;   // transaction finished
-	}
-	// if we get here, the transaction still isn't finished: weird
-	pr_warn("spi _pollstatus: still busy: %02X\n", rdata[0]);
-	return rdata[0];
+static void bbsi_op_fail(struct bbsi_operation *t) {
+	t->state = FAIL;
+	complete(&t->done);
 }
 
-static uint32_t __spi_read32a(struct spi_device *spi, uint32_t addr,
-				int speculative) {
-	uint8_t waddr[] = {
-		0x81, 0x07,
-		0x01 | (speculative ? 0x02 : 0),
-		0, 0, 0, 0 };
-	struct spi_transfer addrt[1] = {
-		{ .tx_buf = waddr, .len = sizeof(waddr) },
-	};
-	struct spi_message addrm;
-	int j, st;
-
-	spi_message_init(&addrm);
-	spi_message_add_tail(&addrt[0], &addrm);
-
-	__pollstatus(spi);
-	for (j = 0; j < 10; j++) {
-		// write address reg, which triggers the read
-		writel(cpu_to_be32(addr), waddr + sizeof(waddr) - 4);
-		if (spi_sync_locked(spi, &addrm) < 0) {
-			pr_warn("spi_read_addr: error\n");
-		}
-		st = __pollstatus(spi);
-		if (!st) break;
+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 st;
+	return ret;
 }
 
-static uint32_t __spi_read32d_noswap(struct spi_device *spi) {
-	uint8_t wdata[] = { 0x80, 0x0c };
-	uint8_t rdata[4];
-	struct spi_transfer datat[2] = {
-		{ .tx_buf = wdata, .len = sizeof(wdata) },
-		{ .rx_buf = rdata, .len = sizeof(rdata) },
-	};
-	struct spi_message datam;
-
-	spi_message_init(&datam);
-	spi_message_add_tail(&datat[0], &datam);
-	spi_message_add_tail(&datat[1], &datam);
-
-	// retrieve actual data bits
-	if (spi_sync_locked(spi, &datam) < 0) {
-		pr_warn("spi_read_data: error\n");
+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 readl(rdata);
+	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) {
-	int st;
-	uint32_t retval;
+	uint32_t retval = 0;
 
 	spi_bus_lock(spi->master);
-
-	st = __spi_read32a(spi, addr, 0);
-	if (st) {
-		retval = 0x00000000; // error
-	} else {
-		retval = be32_to_cpu(__spi_read32d_noswap(spi));
-	}
+	bbsi_read(spi, addr, &retval, sizeof(retval));
 	spi_bus_unlock(spi->master);
-	return retval;
+	return be32_to_cpu(retval);
 }
 
-static void __spi_write32a(struct spi_device *spi, uint32_t addr) {
-	uint8_t waddr[] = { 0x81, 0x07, 0x00, 0, 0, 0, 0  };
-	struct spi_transfer t[1] = {
-		{ .tx_buf = waddr, .len = sizeof(waddr) },
-	};
-	struct spi_message m;
-
-	spi_message_init(&m);
-	spi_message_add_tail(&t[0], &m);
-
-	// write address reg
-	writel(cpu_to_be32(addr), waddr + sizeof(waddr) - 4);
-	if (spi_sync_locked(spi, &m) < 0) {
-		pr_warn("spi_write: error\n");
-	}
-}
-
-static void __spi_write32d_noswap(struct spi_device *spi, uint32_t value) {
-	uint8_t wdata[] = { 0x81, 0x0c, 0, 0, 0, 0 };
-	struct spi_transfer t[1] = {
-		{ .tx_buf = wdata, .len = sizeof(wdata) },
-	};
-	struct spi_message m;
-
-	spi_message_init(&m);
-	spi_message_add_tail(&t[0], &m);
-
-	// write data reg
-	writel(value, wdata + sizeof(wdata) - 4);
-	if (spi_sync_locked(spi, &m) < 0) {
-		pr_warn("spi_write: error\n");
-	}
-}
-
-
 static void _spi_write32(struct spi_device *spi, uint32_t addr, uint32_t value) {
 	spi_bus_lock(spi->master);
-	__pollstatus(spi);
-	__spi_write32a(spi, addr);
-	__spi_write32d_noswap(spi, cpu_to_be32(value));
-	__pollstatus(spi);
+	value = cpu_to_be32(value);
+	bbsi_write(spi, addr, &value, sizeof(value));
 	spi_bus_unlock(spi->master);
 }
 
@@ -227,72 +454,32 @@
 }
 
 static void kerSysBcmSpiSlaveReadBuf(struct spi_device *spi, uint32_t addr, void *dst, int len, int wordsize) {
-	int i;
-	uint32_t *buf = dst;
-
-	spi_bus_lock(spi->master);
-
+	if (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_read32a(spi, addr, 1);
-	for (i = 0; i < len; i += wordsize) {
-		buf[i/4] = __spi_read32d_noswap(spi);
-		__pollstatus(spi);
-	}
-
+	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) {
-	int i, nelems = len/4;
-	const uint32_t *buf = src;
-	uint8_t wdata[] = { 0x81, 0x0c };
-	struct spi_transfer *t, *tp;
-	struct spi_message m;
-
 	if (len > 8192) {
 		pr_warn("spi writebuf: buffer size %d is too large\n", len);
 		return;
 	}
-	if (wordsize != 4) {
+	if (wordsize != 4 || len&3) {
 		pr_err("SPI writebuf: only word size == 4 bytes is supported!\n");
 		return;
 	}
 
-	t = vmalloc(nelems * sizeof(struct spi_transfer) * 2);
-	if (!t) {
-		pr_warn("spi writebuf: out of memory\n");
-		return;
-	}
-
-	memset(t, 0, nelems * sizeof(struct spi_transfer) * 2);
-	spi_message_init(&m);
-
-	for (i = 0, tp = t; i < nelems; i++) {
-		tp->tx_buf = wdata;
-		tp->len = sizeof(wdata);
-		spi_message_add_tail(tp, &m);
-		tp++;
-
-		tp->tx_buf = &buf[i];
-		tp->len = 4;
-		tp->cs_change = 1;
-		spi_message_add_tail(tp, &m);
-		tp++;
-	}
-
 	spi_bus_lock(spi->master);
-
-	__pollstatus(spi);
-	writel(cpu_to_be32(addr), wdata + 2);
-	__spi_write32a(spi, addr);
-	spi_sync_locked(spi, &m);
-	__pollstatus(spi);
-
+	bbsi_write(spi, addr, src, len);
 	spi_bus_unlock(spi->master);
-	vfree(t);
 }
 
 #endif // __BBSI_H