diff options
Diffstat (limited to 'util/comm-lpc.c')
-rw-r--r-- | util/comm-lpc.c | 120 |
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); |