bif: bif-core: add support for BIF slave NVM read and write

Add support for reading and writing the non-volatile memory (NVM)
of BIF slaves.

Change-Id: Ie422ec57327102584bab2b039a733505320b065b
Signed-off-by: David Collins <collinsd@codeaurora.org>
diff --git a/Documentation/bif-framework.txt b/Documentation/bif-framework.txt
index 4c9f215..8cad537b 100644
--- a/Documentation/bif-framework.txt
+++ b/Documentation/bif-framework.txt
@@ -302,6 +302,20 @@
 int bif_slave_write(struct bif_slave *slave, u16 addr, u8 *buf, int len);
 
 
+BIF slave non-volatile memory manipulation:
+-------------------------------------------
+
+int bif_slave_nvm_raw_read(struct bif_slave *slave, u16 offset, u8 *buf,
+				int len);
+
+int bif_slave_nvm_raw_write(struct bif_slave *slave, u16 offset, u8 *buf,
+				int len);
+
+Raw NVM writing may be needed in order to intialize the NVM BIF object list.
+However, its use can be dangerous as it can overwrite existing objects in the
+list and make the list unparsable.
+
+
 Get or set the BIF bus state or period:
 ---------------------------------------
 
diff --git a/drivers/bif/bif-core.c b/drivers/bif/bif-core.c
index 2cd9c98..731d820 100644
--- a/drivers/bif/bif-core.c
+++ b/drivers/bif/bif-core.c
@@ -205,13 +205,14 @@
 	}
 
 	if (sdev->nvm_function) {
-		pr_debug("  NVM function: pointer=0x%04X, task=%d, wr_buf_size=%d, nvm_base=0x%04X, nvm_size=%d\n",
+		pr_debug("  NVM function: pointer=0x%04X, task=%d, wr_buf_size=%d, nvm_base=0x%04X, nvm_size=%d, nvm_lock_offset=%d\n",
 			sdev->nvm_function->nvm_pointer,
 			sdev->nvm_function->slave_control_channel,
 			(sdev->nvm_function->write_buffer_size
 				? sdev->nvm_function->write_buffer_size : 0),
 			sdev->nvm_function->nvm_base_address,
-			sdev->nvm_function->nvm_size);
+			sdev->nvm_function->nvm_size,
+			sdev->nvm_function->nvm_lock_offset);
 		if (sdev->nvm_function->object_count)
 			pr_debug("  NVM objects:\n");
 		i = 0;
@@ -704,6 +705,106 @@
 	return rc;
 }
 
+/*
+ * The MIPI-BIF spec does not define a maximum time in which an NVM write must
+ * complete.  The following delay and recheck count therefore represent
+ * arbitrary but reasonable values.
+ */
+#define NVM_WRITE_POLL_DELAY_MS		20
+#define NVM_WRITE_MAX_POLL_COUNT	50
+
+static int _bif_slave_nvm_raw_write(struct bif_slave_dev *sdev, u16 offset,
+				u8 *buf, int len)
+{
+	int rc = 0;
+	int write_len, poll_count, rc2;
+	u8 write_buf[3];
+
+	if (!sdev->nvm_function) {
+		pr_err("BIF slave has no NVM function\n");
+		return -ENODEV;
+	} else if (offset + len > sdev->nvm_function->nvm_size) {
+		pr_err("write offset + len = %d > NVM size = %d\n",
+			offset + len, sdev->nvm_function->nvm_size);
+		return -EINVAL;
+	} else if (offset < sdev->nvm_function->nvm_lock_offset) {
+		pr_err("write offset = %d < first writable offset = %d\n",
+			offset, sdev->nvm_function->nvm_lock_offset);
+		return -EINVAL;
+	}
+
+	rc = _bif_enable_auto_task(sdev,
+			sdev->nvm_function->slave_control_channel);
+	if (rc) {
+		pr_err("Failed to enable NVM auto task, rc=%d\n", rc);
+		return rc;
+	}
+
+	while (len > 0) {
+		write_len = sdev->nvm_function->write_buffer_size;
+		if (write_len == 0)
+			write_len = 256;
+		write_len = min(write_len, len);
+
+		write_buf[0] = offset >> 8;
+		write_buf[1] = offset;
+		write_buf[2] = (write_len == 256) ? 0 : write_len;
+
+		/* Write offset and size registers. */
+		rc = _bif_slave_write(sdev, sdev->nvm_function->nvm_pointer + 6,
+					write_buf, 3);
+		if (rc) {
+			pr_err("BIF slave write failed, rc=%d\n", rc);
+			goto done;
+		}
+
+		/* Write to NVM write buffer registers. */
+		rc = _bif_slave_write(sdev, sdev->nvm_function->nvm_pointer + 9,
+					buf, write_len);
+		if (rc) {
+			pr_err("BIF slave write failed, rc=%d\n", rc);
+			goto done;
+		}
+
+		/*
+		 * Wait for completion of the NVM write which was auto-triggered
+		 * by the register write of the last byte in the NVM write
+		 * buffer.
+		 */
+		poll_count = NVM_WRITE_MAX_POLL_COUNT;
+		do {
+			msleep(NVM_WRITE_POLL_DELAY_MS);
+			rc = _bif_task_is_busy(sdev,
+				sdev->nvm_function->slave_control_channel);
+			poll_count--;
+		} while (rc > 0 && poll_count > 0);
+
+		if (rc < 0) {
+			pr_err("Failed to check task state, rc=%d", rc);
+			goto done;
+		} else if (rc > 0) {
+			pr_err("BIF slave NVM write not completed after %d ms\n",
+			    NVM_WRITE_POLL_DELAY_MS * NVM_WRITE_MAX_POLL_COUNT);
+			rc = -ETIMEDOUT;
+			goto done;
+		}
+
+		len -= write_len;
+		offset += write_len;
+		buf += write_len;
+	}
+
+done:
+	rc2 = _bif_disable_auto_task(sdev,
+			sdev->nvm_function->slave_control_channel);
+	if (rc2) {
+		pr_err("Failed to disable NVM auto task, rc=%d\n", rc2);
+		return rc2;
+	}
+
+	return rc;
+}
+
 /* Takes a mutex if this consumer is not an exclusive bus user. */
 static void bif_ctrl_lock(struct bif_ctrl *ctrl)
 {
@@ -1659,6 +1760,74 @@
 EXPORT_SYMBOL(bif_slave_write);
 
 /**
+ * bif_slave_nvm_raw_read() - read contiguous memory values from a BIF slave's
+ *		non-volatile memory (NVM)
+ * @slave:	BIF slave handle
+ * @offset:	Offset from the beginning of BIF slave NVM to begin reading at
+ * @buf:	Buffer to fill with memory values
+ * @len:	Number of byte to read
+ *
+ * Returns 0 for success or errno if an error occurred.
+ */
+int bif_slave_nvm_raw_read(struct bif_slave *slave, u16 offset, u8 *buf,
+				int len)
+{
+	if (IS_ERR_OR_NULL(slave)) {
+		pr_err("Invalid slave pointer=%ld\n", PTR_ERR(slave));
+		return -EINVAL;
+	} else if (IS_ERR_OR_NULL(buf)) {
+		pr_err("Invalid buffer pointer=%ld\n", PTR_ERR(buf));
+		return -EINVAL;
+	} else if (!slave->sdev->nvm_function) {
+		pr_err("BIF slave has no NVM function\n");
+		return -ENODEV;
+	} else if (offset + len > slave->sdev->nvm_function->nvm_size) {
+		pr_err("read offset + len = %d > NVM size = %d\n",
+			offset + len, slave->sdev->nvm_function->nvm_size);
+		return -EINVAL;
+	}
+
+	return bif_slave_read(slave,
+		slave->sdev->nvm_function->nvm_base_address + offset, buf, len);
+}
+EXPORT_SYMBOL(bif_slave_nvm_raw_read);
+
+/**
+ * bif_slave_nvm_raw_write() - write contiguous memory values to a BIF slave's
+ *		non-volatile memory (NVM)
+ * @slave:	BIF slave handle
+ * @offset:	Offset from the beginning of BIF slave NVM to begin writing at
+ * @buf:	Buffer containing values to write
+ * @len:	Number of byte to write
+ *
+ * Note that this function does *not* respect the MIPI-BIF object data
+ * formatting specification.  It can cause corruption of the object data list
+ * stored in NVM if used improperly.
+ *
+ * Returns 0 for success or errno if an error occurred.
+ */
+int bif_slave_nvm_raw_write(struct bif_slave *slave, u16 offset, u8 *buf,
+				int len)
+{
+	int rc;
+
+	if (IS_ERR_OR_NULL(slave)) {
+		pr_err("Invalid slave pointer=%ld\n", PTR_ERR(slave));
+		return -EINVAL;
+	} else if (IS_ERR_OR_NULL(buf)) {
+		pr_err("Invalid buffer pointer=%ld\n", PTR_ERR(buf));
+		return -EINVAL;
+	}
+
+	bif_slave_ctrl_lock(slave);
+	rc = _bif_slave_nvm_raw_write(slave->sdev, offset, buf, len);
+	bif_slave_ctrl_unlock(slave);
+
+	return rc;
+}
+EXPORT_SYMBOL(bif_slave_nvm_raw_write);
+
+/**
  * bif_slave_is_present() - check if a slave is currently physically present
  *		in the system
  * @slave:	BIF slave handle
@@ -2276,12 +2445,21 @@
 		return rc;
 	}
 
-	sdev->nvm_function->nvm_pointer		= buf[0] << 8 | buf[1];
+	sdev->nvm_function->nvm_pointer			= buf[0] << 8 | buf[1];
 	sdev->nvm_function->slave_control_channel	= buf[2];
 	sdev->nvm_function->write_buffer_size		= buf[3];
 	sdev->nvm_function->nvm_base_address		= buf[4] << 8 | buf[5];
 	sdev->nvm_function->nvm_size			= buf[6] << 8 | buf[7];
 
+	/* Read NVM lock offset */
+	rc = _bif_slave_read(sdev, sdev->nvm_function->nvm_pointer, buf, 2);
+	if (rc) {
+		pr_err("Slave memory read failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	sdev->nvm_function->nvm_lock_offset		= buf[0] << 8 | buf[1];
+
 	INIT_LIST_HEAD(&sdev->nvm_function->object_list);
 
 	/* Read object list */
diff --git a/include/linux/bif/consumer.h b/include/linux/bif/consumer.h
index 8af7c93..9693a2a 100644
--- a/include/linux/bif/consumer.h
+++ b/include/linux/bif/consumer.h
@@ -342,6 +342,8 @@
  *				is used to denote a 256 byte buffer.
  * @nvm_base_address:		BIF slave address where NVM begins
  * @nvm_size:			NVM size in bytes
+ * @nvm_lock_offset:		Offset from the beginning of NVM of the first
+ *				writable address
  * @object_count:		Number of BIF objects read from NVM
  * @object_list:		List of BIF objects read from NVM
  */
@@ -351,6 +353,7 @@
 	u8			write_buffer_size;
 	u16			nvm_base_address;
 	u16			nvm_size;
+	u16			nvm_lock_offset;
 	int			object_count;
 	struct list_head	object_list;
 };
@@ -505,6 +508,11 @@
 int bif_slave_read(struct bif_slave *slave, u16 addr, u8 *buf, int len);
 int bif_slave_write(struct bif_slave *slave, u16 addr, u8 *buf, int len);
 
+int bif_slave_nvm_raw_read(struct bif_slave *slave, u16 offset, u8 *buf,
+				int len);
+int bif_slave_nvm_raw_write(struct bif_slave *slave, u16 offset, u8 *buf,
+				int len);
+
 int bif_slave_is_present(struct bif_slave *slave);
 
 int bif_slave_is_selected(struct bif_slave *slave);
@@ -587,6 +595,13 @@
 				int len)
 { return -EPERM; }
 
+static inline int bif_slave_nvm_raw_read(struct bif_slave *slave, u16 offset,
+				u8 *buf, int len)
+{ return -EPERM; }
+static inline int bif_slave_nvm_raw_write(struct bif_slave *slave, u16 offset,
+				u8 *buf, int len)
+{ return -EPERM; }
+
 static inline int bif_slave_is_present(struct bif_slave *slave)
 { return -EPERM; }