summaryrefslogtreecommitdiff
path: root/util/comm-lpc.c
diff options
context:
space:
mode:
authorRandall Spangler <rspangler@chromium.org>2012-07-12 10:40:51 -0700
committerGerrit <chrome-bot@google.com>2012-07-12 18:10:30 -0700
commit02d820786c2fda890411494ddbc9e6d2ddf90f32 (patch)
tree264cbf06f8a0bc14c52be1dae895abe6efd88b39 /util/comm-lpc.c
parentc304ff7d81a2a3072e5388584b232413a76f3343 (diff)
downloadchrome-ec-02d820786c2fda890411494ddbc9e6d2ddf90f32.tar.gz
Support new-style LPC command interface in EC, ectool
Both EC and ectool are still backwards-compatible to the old interface. BUG=chrome-os-partner:11275 TEST=manual From U-boot prompt: mkbp hash // test old host talking to new EC From root shell: ectool echash // test new host talking to new EC You can also update just the OS and use an old EC, and verify that 'ectool echash' still works, which tests a new host talking to an old EC. Change-Id: I2afbb208cb16836f842ba119b74b1ab6a38ce5d5 Signed-off-by: Randall Spangler <rspangler@chromium.org> Reviewed-on: https://gerrit.chromium.org/gerrit/27313
Diffstat (limited to 'util/comm-lpc.c')
-rw-r--r--util/comm-lpc.c120
1 files changed, 115 insertions, 5 deletions
diff --git a/util/comm-lpc.c b/util/comm-lpc.c
index 54686dbcbb..2e4fe0f08d 100644
--- a/util/comm-lpc.c
+++ b/util/comm-lpc.c
@@ -14,6 +14,8 @@
#define INITIAL_UDELAY 5 /* 5 us */
#define MAXIMUM_UDELAY 10000 /* 10 ms */
+static int lpc_cmd_args_supported;
+
int comm_init(void)
{
int i;
@@ -48,6 +50,20 @@ int comm_init(void)
return -4;
}
+ /*
+ * Test if LPC command args are supported.
+ *
+ * The cheapest way to do this is by looking for the memory-mapped
+ * flag. This is faster than sending a new-style 'hello' command and
+ * seeing whether the EC sets the EC_HOST_ARGS_FLAG_FROM_HOST flag
+ * in args when it responds.
+ */
+ if (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID) == 'E' &&
+ inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_ID + 1) == 'C' &&
+ (inb(EC_LPC_ADDR_MEMMAP + EC_MEMMAP_HOST_CMD_FLAGS) &
+ EC_HOST_CMD_FLAG_LPC_ARGS_SUPPORTED))
+ lpc_cmd_args_supported = 1;
+
return 0;
}
@@ -81,14 +97,15 @@ static int wait_for_ec(int status_addr, int timeout_usec)
return -1; /* Timeout */
}
-int ec_command(int command, const void *indata, int insize,
- void *outdata, int outsize) {
+/* Old-style command interface, without args */
+static int ec_command_old(int command, const void *indata, int insize,
+ void *outdata, int outsize) {
uint8_t *d;
int i;
- int cmd_addr = EC_LPC_ADDR_HOST_CMD;
- int data_addr = EC_LPC_ADDR_HOST_DATA;
- int param_addr = EC_LPC_ADDR_OLD_PARAM;
+ const int cmd_addr = EC_LPC_ADDR_HOST_CMD;
+ const int data_addr = EC_LPC_ADDR_HOST_DATA;
+ const int param_addr = EC_LPC_ADDR_OLD_PARAM;
if (insize > EC_OLD_PARAM_SIZE) {
fprintf(stderr, "Data size too big\n");
@@ -135,6 +152,99 @@ int ec_command(int command, const void *indata, int insize,
return outsize;
}
+int ec_command(int command, int version, const void *indata, int insize,
+ void *outdata, int outsize) {
+
+ const int cmd_addr = EC_LPC_ADDR_HOST_CMD;
+ const int data_addr = EC_LPC_ADDR_HOST_DATA;
+ const int args_addr = EC_LPC_ADDR_HOST_ARGS;
+ const int param_addr = EC_LPC_ADDR_HOST_PARAM;
+
+ struct ec_lpc_host_args args;
+ const uint8_t *d;
+ uint8_t *dout;
+ int csum;
+ int i;
+
+ /* Fall back to old-style command interface if args aren't supported */
+ if (!lpc_cmd_args_supported)
+ return ec_command_old(command, indata, insize,
+ outdata, outsize);
+
+ /* Fill in args */
+ args.flags = EC_HOST_ARGS_FLAG_FROM_HOST;
+ args.command_version = version;
+ args.data_size = insize;
+
+ /* Calculate checksum */
+ csum = command + args.flags + args.command_version + args.data_size;
+ for (i = 0, d = (const uint8_t *)indata; i < insize; i++, d++)
+ csum += *d;
+
+ args.checksum = (uint8_t)csum;
+
+ /* Write args */
+ for (i = 0, d = (const uint8_t *)&args; i < sizeof(args); i++, d++)
+ outb(*d, args_addr + i);
+
+ /* Write data, if any */
+ /* TODO: optimized copy using outl() */
+ for (i = 0, d = (uint8_t *)indata; i < insize; i++, d++)
+ outb(*d, param_addr + i);
+
+ outb(command, cmd_addr);
+
+ if (wait_for_ec(cmd_addr, 1000000)) {
+ fprintf(stderr, "Timeout waiting for EC response\n");
+ return -EC_RES_ERROR;
+ }
+
+ /* Check result */
+ i = inb(data_addr);
+ if (i) {
+ fprintf(stderr, "EC returned error result code %d\n", i);
+ return -i;
+ }
+
+ /* Read back args */
+ for (i = 0, dout = (uint8_t *)&args; i < sizeof(args); i++, dout++)
+ *dout = inb(args_addr + i);
+
+ /*
+ * If EC didn't modify args flags, then somehow we sent a new-style
+ * command to an old EC, which means it would have read its params
+ * from the wrong place.
+ */
+ if (!(args.flags & EC_HOST_ARGS_FLAG_TO_HOST)) {
+ fprintf(stderr, "EC protocol mismatch\n");
+ return -EC_RES_INVALID_RESPONSE;
+ }
+
+ if (args.data_size > outsize) {
+ fprintf(stderr, "EC returned too much data\n");
+ return -EC_RES_INVALID_RESPONSE;
+ }
+
+ /* Read data, if any */
+ /* TODO: optimized copy using outl() */
+ for (i = 0, dout = (uint8_t *)outdata; i < args.data_size; i++, dout++)
+ *dout = inb(param_addr + i);
+
+ /* Verify checksum */
+ csum = command + args.flags + args.command_version + args.data_size;
+ for (i = 0, d = (const uint8_t *)outdata; i < args.data_size; i++, d++)
+ csum += *d;
+
+ if (args.checksum != (uint8_t)csum) {
+ fprintf(stderr, "EC response has invalid checksum\n");
+ return -EC_RES_INVALID_CHECKSUM;
+ }
+
+ /* Return actual amount of data received */
+ return args.data_size;
+}
+
+
uint8_t read_mapped_mem8(uint8_t offset)
{
return inb(EC_LPC_ADDR_MEMMAP + offset);