soc/amd/common/block/espi_util: Work around in-band reset race condition

When performing an in-band reset the host controller and the
peripheral can have mismatched IO configs.

i.e., The eSPI peripheral can be in IO-4 mode while, the
eSPI host will be in IO-1. This results in the peripheral
getting invalid packets and thus not responding. This causes the
NO_RESPONSE status bit to be set and cause eSPI init to fail.

If the peripheral is alerting when we perform an in-band
reset, there is a race condition in espi_send_command.
1) espi_send_command clears the interrupt status.
2) eSPI host controller hardware notices the alert and sends
   a GET_STATUS.
3) espi_send_command writes the in-band reset command.
4) eSPI hardware enqueues the in-band reset until GET_STATUS
   is complete.
5) GET_STATUS fails with NO_RESPONSE and sets the interrupt
   status.
6) eSPI hardware performs in-band reset.
7) espi_send_command checks the status and sees a
   NO_RESPONSE bit.

As a workaround we allow the NO_RESPONSE status code when
we perform an in-band reset.

BUG=b:186135022
TEST=suspend_stress_test and S5->S0 tests on guybrush and zork.

Signed-off-by: Raul E Rangel <rrangel@chromium.org>
Change-Id: I71271377f20eaf29032214be98794e1645d9b70a
Reviewed-on: https://review.coreboot.org/c/coreboot/+/54070
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Reviewed-by: Felix Held <felix-coreboot@felixheld.de>
Reviewed-by: Rob Barnes <robbarnes@google.com>
This commit is contained in:
Raul E Rangel 2021-05-11 11:13:38 -06:00 committed by Raul Rangel
parent 224b578420
commit 12c0542e6f
1 changed files with 30 additions and 2 deletions

View File

@ -419,6 +419,7 @@ struct espi_cmd {
union espi_txhdr1 hdr1;
union espi_txhdr2 hdr2;
union espi_txdata data;
uint32_t expected_status_codes;
} __packed;
/* Wait up to ESPI_CMD_TIMEOUT_US for hardware to clear DNCMD_STATUS bit. */
@ -512,13 +513,13 @@ static int espi_send_command(const struct espi_cmd *cmd)
return -1;
}
if (status & ~ESPI_STATUS_DNCMD_COMPLETE) {
if (status & ~(ESPI_STATUS_DNCMD_COMPLETE | cmd->expected_status_codes)) {
espi_show_failure(cmd, "Error: unexpected eSPI status register bits set",
status);
return -1;
}
espi_write32(ESPI_SLAVE0_INT_STS, ESPI_STATUS_DNCMD_COMPLETE);
espi_write32(ESPI_SLAVE0_INT_STS, status);
return 0;
}
@ -530,6 +531,33 @@ static int espi_send_reset(void)
.cmd_type = CMD_TYPE_IN_BAND_RESET,
.cmd_sts = 1,
},
/*
* When performing an in-band reset the host controller and the
* peripheral can have mismatched IO configs.
*
* i.e., The eSPI peripheral can be in IO-4 mode while, the
* eSPI host will be in IO-1. This results in the peripheral
* getting invalid packets and thus not responding.
*
* If the peripheral is alerting when we perform an in-band
* reset, there is a race condition in espi_send_command.
* 1) espi_send_command clears the interrupt status.
* 2) eSPI host controller hardware notices the alert and sends
* a GET_STATUS.
* 3) espi_send_command writes the in-band reset command.
* 4) eSPI hardware enqueues the in-band reset until GET_STATUS
* is complete.
* 5) GET_STATUS fails with NO_RESPONSE and sets the interrupt
* status.
* 6) eSPI hardware performs in-band reset.
* 7) espi_send_command checks the status and sees a
* NO_RESPONSE bit.
*
* As a workaround we allow the NO_RESPONSE status code when
* we perform an in-band reset.
*/
.expected_status_codes = ESPI_STATUS_NO_RESPONSE,
};
return espi_send_command(&cmd);