aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorBrian Norris <briannorris@chromium.org>2018-05-22 20:23:10 -0400
committerLee Jones <lee.jones@linaro.org>2018-05-23 01:59:00 -0400
commit11799564fc7eedff50801950090773928f867996 (patch)
tree900623616bd4d55b38798be4325376e916649b57 /drivers
parent771c577c23bac90597c685971d7297ea00f99d11 (diff)
mfd: cros_ec: Retry commands when EC is known to be busy
Commit 001dde9400d5 ("mfd: cros ec: spi: Fix "in progress" error signaling") pointed out some bad code, but its analysis and conclusion was not 100% correct. It *is* correct that we should not propagate result==EC_RES_IN_PROGRESS for transport errors, because this has a special meaning -- that we should follow up with EC_CMD_GET_COMMS_STATUS until the EC is no longer busy. This is definitely the wrong thing for many commands, because among other problems, EC_CMD_GET_COMMS_STATUS doesn't actually retrieve any RX data from the EC, so commands that expected some data back will instead start processing junk. For such commands, the right answer is to either propagate the error (and return that error to the caller) or resend the original command (*not* EC_CMD_GET_COMMS_STATUS). Unfortunately, commit 001dde9400d5 forgets a crucial point: that for some long-running operations, the EC physically cannot respond to commands any more. For example, with EC_CMD_FLASH_ERASE, the EC may be re-flashing its own code regions, so it can't respond to SPI interrupts. Instead, the EC prepares us ahead of time for being busy for a "long" time, and fills its hardware buffer with EC_SPI_PAST_END. Thus, we expect to see several "transport" errors (or, messages filled with EC_SPI_PAST_END). So we should really translate that to a retryable error (-EAGAIN) and continue sending EC_CMD_GET_COMMS_STATUS until we get a ready status. IOW, it is actually important to treat some of these "junk" values as retryable errors. Together with commit 001dde9400d5, this resolves bugs like the following: 1. EC_CMD_FLASH_ERASE now works again (with commit 001dde9400d5, we would abort the first time we saw EC_SPI_PAST_END) 2. Before commit 001dde9400d5, transport errors (e.g., EC_SPI_RX_BAD_DATA) seen in other commands (e.g., EC_CMD_RTC_GET_VALUE) used to yield junk data in the RX buffer; they will now yield -EAGAIN return values, and tools like 'hwclock' will simply fail instead of retrieving and re-programming undefined time values Fixes: 001dde9400d5 ("mfd: cros ec: spi: Fix "in progress" error signaling") Signed-off-by: Brian Norris <briannorris@chromium.org> Signed-off-by: Lee Jones <lee.jones@linaro.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/mfd/cros_ec_spi.c24
-rw-r--r--drivers/platform/chrome/cros_ec_proto.c2
2 files changed, 22 insertions, 4 deletions
diff --git a/drivers/mfd/cros_ec_spi.c b/drivers/mfd/cros_ec_spi.c
index 1b52b8557034..2060d1483043 100644
--- a/drivers/mfd/cros_ec_spi.c
+++ b/drivers/mfd/cros_ec_spi.c
@@ -419,10 +419,25 @@ static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
419 /* Verify that EC can process command */ 419 /* Verify that EC can process command */
420 for (i = 0; i < len; i++) { 420 for (i = 0; i < len; i++) {
421 rx_byte = rx_buf[i]; 421 rx_byte = rx_buf[i];
422 /*
423 * Seeing the PAST_END, RX_BAD_DATA, or NOT_READY
424 * markers are all signs that the EC didn't fully
425 * receive our command. e.g., if the EC is flashing
426 * itself, it can't respond to any commands and instead
427 * clocks out EC_SPI_PAST_END from its SPI hardware
428 * buffer. Similar occurrences can happen if the AP is
429 * too slow to clock out data after asserting CS -- the
430 * EC will abort and fill its buffer with
431 * EC_SPI_RX_BAD_DATA.
432 *
433 * In all cases, these errors should be safe to retry.
434 * Report -EAGAIN and let the caller decide what to do
435 * about that.
436 */
422 if (rx_byte == EC_SPI_PAST_END || 437 if (rx_byte == EC_SPI_PAST_END ||
423 rx_byte == EC_SPI_RX_BAD_DATA || 438 rx_byte == EC_SPI_RX_BAD_DATA ||
424 rx_byte == EC_SPI_NOT_READY) { 439 rx_byte == EC_SPI_NOT_READY) {
425 ret = -EREMOTEIO; 440 ret = -EAGAIN;
426 break; 441 break;
427 } 442 }
428 } 443 }
@@ -431,7 +446,7 @@ static int cros_ec_pkt_xfer_spi(struct cros_ec_device *ec_dev,
431 if (!ret) 446 if (!ret)
432 ret = cros_ec_spi_receive_packet(ec_dev, 447 ret = cros_ec_spi_receive_packet(ec_dev,
433 ec_msg->insize + sizeof(*response)); 448 ec_msg->insize + sizeof(*response));
434 else 449 else if (ret != -EAGAIN)
435 dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); 450 dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
436 451
437 final_ret = terminate_request(ec_dev); 452 final_ret = terminate_request(ec_dev);
@@ -537,10 +552,11 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
537 /* Verify that EC can process command */ 552 /* Verify that EC can process command */
538 for (i = 0; i < len; i++) { 553 for (i = 0; i < len; i++) {
539 rx_byte = rx_buf[i]; 554 rx_byte = rx_buf[i];
555 /* See comments in cros_ec_pkt_xfer_spi() */
540 if (rx_byte == EC_SPI_PAST_END || 556 if (rx_byte == EC_SPI_PAST_END ||
541 rx_byte == EC_SPI_RX_BAD_DATA || 557 rx_byte == EC_SPI_RX_BAD_DATA ||
542 rx_byte == EC_SPI_NOT_READY) { 558 rx_byte == EC_SPI_NOT_READY) {
543 ret = -EREMOTEIO; 559 ret = -EAGAIN;
544 break; 560 break;
545 } 561 }
546 } 562 }
@@ -549,7 +565,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
549 if (!ret) 565 if (!ret)
550 ret = cros_ec_spi_receive_response(ec_dev, 566 ret = cros_ec_spi_receive_response(ec_dev,
551 ec_msg->insize + EC_MSG_TX_PROTO_BYTES); 567 ec_msg->insize + EC_MSG_TX_PROTO_BYTES);
552 else 568 else if (ret != -EAGAIN)
553 dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret); 569 dev_err(ec_dev->dev, "spi transfer failed: %d\n", ret);
554 570
555 final_ret = terminate_request(ec_dev); 571 final_ret = terminate_request(ec_dev);
diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c
index e7bbdf947bbc..8350ca2311c7 100644
--- a/drivers/platform/chrome/cros_ec_proto.c
+++ b/drivers/platform/chrome/cros_ec_proto.c
@@ -91,6 +91,8 @@ static int send_command(struct cros_ec_device *ec_dev,
91 usleep_range(10000, 11000); 91 usleep_range(10000, 11000);
92 92
93 ret = (*xfer_fxn)(ec_dev, status_msg); 93 ret = (*xfer_fxn)(ec_dev, status_msg);
94 if (ret == -EAGAIN)
95 continue;
94 if (ret < 0) 96 if (ret < 0)
95 break; 97 break;
96 98