mpfs/mpfs_corespi: Several speed optimizations to the FPGA driver

This is a collection of tweaks / optimizations to the driver to limit
CPU usage as well as interrupt processing times.

The changes are as follows:
- setfrequency is now no-op if the frequency does not change. Accessing
  MPFS_SPI_CONTROL requires synchronization to the FIC domain, which
  takes unnecessary time if nothing changes
- load/unload FIFO loops optimized so !buffer, priv->nbits and i==last are
  only tested once (instead of for every word written in loop).
- Disable the RX interrupt only once (again, FIC domain access is slow)
- In case a spurious MPFS_SPI_DATA_RX interrupt arrives, just wipe the
  whole RX FIFO, instead of trying to read it byte-by-byte
This commit is contained in:
Ville Juven 2023-09-22 14:34:22 +03:00 committed by Xiang Xiao
parent ded321a515
commit 9be93addea

View file

@ -529,6 +529,13 @@ static uint32_t mpfs_spi_setfrequency(struct spi_dev_s *dev,
DEBUGASSERT(frequency > 0);
if (priv->frequency == frequency)
{
/* Nothing changes */
return priv->actual;
}
if (priv->enabled)
{
modifyreg32(MPFS_SPI_CONTROL, MPFS_SPI_ENABLE, 0);
@ -739,54 +746,38 @@ static void mpfs_spi_load_tx_fifo(struct mpfs_spi_priv_s *priv,
{
uint16_t *data16;
uint8_t *data8;
int last;
int i;
DEBUGASSERT(nwords > 0);
data16 = (uint16_t *)txbuffer;
data8 = (uint8_t *)txbuffer;
last = nwords - 1;
for (i = 0; i < nwords; i++)
if (!txbuffer)
{
if (txbuffer)
for (i = 0; i < nwords - 1; i++)
{
if (priv->nbits == 8)
{
if (i == last)
{
putreg32((uint32_t)data8[priv->tx_pos], MPFS_SPI_TX_LAST);
}
else
{
putreg32((uint32_t)data8[priv->tx_pos], MPFS_SPI_TX_DATA);
}
}
else
{
if (i == last)
{
putreg32((uint32_t)data16[priv->tx_pos], MPFS_SPI_TX_LAST);
}
else
{
putreg32((uint32_t)data16[priv->tx_pos], MPFS_SPI_TX_DATA);
}
}
}
else
{
if (i == last)
{
putreg32(0, MPFS_SPI_TX_LAST);
}
else
{
putreg32(0, MPFS_SPI_TX_DATA);
}
putreg32(0, MPFS_SPI_TX_DATA);
}
priv->tx_pos++;
putreg32(0, MPFS_SPI_TX_LAST);
}
else if (priv->nbits == 8)
{
for (i = 0; i < nwords - 1; i++)
{
putreg32((uint32_t)data8[priv->tx_pos++], MPFS_SPI_TX_DATA);
}
putreg32((uint32_t)data8[priv->tx_pos++], MPFS_SPI_TX_LAST);
}
else
{
for (i = 0; i < nwords - 1; i++)
{
putreg32((uint32_t)data16[priv->tx_pos++], MPFS_SPI_TX_DATA);
}
putreg32((uint32_t)data16[priv->tx_pos++], MPFS_SPI_TX_LAST);
}
}
@ -813,48 +804,40 @@ static void mpfs_spi_unload_rx_fifo(struct mpfs_spi_priv_s *priv,
{
uint16_t *data16;
uint8_t *data8;
int last;
int i;
DEBUGASSERT(nwords > 0);
data16 = (uint16_t *)rxbuffer;
data8 = (uint8_t *)rxbuffer;
last = nwords - 1;
for (i = 0; i < nwords; i++)
if (!rxbuffer)
{
/* The last character might not be available yet due to bus delays */
if (i == last)
modifyreg32(MPFS_SPI_COMMAND, 0, MPFS_SPI_RXFIFORST);
}
else if (priv->nbits == 8)
{
for (i = 0; i < nwords - 1; i++)
{
if (mpfs_rx_wait_last_frame(priv) < 0)
{
/* Nothing came, get out */
return;
}
data8[priv->rx_pos++] = getreg32(MPFS_SPI_RX_DATA);
}
if (rxbuffer)
if (mpfs_rx_wait_last_frame(priv) == 0)
{
if (priv->nbits == 8)
{
data8[priv->rx_pos] = getreg32(MPFS_SPI_RX_DATA);
}
else
{
data16[priv->rx_pos] = getreg32(MPFS_SPI_RX_DATA);
}
data8[priv->rx_pos++] = getreg32(MPFS_SPI_RX_DATA);
}
else
}
else if (priv->nbits == 16)
{
for (i = 0; i < nwords - 1; i++)
{
getreg32(MPFS_SPI_RX_DATA);
data16[priv->rx_pos++] = getreg32(MPFS_SPI_RX_DATA);
}
priv->rx_pos++;
DEBUGASSERT(priv->rx_pos <= priv->rxwords);
if (mpfs_rx_wait_last_frame(priv) == 0)
{
data16[priv->rx_pos++] = getreg32(MPFS_SPI_RX_DATA);
}
}
}
@ -935,10 +918,6 @@ static void mpfs_spi_irq_exchange(struct mpfs_spi_priv_s *priv,
MPFS_SPI_INTRXOVRFLOW |
MPFS_SPI_INTTXDONE);
/* Make sure the RX interrupt is disabled */
modifyreg32(MPFS_SPI_CONTROL2, MPFS_SPI_INTEN_DATA_RX, 0);
if (mpfs_spi_sem_waitdone(priv) < 0)
{
spiinfo("Message timed out\n");
@ -1304,22 +1283,6 @@ static int mpfs_spi_irq(int cpuint, void *context, void *arg)
spiinfo("irq status=%x\n", status);
if (status & MPFS_SPI_DATA_RX)
{
remaining = priv->rxwords - priv->rx_pos;
if (remaining <= priv->fifosize)
{
mpfs_spi_unload_rx_fifo(priv, priv->rxbuf, remaining);
}
else
{
mpfs_spi_unload_rx_fifo(priv, priv->rxbuf, priv->fifolevel);
}
putreg32(MPFS_SPI_DATA_RX, MPFS_SPI_INT_CLEAR);
}
if (status & MPFS_SPI_TXDONE)
{
/* TX is done, we know RX is done too -> offload the RX FIFO */
@ -1357,6 +1320,14 @@ static int mpfs_spi_irq(int cpuint, void *context, void *arg)
}
}
if (status & MPFS_SPI_DATA_RX)
{
/* We don't expect data RX interrupts, just reset RX FIFO */
modifyreg32(MPFS_SPI_COMMAND, 0, MPFS_SPI_RXFIFORST);
putreg32(MPFS_SPI_DATA_RX, MPFS_SPI_INT_CLEAR);
}
if (status & MPFS_SPI_RXCHOVRFLW)
{
/* Handle receive overflow */
@ -1449,6 +1420,10 @@ static void mpfs_spi_init(struct spi_dev_s *dev)
0);
modifyreg32(MPFS_SYSREG_SUBBLK_CLOCK_CR, 0, MPFS_SYSREG_SUBBLK_CORESPI);
/* Make sure the RX interrupt is disabled (we don't use it) */
modifyreg32(MPFS_SPI_CONTROL2, MPFS_SPI_INTEN_DATA_RX, 0);
/* Install some default values, mode and nbits for read back */
mpfs_spi_setfrequency(dev, config->clk_freq);