From 2835e5594e19d3de99fa4f77131908a13fd8a36d Mon Sep 17 00:00:00 2001 From: Louis Yung-Chieh Lo Date: Tue, 29 May 2012 18:03:42 +0800 Subject: [PATCH] Improve the timeout when running on non-Google EC boards. The udelay() has big overhead so that repeating calling leads big errors (expect 1 sec of timeout, but actually 12 secs of timeout). So, the improvement is to double the udelay count when BUSY bit is set. Even better, if we can check the I/O port content before really running the EC command, it can save more time. BUG=chrome-os-partner:10003 TEST=tested on link, alex, zgb, lumpy, stumpy and mario. Only mario takes 1 second to timeout. Others stop when checking ports (takes around 0.01 second). Change-Id: I96c6d8cbe6226d05428a2ab126815e934942f5a9 Signed-off-by: Louis Yung-Chieh Lo --- util/comm-lpc.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/util/comm-lpc.c b/util/comm-lpc.c index 1eff85c4e1..8b42763753 100644 --- a/util/comm-lpc.c +++ b/util/comm-lpc.c @@ -6,19 +6,42 @@ #include #include #include +#include #include #include "comm-host.h" #include "ec_commands.h" +#define INITIAL_UDELAY 10 /* 10 us */ +#define MAXIMUM_UDELAY 10000 /* 10 ms */ + int comm_init(void) { + int i; + int byte = 0xff; + /* Request I/O privilege */ if (iopl(3) < 0) { perror("Error getting I/O privilege"); return -3; } + + /* Test if the I/O port has been configured for GEC. + * If they all are 0xff, then very possible you cannot access GEC. */ + byte &= inb(EC_LPC_ADDR_USER_CMD); + byte &= inb(EC_LPC_ADDR_USER_DATA); + for (i = 0; i < EC_FLASH_SIZE_MAX /* big enough */; ++i) + byte &= inb(EC_LPC_ADDR_USER_PARAM + i); + if (byte == 0xff) { + fprintf(stderr, "Port 0x%x,0x%x,0x%x-0x%x are all 0xFF.\n", + EC_LPC_ADDR_USER_CMD, EC_LPC_ADDR_USER_DATA, + EC_LPC_ADDR_USER_PARAM, + EC_LPC_ADDR_USER_PARAM + EC_FLASH_SIZE_MAX - 1); + fprintf(stderr, "Very likely this board doesn't have GEC.\n"); + return -4; + } + return 0; } @@ -28,10 +51,21 @@ int comm_init(void) static int wait_for_ec(int status_addr, int timeout_usec) { int i; - for (i = 0; i < timeout_usec; i += 10) { - usleep(10); /* Delay first, in case we just sent a command */ + int delay = INITIAL_UDELAY; + + for (i = 0; i < timeout_usec; i += delay) { + /* Delay first, in case we just sent out a command but + * the EC hasn't raise the busy flag. However, I think + * this doesn't happen since the LPC commands are executed + * in order and the busy flag is set by hardware. + * TODO: move this delay after inb(status). */ + usleep(MIN(delay, timeout_usec - i)); + if (!(inb(status_addr) & EC_LPC_STATUS_BUSY_MASK)) return 0; + + /* Increase the delay interval */ + delay = MIN(delay * 2, MAXIMUM_UDELAY); } return -1; /* Timeout */ }