[RFC] i2c: Move tpm i2c wrapper code into core

Message ID 20170814042825.19974-1-andrew.donnellan@au1.ibm.com
State RFC
Headers show

Commit Message

Andrew Donnellan Aug. 14, 2017, 4:28 a.m.
The TPM code has a wrapper around the main i2c API to allow synchronous use.

Move it into core/i2c.c so it can be used by other possible users. In
particular, a future patch will use this to drive OpenCAPI device resets
during boot time.

Cc: Claudio Carvalho <cclaudio@linux.vnet.ibm.com>
Signed-off-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com>

---

RFC till I get OpenCAPI ZZ card reset fully working

Claudio, I didn't manage to test this against a machine with a TPM due to
a TPM shortage in OzLabs. Please let me know if this causes any issues.
---
 core/i2c.c                         | 107 +++++++++++++++++++++++++++++++++++++
 include/i2c.h                      |   5 ++
 libstb/drivers/tpm_i2c_interface.c | 105 ++++--------------------------------
 libstb/drivers/tpm_i2c_interface.h |   1 -
 4 files changed, 123 insertions(+), 95 deletions(-)

Patch

diff --git a/core/i2c.c b/core/i2c.c
index c1646249..d4311c27 100644
--- a/core/i2c.c
+++ b/core/i2c.c
@@ -19,6 +19,7 @@ 
 #include <opal.h>
 #include <device.h>
 #include <opal-msg.h>
+#include <timebase.h>
 
 static LIST_HEAD(i2c_bus_list);
 
@@ -134,4 +135,110 @@  static int opal_i2c_request(uint64_t async_token, uint32_t bus_id,
 }
 opal_call(OPAL_I2C_REQUEST, opal_i2c_request, 3);
 
+#define MAX_NACK_RETRIES		 2
+#define REQ_COMPLETE_POLLING		 5  /* Check if req is complete
+					       in 5ms interval */
 
+struct i2c_sync_userdata {
+	int rc;
+	bool done;
+};
+
+static void i2c_sync_request_complete(int rc, struct i2c_request *req)
+{
+	struct i2c_sync_userdata *ud = req->user_data;
+	ud->rc = rc;
+	ud->done = true;
+}
+
+/**
+ * i2c_request_send - send request to i2c bus synchronously
+ * @bus_id: i2c bus id
+ * @dev_addr: address of the device
+ * @read_write: SMBUS_READ or SMBUS_WRITE
+ * @offset: any of the I2C interface offset defined
+ * @offset_bytes: offset size in bytes
+ * @buf: data to be read or written
+ * @buflen: buf length
+ * @timeout: request timeout in milliseconds
+ *
+ * Send an I2C request to a device synchronously
+ *
+ * Returns: Zero on success otherwise a negative error code
+ */
+int i2c_request_send(int bus_id, int dev_addr, int read_write,
+		     uint32_t offset, uint32_t offset_bytes, void* buf,
+		     size_t buflen, int timeout)
+{
+	int rc, waited, retries;
+	struct i2c_request *req;
+	struct i2c_bus *bus;
+	uint64_t time_to_wait = 0;
+	struct i2c_sync_userdata ud;
+
+	bus = i2c_find_bus_by_id(bus_id);
+	if (!bus) {
+		/**
+		 * @fwts-label I2CInvalidBusID
+		 * @fwts-advice i2c_request_send was passed an invalid bus
+		 * ID. This indicates a bug.
+		 */
+		prlog(PR_ERR, "I2C: Invalid bus_id=%x\n", bus_id);
+		return OPAL_PARAMETER;
+	}
+
+	req = i2c_alloc_req(bus);
+	if (!req) {
+		/**
+		 * @fwts-label I2CAllocationFailed
+		 * @fwts-advice OPAL failed to allocate memory for an
+		 * i2c_request. This points to an OPAL bug as OPAL run out of
+		 * memory and this should never happen.
+		 */
+		prlog(PR_ERR, "I2C: i2c_alloc_req failed\n");
+		return OPAL_INTERNAL_ERROR;
+	}
+
+	req->dev_addr   = dev_addr;
+	req->op         = read_write;
+	req->offset     = offset;
+	req->offset_bytes = offset_bytes;
+	req->rw_buf     = (void*) buf;
+	req->rw_len     = buflen;
+	req->completion = i2c_sync_request_complete;
+	ud.done = false;
+	req->user_data = &ud;
+
+	for (retries = 0; retries <= MAX_NACK_RETRIES; retries++) {
+		waited = 0;
+		i2c_set_req_timeout(req, timeout);
+		i2c_queue_req(req);
+
+		do {
+			time_to_wait = i2c_run_req(req);
+			if (!time_to_wait)
+				time_to_wait = REQ_COMPLETE_POLLING;
+			time_wait(time_to_wait);
+			waited += time_to_wait;
+		} while (!ud.done);
+
+		rc = ud.rc;
+
+		if (rc == OPAL_I2C_NACK_RCVD)
+			continue;
+		else
+			/* error or success */
+			break;
+	}
+
+	prlog(PR_DEBUG, "I2C: %s req op=%x offset=%x buf=%016llx buflen=%d "
+	      "delay=%lu/%d rc=%d\n",
+	      (rc) ? "!!!!" : "----", req->op, req->offset,
+	      *(uint64_t*) buf, req->rw_len, tb_to_msecs(waited), timeout, rc);
+
+	i2c_free_req(req);
+	if (rc)
+		return OPAL_HARDWARE;
+
+	return OPAL_SUCCESS;
+}
diff --git a/include/i2c.h b/include/i2c.h
index e5e8584b..669c30a2 100644
--- a/include/i2c.h
+++ b/include/i2c.h
@@ -106,6 +106,11 @@  static inline int i2c_check_quirk(struct i2c_request *req, int *rc)
 	return 0;
 }
 
+/* I2C synchronous request API */
+int i2c_request_send(int bus_id, int dev_addr, int read_write,
+		     uint32_t offset, uint32_t offset_bytes, void* buf,
+		     size_t buflen, int timeout);
+
 /* P8 implementation details */
 extern void p8_i2c_init(void);
 extern void p8_i2c_interrupt(uint32_t chip_id);
diff --git a/libstb/drivers/tpm_i2c_interface.c b/libstb/drivers/tpm_i2c_interface.c
index cc7bf1ad..a6342e0c 100644
--- a/libstb/drivers/tpm_i2c_interface.c
+++ b/libstb/drivers/tpm_i2c_interface.c
@@ -15,32 +15,13 @@ 
  */
 
 #include <skiboot.h>
-#include <timebase.h>
 #include <opal-api.h>
 #include <i2c.h>
 
 #include "tpm_i2c_interface.h"
 #include "../status_codes.h"
 
-//#define DBG(fmt, ...) prlog(PR_DEBUG, fmt, ##__VA_ARGS__)
-#define DBG(fmt, ...)
-
 #define I2C_BYTE_TIMEOUT_MS		30  /* 30ms/byte timeout */
-#define TPM_MAX_NACK_RETRIES		 2
-#define REQ_COMPLETE_POLLING		 5  /* Check if req is complete
-					       in 5ms interval */
-
-struct tpm_i2c_userdata {
-	int rc;
-	bool done;
-};
-
-void tpm_i2c_request_complete(int rc, struct i2c_request *req)
-{
-	struct tpm_i2c_userdata *ud = req->user_data;
-	ud->rc = rc;
-	ud->done = true;
-}
 
 /**
  * tpm_i2c_request_send - send request to i2c bus
@@ -57,88 +38,24 @@  void tpm_i2c_request_complete(int rc, struct i2c_request *req)
  *
  * Returns: Zero on success otherwise a negative error code
  */
-int tpm_i2c_request_send(int tpm_bus_id, int tpm_dev_addr, int read_write,
+int tpm_i2c_request_send(int bus_id, int dev_addr, int read_write,
 			 uint32_t offset, uint32_t offset_bytes, void* buf,
 			 size_t buflen)
 {
-	int rc, waited, retries, timeout;
-	struct i2c_request *req;
-	struct i2c_bus *bus;
-	uint64_t time_to_wait = 0;
-	struct tpm_i2c_userdata ud;
-
-	bus = i2c_find_bus_by_id(tpm_bus_id);
-	if (!bus) {
-		/**
-		 * @fwts-label TPMI2CInvalidBusID
-		 * @fwts-advice tpm_i2c_request_send was passed an invalid bus
-		 * ID. This indicates a tb_init() bug.
-		 */
-		prlog(PR_ERR, "TPM: Invalid bus_id=%x\n", tpm_bus_id);
-		rc = STB_ARG_ERROR;
-		goto out;
-	}
-
-	req = i2c_alloc_req(bus);
-	if (!req) {
-		/**
-		 * @fwts-label TPMI2CAllocationFailed
-		 * @fwts-advice OPAL failed to allocate memory for an
-		 * i2c_request. This points to an OPAL bug as OPAL run out of
-		 * memory and this should never happen.
-		 */
-		prlog(PR_ERR, "TPM: i2c_alloc_req failed\n");
-		rc = STB_DRIVER_ERROR;
-		goto out;
-	}
-
-	req->dev_addr   = tpm_dev_addr;
-	req->op         = read_write;
-	req->offset     = offset;
-	req->offset_bytes = offset_bytes;
-	req->rw_buf     = (void*) buf;
-	req->rw_len     = buflen;
-	req->completion = tpm_i2c_request_complete;
-	ud.done = false;
-	req->user_data = &ud;
+	int rc, timeout;
 
 	/*
-	 * Set the request timeout to 10ms per byte. Otherwise, we get
-	 * an I2C master timeout for all requests sent to the TPM device
+	 * Set the request timeout to 30ms per byte. Otherwise, we get
+	 * an I2C master timeout for all requests sent to the device
 	 * since the I2C master's timeout is too short (1ms per byte).
 	 */
 	timeout = (buflen + offset_bytes + 2) * I2C_BYTE_TIMEOUT_MS;
 
-	for (retries = 0; retries <= TPM_MAX_NACK_RETRIES; retries++) {
-		waited = 0;
-		i2c_set_req_timeout(req, timeout);
-		i2c_queue_req(req);
-
-		do {
-			time_to_wait = i2c_run_req(req);
-			if (!time_to_wait)
-				time_to_wait = REQ_COMPLETE_POLLING;
-			time_wait(time_to_wait);
-			waited += time_to_wait;
-		} while (!ud.done);
-
-		rc = ud.rc;
-
-		if (rc == OPAL_I2C_NACK_RCVD)
-			continue;
-		else
-			/* error or success */
-			break;
-	}
-
-	DBG("%s tpm req op=%x offset=%x buf=%016llx buflen=%d delay=%lu/%d,"
-	    "rc=%d\n",
-	    (rc) ? "!!!!" : "----", req->op, req->offset,
-	    *(uint64_t*) buf, req->rw_len, tb_to_msecs(waited), timeout, rc);
-
-	i2c_free_req(req);
-	if (rc)
-		rc = STB_DRIVER_ERROR;
-out:
-	return rc;
+	rc = i2c_request_send(bus_id, dev_addr, read_write, offset,
+			      offset_bytes, buf, buflen, timeout);
+	if (rc == OPAL_PARAMETER)
+		return STB_ARG_ERROR;
+	else if (rc < 0)
+		return STB_DRIVER_ERROR;
+	return 0;
 }
diff --git a/libstb/drivers/tpm_i2c_interface.h b/libstb/drivers/tpm_i2c_interface.h
index 0ac021ed..d2acc1ae 100644
--- a/libstb/drivers/tpm_i2c_interface.h
+++ b/libstb/drivers/tpm_i2c_interface.h
@@ -20,7 +20,6 @@ 
 #include <i2c.h>
 #include <stdlib.h>
 
-extern void tpm_i2c_request_complete(int rc, struct i2c_request *req);
 extern int tpm_i2c_request_send(int tpm_bus_id, int tpm_dev_addr, int read_write,
 				uint32_t offset, uint32_t offset_bytes, void* buf,
 				size_t buflen);