diff options
author | Simon Glass <sjg@chromium.org> | 2012-08-15 14:33:13 -0700 |
---|---|---|
committer | Gerrit <chrome-bot@google.com> | 2012-08-16 18:45:20 -0700 |
commit | 76619f904df8ed519fac0d46d9c6c3a1956a88c2 (patch) | |
tree | 008138b09eb57cd3f4d8ec76914c58e69952b0f6 | |
parent | ea56741fb3391989525aae2cc6dec112bb093f6d (diff) | |
download | chrome-ec-76619f904df8ed519fac0d46d9c6c3a1956a88c2.tar.gz |
stm32: i2c: Implement in-progress commands
When a command is marked as in-progress, provide an interim EC_RES_IN_PROGRESS
response and then stash the real response (when available) ready for a
EC_CMD_RESEND_RESPONSE message.
Track whether the host_command processor is busy internally within this
driver. Provide this information through an EC_CMD_GET_STATUS message.
BUG=chrome-os-partner:12685
BRANCH=snow,link
TEST=manual
build and boot to kernel on snow
Change-Id: I5acece074ad8408c978ca36b73d1330fa51575ae
Signed-off-by: Simon Glass <sjg@chromium.org>
Reviewed-on: https://gerrit.chromium.org/gerrit/30470
Reviewed-by: David Hendricks <dhendrix@chromium.org>
-rw-r--r-- | chip/stm32/i2c.c | 103 | ||||
-rw-r--r-- | include/ec_commands.h | 30 |
2 files changed, 132 insertions, 1 deletions
diff --git a/chip/stm32/i2c.c b/chip/stm32/i2c.c index bc3aac9eac..8ac6823cea 100644 --- a/chip/stm32/i2c.c +++ b/chip/stm32/i2c.c @@ -23,6 +23,12 @@ #define CPUTS(outstr) cputs(CC_I2C, outstr) #define CPRINTF(format, args...) cprintf(CC_I2C, format, ## args) +#ifdef DEBUG +#define debug(f, a...) CPRINTF(f, ##a) +#else +#define debug(f, a...) +#endif + /* 8-bit I2C slave address */ #define I2C_ADDRESS 0x3c @@ -65,6 +71,12 @@ static struct host_cmd_handler_args host_cmd_args; /* Flag indicating if a command is currently in the buffer */ static uint8_t rx_pending; +/* Indicates that a command is in progress */ +static uint8_t command_pending; + +/* The result of the last 'slow' operation */ +static uint8_t saved_result = EC_RES_UNAVAILABLE; + static inline void disable_i2c_interrupt(int port) { STM32_I2C_CR2(port) &= ~(3 << 8); @@ -143,8 +155,45 @@ static void i2c_send_response(struct host_cmd_handler_args *args) const uint8_t *data = args->response; int size = args->response_size; uint8_t *out = host_buffer; + int watch_command_pending = !in_interrupt_context(); int sum = 0, i; + /* + * TODO(sjg@chromium.org): + * The logic here is a little painful since we are avoiding changing + * host_command. If we got an 'in progress' previously, then this + * must be the completion of that command, so stash the result + * code. We can't send it back to the host now since we already sent + * the in-progress response and the host is on to other things now. + * + * Of course, if we are in interrupt context, then we are just + * handling a get_status response. We can't check that in + * args->command of course because the original command value has + * now been overwritten. This would be much easier to do in + * host_command since it actually knows what is going on. + * + * When a EC_CMD_RESEND_RESPONSE arrives we will supply this response + * to that command. + * + * We don't support stashing response data, so mark the response as + * unavailable in that case. + * + * TODO(sjg@chromium): It would all be easier if drivers used a + * stack variable for args and host_command was responsible for + * saving the command before execution. Perhaps fast commands could + * be executed in interrupt context anyway? + */ + if (command_pending && watch_command_pending) { + debug("pending complete, size=%d, result=%d\n", + args->response_size, args->result); + if (args->response_size != 0) + saved_result = EC_RES_UNAVAILABLE; + else + saved_result = args->result; + command_pending = 0; + return; + } + *out++ = args->result; if (!args->i2c_old_response) { *out++ = size; @@ -159,6 +208,12 @@ static void i2c_send_response(struct host_cmd_handler_args *args) /* send the answer to the AP */ i2c_write_raw_slave(I2C2, host_buffer, out - host_buffer); + + if (watch_command_pending) { + command_pending = (args->result == EC_RES_IN_PROGRESS); + if (command_pending) + debug("Command pending\n"); + } } /* Process the command in the i2c host buffer */ @@ -200,7 +255,21 @@ static void i2c_process_command(void) args->response = host_buffer + 2; args->response_max = EC_HOST_PARAM_SIZE; args->response_size = 0; - host_command_received(args); + + /* + * Special handling for GET_STATUS, which happens entirely outside + * host_command. + */ + if (args->command == EC_CMD_GET_COMMS_STATUS) { + /* + * Could do this directly, but then we get no logging + * args->result = host_command_get_comms_status(args); + */ + args->result = host_command_process(args); + args->send_response(args); + } else { + host_command_received(args); + } } static void i2c_event_handler(int port) @@ -351,6 +420,38 @@ static int i2c_init(void) DECLARE_HOOK(HOOK_INIT, i2c_init, HOOK_PRIO_DEFAULT); +/* Returns current command status (busy or not) */ +static int host_command_get_comms_status(struct host_cmd_handler_args *args) +{ + struct ec_response_get_comms_status *r = args->response; + + r->flags = command_pending ? EC_COMMS_STATUS_PROCESSING : 0; + args->response_size = sizeof(*r); + + return EC_SUCCESS; +} + +DECLARE_HOST_COMMAND(EC_CMD_GET_COMMS_STATUS, + host_command_get_comms_status, + EC_VER_MASK(0)); + +/* Resend the last saved response */ +static int host_command_resend_response(struct host_cmd_handler_args *args) +{ + /* Handle resending response */ + args->result = saved_result; + args->response_size = 0; + + saved_result = EC_RES_UNAVAILABLE; + + return EC_SUCCESS; +} + +DECLARE_HOST_COMMAND(EC_CMD_RESEND_RESPONSE, + host_command_resend_response, + EC_VER_MASK(0)); + + /*****************************************************************************/ /* STM32 Host I2C */ diff --git a/include/ec_commands.h b/include/ec_commands.h index 66c5a1d04f..cecf44e000 100644 --- a/include/ec_commands.h +++ b/include/ec_commands.h @@ -187,6 +187,8 @@ enum ec_status { EC_RES_INVALID_VERSION = 6, EC_RES_INVALID_CHECKSUM = 7, EC_RES_IN_PROGRESS = 8, /* Accepted, command in progress */ + EC_RES_UNAVAILABLE = 9, /* No response available */ + EC_RES_TIMEOUT = 10, /* We got a timeout */ }; /* @@ -389,6 +391,25 @@ struct ec_response_get_cmd_versions { uint32_t version_mask; } __packed; +/* + * Check EC communcations status (busy). This is needed on i2c/spi but not + * on lpc since it has its own out-of-band busy indicator. + * + * lpc must read the status from the command register. Attempting this on + * lpc will overwrite the args/parameter space and corrupt its data. + */ +#define EC_CMD_GET_COMMS_STATUS 0x09 + +/* Avoid using ec_status which is for return values */ +enum ec_comms_status { + EC_COMMS_STATUS_PROCESSING = 1 << 0, /* Processing cmd */ +}; + +struct ec_response_get_comms_status { + uint32_t flags; /* Mask of enum ec_comms_status */ +} __packed; + + /*****************************************************************************/ /* Flash commands */ @@ -1054,6 +1075,15 @@ struct ec_params_reboot_ec { #define EC_CMD_REBOOT 0xd1 /* Think "die" */ /* + * Resend last response (not supported on LPC). + * + * Returns EC_RES_UNAVAILABLE if there is no response available - for example, + * there was no previous command, or the previous command's response was too + * big to save. + */ +#define EC_CMD_RESEND_RESPONSE 0xdb + +/* * This header byte on a command indicate version 0. Any header byte less * than this means that we are talking to an old EC which doesn't support * versioning. In that case, we assume version 0. |