aboutsummaryrefslogtreecommitdiff
path: root/core
diff options
context:
space:
mode:
authorAndrew Donnellan <andrew.donnellan@au1.ibm.com>2017-09-06 17:34:35 +1000
committerStewart Smith <stewart@linux.vnet.ibm.com>2017-10-02 21:59:33 -0500
commit6c22ac75211b140e56f28825fb3518f5b07aa305 (patch)
tree6d4d0553724d7f34886405ed591f836080decb88 /core
parent47cb47a1c0dad85514773fd7686fb50e5ace3966 (diff)
downloadskiboot-6c22ac75211b140e56f28825fb3518f5b07aa305.zip
skiboot-6c22ac75211b140e56f28825fb3518f5b07aa305.tar.gz
skiboot-6c22ac75211b140e56f28825fb3518f5b07aa305.tar.bz2
i2c: Move tpm i2c wrapper code into core
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> Cc: Frederic Barrat <fbarrat@linux.vnet.ibm.com> Signed-off-by: Andrew Donnellan <andrew.donnellan@au1.ibm.com> Signed-off-by: Stewart Smith <stewart@linux.vnet.ibm.com>
Diffstat (limited to 'core')
-rw-r--r--core/i2c.c107
1 files changed, 107 insertions, 0 deletions
diff --git a/core/i2c.c b/core/i2c.c
index c164624..d4311c2 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;
+}