summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2013-04-30 12:16:39 -0700
committerChromeBot <chrome-bot@google.com>2013-05-09 16:36:53 -0700
commit1e4b0b6194b3ff16233e824daa4bab18a8dcce0f (patch)
treede57b1a8f1bbda6be2f515ae2a59882ef6365e23
parentd2ca284bc6da637aabb0dbfd4a4d67451646f23a (diff)
downloadchrome-ec-1e4b0b6194b3ff16233e824daa4bab18a8dcce0f.tar.gz
Implement I2C passthru command
This is a revised version of passthru which more closely resembles the kernel interface. It allows multiple read/write messages in a single transaction, and sends back one accumulated result. BUG=chrome-os-partner:18778 BRANCH=none TEST=On link, from root shell: ectool i2cxfer 0 0xb 6 0x21 Read bytes: 0x05 0x41 0x52 0x52 0x4f 0x57 (I did not actually run this with the updated code) On pit, in U-Boot: Read i2c values: Peach # crosec i2c md 48 0 0000: 00 00 3e 00 12 20 4b bf ff ff 20 00 1e 1e 1e 1f ..>.. K... ..... Peach # crosec i2c md 48 0 20 0000: 00 00 3e 00 12 20 4b bf ff ff 20 00 1e 1e 1e 1f ..>.. K... ..... 0010: 1f 1f 1f 1f 1f 1f 20 00 00 07 00 00 00 00 00 00 ...... ......... Update value at offset 10: Peach # crosec i2c mw 48 10 4 Peach # crosec i2c md 48 0 20 0000: 00 00 3e 00 12 00 0b 1f 1f ff 20 00 1e 1e 1e 1f ..>....... ..... 0010: 04 1f 1f 1f 1f 1f 20 00 00 07 00 00 00 00 00 00 ...... ......... Peach # On pit, in kernel: localhost ~ # i2cdetect -y -a -r 20 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- UU -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- localhost ~ # i2cdump -f -y 20 0x48 No size specified (using byte-data access) 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef 00: 00 00 3e 00 12 00 0b 1f 1f ff 20 00 1e 1e 1e 1f ..>.?.???. .???? 10: 1f 1f 0e 1f 1f 0e 20 00 00 07 00 00 00 00 00 00 ?????? ..?...... 20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ localhost ~ # i2cset -f -y 20 0x48 0x10 0 localhost ~ # i2cdump -f -y 20 0x48 No size specified (using byte-data access) 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef 00: 00 00 3e 00 12 00 0b 1f 1f ff 20 00 1e 1e 1e 1f ..>.?.???. .???? 10: 00 1f 0e 1f 1f 0e 20 00 00 07 00 00 00 00 00 00 .????? ..?...... 20: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ localhost ~ # i2cset -f -y 20 0x48 0x10 0x1f localhost ~ # i2cdump -f -y 20 0x48 No size specified (using byte-data access) 0 1 2 3 4 5 6 7 8 9 a b c d e f 0123456789abcdef 00: 00 00 3e 00 12 00 0b 1f 1f ff 20 00 1e 1e 1e 1f ..>.?.???. .???? 10: 1f 1f 0e 1f 1f 0e 20 00 00 07 00 00 00 00 00 00 ?????? ..?...... Change-Id: I14d47e1712828f726ac5caddc4beede251570ad3 Signed-off-by: Randall Spangler <rspangler@chromium.org> Updated to simplify protocol: Signed-off-by: Simon Glass <sjg@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/49958 Commit-Queue: Doug Anderson <dianders@chromium.org> Reviewed-by: Doug Anderson <dianders@chromium.org> Tested-by: Doug Anderson <dianders@chromium.org>
-rw-r--r--board/link/board.h1
-rw-r--r--common/i2c_common.c160
-rw-r--r--include/ec_commands.h43
-rw-r--r--util/ectool.c121
4 files changed, 323 insertions, 2 deletions
diff --git a/board/link/board.h b/board/link/board.h
index f923d45a46..9552e62b5b 100644
--- a/board/link/board.h
+++ b/board/link/board.h
@@ -24,6 +24,7 @@
#endif
#define CONFIG_CUSTOM_KEYSCAN
#define CONFIG_EXTPOWER_GPIO
+#define CONFIG_I2C_PASSTHRU_RESTRICTED
#ifdef HAS_TASK_KEYPROTO
#define CONFIG_KEYBOARD_PROTOCOL_8042
#endif
diff --git a/common/i2c_common.c b/common/i2c_common.c
index e3f4293a6a..6ee923fadd 100644
--- a/common/i2c_common.c
+++ b/common/i2c_common.c
@@ -14,6 +14,9 @@
#include "util.h"
#include "watchdog.h"
+#define CPUTS(outstr) cputs(CC_I2C, outstr)
+#define CPRINTF(format, args...) cprintf(CC_I2C, format, ## args)
+
extern const struct i2c_port_t i2c_ports[I2C_PORTS_USED];
static struct mutex port_mutex[I2C_PORT_COUNT];
@@ -123,8 +126,10 @@ static int i2c_command_read(struct host_cmd_handler_args *args)
struct ec_response_i2c_read *r = args->response;
int data, rv = -1;
+#ifdef CONFIG_I2C_PASSTHRU_RESTRICTED
if (system_is_locked())
return EC_RES_ACCESS_DENIED;
+#endif
if (p->read_size == 16)
rv = i2c_read16(p->port, p->addr, p->offset, &data);
@@ -145,8 +150,10 @@ static int i2c_command_write(struct host_cmd_handler_args *args)
const struct ec_params_i2c_write *p = args->params;
int rv = -1;
+#ifdef CONFIG_I2C_PASSTHRU_RESTRICTED
if (system_is_locked())
return EC_RES_ACCESS_DENIED;
+#endif
if (p->write_size == 16)
rv = i2c_write16(p->port, p->addr, p->offset, p->data);
@@ -160,6 +167,159 @@ static int i2c_command_write(struct host_cmd_handler_args *args)
}
DECLARE_HOST_COMMAND(EC_CMD_I2C_WRITE, i2c_command_write, EC_VER_MASK(0));
+/* TODO: remove temporary extra debugging for help host-side debugging */
+#ifdef CONFIG_I2C_DEBUG_PASSTHRU
+#define PTHRUPRINTF(format, args...) cprintf(CC_I2C, format, ## args)
+#else
+#define PTHRUPRINTF(format, args...)
+#endif
+
+/**
+ * Perform the voluminous checking required for this message
+ *
+ * @param args Arguments
+ * @return 0 if OK, EC_RES_INVALID_PARAM on error
+ */
+static int check_i2c_params(const struct host_cmd_handler_args *args)
+{
+ const struct ec_params_i2c_passthru *params = args->params;
+ const struct ec_params_i2c_passthru_msg *msg;
+ int read_len = 0, write_len = 0;
+ unsigned int size;
+ int msgnum;
+
+ if (args->params_size < sizeof(*params)) {
+ PTHRUPRINTF("[%T i2c passthru no params, params_size=%d, need at least %d]\n",
+ args->params_size, sizeof(*params));
+ return EC_RES_INVALID_PARAM;
+ }
+ size = sizeof(*params) + params->num_msgs * sizeof(*msg);
+ if (args->params_size < size) {
+ PTHRUPRINTF("[%T i2c passthru params_size=%d, need at least %d]\n",
+ args->params_size, size);
+ return EC_RES_INVALID_PARAM;
+ }
+
+ if (params->port >= I2C_PORT_COUNT) {
+ PTHRUPRINTF("[%T i2c passthru invalid port %d]\n",
+ params->port);
+ return EC_RES_INVALID_PARAM;
+ }
+
+ /* Loop and process messages */;
+ for (msgnum = 0, msg = params->msg; msgnum < params->num_msgs;
+ msgnum++, msg++) {
+ unsigned int addr_flags = msg->addr_flags;
+
+ /* Parse slave address if necessary */
+ if (addr_flags & EC_I2C_FLAG_10BIT) {
+ /* 10-bit addressing not supported yet */
+ PTHRUPRINTF("[%T i2c passthru no 10-bit addressing]\n");
+ return EC_RES_INVALID_PARAM;
+ }
+
+ PTHRUPRINTF("[%T i2c passthru port=%d, %s, addr=0x%02x, len=0x%02x]\n",
+ params->port,
+ addr_flags & EC_I2C_FLAG_READ ? "read" : "write",
+ addr_flags & EC_I2C_ADDR_MASK,
+ msg->len);
+
+ if (addr_flags & EC_I2C_FLAG_READ)
+ read_len += msg->len;
+ else
+ write_len += msg->len;
+ }
+
+ /* Check there is room for the data */
+ if (args->response_max <
+ sizeof(struct ec_response_i2c_passthru) + read_len) {
+ PTHRUPRINTF("[%T i2c passthru overflow1]\n");
+ return EC_RES_INVALID_PARAM;
+ }
+
+ /* Must have bytes to write */
+ if (args->params_size < size + write_len) {
+ PTHRUPRINTF("[%T i2c passthru overflow2]\n");
+ return EC_RES_INVALID_PARAM;
+ }
+
+ return EC_RES_SUCCESS;
+}
+
+static int i2c_command_passthru(struct host_cmd_handler_args *args)
+{
+ const struct ec_params_i2c_passthru *params = args->params;
+ const struct ec_params_i2c_passthru_msg *msg;
+ struct ec_response_i2c_passthru *resp = args->response;
+ const uint8_t *out;
+ int in_len;
+ int ret;
+
+#ifdef CONFIG_I2C_PASSTHRU_RESTRICTED
+ if (system_is_locked())
+ return EC_RES_ACCESS_DENIED;
+#endif
+
+ ret = check_i2c_params(args);
+ if (ret)
+ return ret;
+
+ /* Loop and process messages */
+ resp->i2c_status = 0;
+ out = args->params + sizeof(*params) + params->num_msgs * sizeof(*msg);
+ in_len = 0;
+
+ i2c_lock(params->port, 1);
+
+ for (resp->num_msgs = 0, msg = params->msg;
+ resp->num_msgs < params->num_msgs;
+ resp->num_msgs++, msg++) {
+ /* EC uses 8-bit slave address */
+ unsigned int addr = (msg->addr_flags & EC_I2C_ADDR_MASK) << 1;
+ int xferflags = I2C_XFER_START;
+ int read_len = 0, write_len = 0;
+ int rv;
+
+ if (msg->addr_flags & EC_I2C_FLAG_READ)
+ read_len = msg->len;
+ else
+ write_len = msg->len;
+
+ /* Set stop bit for last message */
+ if (resp->num_msgs == params->num_msgs - 1)
+ xferflags |= I2C_XFER_STOP;
+
+ /* Transfer next message */
+ PTHRUPRINTF("[%T i2c passthru xfer port=%x, addr=%x, out=%p, write_len=%x, data=%p, read_len=%x, xferflags=%x]\n",
+ params->port, addr, out, write_len,
+ &resp->data[in_len], read_len, xferflags);
+ rv = i2c_xfer(params->port, addr, out, write_len,
+ &resp->data[in_len], read_len, xferflags);
+ if (rv) {
+ /* Driver will have sent a stop bit here */
+ if (rv == EC_ERROR_TIMEOUT)
+ resp->i2c_status = EC_I2C_STATUS_TIMEOUT;
+ else
+ resp->i2c_status = EC_I2C_STATUS_NAK;
+ break;
+ }
+
+ in_len += read_len;
+ out += write_len;
+ }
+ args->response_size = sizeof(*resp) + in_len;
+
+ /* Unlock port */
+ i2c_lock(params->port, 0);
+
+ /*
+ * Return success even if transfer failed so response is sent. Host
+ * will check message status to determine the transfer result.
+ */
+ return EC_RES_SUCCESS;
+}
+DECLARE_HOST_COMMAND(EC_CMD_I2C_PASSTHRU, i2c_command_passthru, EC_VER_MASK(0));
+
/*****************************************************************************/
/* Console commands */
diff --git a/include/ec_commands.h b/include/ec_commands.h
index a8944cb719..56df6291d4 100644
--- a/include/ec_commands.h
+++ b/include/ec_commands.h
@@ -1140,7 +1140,7 @@ struct ec_response_gpio_get {
#define EC_CMD_I2C_READ 0x94
struct ec_params_i2c_read {
- uint16_t addr;
+ uint16_t addr; /* 8-bit address (7-bit shifted << 1) */
uint8_t read_size; /* Either 8 or 16. */
uint8_t port;
uint8_t offset;
@@ -1154,7 +1154,7 @@ struct ec_response_i2c_read {
struct ec_params_i2c_write {
uint16_t data;
- uint16_t addr;
+ uint16_t addr; /* 8-bit address (7-bit shifted << 1) */
uint8_t write_size; /* Either 8 or 16. */
uint8_t port;
uint8_t offset;
@@ -1254,6 +1254,45 @@ struct ec_response_power_info {
} __packed;
/*****************************************************************************/
+/* I2C passthru command */
+
+#define EC_CMD_I2C_PASSTHRU 0x9e
+
+/* Slave address is 10 (not 7) bit */
+#define EC_I2C_FLAG_10BIT (1 << 16)
+
+/* Read data; if not present, message is a write */
+#define EC_I2C_FLAG_READ (1 << 15)
+
+/* Mask for address */
+#define EC_I2C_ADDR_MASK 0x3ff
+
+#define EC_I2C_STATUS_NAK (1 << 0) /* Transfer was not acknowledged */
+#define EC_I2C_STATUS_TIMEOUT (1 << 1) /* Timeout during transfer */
+
+/* Any error */
+#define EC_I2C_STATUS_ERROR (EC_I2C_STATUS_NAK | EC_I2C_STATUS_TIMEOUT)
+
+struct ec_params_i2c_passthru_msg {
+ uint16_t addr_flags; /* I2C slave address (7 or 10 bits) and flags */
+ uint16_t len; /* Number of bytes to write*/
+} __packed;
+
+struct ec_params_i2c_passthru {
+ uint8_t port; /* I2C port number */
+ uint8_t num_msgs; /* Number of messages */
+ struct ec_params_i2c_passthru_msg msg[];
+ /* Data for all messages is concatenated here */
+} __packed;
+
+struct ec_response_i2c_passthru {
+ uint8_t i2c_status; /* Status flags (EC_I2C_STATUS_...) */
+ uint8_t num_msgs; /* Number of messages processed */
+ uint8_t data[]; /* Data for all messages concatenated here */
+} __packed;
+
+
+/*****************************************************************************/
/* Temporary debug commands. TODO: remove this crosbug.com/p/13849 */
/*
diff --git a/util/ectool.c b/util/ectool.c
index 0315b6dbb1..52b4df94d1 100644
--- a/util/ectool.c
+++ b/util/ectool.c
@@ -94,6 +94,8 @@ const char help_str[] =
" Read I2C bus\n"
" i2cwrite\n"
" Write I2C bus\n"
+ " i2cxfer <port> <slave_addr> <read_count> [write bytes...]\n"
+ " Perform I2C transfer on EC's I2C bus\n"
" keyscan <beat_us> <filename>\n"
" Test low-level key scanning\n"
" lightbar [CMDS]\n"
@@ -1933,6 +1935,10 @@ int cmd_i2c_read(int argc, char *argv[])
return -1;
}
+ /*
+ * TODO: use I2C_XFER command if supported, then fall back to I2C_READ
+ */
+
rv = ec_command(EC_CMD_I2C_READ, 0, &p, sizeof(p), &r, sizeof(r));
if (rv < 0)
@@ -1987,6 +1993,10 @@ int cmd_i2c_write(int argc, char *argv[])
return -1;
}
+ /*
+ * TODO: use I2C_XFER command if supported, then fall back to I2C_WRITE
+ */
+
rv = ec_command(EC_CMD_I2C_WRITE, 0, &p, sizeof(p), NULL, 0);
if (rv < 0)
@@ -1998,6 +2008,116 @@ int cmd_i2c_write(int argc, char *argv[])
}
+int cmd_i2c_xfer(int argc, char *argv[])
+{
+ union {
+ struct ec_params_i2c_passthru p;
+ uint8_t outbuf[EC_HOST_PARAM_SIZE];
+ } params;
+ union {
+ struct ec_response_i2c_passthru r;
+ uint8_t inbuf[EC_HOST_PARAM_SIZE];
+ } response;
+ struct ec_params_i2c_passthru *p = &params.p;
+ struct ec_response_i2c_passthru *r = &response.r;
+ struct ec_params_i2c_passthru_msg *msg = p->msg;
+ unsigned int addr;
+ uint8_t *pdata;
+ char *e;
+ int read_len, write_len;
+ int size;
+ int rv, i;
+
+ if (argc < 4) {
+ fprintf(stderr,
+ "Usage: %s <port> <slave_addr> <read_count> "
+ "[write bytes...]\n", argv[0]);
+ return -1;
+ }
+
+ p->port = strtol(argv[1], &e, 0);
+ if (e && *e) {
+ fprintf(stderr, "Bad port.\n");
+ return -1;
+ }
+
+ addr = strtol(argv[2], &e, 0) & 0x7f;
+ if (e && *e) {
+ fprintf(stderr, "Bad slave address.\n");
+ return -1;
+ }
+
+ read_len = strtol(argv[3], &e, 0);
+ if (e && *e) {
+ fprintf(stderr, "Bad read length.\n");
+ return -1;
+ }
+
+ /* Skip over params to bytes to write */
+ argc -= 4;
+ argv += 4;
+ write_len = argc;
+ p->num_msgs = (read_len != 0) + (write_len != 0);
+
+ size = sizeof(*p) + p->num_msgs * sizeof(*msg);
+ if (size + write_len > sizeof(params)) {
+ fprintf(stderr, "Params too large for buffer\n");
+ return -1;
+ }
+ if (sizeof(*r) + read_len > sizeof(response)) {
+ fprintf(stderr, "Read length too big for buffer\n");
+ return -1;
+ }
+
+ pdata = (uint8_t *)p + size;
+ if (write_len) {
+ msg->addr_flags = addr;
+ msg->len = write_len;
+
+ for (i = 0; i < write_len; i++) {
+ pdata[i] = strtol(argv[i], &e, 0);
+ if (e && *e) {
+ fprintf(stderr, "Bad write byte %d\n", i);
+ return -1;
+ }
+ }
+ msg++;
+ }
+
+ if (read_len) {
+ msg->addr_flags = addr | EC_I2C_FLAG_READ;
+ msg->len = read_len;
+ }
+
+ rv = ec_command(EC_CMD_I2C_PASSTHRU, 0, p, size + write_len,
+ r, sizeof(*r) + read_len);
+ if (rv < 0)
+ return rv;
+
+ /* Parse response */
+ if (r->i2c_status & (EC_I2C_STATUS_NAK | EC_I2C_STATUS_TIMEOUT)) {
+ fprintf(stderr, "Transfer failed with status=0x%x\n",
+ r->i2c_status);
+ return -1;
+ }
+
+ if (rv < sizeof(*r) + read_len) {
+ fprintf(stderr, "Truncated read response\n");
+ return -1;
+ }
+
+ if (read_len) {
+ printf("Read bytes:");
+ for (i = 0; i < read_len; i++)
+ printf(" %#02x", r->data[i]);
+ printf("\n");
+ } else {
+ printf("Write successful.\n");
+ }
+
+ return 0;
+}
+
int cmd_lcd_backlight(int argc, char *argv[])
{
struct ec_params_switch_enable_backlight p;
@@ -2787,6 +2907,7 @@ const struct command commands[] = {
{"kbpress", cmd_kbpress},
{"i2cread", cmd_i2c_read},
{"i2cwrite", cmd_i2c_write},
+ {"i2cxfer", cmd_i2c_xfer},
{"lightbar", cmd_lightbar},
{"keyconfig", cmd_keyconfig},
{"keyscan", cmd_keyscan},