Hi!

On Thu, Oct 15, 2020 at 06:47:40PM +0300, Alexander Kochetkov wrote:
> DMA-based transfer will be enabled if data length is larger than FIFO size
> (64 bytes for A64). This greatly reduce number of interrupts for
> transferring data.
> 
> For smaller data size PIO mode will be used. In PIO mode whole buffer will
> be loaded into FIFO.
> 
> If driver failed to request DMA channels then it fallback for PIO mode.
> 
> Tested on SOPINE (https://www.pine64.org/sopine/)
> 
> Signed-off-by: Alexander Kochetkov <al.koc...@gmail.com>

Thanks for working on this, it's been a bit overdue

> ---
>  drivers/spi/spi-sun6i.c | 171 +++++++++++++++++++++++++++++++++++++---
>  1 file changed, 159 insertions(+), 12 deletions(-)
> 
> diff --git a/drivers/spi/spi-sun6i.c b/drivers/spi/spi-sun6i.c
> index 19238e1b76b4..38e5b8af7da6 100644
> --- a/drivers/spi/spi-sun6i.c
> +++ b/drivers/spi/spi-sun6i.c
> @@ -18,6 +18,7 @@
>  #include <linux/platform_device.h>
>  #include <linux/pm_runtime.h>
>  #include <linux/reset.h>
> +#include <linux/dmaengine.h>
>  
>  #include <linux/spi/spi.h>
>  
> @@ -52,10 +53,12 @@
>  
>  #define SUN6I_FIFO_CTL_REG           0x18
>  #define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_MASK        0xff
> +#define SUN6I_FIFO_CTL_RF_DRQ_EN             BIT(8)
>  #define SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS        0
>  #define SUN6I_FIFO_CTL_RF_RST                        BIT(15)
>  #define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_MASK        0xff
>  #define SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS        16
> +#define SUN6I_FIFO_CTL_TF_DRQ_EN             BIT(24)
>  #define SUN6I_FIFO_CTL_TF_RST                        BIT(31)
>  
>  #define SUN6I_FIFO_STA_REG           0x1c
> @@ -83,6 +86,8 @@
>  struct sun6i_spi {
>       struct spi_master       *master;
>       void __iomem            *base_addr;
> +     dma_addr_t              dma_addr_rx;
> +     dma_addr_t              dma_addr_tx;
>       struct clk              *hclk;
>       struct clk              *mclk;
>       struct reset_control    *rstc;
> @@ -92,6 +97,7 @@ struct sun6i_spi {
>       const u8                *tx_buf;
>       u8                      *rx_buf;
>       int                     len;
> +     bool                    use_dma;
>       unsigned long           fifo_depth;
>  };
>  
> @@ -182,6 +188,66 @@ static size_t sun6i_spi_max_transfer_size(struct 
> spi_device *spi)
>       return SUN6I_MAX_XFER_SIZE - 1;
>  }
>  
> +static int sun6i_spi_prepare_dma(struct sun6i_spi *sspi,
> +                              struct spi_transfer *tfr)
> +{
> +     struct dma_async_tx_descriptor *rxdesc, *txdesc;
> +     struct spi_master *master = sspi->master;
> +
> +     rxdesc = NULL;
> +     if (tfr->rx_buf) {
> +             struct dma_slave_config rxconf = {
> +                     .direction = DMA_DEV_TO_MEM,
> +                     .src_addr = sspi->dma_addr_rx,
> +                     .src_addr_width = 1,
> +                     .src_maxburst = 1,
> +             };

That doesn't really look optimal, the controller seems to be able to
read / write 32 bits at a time from its FIFO and we probably can
increase the burst length too?

> +
> +             dmaengine_slave_config(master->dma_rx, &rxconf);
> +
> +             rxdesc = dmaengine_prep_slave_sg(
> +                             master->dma_rx,
> +                             tfr->rx_sg.sgl, tfr->rx_sg.nents,
> +                             DMA_DEV_TO_MEM, DMA_PREP_INTERRUPT);

checkpatch --strict complains about this one (your line shouldn't end
with a parenthesis).

> +             if (!rxdesc)
> +                     return -EINVAL;
> +     }
> +
> +     txdesc = NULL;
> +     if (tfr->tx_buf) {
> +             struct dma_slave_config txconf = {
> +                     .direction = DMA_MEM_TO_DEV,
> +                     .dst_addr = sspi->dma_addr_tx,
> +                     .dst_addr_width = 1,
> +                     .dst_maxburst = 1,
> +             };
> +
> +             dmaengine_slave_config(master->dma_tx, &txconf);
> +
> +             txdesc = dmaengine_prep_slave_sg(
> +                             master->dma_tx,
> +                             tfr->tx_sg.sgl, tfr->tx_sg.nents,
> +                             DMA_MEM_TO_DEV, DMA_PREP_INTERRUPT);
> +             if (!txdesc) {
> +                     if (rxdesc)
> +                             dmaengine_terminate_sync(master->dma_rx);
> +                     return -EINVAL;
> +             }
> +     }
> +
> +     if (tfr->rx_buf) {
> +             dmaengine_submit(rxdesc);
> +             dma_async_issue_pending(master->dma_rx);
> +     }
> +
> +     if (tfr->tx_buf) {
> +             dmaengine_submit(txdesc);
> +             dma_async_issue_pending(master->dma_tx);
> +     }
> +
> +     return 0;
> +}
> +
>  static int sun6i_spi_transfer_one(struct spi_master *master,
>                                 struct spi_device *spi,
>                                 struct spi_transfer *tfr)
> @@ -201,6 +267,8 @@ static int sun6i_spi_transfer_one(struct spi_master 
> *master,
>       sspi->tx_buf = tfr->tx_buf;
>       sspi->rx_buf = tfr->rx_buf;
>       sspi->len = tfr->len;
> +     sspi->use_dma = master->can_dma ?
> +                     master->can_dma(master, spi, tfr) : false;
>  
>       /* Clear pending interrupts */
>       sun6i_spi_write(sspi, SUN6I_INT_STA_REG, ~0);
> @@ -216,9 +284,17 @@ static int sun6i_spi_transfer_one(struct spi_master 
> *master,
>        * (See spi-sun4i.c)
>        */
>       trig_level = sspi->fifo_depth / 4 * 3;
> -     sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG,
> -                     (trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS) |
> -                     (trig_level << SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS));
> +     reg = (trig_level << SUN6I_FIFO_CTL_RF_RDY_TRIG_LEVEL_BITS) |
> +           (trig_level << SUN6I_FIFO_CTL_TF_ERQ_TRIG_LEVEL_BITS);
> +
> +     if (sspi->use_dma) {
> +             if (tfr->tx_buf)
> +                     reg |= SUN6I_FIFO_CTL_TF_DRQ_EN;
> +             if (tfr->rx_buf)
> +                     reg |= SUN6I_FIFO_CTL_RF_DRQ_EN;
> +     }
> +
> +     sun6i_spi_write(sspi, SUN6I_FIFO_CTL_REG, reg);
>  
>       /*
>        * Setup the transfer control register: Chip Select,
> @@ -300,16 +376,28 @@ static int sun6i_spi_transfer_one(struct spi_master 
> *master,
>       sun6i_spi_write(sspi, SUN6I_XMIT_CNT_REG, tx_len);
>       sun6i_spi_write(sspi, SUN6I_BURST_CTL_CNT_REG, tx_len);
>  
> -     /* Fill the TX FIFO */
> -     sun6i_spi_fill_fifo(sspi);
> +     if (!sspi->use_dma) {
> +             /* Fill the TX FIFO */
> +             sun6i_spi_fill_fifo(sspi);
> +     } else {
> +             ret = sun6i_spi_prepare_dma(sspi, tfr);
> +             if (ret) {
> +                     dev_warn(&master->dev,
> +                              "%s: prepare DMA failed, ret=%d",
> +                              dev_name(&spi->dev), ret);
> +                     return ret;
> +             }
> +     }
>  
>       /* Enable the interrupts */
>       reg = SUN6I_INT_CTL_TC;
>  
> -     if (rx_len > sspi->fifo_depth)
> -             reg |= SUN6I_INT_CTL_RF_RDY;
> -     if (tx_len > sspi->fifo_depth)
> -             reg |= SUN6I_INT_CTL_TF_ERQ;
> +     if (!sspi->use_dma) {
> +             if (rx_len > sspi->fifo_depth)
> +                     reg |= SUN6I_INT_CTL_RF_RDY;
> +             if (tx_len > sspi->fifo_depth)
> +                     reg |= SUN6I_INT_CTL_TF_ERQ;
> +     }
>  
>       sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, reg);
>  
> @@ -332,6 +420,11 @@ static int sun6i_spi_transfer_one(struct spi_master 
> *master,
>  
>       sun6i_spi_write(sspi, SUN6I_INT_CTL_REG, 0);
>  
> +     if (ret && sspi->use_dma) {
> +             dmaengine_terminate_sync(master->dma_rx);
> +             dmaengine_terminate_sync(master->dma_tx);
> +     }
> +
>       return ret;
>  }
>  
> @@ -343,7 +436,8 @@ static irqreturn_t sun6i_spi_handler(int irq, void 
> *dev_id)
>       /* Transfer complete */
>       if (status & SUN6I_INT_CTL_TC) {
>               sun6i_spi_write(sspi, SUN6I_INT_STA_REG, SUN6I_INT_CTL_TC);
> -             sun6i_spi_drain_fifo(sspi);
> +             if (!sspi->use_dma)
> +                     sun6i_spi_drain_fifo(sspi);

Is it causing any issue? The FIFO will be drained only if there's
something remaining in the FIFO, which shouldn't happen with DMA?

Maxime

Attachment: signature.asc
Description: PGP signature

Reply via email to