diff options
author | Duncan Laurie <dlaurie@chromium.org> | 2014-09-18 12:51:07 -0700 |
---|---|---|
committer | Patrick Georgi <pgeorgi@google.com> | 2015-04-02 17:27:34 +0200 |
commit | 60e6bf80dbad367250b19374e8b9d155cdb3fe90 (patch) | |
tree | 183e1e3992edec14c2bd0ec8c2f72bdaaee89881 /src | |
parent | fc0f5175fbf8a60dbc1894484c69d8da246dd671 (diff) | |
download | coreboot-60e6bf80dbad367250b19374e8b9d155cdb3fe90.tar.xz |
chromeec: Add support for v3 commands on LPC
In order to talk to the PD controller with a passthru command
coreboot needs to be able to use v3 commands.
The command version is automatically detected based on the
advertized flags from the EC.
BUG=chrome-os-partner:30079
BRANCH=none
TEST=boot on samus EVT
Change-Id: I032eb185d80d5b68c82609910045e21d4521afcc
Signed-off-by: Stefan Reinauer <reinauer@chromium.org>
Original-Commit-Id: 4f664b22645f0def87a73e9255297b3edccf436e
Original-Change-Id: I94ace7741c9cd592921625fb793787247a5ca2aa
Original-Signed-off-by: Duncan Laurie <dlaurie@chromium.org>
Original-Reviewed-on: https://chromium-review.googlesource.com/218902
Original-Reviewed-by: Aaron Durbin <adurbin@chromium.org>
Reviewed-on: http://review.coreboot.org/9203
Tested-by: build bot (Jenkins)
Reviewed-by: Patrick Georgi <pgeorgi@google.com>
Diffstat (limited to 'src')
-rw-r--r-- | src/ec/google/chromeec/ec_lpc.c | 169 |
1 files changed, 167 insertions, 2 deletions
diff --git a/src/ec/google/chromeec/ec_lpc.c b/src/ec/google/chromeec/ec_lpc.c index 5344716d47..4bd58ef792 100644 --- a/src/ec/google/chromeec/ec_lpc.c +++ b/src/ec/google/chromeec/ec_lpc.c @@ -29,6 +29,30 @@ #include "ec.h" #include "ec_commands.h" +static int google_chromeec_command_version(void) +{ + u8 id1, id2, flags; + + id1 = inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID); + id2 = inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1); + flags = inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_HOST_CMD_FLAGS); + + if (id1 != 'E' || id2 != 'C') { + printk(BIOS_ERR, "Missing Chromium EC memory map.\n"); + return -1; + } + + if (flags & EC_HOST_CMD_FLAG_VERSION_3) { + return EC_HOST_CMD_FLAG_VERSION_3; + } else if (flags & EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED) { + return EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED; + } else { + printk(BIOS_ERR, + "Chromium EC command version unsupported\n"); + return -1; + } +} + static int google_chromeec_wait_ready(u16 port) { u8 ec_status = inb(port); @@ -51,7 +75,119 @@ static int google_chromeec_wait_ready(u16 port) return 0; } -int google_chromeec_command(struct chromeec_command *cec_command) +static int google_chromeec_command_v3(struct chromeec_command *cec_command) +{ + struct ec_host_request rq; + struct ec_host_response rs; + const u8 *d; + u8 *dout; + int csum = 0; + int i; + + if (cec_command->cmd_size_in + sizeof(rq) > EC_LPC_HOST_PACKET_SIZE) { + printk(BIOS_ERR, "EC cannot send %ld bytes\n", + cec_command->cmd_size_in + sizeof(rq)); + return -1; + } + + if (cec_command->cmd_size_out > EC_LPC_HOST_PACKET_SIZE) { + printk(BIOS_ERR, "EC cannot receive %d bytes\n", + cec_command->cmd_size_out); + return -1; + } + + if (google_chromeec_wait_ready(EC_LPC_ADDR_HOST_CMD)) { + printk(BIOS_ERR, "Timeout waiting for EC start command %d!\n", + cec_command->cmd_code); + return -1; + } + + /* Fill in request packet */ + rq.struct_version = EC_HOST_REQUEST_VERSION; + rq.checksum = 0; + rq.command = cec_command->cmd_code | + EC_CMD_PASSTHRU_OFFSET(cec_command->cmd_dev_index); + rq.command_version = cec_command->cmd_version; + rq.reserved = 0; + rq.data_len = cec_command->cmd_size_in; + + /* Copy data and start checksum */ + for (i = 0, d = (const u8 *)cec_command->cmd_data_in; + i < cec_command->cmd_size_in; i++, d++) { + outb(*d, EC_LPC_ADDR_HOST_PACKET + sizeof(rq) + i); + csum += *d; + } + + /* Finish checksum */ + for (i = 0, d = (const u8 *)&rq; i < sizeof(rq); i++, d++) + csum += *d; + + /* Write checksum field so the entire packet sums to 0 */ + rq.checksum = (u8)(-csum); + + /* Copy header */ + for (i = 0, d = (const uint8_t *)&rq; i < sizeof(rq); i++, d++) + outb(*d, EC_LPC_ADDR_HOST_PACKET + i); + + /* Start the command */ + outb(EC_COMMAND_PROTOCOL_3, EC_LPC_ADDR_HOST_CMD); + + if (google_chromeec_wait_ready(EC_LPC_ADDR_HOST_CMD)) { + printk(BIOS_ERR, "Timeout waiting for EC process command %d!\n", + cec_command->cmd_code); + return -1; + } + + /* Check result */ + cec_command->cmd_code = inb(EC_LPC_ADDR_HOST_DATA); + if (cec_command->cmd_code) { + printk(BIOS_ERR, "EC returned error result code %d\n", + cec_command->cmd_code); + return -i; + } + + /* Read back response header and start checksum */ + csum = 0; + for (i = 0, dout = (u8 *)&rs; i < sizeof(rs); i++, dout++) { + *dout = inb(EC_LPC_ADDR_HOST_PACKET + i); + csum += *dout; + } + + if (rs.struct_version != EC_HOST_RESPONSE_VERSION) { + printk(BIOS_ERR, "EC response version mismatch (%d != %d)\n", + rs.struct_version, EC_HOST_RESPONSE_VERSION); + return -1; + } + + if (rs.reserved) { + printk(BIOS_ERR, "EC response reserved is %d, should be 0\n", + rs.reserved); + return -1; + } + + if (rs.data_len > cec_command->cmd_size_out) { + printk(BIOS_ERR, "EC returned too much data (%d > %d)\n", + rs.data_len, cec_command->cmd_size_out); + return -1; + } + + /* Read back data and update checksum */ + for (i = 0, dout = (uint8_t *)cec_command->cmd_data_out; + i < rs.data_len; i++, dout++) { + *dout = inb(EC_LPC_ADDR_HOST_PACKET + sizeof(rs) + i); + csum += *dout; + } + + /* Verify checksum */ + if ((u8)csum) { + printk(BIOS_ERR, "EC response has invalid checksum\n"); + return -1; + } + + return 0; +} + +static int google_chromeec_command_v1(struct chromeec_command *cec_command) { struct ec_lpc_host_args args; const u8 *d; @@ -134,7 +270,36 @@ int google_chromeec_command(struct chromeec_command *cec_command) return 0; } -#ifndef __PRE_RAM__ +#ifdef __PRE_RAM__ + +int google_chromeec_command(struct chromeec_command *cec_command) +{ + switch (google_chromeec_command_version()) { + case EC_HOST_CMD_FLAG_VERSION_3: + return google_chromeec_command_v3(cec_command); + case EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED: + return google_chromeec_command_v1(cec_command); + } + return -1; +} + +#else /* !__PRE_RAM__ */ + +int google_chromeec_command(struct chromeec_command *cec_command) +{ + static int command_version = 0; + + if (command_version <= 0) + command_version = google_chromeec_command_version(); + + switch (command_version) { + case EC_HOST_CMD_FLAG_VERSION_3: + return google_chromeec_command_v3(cec_command); + case EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED: + return google_chromeec_command_v1(cec_command); + } + return -1; +} #ifndef __SMM__ static void lpc_ec_init(struct device *dev) |