summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRong Chang <rongchang@google.com>2017-02-20 16:15:09 +0800
committerchrome-bot <chrome-bot@chromium.org>2017-05-22 05:02:46 -0700
commitb3d0d05aa954bc633deb00f82e40f79f889ca661 (patch)
tree2aa0fbaf8d8050512fd23ca365834a9be65f9e07
parent1d10236f07a453a438745e9ae541307f69df61ce (diff)
downloadchrome-ec-b3d0d05aa954bc633deb00f82e40f79f889ca661.tar.gz
rose: spi: add SPI master halfduplex mode
This change adds 3-wire mode support in STM32 SPI master driver. BUG=chromium:688979 TEST=manual enable CONFIG_SPI_HALFDUPLEX read id from SPI heatmap sensor BRANCH=none Change-Id: I09139dcbfe39a427721451db6842ea712abf2e33 Signed-off-by: Rong Chang <rongchang@chromium.org> Reviewed-on: https://chromium-review.googlesource.com/444630 Reviewed-by: Wei-Ning Huang <wnhuang@chromium.org> Reviewed-by: Vincent Palatin <vpalatin@chromium.org>
-rw-r--r--chip/stm32/spi_master.c87
-rw-r--r--include/config.h3
2 files changed, 63 insertions, 27 deletions
diff --git a/chip/stm32/spi_master.c b/chip/stm32/spi_master.c
index cc064b9d23..353dac3085 100644
--- a/chip/stm32/spi_master.c
+++ b/chip/stm32/spi_master.c
@@ -162,7 +162,7 @@ static int spi_master_shutdown(int port)
spi->cr1 &= ~STM32_SPI_CR1_SPE;
/* Read until FRLVL[1:0] is empty */
- while (spi->sr & STM32_SPI_SR_FTLVL)
+ while (spi->sr & (STM32_SPI_SR_FTLVL | STM32_SPI_SR_RXNE))
dummy = spi->dr;
/* Disable DMA buffers */
@@ -187,16 +187,25 @@ static int spi_dma_start(int port, const uint8_t *txdata,
dma_chan_t *txdma;
/* Set up RX DMA */
- dma_start_rx(&dma_rx_option[port], len, rxdata);
+ if (rxdata)
+ dma_start_rx(&dma_rx_option[port], len, rxdata);
/* Set up TX DMA */
- txdma = dma_get_channel(dma_tx_option[port].channel);
- dma_prepare_tx(&dma_tx_option[port], len, txdata);
- dma_go(txdma);
+ if (txdata) {
+ txdma = dma_get_channel(dma_tx_option[port].channel);
+ dma_prepare_tx(&dma_tx_option[port], len, txdata);
+ dma_go(txdma);
+ }
return EC_SUCCESS;
}
+static inline int dma_is_enabled(const struct dma_option *option)
+{
+ /* dma_bytes_done() returns 0 if channel is not enabled */
+ return dma_bytes_done(dma_get_channel(option->channel), -1);
+}
+
static int spi_dma_wait(int port)
{
timestamp_t timeout;
@@ -204,32 +213,36 @@ static int spi_dma_wait(int port)
int rv = EC_SUCCESS;
/* Wait for DMA transmission to complete */
- rv = dma_wait(dma_tx_option[port].channel);
- if (rv)
- return rv;
+ if (dma_is_enabled(&dma_tx_option[port])) {
+ rv = dma_wait(dma_tx_option[port].channel);
+ if (rv)
+ return rv;
- timeout.val = get_time().val + SPI_TRANSACTION_TIMEOUT_USEC;
- /* Wait for FIFO empty and BSY bit clear to indicate completion */
- while ((spi->sr & STM32_SPI_SR_FTLVL) || (spi->sr & STM32_SPI_SR_BSY))
- if (get_time().val > timeout.val)
- return EC_ERROR_TIMEOUT;
+ timeout.val = get_time().val + SPI_TRANSACTION_TIMEOUT_USEC;
+ /* Wait for FIFO empty and BSY bit clear */
+ while (spi->sr & (STM32_SPI_SR_FTLVL | STM32_SPI_SR_BSY))
+ if (get_time().val > timeout.val)
+ return EC_ERROR_TIMEOUT;
- /* Disable TX DMA */
- dma_disable(dma_tx_option[port].channel);
+ /* Disable TX DMA */
+ dma_disable(dma_tx_option[port].channel);
+ }
/* Wait for DMA reception to complete */
- rv = dma_wait(dma_rx_option[port].channel);
- if (rv)
- return rv;
+ if (dma_is_enabled(&dma_rx_option[port])) {
+ rv = dma_wait(dma_rx_option[port].channel);
+ if (rv)
+ return rv;
- timeout.val = get_time().val + SPI_TRANSACTION_TIMEOUT_USEC;
- /* Wait for FRLVL[1:0] to indicate FIFO empty */
- while (spi->sr & STM32_SPI_SR_FRLVL)
- if (get_time().val > timeout.val)
- return EC_ERROR_TIMEOUT;
+ timeout.val = get_time().val + SPI_TRANSACTION_TIMEOUT_USEC;
+ /* Wait for FRLVL[1:0] to indicate FIFO empty */
+ while (spi->sr & STM32_SPI_SR_FRLVL)
+ if (get_time().val > timeout.val)
+ return EC_ERROR_TIMEOUT;
- /* Disable RX DMA */
- dma_disable(dma_rx_option[port].channel);
+ /* Disable RX DMA */
+ dma_disable(dma_rx_option[port].channel);
+ }
return rv;
}
@@ -243,8 +256,9 @@ int spi_transaction_async(const struct spi_device_t *spi_device,
int full_readback = 0;
stm32_spi_regs_t *spi = SPI_REGS[port];
- char *buf;
+ char *buf = NULL;
+#ifndef CONFIG_SPI_HALFDUPLEX
if (rxlen == SPI_READBACK_ALL) {
buf = rxdata;
full_readback = 1;
@@ -253,14 +267,19 @@ int spi_transaction_async(const struct spi_device_t *spi_device,
if (rv != EC_SUCCESS)
return rv;
}
+#endif
/* Drive SS low */
gpio_set_level(spi_device->gpio_cs, 0);
/* Clear out the FIFO. */
- while (spi->sr & STM32_SPI_SR_FRLVL)
+ while (spi->sr & (STM32_SPI_SR_FRLVL | STM32_SPI_SR_RXNE))
(void) (uint8_t) spi->dr;
+#ifdef CONFIG_SPI_HALFDUPLEX
+ /* Enable bidirection mode and select output direction */
+ spi->cr1 |= STM32_SPI_CR1_BIDIMODE | STM32_SPI_CR1_BIDIOE;
+#endif
rv = spi_dma_start(port, txdata, buf, txlen);
if (rv != EC_SUCCESS)
goto err_free;
@@ -273,21 +292,35 @@ int spi_transaction_async(const struct spi_device_t *spi_device,
goto err_free;
if (rxlen) {
+#ifdef CONFIG_SPI_HALFDUPLEX
+ /* Select input direction */
+ spi->cr1 &= ~STM32_SPI_CR1_BIDIOE;
+#endif
rv = spi_dma_start(port, buf, rxdata, rxlen);
if (rv != EC_SUCCESS)
goto err_free;
}
err_free:
+#ifndef CONFIG_SPI_HALFDUPLEX
if (!full_readback)
shared_mem_release(buf);
+#endif
return rv;
}
+#define SPI_BUSY (STM32_SPI_SR_FRLVL | STM32_SPI_SR_FTLVL | STM32_SPI_SR_BSY | \
+ STM32_SPI_SR_RXNE)
+
int spi_transaction_flush(const struct spi_device_t *spi_device)
{
int rv = spi_dma_wait(spi_device->port);
+#ifdef CONFIG_SPI_HALFDUPLEX
+ /* Disable receive-only mode by turning off CR1 BIDIMODE */
+ SPI_REGS[spi_device->port]->cr1 &= ~STM32_SPI_CR1_BIDIMODE;
+#endif
+
/* Drive SS high */
gpio_set_level(spi_device->gpio_cs, 1);
diff --git a/include/config.h b/include/config.h
index ddaac00513..aec133e624 100644
--- a/include/config.h
+++ b/include/config.h
@@ -2018,6 +2018,9 @@
/* SPI master feature */
#undef CONFIG_SPI_MASTER
+/* SPI master halfduplex/3-wire mode */
+#undef CONFIG_SPI_HALFDUPLEX
+
/* Support STM32 SPI1 as master. */
#undef CONFIG_STM32_SPI1_MASTER