summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--chip/g/spi_controller.c42
-rw-r--r--include/spi.h23
2 files changed, 52 insertions, 13 deletions
diff --git a/chip/g/spi_controller.c b/chip/g/spi_controller.c
index 912520e52f..4f7a5ee512 100644
--- a/chip/g/spi_controller.c
+++ b/chip/g/spi_controller.c
@@ -33,10 +33,12 @@ static enum spi_clock_mode clock_mode[SPI_NUM_PORTS];
* The Cr50 SPI controller is not DMA auto-fill/drain capable, so async and
* flush are not defined on purpose.
*/
-int spi_transaction(const struct spi_device_t *spi_device,
- const uint8_t *txdata, int txlen,
- uint8_t *rxdata, int rxlen)
+int spi_sub_transaction(const struct spi_device_t *spi_device,
+ const uint8_t *txdata, int txlen, uint8_t *rxdata,
+ int rxlen, bool deassert_cs)
{
+ static bool cs_asserted;
+
int port = spi_device->port;
int rv = EC_SUCCESS;
timestamp_t timeout;
@@ -74,8 +76,9 @@ int spi_transaction(const struct spi_device_t *spi_device,
rxoffset = txlen;
}
- /* Grab the port's mutex. */
- mutex_lock(&spi_mutex[port]);
+ if (!cs_asserted)
+ /* Grab the port's mutex. */
+ mutex_lock(&spi_mutex[port]);
#ifdef CONFIG_STREAM_SIGNATURE
/*
@@ -88,10 +91,13 @@ int spi_transaction(const struct spi_device_t *spi_device,
/* Copy the txdata into the 128B Transmit Buffer. */
memmove((uint8_t *)GREG32_ADDR_I(SPI, port, TX_DATA), txdata, txlen);
+ if (!cs_asserted) {
#ifndef CONFIG_SPI_CONTROLLER_NO_CS_GPIOS
- /* Drive chip select low. */
- gpio_set_level(spi_device->gpio_cs, 0);
-#endif /* CONFIG_SPI_CONTROLLER_NO_CS_GPIOS */
+ /* Drive chip select low. */
+ gpio_set_level(spi_device->gpio_cs, 0);
+#endif
+ cs_asserted = true;
+ }
/* Initiate the transaction. */
GWRITE_FIELD_I(SPI, port, ISTATE_CLR, TXDONE, 1);
@@ -120,16 +126,26 @@ int spi_transaction(const struct spi_device_t *spi_device,
rxlen);
err_cs_high:
+ if ((rv != EC_SUCCESS) || deassert_cs) {
#ifndef CONFIG_SPI_CONTROLLER_NO_CS_GPIOS
- /* Drive chip select high. */
- gpio_set_level(spi_device->gpio_cs, 1);
+ /* Drive chip select high. */
+ gpio_set_level(spi_device->gpio_cs, 1);
#endif /* CONFIG_SPI_CONTROLLER_NO_CS_GPIOS */
-
- /* Release the port's mutex. */
- mutex_unlock(&spi_mutex[port]);
+ cs_asserted = false;
+ /* Release the port's mutex. */
+ mutex_unlock(&spi_mutex[port]);
+ }
return rv;
}
+int spi_transaction(const struct spi_device_t *spi_device,
+ const uint8_t *txdata, int txlen, uint8_t *rxdata,
+ int rxlen)
+{
+ return spi_sub_transaction(spi_device, txdata, txlen, rxdata, rxlen,
+ true);
+}
+
/*
* Configure the SPI port's clock mode. The SPI port must be re-enabled after
* changing the clocking mode.
diff --git a/include/spi.h b/include/spi.h
index 10286accab..b06f266d0c 100644
--- a/include/spi.h
+++ b/include/spi.h
@@ -85,6 +85,29 @@ int spi_transaction(const struct spi_device_t *spi_device,
const uint8_t *txdata, int txlen,
uint8_t *rxdata, int rxlen);
+/* Issue a part of SPI transaction. Assumes SPI port has already been enabled.
+ *
+ * If CS is not asserted, asserts it first.
+ * Transmits <txlen> bytes from <txdata>, throwing away the corresponding
+ * received data, then transmits <rxlen> unused bytes, saving the received data
+ * in <rxdata>.
+ * If SPI_READBACK_ALL is set in <rxlen>, the received data during transmission
+ * is recorded in rxdata buffer and it assumes that the real <rxlen> is equal
+ * to <txlen>.
+ * If 'final' is true, deasserts CS once transfer is completed.
+ *
+ * @param spi_device the SPI device to use
+ * @param txdata buffer to transmit
+ * @param txlen number of bytes in txdata.
+ * @param rxdata receive buffer.
+ * @param rxlen number of bytes in rxdata or SPI_READBACK_ALL.
+ * @param deassert_cs flag indicating that CS needs to be deasserted in the
+ * end of this sub transaction.
+ */
+int spi_sub_transaction(const struct spi_device_t *spi_device,
+ const uint8_t *txdata, int txlen, uint8_t *rxdata,
+ int rxlen, bool deassert_cs);
+
/* Similar to spi_transaction(), but hands over to DMA for reading response.
* Must call spi_transaction_flush() after this to make sure the response is
* received.