summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chip/stm32/spi.c56
1 files changed, 55 insertions, 1 deletions
diff --git a/chip/stm32/spi.c b/chip/stm32/spi.c
index 960c6934b6..c6fd24bc36 100644
--- a/chip/stm32/spi.c
+++ b/chip/stm32/spi.c
@@ -89,6 +89,12 @@ static uint8_t enabled;
static struct host_cmd_handler_args args;
static struct host_packet spi_packet;
+/*
+ * This is set if SPI NSS raises to high while EC is still processing a
+ * command.
+ */
+static int setup_transaction_later;
+
enum spi_state {
/* SPI not enabled (initial state, and when chipset is off) */
SPI_STATE_DISABLED = 0,
@@ -236,6 +242,9 @@ static void setup_for_transaction(void)
stm32_spi_regs_t *spi = STM32_SPI1_REGS;
volatile uint8_t dummy __attribute__((unused));
+ /* clear this as soon as possible */
+ setup_transaction_later = 0;
+
/* Not ready to receive yet */
spi->dr = EC_SPI_NOT_READY;
@@ -259,6 +268,24 @@ static void setup_for_transaction(void)
spi->dr = EC_SPI_OLD_READY;
}
+
+/*
+ * If a setup_for_transaction() was postponed, call it now.
+ * Note that setup_for_transaction() cancels Tx DMA.
+ */
+static void check_setup_transaction_later(void)
+{
+ if (setup_transaction_later) {
+ setup_for_transaction();
+ /*
+ * 'state' is set to SPI_STATE_READY_TO_RX. Somehow AP
+ * de-asserted the SPI NSS during the handler was running.
+ * Thus, the pending result will be dropped anyway.
+ */
+ }
+}
+
+
/**
* Called for V2 protocol to indicate that a command has completed
*
@@ -278,12 +305,21 @@ static void spi_send_response(struct host_cmd_handler_args *args)
if (state != SPI_STATE_PROCESSING)
return;
+ /* state == SPI_STATE_PROCESSING */
+
if (args->response_size > args->response_max)
result = EC_RES_INVALID_RESPONSE;
/* Transmit the reply */
txdma = dma_get_channel(STM32_DMAC_SPI1_TX);
reply(txdma, result, args->response, args->response_size);
+
+ /*
+ * Before the state is set to SENDING, any CS de-assertion would
+ * set setup_transaction_later to 1.
+ */
+ state = SPI_STATE_SENDING;
+ check_setup_transaction_later();
}
/**
@@ -304,6 +340,8 @@ static void spi_send_response_packet(struct host_packet *pkt)
if (state != SPI_STATE_PROCESSING)
return;
+ /* state == SPI_STATE_PROCESSING */
+
/* Append our past-end byte, which we reserved space for. */
((uint8_t *)pkt->response)[pkt->response_size] = EC_SPI_PAST_END;
@@ -312,6 +350,13 @@ static void spi_send_response_packet(struct host_packet *pkt)
dma_prepare_tx(&dma_tx_option,
sizeof(out_preamble) + pkt->response_size + 1, out_msg);
dma_go(txdma);
+
+ /*
+ * Before the state is set to SENDING, any CS de-assertion would
+ * set setup_transaction_later to 1.
+ */
+ state = SPI_STATE_SENDING;
+ check_setup_transaction_later();
}
/**
@@ -333,9 +378,18 @@ void spi_event(enum gpio_signal signal)
if (!enabled)
return;
- /* Check chip select. If it's high, the AP ended a tranaction. */
+ /* Check chip select. If it's high, the AP ended a transaction. */
nss_reg = gpio_get_level_reg(GPIO_SPI1_NSS, &nss_mask);
if (REG16(nss_reg) & nss_mask) {
+ /*
+ * If the buffer is still used by the host command, postpone
+ * the DMA rx setup.
+ */
+ if (state == SPI_STATE_PROCESSING) {
+ setup_transaction_later = 1;
+ return;
+ }
+
/* Set up for the next transaction */
setup_for_transaction();
return;