diff options
-rw-r--r-- | chip/g/spi_controller.c | 42 | ||||
-rw-r--r-- | include/spi.h | 23 |
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. |