mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-02 21:25:03 +00:00
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>
This commit is contained in:
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user