diff options
author | Gabe Black <gabeblack@google.com> | 2013-06-28 14:24:33 -0700 |
---|---|---|
committer | Stefan Reinauer <stefan.reinauer@coreboot.org> | 2013-07-10 23:18:30 +0200 |
commit | 9f96aa6b5e6bc5af8feb7bb29239f8421ded1f14 (patch) | |
tree | 2ef6a5a9bf7f316e62a1232c04154c5d24e3a5dd | |
parent | 2b3167908be680b5aa1cd6fe2f42a44d4c118f3f (diff) |
chromeec: Add a function to send passthrough i2c messages.
Change-Id: I576d0dbf65693f40d7d1c20d3d5e7a75b8e14dc9
Signed-off-by: Gabe Black <gabeblack@google.com>
Reviewed-on: http://review.coreboot.org/3752
Tested-by: build bot (Jenkins)
Reviewed-by: Stefan Reinauer <stefan.reinauer@coreboot.org>
-rw-r--r-- | src/ec/google/chromeec/ec.c | 88 | ||||
-rw-r--r-- | src/ec/google/chromeec/ec.h | 2 |
2 files changed, 90 insertions, 0 deletions
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 <stdint.h> #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); |