mirror of
https://github.com/hardkernel/linux.git
synced 2026-06-04 02:02:28 +09:00
Merge series "spi: stm32: various fixes & cleanup" from Alain Volmat <alain.volmat@foss.st.com>:
This series contains fixes & cleanup mainly regarding fifo
and the way end of transfer triggered, when used with or
without DMA.
An additional patch cleans up the pm_runtime calls and another
one enables the autosuspend.
v2: - split pm_runtime fix patch into two
- correct revert commit subject line
Alain Volmat (6):
spi: stm32: fixes pm_runtime calls in probe/remove
spi: stm32: enable pm_runtime autosuspend
spi: stm32h7: fix full duplex irq handler handling
spi: stm32: Revert "properly handle 0 byte transfer"
spi: stm32h7: don't wait for EOT and flush fifo on disable
spi: stm32: finalize message either on dma callback or EOT
Amelie Delaunay (1):
spi: stm32h7: rework rx fifo read function
drivers/spi/spi-stm32.c | 146 +++++++++++++++++-----------------------
1 file changed, 61 insertions(+), 85 deletions(-)
--
2.25.1
This commit is contained in:
@@ -162,6 +162,8 @@
|
||||
#define SPI_3WIRE_TX 3
|
||||
#define SPI_3WIRE_RX 4
|
||||
|
||||
#define STM32_SPI_AUTOSUSPEND_DELAY 1 /* 1 ms */
|
||||
|
||||
/*
|
||||
* use PIO for small transfers, avoiding DMA setup/teardown overhead for drivers
|
||||
* without fifo buffers.
|
||||
@@ -568,29 +570,30 @@ static void stm32f4_spi_read_rx(struct stm32_spi *spi)
|
||||
/**
|
||||
* stm32h7_spi_read_rxfifo - Read bytes in Receive Data Register
|
||||
* @spi: pointer to the spi controller data structure
|
||||
* @flush: boolean indicating that FIFO should be flushed
|
||||
*
|
||||
* Write in rx_buf depends on remaining bytes to avoid to write beyond
|
||||
* rx_buf end.
|
||||
*/
|
||||
static void stm32h7_spi_read_rxfifo(struct stm32_spi *spi, bool flush)
|
||||
static void stm32h7_spi_read_rxfifo(struct stm32_spi *spi)
|
||||
{
|
||||
u32 sr = readl_relaxed(spi->base + STM32H7_SPI_SR);
|
||||
u32 rxplvl = FIELD_GET(STM32H7_SPI_SR_RXPLVL, sr);
|
||||
|
||||
while ((spi->rx_len > 0) &&
|
||||
((sr & STM32H7_SPI_SR_RXP) ||
|
||||
(flush && ((sr & STM32H7_SPI_SR_RXWNE) || (rxplvl > 0))))) {
|
||||
((sr & STM32H7_SPI_SR_EOT) &&
|
||||
((sr & STM32H7_SPI_SR_RXWNE) || (rxplvl > 0))))) {
|
||||
u32 offs = spi->cur_xferlen - spi->rx_len;
|
||||
|
||||
if ((spi->rx_len >= sizeof(u32)) ||
|
||||
(flush && (sr & STM32H7_SPI_SR_RXWNE))) {
|
||||
(sr & STM32H7_SPI_SR_RXWNE)) {
|
||||
u32 *rx_buf32 = (u32 *)(spi->rx_buf + offs);
|
||||
|
||||
*rx_buf32 = readl_relaxed(spi->base + STM32H7_SPI_RXDR);
|
||||
spi->rx_len -= sizeof(u32);
|
||||
} else if ((spi->rx_len >= sizeof(u16)) ||
|
||||
(flush && (rxplvl >= 2 || spi->cur_bpw > 8))) {
|
||||
(!(sr & STM32H7_SPI_SR_RXWNE) &&
|
||||
(rxplvl >= 2 || spi->cur_bpw > 8))) {
|
||||
u16 *rx_buf16 = (u16 *)(spi->rx_buf + offs);
|
||||
|
||||
*rx_buf16 = readw_relaxed(spi->base + STM32H7_SPI_RXDR);
|
||||
@@ -606,8 +609,8 @@ static void stm32h7_spi_read_rxfifo(struct stm32_spi *spi, bool flush)
|
||||
rxplvl = FIELD_GET(STM32H7_SPI_SR_RXPLVL, sr);
|
||||
}
|
||||
|
||||
dev_dbg(spi->dev, "%s%s: %d bytes left\n", __func__,
|
||||
flush ? "(flush)" : "", spi->rx_len);
|
||||
dev_dbg(spi->dev, "%s: %d bytes left (sr=%08x)\n",
|
||||
__func__, spi->rx_len, sr);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -674,18 +677,12 @@ static void stm32f4_spi_disable(struct stm32_spi *spi)
|
||||
* stm32h7_spi_disable - Disable SPI controller
|
||||
* @spi: pointer to the spi controller data structure
|
||||
*
|
||||
* RX-Fifo is flushed when SPI controller is disabled. To prevent any data
|
||||
* loss, use stm32h7_spi_read_rxfifo(flush) to read the remaining bytes in
|
||||
* RX-Fifo.
|
||||
* Normally, if TSIZE has been configured, we should relax the hardware at the
|
||||
* reception of the EOT interrupt. But in case of error, EOT will not be
|
||||
* raised. So the subsystem unprepare_message call allows us to properly
|
||||
* complete the transfer from an hardware point of view.
|
||||
* RX-Fifo is flushed when SPI controller is disabled.
|
||||
*/
|
||||
static void stm32h7_spi_disable(struct stm32_spi *spi)
|
||||
{
|
||||
unsigned long flags;
|
||||
u32 cr1, sr;
|
||||
u32 cr1;
|
||||
|
||||
dev_dbg(spi->dev, "disable controller\n");
|
||||
|
||||
@@ -698,25 +695,6 @@ static void stm32h7_spi_disable(struct stm32_spi *spi)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Wait on EOT or suspend the flow */
|
||||
if (readl_relaxed_poll_timeout_atomic(spi->base + STM32H7_SPI_SR,
|
||||
sr, !(sr & STM32H7_SPI_SR_EOT),
|
||||
10, 100000) < 0) {
|
||||
if (cr1 & STM32H7_SPI_CR1_CSTART) {
|
||||
writel_relaxed(cr1 | STM32H7_SPI_CR1_CSUSP,
|
||||
spi->base + STM32H7_SPI_CR1);
|
||||
if (readl_relaxed_poll_timeout_atomic(
|
||||
spi->base + STM32H7_SPI_SR,
|
||||
sr, !(sr & STM32H7_SPI_SR_SUSP),
|
||||
10, 100000) < 0)
|
||||
dev_warn(spi->dev,
|
||||
"Suspend request timeout\n");
|
||||
}
|
||||
}
|
||||
|
||||
if (!spi->cur_usedma && spi->rx_buf && (spi->rx_len > 0))
|
||||
stm32h7_spi_read_rxfifo(spi, true);
|
||||
|
||||
if (spi->cur_usedma && spi->dma_tx)
|
||||
dmaengine_terminate_all(spi->dma_tx);
|
||||
if (spi->cur_usedma && spi->dma_rx)
|
||||
@@ -911,7 +889,7 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id)
|
||||
if (__ratelimit(&rs))
|
||||
dev_dbg_ratelimited(spi->dev, "Communication suspended\n");
|
||||
if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0)))
|
||||
stm32h7_spi_read_rxfifo(spi, false);
|
||||
stm32h7_spi_read_rxfifo(spi);
|
||||
/*
|
||||
* If communication is suspended while using DMA, it means
|
||||
* that something went wrong, so stop the current transfer
|
||||
@@ -932,8 +910,11 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id)
|
||||
|
||||
if (sr & STM32H7_SPI_SR_EOT) {
|
||||
if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0)))
|
||||
stm32h7_spi_read_rxfifo(spi, true);
|
||||
end = true;
|
||||
stm32h7_spi_read_rxfifo(spi);
|
||||
if (!spi->cur_usedma ||
|
||||
(spi->cur_usedma && (spi->cur_comm == SPI_SIMPLEX_TX ||
|
||||
spi->cur_comm == SPI_3WIRE_TX)))
|
||||
end = true;
|
||||
}
|
||||
|
||||
if (sr & STM32H7_SPI_SR_TXP)
|
||||
@@ -942,7 +923,7 @@ static irqreturn_t stm32h7_spi_irq_thread(int irq, void *dev_id)
|
||||
|
||||
if (sr & STM32H7_SPI_SR_RXP)
|
||||
if (!spi->cur_usedma && (spi->rx_buf && (spi->rx_len > 0)))
|
||||
stm32h7_spi_read_rxfifo(spi, false);
|
||||
stm32h7_spi_read_rxfifo(spi);
|
||||
|
||||
writel_relaxed(sr & mask, spi->base + STM32H7_SPI_IFCR);
|
||||
|
||||
@@ -1041,42 +1022,17 @@ static void stm32f4_spi_dma_tx_cb(void *data)
|
||||
}
|
||||
|
||||
/**
|
||||
* stm32f4_spi_dma_rx_cb - dma callback
|
||||
* stm32_spi_dma_rx_cb - dma callback
|
||||
* @data: pointer to the spi controller data structure
|
||||
*
|
||||
* DMA callback is called when the transfer is complete for DMA RX channel.
|
||||
*/
|
||||
static void stm32f4_spi_dma_rx_cb(void *data)
|
||||
static void stm32_spi_dma_rx_cb(void *data)
|
||||
{
|
||||
struct stm32_spi *spi = data;
|
||||
|
||||
spi_finalize_current_transfer(spi->master);
|
||||
stm32f4_spi_disable(spi);
|
||||
}
|
||||
|
||||
/**
|
||||
* stm32h7_spi_dma_cb - dma callback
|
||||
* @data: pointer to the spi controller data structure
|
||||
*
|
||||
* DMA callback is called when the transfer is complete or when an error
|
||||
* occurs. If the transfer is complete, EOT flag is raised.
|
||||
*/
|
||||
static void stm32h7_spi_dma_cb(void *data)
|
||||
{
|
||||
struct stm32_spi *spi = data;
|
||||
unsigned long flags;
|
||||
u32 sr;
|
||||
|
||||
spin_lock_irqsave(&spi->lock, flags);
|
||||
|
||||
sr = readl_relaxed(spi->base + STM32H7_SPI_SR);
|
||||
|
||||
spin_unlock_irqrestore(&spi->lock, flags);
|
||||
|
||||
if (!(sr & STM32H7_SPI_SR_EOT))
|
||||
dev_warn(spi->dev, "DMA error (sr=0x%08x)\n", sr);
|
||||
|
||||
/* Now wait for EOT, or SUSP or OVR in case of error */
|
||||
spi->cfg->disable(spi);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1242,11 +1198,13 @@ static void stm32f4_spi_transfer_one_dma_start(struct stm32_spi *spi)
|
||||
*/
|
||||
static void stm32h7_spi_transfer_one_dma_start(struct stm32_spi *spi)
|
||||
{
|
||||
/* Enable the interrupts relative to the end of transfer */
|
||||
stm32_spi_set_bits(spi, STM32H7_SPI_IER, STM32H7_SPI_IER_EOTIE |
|
||||
STM32H7_SPI_IER_TXTFIE |
|
||||
STM32H7_SPI_IER_OVRIE |
|
||||
STM32H7_SPI_IER_MODFIE);
|
||||
uint32_t ier = STM32H7_SPI_IER_OVRIE | STM32H7_SPI_IER_MODFIE;
|
||||
|
||||
/* Enable the interrupts */
|
||||
if (spi->cur_comm == SPI_SIMPLEX_TX || spi->cur_comm == SPI_3WIRE_TX)
|
||||
ier |= STM32H7_SPI_IER_EOTIE | STM32H7_SPI_IER_TXTFIE;
|
||||
|
||||
stm32_spi_set_bits(spi, STM32H7_SPI_IER, ier);
|
||||
|
||||
stm32_spi_enable(spi);
|
||||
|
||||
@@ -1645,10 +1603,6 @@ static int stm32_spi_transfer_one(struct spi_master *master,
|
||||
struct stm32_spi *spi = spi_master_get_devdata(master);
|
||||
int ret;
|
||||
|
||||
/* Don't do anything on 0 bytes transfers */
|
||||
if (transfer->len == 0)
|
||||
return 0;
|
||||
|
||||
spi->tx_buf = transfer->tx_buf;
|
||||
spi->rx_buf = transfer->rx_buf;
|
||||
spi->tx_len = spi->tx_buf ? transfer->len : 0;
|
||||
@@ -1762,7 +1716,7 @@ static const struct stm32_spi_cfg stm32f4_spi_cfg = {
|
||||
.set_mode = stm32f4_spi_set_mode,
|
||||
.transfer_one_dma_start = stm32f4_spi_transfer_one_dma_start,
|
||||
.dma_tx_cb = stm32f4_spi_dma_tx_cb,
|
||||
.dma_rx_cb = stm32f4_spi_dma_rx_cb,
|
||||
.dma_rx_cb = stm32_spi_dma_rx_cb,
|
||||
.transfer_one_irq = stm32f4_spi_transfer_one_irq,
|
||||
.irq_handler_event = stm32f4_spi_irq_event,
|
||||
.irq_handler_thread = stm32f4_spi_irq_thread,
|
||||
@@ -1782,8 +1736,11 @@ static const struct stm32_spi_cfg stm32h7_spi_cfg = {
|
||||
.set_data_idleness = stm32h7_spi_data_idleness,
|
||||
.set_number_of_data = stm32h7_spi_number_of_data,
|
||||
.transfer_one_dma_start = stm32h7_spi_transfer_one_dma_start,
|
||||
.dma_rx_cb = stm32h7_spi_dma_cb,
|
||||
.dma_tx_cb = stm32h7_spi_dma_cb,
|
||||
.dma_rx_cb = stm32_spi_dma_rx_cb,
|
||||
/*
|
||||
* dma_tx_cb is not necessary since in case of TX, dma is followed by
|
||||
* SPI access hence handling is performed within the SPI interrupt
|
||||
*/
|
||||
.transfer_one_irq = stm32h7_spi_transfer_one_irq,
|
||||
.irq_handler_thread = stm32h7_spi_irq_thread,
|
||||
.baud_rate_div_min = STM32H7_SPI_MBR_DIV_MIN,
|
||||
@@ -1927,6 +1884,9 @@ static int stm32_spi_probe(struct platform_device *pdev)
|
||||
if (spi->dma_tx || spi->dma_rx)
|
||||
master->can_dma = stm32_spi_can_dma;
|
||||
|
||||
pm_runtime_set_autosuspend_delay(&pdev->dev,
|
||||
STM32_SPI_AUTOSUSPEND_DELAY);
|
||||
pm_runtime_use_autosuspend(&pdev->dev);
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_get_noresume(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
@@ -1938,6 +1898,9 @@ static int stm32_spi_probe(struct platform_device *pdev)
|
||||
goto err_pm_disable;
|
||||
}
|
||||
|
||||
pm_runtime_mark_last_busy(&pdev->dev);
|
||||
pm_runtime_put_autosuspend(&pdev->dev);
|
||||
|
||||
dev_info(&pdev->dev, "driver initialized\n");
|
||||
|
||||
return 0;
|
||||
@@ -1946,6 +1909,7 @@ err_pm_disable:
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
err_dma_release:
|
||||
if (spi->dma_tx)
|
||||
dma_release_channel(spi->dma_tx);
|
||||
@@ -1970,6 +1934,8 @@ static int stm32_spi_remove(struct platform_device *pdev)
|
||||
pm_runtime_disable(&pdev->dev);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_set_suspended(&pdev->dev);
|
||||
pm_runtime_dont_use_autosuspend(&pdev->dev);
|
||||
|
||||
if (master->dma_tx)
|
||||
dma_release_channel(master->dma_tx);
|
||||
if (master->dma_rx)
|
||||
|
||||
Reference in New Issue
Block a user