From 2384bae15f324b0c451c08920951614e89d08ef2 Mon Sep 17 00:00:00 2001 From: Gabe Black Date: Fri, 28 Jun 2013 14:24:33 -0700 Subject: [PATCH] chromeec: Add a function to send passthrough i2c messages. BUG=chrome-os-partner:19420 TEST=Built and booted into depthcharge on pit. Used the function to read and write i2c registers on the tps65090 and verified that they were consistent with expected values and were affected by writes in a reasonable way. BRANCH=None Change-Id: I57ea6be8b81e31191399a7457ef5106c56f7f27d Signed-off-by: Gabe Black Reviewed-on: https://gerrit.chromium.org/gerrit/60516 Reviewed-by: Ronald G. Minnich Reviewed-by: David Hendricks Commit-Queue: Gabe Black Tested-by: Gabe Black --- src/ec/google/chromeec/ec.c | 88 +++++++++++++++++++++++++++++++++++++ src/ec/google/chromeec/ec.h | 2 + 2 files changed, 90 insertions(+) diff --git a/src/ec/google/chromeec/ec.c b/src/ec/google/chromeec/ec.c index 9b848c06c2..7546a82ee8 100644 --- a/src/ec/google/chromeec/ec.c +++ b/src/ec/google/chromeec/ec.c @@ -136,6 +136,94 @@ void google_chromeec_early_init(void) #ifndef __PRE_RAM__ +int google_chromeec_i2c_xfer(uint8_t chip, uint8_t addr, int alen, + uint8_t *buffer, int len, int is_read) +{ + 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 = ¶ms.p; + struct ec_response_i2c_passthru *r = &response.r; + struct ec_params_i2c_passthru_msg *msg = p->msg; + struct chromeec_command cmd; + uint8_t *pdata; + int read_len, write_len; + int size; + int rv; + + p->port = 0; + + if (alen != 1) { + printk(BIOS_ERR, "Unsupported address length %d\n", alen); + return -1; + } + if (is_read) { + read_len = len; + write_len = alen; + p->num_msgs = 2; + } else { + read_len = 0; + write_len = alen + len; + p->num_msgs = 1; + } + + size = sizeof(*p) + p->num_msgs * sizeof(*msg); + if (size + write_len > sizeof(params)) { + printk(BIOS_ERR, "Params too large for buffer\n"); + return -1; + } + if (sizeof(*r) + read_len > sizeof(response)) { + printk(BIOS_ERR, "Read length too big for buffer\n"); + return -1; + } + + /* Create a message to write the register address and optional data */ + pdata = (uint8_t *)p + size; + msg->addr_flags = chip; + msg->len = write_len; + pdata[0] = addr; + if (!is_read) + memcpy(pdata + 1, buffer, len); + msg++; + + if (read_len) { + msg->addr_flags = chip | EC_I2C_FLAG_READ; + msg->len = read_len; + } + + cmd.cmd_code = EC_CMD_I2C_PASSTHRU; + cmd.cmd_version = 0; + cmd.cmd_data_in = p; + cmd.cmd_size_in = size + write_len; + cmd.cmd_data_out = r; + cmd.cmd_size_out = sizeof(*r) + read_len; + rv = google_chromeec_command(&cmd); + if (rv != 0) + return rv; + + /* Parse response */ + if (r->i2c_status & EC_I2C_STATUS_ERROR) { + printk(BIOS_ERR, "Transfer failed with status=0x%x\n", + r->i2c_status); + return -1; + } + + if (cmd.cmd_size_out < sizeof(*r) + read_len) { + printk(BIOS_ERR, "Truncated read response\n"); + return -1; + } + + if (read_len) + memcpy(buffer, r->data, read_len); + + return 0; +} + static int google_chromeec_set_mask(u8 type, u32 mask) { struct ec_params_host_event_mask req; diff --git a/src/ec/google/chromeec/ec.h b/src/ec/google/chromeec/ec.h index 356d2d215d..f661d311a8 100644 --- a/src/ec/google/chromeec/ec.h +++ b/src/ec/google/chromeec/ec.h @@ -25,6 +25,8 @@ #include #ifndef __PRE_RAM__ +int google_chromeec_i2c_xfer(uint8_t chip, uint8_t addr, int alen, + uint8_t *buffer, int len, int is_read); u32 google_chromeec_get_wake_mask(void); int google_chromeec_set_sci_mask(u32 mask); int google_chromeec_set_smi_mask(u32 mask);