summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ec/google/chromeec/ec_lpc.c169
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)