diff mbox series

[v2] spi: meson-spicc: add DMA support

Message ID 20250414-spi-dma-v2-1-84bbd92fa469@amlogic.com
State New
Headers show
Series [v2] spi: meson-spicc: add DMA support | expand

Commit Message

Xianwei Zhao via B4 Relay April 14, 2025, 6:30 a.m. UTC
From: Xianwei Zhao <xianwei.zhao@amlogic.com>

Add DMA support for spicc driver.

DMA works if the transfer meets the following conditions:
1. 64 bits per word;
2. The transfer length must be multiples of the dma_burst_len,
   and the dma_burst_len should be one of 8,7...2,
   otherwise, it will be split into several SPI bursts.

Signed-off-by: Sunny Luo <sunny.luo@amlogic.com>
Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com>
---
Changes in v2:
- Make formatting adjustments and code optimizations according to Neil's suggestions.
- Remove two special DMA trigger modes that are not fully implemented.
- Link to v1: https://lore.kernel.org/r/20250408-spi-dma-v1-1-3c38be62c09c@amlogic.com
---
 drivers/spi/spi-meson-spicc.c | 241 ++++++++++++++++++++++++++++++++++++++----
 1 file changed, 220 insertions(+), 21 deletions(-)


---
base-commit: 49807ed87851916ef655f72e9562f96355183090
change-id: 20250408-spi-dma-c499f560d295

Best regards,

Comments

Mark Brown April 25, 2025, 7:09 p.m. UTC | #1
On Mon, 14 Apr 2025 14:30:16 +0800, Xianwei Zhao wrote:
> Add DMA support for spicc driver.
> 
> DMA works if the transfer meets the following conditions:
> 1. 64 bits per word;
> 2. The transfer length must be multiples of the dma_burst_len,
>    and the dma_burst_len should be one of 8,7...2,
>    otherwise, it will be split into several SPI bursts.
> 
> [...]

Applied to

   https://git.kernel.org/pub/scm/linux/kernel/git/broonie/spi.git for-next

Thanks!

[1/1] spi: meson-spicc: add DMA support
      commit: 18197e98353d931fc7bb2bb9ec671d3aa407831d

All being well this means that it will be integrated into the linux-next
tree (usually sometime in the next 24 hours) and sent to Linus during
the next merge window (or sooner if it is a bug fix), however if
problems are discovered then the patch may be dropped or reverted.

You may get further e-mails resulting from automated or manual testing
and review of the tree, please engage with people reporting problems and
send followup patches addressing any issues that are reported if needed.

If any updates are required or you are submitting further changes they
should be sent as incremental updates against current git, existing
patches will not be replaced.

Please add any relevant lists and maintainers to the CCs when replying
to this mail.

Thanks,
Mark
Da Xue April 30, 2025, 2:13 a.m. UTC | #2
On Mon, Apr 14, 2025 at 2:30 AM Xianwei Zhao via B4 Relay
<devnull+xianwei.zhao.amlogic.com@kernel.org> wrote:
>
> From: Xianwei Zhao <xianwei.zhao@amlogic.com>
>
> Add DMA support for spicc driver.
>
> DMA works if the transfer meets the following conditions:
> 1. 64 bits per word;
> 2. The transfer length must be multiples of the dma_burst_len,
>    and the dma_burst_len should be one of 8,7...2,
>    otherwise, it will be split into several SPI bursts.
>
> Signed-off-by: Sunny Luo <sunny.luo@amlogic.com>
> Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com>
> ---
> Changes in v2:
> - Make formatting adjustments and code optimizations according to Neil's suggestions.
> - Remove two special DMA trigger modes that are not fully implemented.
> - Link to v1: https://lore.kernel.org/r/20250408-spi-dma-v1-1-3c38be62c09c@amlogic.com
> ---
>  drivers/spi/spi-meson-spicc.c | 241 ++++++++++++++++++++++++++++++++++++++----
>  1 file changed, 220 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c
> index df74ad5060f8..6b9137307533 100644
> --- a/drivers/spi/spi-meson-spicc.c
> +++ b/drivers/spi/spi-meson-spicc.c
> @@ -21,18 +21,26 @@
>  #include <linux/interrupt.h>
>  #include <linux/reset.h>
>  #include <linux/pinctrl/consumer.h>
> +#include <linux/dma-mapping.h>
>
>  /*
> - * The Meson SPICC controller could support DMA based transfers, but is not
> - * implemented by the vendor code, and while having the registers documentation
> - * it has never worked on the GXL Hardware.
> - * The PIO mode is the only mode implemented, and due to badly designed HW :
> - * - all transfers are cutted in 16 words burst because the FIFO hangs on
> - *   TX underflow, and there is no TX "Half-Empty" interrupt, so we go by
> - *   FIFO max size chunk only
> - * - CS management is dumb, and goes UP between every burst, so is really a
> - *   "Data Valid" signal than a Chip Select, GPIO link should be used instead
> - *   to have a CS go down over the full transfer
> + * There are two modes for data transmission: PIO and DMA.
> + * When bits_per_word is 8, 16, 24, or 32, data is transferred using PIO mode.
> + * When bits_per_word is 64, DMA mode is used by default.
> + *
> + * DMA achieves a transfer with one or more SPI bursts, each SPI burst is made
> + * up of one or more DMA bursts. The DMA burst implementation mechanism is,
> + * For TX, when the number of words in TXFIFO is less than the preset
> + * reading threshold, SPICC starts a reading DMA burst, which reads the preset
> + * number of words from TX buffer, then writes them into TXFIFO.
> + * For RX, when the number of words in RXFIFO is greater than the preset
> + * writing threshold, SPICC starts a writing request burst, which reads the
> + * preset number of words from RXFIFO, then write them into RX buffer.
> + * DMA works if the transfer meets the following conditions,
> + * - 64 bits per word

Just for clarification, DMA can only send 64-bit words due to hardware
design, right?
The bit-per-word in spi control register (CONREG) has no effect?

> + * - The transfer length in word must be multiples of the dma_burst_len, and
> + *   the dma_burst_len should be one of 8,7...2, otherwise, it will be split
> + *   into several SPI bursts by this driver
>   */
>
>  #define SPICC_MAX_BURST        128
> @@ -128,6 +136,23 @@
>
>  #define SPICC_DWADDR   0x24    /* Write Address of DMA */
>
> +#define SPICC_LD_CNTL0 0x28
> +#define VSYNC_IRQ_SRC_SELECT           BIT(0)
> +#define DMA_EN_SET_BY_VSYNC            BIT(2)
> +#define XCH_EN_SET_BY_VSYNC            BIT(3)
> +#define DMA_READ_COUNTER_EN            BIT(4)
> +#define DMA_WRITE_COUNTER_EN           BIT(5)
> +#define DMA_RADDR_LOAD_BY_VSYNC                BIT(6)
> +#define DMA_WADDR_LOAD_BY_VSYNC                BIT(7)
> +#define DMA_ADDR_LOAD_FROM_LD_ADDR     BIT(8)
> +
> +#define SPICC_LD_CNTL1 0x2c
> +#define DMA_READ_COUNTER               GENMASK(15, 0)
> +#define DMA_WRITE_COUNTER              GENMASK(31, 16)
> +#define DMA_BURST_LEN_DEFAULT          8
> +#define DMA_BURST_COUNT_MAX            0xffff
> +#define SPI_BURST_LEN_MAX      (DMA_BURST_LEN_DEFAULT * DMA_BURST_COUNT_MAX)
> +

LD_CNTL0 and LD_CNTL1 are not in this datasheet for GXL
(S805X/S905X/S905D). Do they exist on these SoCs and are not
documented?

>  #define SPICC_ENH_CTL0 0x38    /* Enhanced Feature */
>  #define SPICC_ENH_CLK_CS_DELAY_MASK    GENMASK(15, 0)
>  #define SPICC_ENH_DATARATE_MASK                GENMASK(23, 16)
> @@ -171,6 +196,9 @@ struct meson_spicc_device {
>         struct pinctrl                  *pinctrl;
>         struct pinctrl_state            *pins_idle_high;
>         struct pinctrl_state            *pins_idle_low;
> +       dma_addr_t                      tx_dma;
> +       dma_addr_t                      rx_dma;
> +       bool                            using_dma;
>  };
>
>  #define pow2_clk_to_spicc(_div) container_of(_div, struct meson_spicc_device, pow2_div)
> @@ -202,6 +230,148 @@ static void meson_spicc_oen_enable(struct meson_spicc_device *spicc)
>         writel_relaxed(conf, spicc->base + SPICC_ENH_CTL0);
>  }
>
> +static int meson_spicc_dma_map(struct meson_spicc_device *spicc,
> +                              struct spi_transfer *t)
> +{
> +       struct device *dev = spicc->host->dev.parent;
> +
> +       if (!(t->tx_buf && t->rx_buf))
> +               return -EINVAL;
> +
> +       t->tx_dma = dma_map_single(dev, (void *)t->tx_buf, t->len, DMA_TO_DEVICE);
> +       if (dma_mapping_error(dev, t->tx_dma))
> +               return -ENOMEM;
> +
> +       t->rx_dma = dma_map_single(dev, t->rx_buf, t->len, DMA_FROM_DEVICE);
> +       if (dma_mapping_error(dev, t->rx_dma))
> +               return -ENOMEM;
> +
> +       spicc->tx_dma = t->tx_dma;
> +       spicc->rx_dma = t->rx_dma;
> +
> +       return 0;
> +}
> +
> +static void meson_spicc_dma_unmap(struct meson_spicc_device *spicc,
> +                                 struct spi_transfer *t)
> +{
> +       struct device *dev = spicc->host->dev.parent;
> +
> +       if (t->tx_dma)
> +               dma_unmap_single(dev, t->tx_dma, t->len, DMA_TO_DEVICE);
> +       if (t->rx_dma)
> +               dma_unmap_single(dev, t->rx_dma, t->len, DMA_FROM_DEVICE);
> +}
> +
> +/*
> + * According to the remain words length, calculate a suitable spi burst length
> + * and a dma burst length for current spi burst
> + */
> +static u32 meson_spicc_calc_dma_len(struct meson_spicc_device *spicc,
> +                                   u32 len, u32 *dma_burst_len)
> +{
> +       u32 i;
> +
> +       if (len <= spicc->data->fifo_size) {
> +               *dma_burst_len = len;
> +               return len;
> +       }
> +
> +       *dma_burst_len = DMA_BURST_LEN_DEFAULT;
> +
> +       if (len == (SPI_BURST_LEN_MAX + 1))
> +               return SPI_BURST_LEN_MAX - DMA_BURST_LEN_DEFAULT;
> +
> +       if (len >= SPI_BURST_LEN_MAX)
> +               return SPI_BURST_LEN_MAX;
> +
> +       for (i = DMA_BURST_LEN_DEFAULT; i > 1; i--)
> +               if ((len % i) == 0) {
> +                       *dma_burst_len = i;
> +                       return len;
> +               }
> +
> +       i = len % DMA_BURST_LEN_DEFAULT;
> +       len -= i;
> +
> +       if (i == 1)
> +               len -= DMA_BURST_LEN_DEFAULT;
> +
> +       return len;
> +}
> +
> +static void meson_spicc_setup_dma(struct meson_spicc_device *spicc)
> +{
> +       unsigned int len;
> +       unsigned int dma_burst_len, dma_burst_count;
> +       unsigned int count_en = 0;
> +       unsigned int txfifo_thres = 0;
> +       unsigned int read_req = 0;
> +       unsigned int rxfifo_thres = 31;
> +       unsigned int write_req = 0;
> +       unsigned int ld_ctr1 = 0;
> +
> +       writel_relaxed(spicc->tx_dma, spicc->base + SPICC_DRADDR);
> +       writel_relaxed(spicc->rx_dma, spicc->base + SPICC_DWADDR);
> +
> +       /* Set the max burst length to support a transmission with length of
> +        * no more than 1024 bytes(128 words), which must use the CS management
> +        * because of some strict timing requirements
> +        */
> +       writel_bits_relaxed(SPICC_BURSTLENGTH_MASK, SPICC_BURSTLENGTH_MASK,
> +                           spicc->base + SPICC_CONREG);
> +
> +       len = meson_spicc_calc_dma_len(spicc, spicc->xfer_remain,
> +                                      &dma_burst_len);
> +       spicc->xfer_remain -= len;
> +       dma_burst_count = DIV_ROUND_UP(len, dma_burst_len);
> +       dma_burst_len--;
> +
> +       if (spicc->tx_dma) {
> +               spicc->tx_dma += len;
> +               count_en |= DMA_READ_COUNTER_EN;
> +               txfifo_thres = spicc->data->fifo_size - dma_burst_len;
> +               read_req = dma_burst_len;
> +               ld_ctr1 |= FIELD_PREP(DMA_READ_COUNTER, dma_burst_count);
> +       }
> +
> +       if (spicc->rx_dma) {
> +               spicc->rx_dma += len;
> +               count_en |= DMA_WRITE_COUNTER_EN;
> +               rxfifo_thres = dma_burst_len;
> +               write_req = dma_burst_len;
> +               ld_ctr1 |= FIELD_PREP(DMA_WRITE_COUNTER, dma_burst_count);
> +       }
> +
> +       writel_relaxed(count_en, spicc->base + SPICC_LD_CNTL0);
> +       writel_relaxed(ld_ctr1, spicc->base + SPICC_LD_CNTL1);
> +       writel_relaxed(SPICC_DMA_ENABLE
> +                   | SPICC_DMA_URGENT
> +                   | FIELD_PREP(SPICC_TXFIFO_THRESHOLD_MASK, txfifo_thres)
> +                   | FIELD_PREP(SPICC_READ_BURST_MASK, read_req)
> +                   | FIELD_PREP(SPICC_RXFIFO_THRESHOLD_MASK, rxfifo_thres)
> +                   | FIELD_PREP(SPICC_WRITE_BURST_MASK, write_req),
> +                   spicc->base + SPICC_DMAREG);
> +}
> +
> +static irqreturn_t meson_spicc_dma_irq(struct meson_spicc_device *spicc)
> +{
> +       if (readl_relaxed(spicc->base + SPICC_DMAREG) & SPICC_DMA_ENABLE)
> +               return IRQ_HANDLED;
> +
> +       if (spicc->xfer_remain) {
> +               meson_spicc_setup_dma(spicc);
> +       } else {
> +               writel_bits_relaxed(SPICC_SMC, 0, spicc->base + SPICC_CONREG);
> +               writel_relaxed(0, spicc->base + SPICC_INTREG);
> +               writel_relaxed(0, spicc->base + SPICC_DMAREG);
> +               meson_spicc_dma_unmap(spicc, spicc->xfer);
> +               complete(&spicc->done);
> +       }
> +
> +       return IRQ_HANDLED;
> +}
> +
>  static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc)
>  {
>         return !!FIELD_GET(SPICC_TF,
> @@ -293,6 +463,9 @@ static irqreturn_t meson_spicc_irq(int irq, void *data)
>
>         writel_bits_relaxed(SPICC_TC, SPICC_TC, spicc->base + SPICC_STATREG);
>
> +       if (spicc->using_dma)
> +               return meson_spicc_dma_irq(spicc);
> +
>         /* Empty RX FIFO */
>         meson_spicc_rx(spicc);
>
> @@ -426,9 +599,6 @@ static int meson_spicc_transfer_one(struct spi_controller *host,
>
>         meson_spicc_reset_fifo(spicc);
>
> -       /* Setup burst */
> -       meson_spicc_setup_burst(spicc);
> -
>         /* Setup wait for completion */
>         reinit_completion(&spicc->done);
>
> @@ -442,11 +612,36 @@ static int meson_spicc_transfer_one(struct spi_controller *host,
>         /* Increase it twice and add 200 ms tolerance */
>         timeout += timeout + 200;
>
> -       /* Start burst */
> -       writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
> +       if (xfer->bits_per_word == 64) {
> +               int ret;
> +
> +               /* dma_burst_len 1 can't trigger a dma burst */
> +               if (xfer->len < 16)
> +                       return -EINVAL;
> +
> +               ret = meson_spicc_dma_map(spicc, xfer);
> +               if (ret) {
> +                       meson_spicc_dma_unmap(spicc, xfer);
> +                       dev_err(host->dev.parent, "dma map failed\n");
> +                       return ret;
> +               }
> +
> +               spicc->using_dma = true;
> +               spicc->xfer_remain = DIV_ROUND_UP(xfer->len, spicc->bytes_per_word);
> +               meson_spicc_setup_dma(spicc);
> +               writel_relaxed(SPICC_TE_EN, spicc->base + SPICC_INTREG);
> +               writel_bits_relaxed(SPICC_SMC, SPICC_SMC, spicc->base + SPICC_CONREG);
> +       } else {
> +               spicc->using_dma = false;
> +               /* Setup burst */
> +               meson_spicc_setup_burst(spicc);
>
> -       /* Enable interrupts */
> -       writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
> +               /* Start burst */
> +               writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
> +
> +               /* Enable interrupts */
> +               writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
> +       }
>
>         if (!wait_for_completion_timeout(&spicc->done, msecs_to_jiffies(timeout)))
>                 return -ETIMEDOUT;
> @@ -545,6 +740,14 @@ static int meson_spicc_setup(struct spi_device *spi)
>         if (!spi->controller_state)
>                 spi->controller_state = spi_controller_get_devdata(spi->controller);
>
> +       /* DMA works at 64 bits, the rest works on PIO */
> +       if (spi->bits_per_word != 8 &&
> +           spi->bits_per_word != 16 &&
> +           spi->bits_per_word != 24 &&
> +           spi->bits_per_word != 32 &&
> +           spi->bits_per_word != 64)
> +               return -EINVAL;
> +
>         return 0;
>  }
>
> @@ -853,10 +1056,6 @@ static int meson_spicc_probe(struct platform_device *pdev)
>         host->num_chipselect = 4;
>         host->dev.of_node = pdev->dev.of_node;
>         host->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LOOP;
> -       host->bits_per_word_mask = SPI_BPW_MASK(32) |
> -                                  SPI_BPW_MASK(24) |
> -                                  SPI_BPW_MASK(16) |
> -                                  SPI_BPW_MASK(8);

This should not be removed. SPI_BPW_MASK(64) needs to be added.
Removing bits_per_word_mask causes other code to assume this is an
8-bit only controller.



>         host->flags = (SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX);
>         host->min_speed_hz = spicc->data->min_speed_hz;
>         host->max_speed_hz = spicc->data->max_speed_hz;
>
> ---
> base-commit: 49807ed87851916ef655f72e9562f96355183090
> change-id: 20250408-spi-dma-c499f560d295
>
> Best regards,
> --
> Xianwei Zhao <xianwei.zhao@amlogic.com>
>
>
>
> _______________________________________________
> linux-amlogic mailing list
> linux-amlogic@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-amlogic
Da Xue April 30, 2025, 3:03 a.m. UTC | #3
On Mon, Apr 14, 2025 at 2:30 AM Xianwei Zhao via B4 Relay
<devnull+xianwei.zhao.amlogic.com@kernel.org> wrote:
>
> From: Xianwei Zhao <xianwei.zhao@amlogic.com>
>
> Add DMA support for spicc driver.
>
> DMA works if the transfer meets the following conditions:
> 1. 64 bits per word;
> 2. The transfer length must be multiples of the dma_burst_len,
>    and the dma_burst_len should be one of 8,7...2,
>    otherwise, it will be split into several SPI bursts.
>
> Signed-off-by: Sunny Luo <sunny.luo@amlogic.com>
> Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com>
> ---
> Changes in v2:
> - Make formatting adjustments and code optimizations according to Neil's suggestions.
> - Remove two special DMA trigger modes that are not fully implemented.
> - Link to v1: https://lore.kernel.org/r/20250408-spi-dma-v1-1-3c38be62c09c@amlogic.com
> ---
>  drivers/spi/spi-meson-spicc.c | 241 ++++++++++++++++++++++++++++++++++++++----
>  1 file changed, 220 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c
> index df74ad5060f8..6b9137307533 100644
> --- a/drivers/spi/spi-meson-spicc.c
> +++ b/drivers/spi/spi-meson-spicc.c
> @@ -21,18 +21,26 @@
>  #include <linux/interrupt.h>
>  #include <linux/reset.h>
>  #include <linux/pinctrl/consumer.h>
> +#include <linux/dma-mapping.h>
>
>  /*
> - * The Meson SPICC controller could support DMA based transfers, but is not
> - * implemented by the vendor code, and while having the registers documentation
> - * it has never worked on the GXL Hardware.
> - * The PIO mode is the only mode implemented, and due to badly designed HW :
> - * - all transfers are cutted in 16 words burst because the FIFO hangs on
> - *   TX underflow, and there is no TX "Half-Empty" interrupt, so we go by
> - *   FIFO max size chunk only
> - * - CS management is dumb, and goes UP between every burst, so is really a
> - *   "Data Valid" signal than a Chip Select, GPIO link should be used instead
> - *   to have a CS go down over the full transfer
> + * There are two modes for data transmission: PIO and DMA.
> + * When bits_per_word is 8, 16, 24, or 32, data is transferred using PIO mode.
> + * When bits_per_word is 64, DMA mode is used by default.
> + *
> + * DMA achieves a transfer with one or more SPI bursts, each SPI burst is made
> + * up of one or more DMA bursts. The DMA burst implementation mechanism is,
> + * For TX, when the number of words in TXFIFO is less than the preset
> + * reading threshold, SPICC starts a reading DMA burst, which reads the preset
> + * number of words from TX buffer, then writes them into TXFIFO.
> + * For RX, when the number of words in RXFIFO is greater than the preset
> + * writing threshold, SPICC starts a writing request burst, which reads the
> + * preset number of words from RXFIFO, then write them into RX buffer.
> + * DMA works if the transfer meets the following conditions,
> + * - 64 bits per word
> + * - The transfer length in word must be multiples of the dma_burst_len, and
> + *   the dma_burst_len should be one of 8,7...2, otherwise, it will be split
> + *   into several SPI bursts by this driver
>   */
>
>  #define SPICC_MAX_BURST        128
> @@ -128,6 +136,23 @@
>
>  #define SPICC_DWADDR   0x24    /* Write Address of DMA */
>
> +#define SPICC_LD_CNTL0 0x28
> +#define VSYNC_IRQ_SRC_SELECT           BIT(0)
> +#define DMA_EN_SET_BY_VSYNC            BIT(2)
> +#define XCH_EN_SET_BY_VSYNC            BIT(3)
> +#define DMA_READ_COUNTER_EN            BIT(4)
> +#define DMA_WRITE_COUNTER_EN           BIT(5)
> +#define DMA_RADDR_LOAD_BY_VSYNC                BIT(6)
> +#define DMA_WADDR_LOAD_BY_VSYNC                BIT(7)
> +#define DMA_ADDR_LOAD_FROM_LD_ADDR     BIT(8)
> +
> +#define SPICC_LD_CNTL1 0x2c
> +#define DMA_READ_COUNTER               GENMASK(15, 0)
> +#define DMA_WRITE_COUNTER              GENMASK(31, 16)
> +#define DMA_BURST_LEN_DEFAULT          8
> +#define DMA_BURST_COUNT_MAX            0xffff
> +#define SPI_BURST_LEN_MAX      (DMA_BURST_LEN_DEFAULT * DMA_BURST_COUNT_MAX)

The controller should also specify host->max_transfer_size.

> +
>  #define SPICC_ENH_CTL0 0x38    /* Enhanced Feature */
>  #define SPICC_ENH_CLK_CS_DELAY_MASK    GENMASK(15, 0)
>  #define SPICC_ENH_DATARATE_MASK                GENMASK(23, 16)
> @@ -171,6 +196,9 @@ struct meson_spicc_device {
>         struct pinctrl                  *pinctrl;
>         struct pinctrl_state            *pins_idle_high;
>         struct pinctrl_state            *pins_idle_low;
> +       dma_addr_t                      tx_dma;
> +       dma_addr_t                      rx_dma;
> +       bool                            using_dma;
>  };
>
>  #define pow2_clk_to_spicc(_div) container_of(_div, struct meson_spicc_device, pow2_div)
> @@ -202,6 +230,148 @@ static void meson_spicc_oen_enable(struct meson_spicc_device *spicc)
>         writel_relaxed(conf, spicc->base + SPICC_ENH_CTL0);
>  }
>
> +static int meson_spicc_dma_map(struct meson_spicc_device *spicc,
> +                              struct spi_transfer *t)
> +{
> +       struct device *dev = spicc->host->dev.parent;
> +
> +       if (!(t->tx_buf && t->rx_buf))
> +               return -EINVAL;
> +
> +       t->tx_dma = dma_map_single(dev, (void *)t->tx_buf, t->len, DMA_TO_DEVICE);
> +       if (dma_mapping_error(dev, t->tx_dma))
> +               return -ENOMEM;
> +
> +       t->rx_dma = dma_map_single(dev, t->rx_buf, t->len, DMA_FROM_DEVICE);
> +       if (dma_mapping_error(dev, t->rx_dma))
> +               return -ENOMEM;
> +
> +       spicc->tx_dma = t->tx_dma;
> +       spicc->rx_dma = t->rx_dma;
> +
> +       return 0;
> +}
> +
> +static void meson_spicc_dma_unmap(struct meson_spicc_device *spicc,
> +                                 struct spi_transfer *t)
> +{
> +       struct device *dev = spicc->host->dev.parent;
> +
> +       if (t->tx_dma)
> +               dma_unmap_single(dev, t->tx_dma, t->len, DMA_TO_DEVICE);
> +       if (t->rx_dma)
> +               dma_unmap_single(dev, t->rx_dma, t->len, DMA_FROM_DEVICE);
> +}
> +
> +/*
> + * According to the remain words length, calculate a suitable spi burst length
> + * and a dma burst length for current spi burst
> + */
> +static u32 meson_spicc_calc_dma_len(struct meson_spicc_device *spicc,
> +                                   u32 len, u32 *dma_burst_len)
> +{
> +       u32 i;
> +
> +       if (len <= spicc->data->fifo_size) {
> +               *dma_burst_len = len;
> +               return len;
> +       }
> +
> +       *dma_burst_len = DMA_BURST_LEN_DEFAULT;
> +
> +       if (len == (SPI_BURST_LEN_MAX + 1))
> +               return SPI_BURST_LEN_MAX - DMA_BURST_LEN_DEFAULT;
> +
> +       if (len >= SPI_BURST_LEN_MAX)
> +               return SPI_BURST_LEN_MAX;
> +
> +       for (i = DMA_BURST_LEN_DEFAULT; i > 1; i--)
> +               if ((len % i) == 0) {
> +                       *dma_burst_len = i;
> +                       return len;
> +               }
> +
> +       i = len % DMA_BURST_LEN_DEFAULT;
> +       len -= i;
> +
> +       if (i == 1)
> +               len -= DMA_BURST_LEN_DEFAULT;
> +
> +       return len;
> +}
> +
> +static void meson_spicc_setup_dma(struct meson_spicc_device *spicc)
> +{
> +       unsigned int len;
> +       unsigned int dma_burst_len, dma_burst_count;
> +       unsigned int count_en = 0;
> +       unsigned int txfifo_thres = 0;
> +       unsigned int read_req = 0;
> +       unsigned int rxfifo_thres = 31;
> +       unsigned int write_req = 0;
> +       unsigned int ld_ctr1 = 0;
> +
> +       writel_relaxed(spicc->tx_dma, spicc->base + SPICC_DRADDR);
> +       writel_relaxed(spicc->rx_dma, spicc->base + SPICC_DWADDR);
> +
> +       /* Set the max burst length to support a transmission with length of
> +        * no more than 1024 bytes(128 words), which must use the CS management
> +        * because of some strict timing requirements
> +        */
> +       writel_bits_relaxed(SPICC_BURSTLENGTH_MASK, SPICC_BURSTLENGTH_MASK,
> +                           spicc->base + SPICC_CONREG);
> +
> +       len = meson_spicc_calc_dma_len(spicc, spicc->xfer_remain,
> +                                      &dma_burst_len);
> +       spicc->xfer_remain -= len;
> +       dma_burst_count = DIV_ROUND_UP(len, dma_burst_len);
> +       dma_burst_len--;
> +
> +       if (spicc->tx_dma) {
> +               spicc->tx_dma += len;
> +               count_en |= DMA_READ_COUNTER_EN;
> +               txfifo_thres = spicc->data->fifo_size - dma_burst_len;
> +               read_req = dma_burst_len;
> +               ld_ctr1 |= FIELD_PREP(DMA_READ_COUNTER, dma_burst_count);
> +       }
> +
> +       if (spicc->rx_dma) {
> +               spicc->rx_dma += len;
> +               count_en |= DMA_WRITE_COUNTER_EN;
> +               rxfifo_thres = dma_burst_len;
> +               write_req = dma_burst_len;
> +               ld_ctr1 |= FIELD_PREP(DMA_WRITE_COUNTER, dma_burst_count);
> +       }
> +
> +       writel_relaxed(count_en, spicc->base + SPICC_LD_CNTL0);
> +       writel_relaxed(ld_ctr1, spicc->base + SPICC_LD_CNTL1);
> +       writel_relaxed(SPICC_DMA_ENABLE
> +                   | SPICC_DMA_URGENT
> +                   | FIELD_PREP(SPICC_TXFIFO_THRESHOLD_MASK, txfifo_thres)
> +                   | FIELD_PREP(SPICC_READ_BURST_MASK, read_req)
> +                   | FIELD_PREP(SPICC_RXFIFO_THRESHOLD_MASK, rxfifo_thres)
> +                   | FIELD_PREP(SPICC_WRITE_BURST_MASK, write_req),
> +                   spicc->base + SPICC_DMAREG);
> +}
> +
> +static irqreturn_t meson_spicc_dma_irq(struct meson_spicc_device *spicc)
> +{
> +       if (readl_relaxed(spicc->base + SPICC_DMAREG) & SPICC_DMA_ENABLE)
> +               return IRQ_HANDLED;
> +
> +       if (spicc->xfer_remain) {
> +               meson_spicc_setup_dma(spicc);
> +       } else {
> +               writel_bits_relaxed(SPICC_SMC, 0, spicc->base + SPICC_CONREG);
> +               writel_relaxed(0, spicc->base + SPICC_INTREG);
> +               writel_relaxed(0, spicc->base + SPICC_DMAREG);
> +               meson_spicc_dma_unmap(spicc, spicc->xfer);
> +               complete(&spicc->done);
> +       }
> +
> +       return IRQ_HANDLED;
> +}
> +
>  static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc)
>  {
>         return !!FIELD_GET(SPICC_TF,
> @@ -293,6 +463,9 @@ static irqreturn_t meson_spicc_irq(int irq, void *data)
>
>         writel_bits_relaxed(SPICC_TC, SPICC_TC, spicc->base + SPICC_STATREG);
>
> +       if (spicc->using_dma)
> +               return meson_spicc_dma_irq(spicc);
> +
>         /* Empty RX FIFO */
>         meson_spicc_rx(spicc);
>
> @@ -426,9 +599,6 @@ static int meson_spicc_transfer_one(struct spi_controller *host,
>
>         meson_spicc_reset_fifo(spicc);
>
> -       /* Setup burst */
> -       meson_spicc_setup_burst(spicc);
> -
>         /* Setup wait for completion */
>         reinit_completion(&spicc->done);
>
> @@ -442,11 +612,36 @@ static int meson_spicc_transfer_one(struct spi_controller *host,
>         /* Increase it twice and add 200 ms tolerance */
>         timeout += timeout + 200;
>
> -       /* Start burst */
> -       writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
> +       if (xfer->bits_per_word == 64) {
> +               int ret;
> +
> +               /* dma_burst_len 1 can't trigger a dma burst */
> +               if (xfer->len < 16)
> +                       return -EINVAL;
> +
> +               ret = meson_spicc_dma_map(spicc, xfer);
> +               if (ret) {
> +                       meson_spicc_dma_unmap(spicc, xfer);
> +                       dev_err(host->dev.parent, "dma map failed\n");
> +                       return ret;
> +               }
> +
> +               spicc->using_dma = true;
> +               spicc->xfer_remain = DIV_ROUND_UP(xfer->len, spicc->bytes_per_word);
> +               meson_spicc_setup_dma(spicc);
> +               writel_relaxed(SPICC_TE_EN, spicc->base + SPICC_INTREG);
> +               writel_bits_relaxed(SPICC_SMC, SPICC_SMC, spicc->base + SPICC_CONREG);
> +       } else {
> +               spicc->using_dma = false;
> +               /* Setup burst */
> +               meson_spicc_setup_burst(spicc);
>
> -       /* Enable interrupts */
> -       writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
> +               /* Start burst */
> +               writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
> +
> +               /* Enable interrupts */
> +               writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
> +       }
>
>         if (!wait_for_completion_timeout(&spicc->done, msecs_to_jiffies(timeout)))
>                 return -ETIMEDOUT;
> @@ -545,6 +740,14 @@ static int meson_spicc_setup(struct spi_device *spi)
>         if (!spi->controller_state)
>                 spi->controller_state = spi_controller_get_devdata(spi->controller);
>
> +       /* DMA works at 64 bits, the rest works on PIO */
> +       if (spi->bits_per_word != 8 &&
> +           spi->bits_per_word != 16 &&
> +           spi->bits_per_word != 24 &&
> +           spi->bits_per_word != 32 &&
> +           spi->bits_per_word != 64)
> +               return -EINVAL;
> +
>         return 0;
>  }
>
> @@ -853,10 +1056,6 @@ static int meson_spicc_probe(struct platform_device *pdev)
>         host->num_chipselect = 4;
>         host->dev.of_node = pdev->dev.of_node;
>         host->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LOOP;
> -       host->bits_per_word_mask = SPI_BPW_MASK(32) |
> -                                  SPI_BPW_MASK(24) |
> -                                  SPI_BPW_MASK(16) |
> -                                  SPI_BPW_MASK(8);
>         host->flags = (SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX);
>         host->min_speed_hz = spicc->data->min_speed_hz;
>         host->max_speed_hz = spicc->data->max_speed_hz;
>
> ---
> base-commit: 49807ed87851916ef655f72e9562f96355183090
> change-id: 20250408-spi-dma-c499f560d295
>
> Best regards,
> --
> Xianwei Zhao <xianwei.zhao@amlogic.com>
>
>
>
> _______________________________________________
> linux-amlogic mailing list
> linux-amlogic@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-amlogic
Neil Armstrong April 30, 2025, 7:43 a.m. UTC | #4
On 30/04/2025 04:13, Da Xue wrote:
> On Mon, Apr 14, 2025 at 2:30 AM Xianwei Zhao via B4 Relay
> <devnull+xianwei.zhao.amlogic.com@kernel.org> wrote:
>>
>> From: Xianwei Zhao <xianwei.zhao@amlogic.com>
>>
>> Add DMA support for spicc driver.
>>
>> DMA works if the transfer meets the following conditions:
>> 1. 64 bits per word;
>> 2. The transfer length must be multiples of the dma_burst_len,
>>     and the dma_burst_len should be one of 8,7...2,
>>     otherwise, it will be split into several SPI bursts.
>>
>> Signed-off-by: Sunny Luo <sunny.luo@amlogic.com>
>> Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com>
>> ---
>> Changes in v2:
>> - Make formatting adjustments and code optimizations according to Neil's suggestions.
>> - Remove two special DMA trigger modes that are not fully implemented.
>> - Link to v1: https://lore.kernel.org/r/20250408-spi-dma-v1-1-3c38be62c09c@amlogic.com
>> ---
>>   drivers/spi/spi-meson-spicc.c | 241 ++++++++++++++++++++++++++++++++++++++----
>>   1 file changed, 220 insertions(+), 21 deletions(-)
>>
>> diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c
>> index df74ad5060f8..6b9137307533 100644
>> --- a/drivers/spi/spi-meson-spicc.c
>> +++ b/drivers/spi/spi-meson-spicc.c
>> @@ -21,18 +21,26 @@
>>   #include <linux/interrupt.h>
>>   #include <linux/reset.h>
>>   #include <linux/pinctrl/consumer.h>
>> +#include <linux/dma-mapping.h>
>>
>>   /*
>> - * The Meson SPICC controller could support DMA based transfers, but is not
>> - * implemented by the vendor code, and while having the registers documentation
>> - * it has never worked on the GXL Hardware.
>> - * The PIO mode is the only mode implemented, and due to badly designed HW :
>> - * - all transfers are cutted in 16 words burst because the FIFO hangs on
>> - *   TX underflow, and there is no TX "Half-Empty" interrupt, so we go by
>> - *   FIFO max size chunk only
>> - * - CS management is dumb, and goes UP between every burst, so is really a
>> - *   "Data Valid" signal than a Chip Select, GPIO link should be used instead
>> - *   to have a CS go down over the full transfer
>> + * There are two modes for data transmission: PIO and DMA.
>> + * When bits_per_word is 8, 16, 24, or 32, data is transferred using PIO mode.
>> + * When bits_per_word is 64, DMA mode is used by default.
>> + *
>> + * DMA achieves a transfer with one or more SPI bursts, each SPI burst is made
>> + * up of one or more DMA bursts. The DMA burst implementation mechanism is,
>> + * For TX, when the number of words in TXFIFO is less than the preset
>> + * reading threshold, SPICC starts a reading DMA burst, which reads the preset
>> + * number of words from TX buffer, then writes them into TXFIFO.
>> + * For RX, when the number of words in RXFIFO is greater than the preset
>> + * writing threshold, SPICC starts a writing request burst, which reads the
>> + * preset number of words from RXFIFO, then write them into RX buffer.
>> + * DMA works if the transfer meets the following conditions,
>> + * - 64 bits per word
> 
> Just for clarification, DMA can only send 64-bit words due to hardware
> design, right?
> The bit-per-word in spi control register (CONREG) has no effect?
> 
>> + * - The transfer length in word must be multiples of the dma_burst_len, and
>> + *   the dma_burst_len should be one of 8,7...2, otherwise, it will be split
>> + *   into several SPI bursts by this driver
>>    */
>>
>>   #define SPICC_MAX_BURST        128
>> @@ -128,6 +136,23 @@
>>
>>   #define SPICC_DWADDR   0x24    /* Write Address of DMA */
>>
>> +#define SPICC_LD_CNTL0 0x28
>> +#define VSYNC_IRQ_SRC_SELECT           BIT(0)
>> +#define DMA_EN_SET_BY_VSYNC            BIT(2)
>> +#define XCH_EN_SET_BY_VSYNC            BIT(3)
>> +#define DMA_READ_COUNTER_EN            BIT(4)
>> +#define DMA_WRITE_COUNTER_EN           BIT(5)
>> +#define DMA_RADDR_LOAD_BY_VSYNC                BIT(6)
>> +#define DMA_WADDR_LOAD_BY_VSYNC                BIT(7)
>> +#define DMA_ADDR_LOAD_FROM_LD_ADDR     BIT(8)
>> +
>> +#define SPICC_LD_CNTL1 0x2c
>> +#define DMA_READ_COUNTER               GENMASK(15, 0)
>> +#define DMA_WRITE_COUNTER              GENMASK(31, 16)
>> +#define DMA_BURST_LEN_DEFAULT          8
>> +#define DMA_BURST_COUNT_MAX            0xffff
>> +#define SPI_BURST_LEN_MAX      (DMA_BURST_LEN_DEFAULT * DMA_BURST_COUNT_MAX)
>> +
> 
> LD_CNTL0 and LD_CNTL1 are not in this datasheet for GXL
> (S805X/S905X/S905D). Do they exist on these SoCs and are not
> documented?
> 
>>   #define SPICC_ENH_CTL0 0x38    /* Enhanced Feature */
>>   #define SPICC_ENH_CLK_CS_DELAY_MASK    GENMASK(15, 0)
>>   #define SPICC_ENH_DATARATE_MASK                GENMASK(23, 16)
>> @@ -171,6 +196,9 @@ struct meson_spicc_device {
>>          struct pinctrl                  *pinctrl;
>>          struct pinctrl_state            *pins_idle_high;
>>          struct pinctrl_state            *pins_idle_low;
>> +       dma_addr_t                      tx_dma;
>> +       dma_addr_t                      rx_dma;
>> +       bool                            using_dma;
>>   };
>>
>>   #define pow2_clk_to_spicc(_div) container_of(_div, struct meson_spicc_device, pow2_div)
>> @@ -202,6 +230,148 @@ static void meson_spicc_oen_enable(struct meson_spicc_device *spicc)
>>          writel_relaxed(conf, spicc->base + SPICC_ENH_CTL0);
>>   }
>>
>> +static int meson_spicc_dma_map(struct meson_spicc_device *spicc,
>> +                              struct spi_transfer *t)
>> +{
>> +       struct device *dev = spicc->host->dev.parent;
>> +
>> +       if (!(t->tx_buf && t->rx_buf))
>> +               return -EINVAL;
>> +
>> +       t->tx_dma = dma_map_single(dev, (void *)t->tx_buf, t->len, DMA_TO_DEVICE);
>> +       if (dma_mapping_error(dev, t->tx_dma))
>> +               return -ENOMEM;
>> +
>> +       t->rx_dma = dma_map_single(dev, t->rx_buf, t->len, DMA_FROM_DEVICE);
>> +       if (dma_mapping_error(dev, t->rx_dma))
>> +               return -ENOMEM;
>> +
>> +       spicc->tx_dma = t->tx_dma;
>> +       spicc->rx_dma = t->rx_dma;
>> +
>> +       return 0;
>> +}
>> +
>> +static void meson_spicc_dma_unmap(struct meson_spicc_device *spicc,
>> +                                 struct spi_transfer *t)
>> +{
>> +       struct device *dev = spicc->host->dev.parent;
>> +
>> +       if (t->tx_dma)
>> +               dma_unmap_single(dev, t->tx_dma, t->len, DMA_TO_DEVICE);
>> +       if (t->rx_dma)
>> +               dma_unmap_single(dev, t->rx_dma, t->len, DMA_FROM_DEVICE);
>> +}
>> +
>> +/*
>> + * According to the remain words length, calculate a suitable spi burst length
>> + * and a dma burst length for current spi burst
>> + */
>> +static u32 meson_spicc_calc_dma_len(struct meson_spicc_device *spicc,
>> +                                   u32 len, u32 *dma_burst_len)
>> +{
>> +       u32 i;
>> +
>> +       if (len <= spicc->data->fifo_size) {
>> +               *dma_burst_len = len;
>> +               return len;
>> +       }
>> +
>> +       *dma_burst_len = DMA_BURST_LEN_DEFAULT;
>> +
>> +       if (len == (SPI_BURST_LEN_MAX + 1))
>> +               return SPI_BURST_LEN_MAX - DMA_BURST_LEN_DEFAULT;
>> +
>> +       if (len >= SPI_BURST_LEN_MAX)
>> +               return SPI_BURST_LEN_MAX;
>> +
>> +       for (i = DMA_BURST_LEN_DEFAULT; i > 1; i--)
>> +               if ((len % i) == 0) {
>> +                       *dma_burst_len = i;
>> +                       return len;
>> +               }
>> +
>> +       i = len % DMA_BURST_LEN_DEFAULT;
>> +       len -= i;
>> +
>> +       if (i == 1)
>> +               len -= DMA_BURST_LEN_DEFAULT;
>> +
>> +       return len;
>> +}
>> +
>> +static void meson_spicc_setup_dma(struct meson_spicc_device *spicc)
>> +{
>> +       unsigned int len;
>> +       unsigned int dma_burst_len, dma_burst_count;
>> +       unsigned int count_en = 0;
>> +       unsigned int txfifo_thres = 0;
>> +       unsigned int read_req = 0;
>> +       unsigned int rxfifo_thres = 31;
>> +       unsigned int write_req = 0;
>> +       unsigned int ld_ctr1 = 0;
>> +
>> +       writel_relaxed(spicc->tx_dma, spicc->base + SPICC_DRADDR);
>> +       writel_relaxed(spicc->rx_dma, spicc->base + SPICC_DWADDR);
>> +
>> +       /* Set the max burst length to support a transmission with length of
>> +        * no more than 1024 bytes(128 words), which must use the CS management
>> +        * because of some strict timing requirements
>> +        */
>> +       writel_bits_relaxed(SPICC_BURSTLENGTH_MASK, SPICC_BURSTLENGTH_MASK,
>> +                           spicc->base + SPICC_CONREG);
>> +
>> +       len = meson_spicc_calc_dma_len(spicc, spicc->xfer_remain,
>> +                                      &dma_burst_len);
>> +       spicc->xfer_remain -= len;
>> +       dma_burst_count = DIV_ROUND_UP(len, dma_burst_len);
>> +       dma_burst_len--;
>> +
>> +       if (spicc->tx_dma) {
>> +               spicc->tx_dma += len;
>> +               count_en |= DMA_READ_COUNTER_EN;
>> +               txfifo_thres = spicc->data->fifo_size - dma_burst_len;
>> +               read_req = dma_burst_len;
>> +               ld_ctr1 |= FIELD_PREP(DMA_READ_COUNTER, dma_burst_count);
>> +       }
>> +
>> +       if (spicc->rx_dma) {
>> +               spicc->rx_dma += len;
>> +               count_en |= DMA_WRITE_COUNTER_EN;
>> +               rxfifo_thres = dma_burst_len;
>> +               write_req = dma_burst_len;
>> +               ld_ctr1 |= FIELD_PREP(DMA_WRITE_COUNTER, dma_burst_count);
>> +       }
>> +
>> +       writel_relaxed(count_en, spicc->base + SPICC_LD_CNTL0);
>> +       writel_relaxed(ld_ctr1, spicc->base + SPICC_LD_CNTL1);
>> +       writel_relaxed(SPICC_DMA_ENABLE
>> +                   | SPICC_DMA_URGENT
>> +                   | FIELD_PREP(SPICC_TXFIFO_THRESHOLD_MASK, txfifo_thres)
>> +                   | FIELD_PREP(SPICC_READ_BURST_MASK, read_req)
>> +                   | FIELD_PREP(SPICC_RXFIFO_THRESHOLD_MASK, rxfifo_thres)
>> +                   | FIELD_PREP(SPICC_WRITE_BURST_MASK, write_req),
>> +                   spicc->base + SPICC_DMAREG);
>> +}
>> +
>> +static irqreturn_t meson_spicc_dma_irq(struct meson_spicc_device *spicc)
>> +{
>> +       if (readl_relaxed(spicc->base + SPICC_DMAREG) & SPICC_DMA_ENABLE)
>> +               return IRQ_HANDLED;
>> +
>> +       if (spicc->xfer_remain) {
>> +               meson_spicc_setup_dma(spicc);
>> +       } else {
>> +               writel_bits_relaxed(SPICC_SMC, 0, spicc->base + SPICC_CONREG);
>> +               writel_relaxed(0, spicc->base + SPICC_INTREG);
>> +               writel_relaxed(0, spicc->base + SPICC_DMAREG);
>> +               meson_spicc_dma_unmap(spicc, spicc->xfer);
>> +               complete(&spicc->done);
>> +       }
>> +
>> +       return IRQ_HANDLED;
>> +}
>> +
>>   static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc)
>>   {
>>          return !!FIELD_GET(SPICC_TF,
>> @@ -293,6 +463,9 @@ static irqreturn_t meson_spicc_irq(int irq, void *data)
>>
>>          writel_bits_relaxed(SPICC_TC, SPICC_TC, spicc->base + SPICC_STATREG);
>>
>> +       if (spicc->using_dma)
>> +               return meson_spicc_dma_irq(spicc);
>> +
>>          /* Empty RX FIFO */
>>          meson_spicc_rx(spicc);
>>
>> @@ -426,9 +599,6 @@ static int meson_spicc_transfer_one(struct spi_controller *host,
>>
>>          meson_spicc_reset_fifo(spicc);
>>
>> -       /* Setup burst */
>> -       meson_spicc_setup_burst(spicc);
>> -
>>          /* Setup wait for completion */
>>          reinit_completion(&spicc->done);
>>
>> @@ -442,11 +612,36 @@ static int meson_spicc_transfer_one(struct spi_controller *host,
>>          /* Increase it twice and add 200 ms tolerance */
>>          timeout += timeout + 200;
>>
>> -       /* Start burst */
>> -       writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
>> +       if (xfer->bits_per_word == 64) {
>> +               int ret;
>> +
>> +               /* dma_burst_len 1 can't trigger a dma burst */
>> +               if (xfer->len < 16)
>> +                       return -EINVAL;
>> +
>> +               ret = meson_spicc_dma_map(spicc, xfer);
>> +               if (ret) {
>> +                       meson_spicc_dma_unmap(spicc, xfer);
>> +                       dev_err(host->dev.parent, "dma map failed\n");
>> +                       return ret;
>> +               }
>> +
>> +               spicc->using_dma = true;
>> +               spicc->xfer_remain = DIV_ROUND_UP(xfer->len, spicc->bytes_per_word);
>> +               meson_spicc_setup_dma(spicc);
>> +               writel_relaxed(SPICC_TE_EN, spicc->base + SPICC_INTREG);
>> +               writel_bits_relaxed(SPICC_SMC, SPICC_SMC, spicc->base + SPICC_CONREG);
>> +       } else {
>> +               spicc->using_dma = false;
>> +               /* Setup burst */
>> +               meson_spicc_setup_burst(spicc);
>>
>> -       /* Enable interrupts */
>> -       writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
>> +               /* Start burst */
>> +               writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
>> +
>> +               /* Enable interrupts */
>> +               writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
>> +       }
>>
>>          if (!wait_for_completion_timeout(&spicc->done, msecs_to_jiffies(timeout)))
>>                  return -ETIMEDOUT;
>> @@ -545,6 +740,14 @@ static int meson_spicc_setup(struct spi_device *spi)
>>          if (!spi->controller_state)
>>                  spi->controller_state = spi_controller_get_devdata(spi->controller);
>>
>> +       /* DMA works at 64 bits, the rest works on PIO */
>> +       if (spi->bits_per_word != 8 &&
>> +           spi->bits_per_word != 16 &&
>> +           spi->bits_per_word != 24 &&
>> +           spi->bits_per_word != 32 &&
>> +           spi->bits_per_word != 64)
>> +               return -EINVAL;
>> +
>>          return 0;
>>   }
>>
>> @@ -853,10 +1056,6 @@ static int meson_spicc_probe(struct platform_device *pdev)
>>          host->num_chipselect = 4;
>>          host->dev.of_node = pdev->dev.of_node;
>>          host->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LOOP;
>> -       host->bits_per_word_mask = SPI_BPW_MASK(32) |
>> -                                  SPI_BPW_MASK(24) |
>> -                                  SPI_BPW_MASK(16) |
>> -                                  SPI_BPW_MASK(8);
> 
> This should not be removed. SPI_BPW_MASK(64) needs to be added.
> Removing bits_per_word_mask causes other code to assume this is an
> 8-bit only controller.

SPI_BPW_MASK(64) doesn't not exist, it's only a 32bit field, removing it is fine,
the check is done later.

Neil

> 
> 
> 
>>          host->flags = (SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX);
>>          host->min_speed_hz = spicc->data->min_speed_hz;
>>          host->max_speed_hz = spicc->data->max_speed_hz;
>>
>> ---
>> base-commit: 49807ed87851916ef655f72e9562f96355183090
>> change-id: 20250408-spi-dma-c499f560d295
>>
>> Best regards,
>> --
>> Xianwei Zhao <xianwei.zhao@amlogic.com>
>>
>>
>>
>> _______________________________________________
>> linux-amlogic mailing list
>> linux-amlogic@lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-amlogic
Da Xue April 30, 2025, 1:33 p.m. UTC | #5
On Wed, Apr 30, 2025 at 3:43 AM Neil Armstrong
<neil.armstrong@linaro.org> wrote:
>
> On 30/04/2025 04:13, Da Xue wrote:
> > On Mon, Apr 14, 2025 at 2:30 AM Xianwei Zhao via B4 Relay
> > <devnull+xianwei.zhao.amlogic.com@kernel.org> wrote:
> >>
> >> From: Xianwei Zhao <xianwei.zhao@amlogic.com>
> >>
> >> Add DMA support for spicc driver.
> >>
> >> DMA works if the transfer meets the following conditions:
> >> 1. 64 bits per word;
> >> 2. The transfer length must be multiples of the dma_burst_len,
> >>     and the dma_burst_len should be one of 8,7...2,
> >>     otherwise, it will be split into several SPI bursts.
> >>
> >> Signed-off-by: Sunny Luo <sunny.luo@amlogic.com>
> >> Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com>
> >> ---
> >> Changes in v2:
> >> - Make formatting adjustments and code optimizations according to Neil's suggestions.
> >> - Remove two special DMA trigger modes that are not fully implemented.
> >> - Link to v1: https://lore.kernel.org/r/20250408-spi-dma-v1-1-3c38be62c09c@amlogic.com
> >> ---
> >>   drivers/spi/spi-meson-spicc.c | 241 ++++++++++++++++++++++++++++++++++++++----
> >>   1 file changed, 220 insertions(+), 21 deletions(-)
> >>
> >> diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c
> >> index df74ad5060f8..6b9137307533 100644
> >> --- a/drivers/spi/spi-meson-spicc.c
> >> +++ b/drivers/spi/spi-meson-spicc.c
> >> @@ -21,18 +21,26 @@
> >>   #include <linux/interrupt.h>
> >>   #include <linux/reset.h>
> >>   #include <linux/pinctrl/consumer.h>
> >> +#include <linux/dma-mapping.h>
> >>
> >>   /*
> >> - * The Meson SPICC controller could support DMA based transfers, but is not
> >> - * implemented by the vendor code, and while having the registers documentation
> >> - * it has never worked on the GXL Hardware.
> >> - * The PIO mode is the only mode implemented, and due to badly designed HW :
> >> - * - all transfers are cutted in 16 words burst because the FIFO hangs on
> >> - *   TX underflow, and there is no TX "Half-Empty" interrupt, so we go by
> >> - *   FIFO max size chunk only
> >> - * - CS management is dumb, and goes UP between every burst, so is really a
> >> - *   "Data Valid" signal than a Chip Select, GPIO link should be used instead
> >> - *   to have a CS go down over the full transfer
> >> + * There are two modes for data transmission: PIO and DMA.
> >> + * When bits_per_word is 8, 16, 24, or 32, data is transferred using PIO mode.
> >> + * When bits_per_word is 64, DMA mode is used by default.
> >> + *
> >> + * DMA achieves a transfer with one or more SPI bursts, each SPI burst is made
> >> + * up of one or more DMA bursts. The DMA burst implementation mechanism is,
> >> + * For TX, when the number of words in TXFIFO is less than the preset
> >> + * reading threshold, SPICC starts a reading DMA burst, which reads the preset
> >> + * number of words from TX buffer, then writes them into TXFIFO.
> >> + * For RX, when the number of words in RXFIFO is greater than the preset
> >> + * writing threshold, SPICC starts a writing request burst, which reads the
> >> + * preset number of words from RXFIFO, then write them into RX buffer.
> >> + * DMA works if the transfer meets the following conditions,
> >> + * - 64 bits per word
> >
> > Just for clarification, DMA can only send 64-bit words due to hardware
> > design, right?
> > The bit-per-word in spi control register (CONREG) has no effect?
> >
> >> + * - The transfer length in word must be multiples of the dma_burst_len, and
> >> + *   the dma_burst_len should be one of 8,7...2, otherwise, it will be split
> >> + *   into several SPI bursts by this driver
> >>    */
> >>
> >>   #define SPICC_MAX_BURST        128
> >> @@ -128,6 +136,23 @@
> >>
> >>   #define SPICC_DWADDR   0x24    /* Write Address of DMA */
> >>
> >> +#define SPICC_LD_CNTL0 0x28
> >> +#define VSYNC_IRQ_SRC_SELECT           BIT(0)
> >> +#define DMA_EN_SET_BY_VSYNC            BIT(2)
> >> +#define XCH_EN_SET_BY_VSYNC            BIT(3)
> >> +#define DMA_READ_COUNTER_EN            BIT(4)
> >> +#define DMA_WRITE_COUNTER_EN           BIT(5)
> >> +#define DMA_RADDR_LOAD_BY_VSYNC                BIT(6)
> >> +#define DMA_WADDR_LOAD_BY_VSYNC                BIT(7)
> >> +#define DMA_ADDR_LOAD_FROM_LD_ADDR     BIT(8)
> >> +
> >> +#define SPICC_LD_CNTL1 0x2c
> >> +#define DMA_READ_COUNTER               GENMASK(15, 0)
> >> +#define DMA_WRITE_COUNTER              GENMASK(31, 16)
> >> +#define DMA_BURST_LEN_DEFAULT          8
> >> +#define DMA_BURST_COUNT_MAX            0xffff
> >> +#define SPI_BURST_LEN_MAX      (DMA_BURST_LEN_DEFAULT * DMA_BURST_COUNT_MAX)
> >> +
> >
> > LD_CNTL0 and LD_CNTL1 are not in this datasheet for GXL
> > (S805X/S905X/S905D). Do they exist on these SoCs and are not
> > documented?
> >
> >>   #define SPICC_ENH_CTL0 0x38    /* Enhanced Feature */
> >>   #define SPICC_ENH_CLK_CS_DELAY_MASK    GENMASK(15, 0)
> >>   #define SPICC_ENH_DATARATE_MASK                GENMASK(23, 16)
> >> @@ -171,6 +196,9 @@ struct meson_spicc_device {
> >>          struct pinctrl                  *pinctrl;
> >>          struct pinctrl_state            *pins_idle_high;
> >>          struct pinctrl_state            *pins_idle_low;
> >> +       dma_addr_t                      tx_dma;
> >> +       dma_addr_t                      rx_dma;
> >> +       bool                            using_dma;
> >>   };
> >>
> >>   #define pow2_clk_to_spicc(_div) container_of(_div, struct meson_spicc_device, pow2_div)
> >> @@ -202,6 +230,148 @@ static void meson_spicc_oen_enable(struct meson_spicc_device *spicc)
> >>          writel_relaxed(conf, spicc->base + SPICC_ENH_CTL0);
> >>   }
> >>
> >> +static int meson_spicc_dma_map(struct meson_spicc_device *spicc,
> >> +                              struct spi_transfer *t)
> >> +{
> >> +       struct device *dev = spicc->host->dev.parent;
> >> +
> >> +       if (!(t->tx_buf && t->rx_buf))
> >> +               return -EINVAL;
> >> +
> >> +       t->tx_dma = dma_map_single(dev, (void *)t->tx_buf, t->len, DMA_TO_DEVICE);
> >> +       if (dma_mapping_error(dev, t->tx_dma))
> >> +               return -ENOMEM;
> >> +
> >> +       t->rx_dma = dma_map_single(dev, t->rx_buf, t->len, DMA_FROM_DEVICE);
> >> +       if (dma_mapping_error(dev, t->rx_dma))
> >> +               return -ENOMEM;
> >> +
> >> +       spicc->tx_dma = t->tx_dma;
> >> +       spicc->rx_dma = t->rx_dma;
> >> +
> >> +       return 0;
> >> +}
> >> +
> >> +static void meson_spicc_dma_unmap(struct meson_spicc_device *spicc,
> >> +                                 struct spi_transfer *t)
> >> +{
> >> +       struct device *dev = spicc->host->dev.parent;
> >> +
> >> +       if (t->tx_dma)
> >> +               dma_unmap_single(dev, t->tx_dma, t->len, DMA_TO_DEVICE);
> >> +       if (t->rx_dma)
> >> +               dma_unmap_single(dev, t->rx_dma, t->len, DMA_FROM_DEVICE);
> >> +}
> >> +
> >> +/*
> >> + * According to the remain words length, calculate a suitable spi burst length
> >> + * and a dma burst length for current spi burst
> >> + */
> >> +static u32 meson_spicc_calc_dma_len(struct meson_spicc_device *spicc,
> >> +                                   u32 len, u32 *dma_burst_len)
> >> +{
> >> +       u32 i;
> >> +
> >> +       if (len <= spicc->data->fifo_size) {
> >> +               *dma_burst_len = len;
> >> +               return len;
> >> +       }
> >> +
> >> +       *dma_burst_len = DMA_BURST_LEN_DEFAULT;
> >> +
> >> +       if (len == (SPI_BURST_LEN_MAX + 1))
> >> +               return SPI_BURST_LEN_MAX - DMA_BURST_LEN_DEFAULT;
> >> +
> >> +       if (len >= SPI_BURST_LEN_MAX)
> >> +               return SPI_BURST_LEN_MAX;
> >> +
> >> +       for (i = DMA_BURST_LEN_DEFAULT; i > 1; i--)
> >> +               if ((len % i) == 0) {
> >> +                       *dma_burst_len = i;
> >> +                       return len;
> >> +               }
> >> +
> >> +       i = len % DMA_BURST_LEN_DEFAULT;
> >> +       len -= i;
> >> +
> >> +       if (i == 1)
> >> +               len -= DMA_BURST_LEN_DEFAULT;
> >> +
> >> +       return len;
> >> +}
> >> +
> >> +static void meson_spicc_setup_dma(struct meson_spicc_device *spicc)
> >> +{
> >> +       unsigned int len;
> >> +       unsigned int dma_burst_len, dma_burst_count;
> >> +       unsigned int count_en = 0;
> >> +       unsigned int txfifo_thres = 0;
> >> +       unsigned int read_req = 0;
> >> +       unsigned int rxfifo_thres = 31;
> >> +       unsigned int write_req = 0;
> >> +       unsigned int ld_ctr1 = 0;
> >> +
> >> +       writel_relaxed(spicc->tx_dma, spicc->base + SPICC_DRADDR);
> >> +       writel_relaxed(spicc->rx_dma, spicc->base + SPICC_DWADDR);
> >> +
> >> +       /* Set the max burst length to support a transmission with length of
> >> +        * no more than 1024 bytes(128 words), which must use the CS management
> >> +        * because of some strict timing requirements
> >> +        */
> >> +       writel_bits_relaxed(SPICC_BURSTLENGTH_MASK, SPICC_BURSTLENGTH_MASK,
> >> +                           spicc->base + SPICC_CONREG);
> >> +
> >> +       len = meson_spicc_calc_dma_len(spicc, spicc->xfer_remain,
> >> +                                      &dma_burst_len);
> >> +       spicc->xfer_remain -= len;
> >> +       dma_burst_count = DIV_ROUND_UP(len, dma_burst_len);
> >> +       dma_burst_len--;
> >> +
> >> +       if (spicc->tx_dma) {
> >> +               spicc->tx_dma += len;
> >> +               count_en |= DMA_READ_COUNTER_EN;
> >> +               txfifo_thres = spicc->data->fifo_size - dma_burst_len;
> >> +               read_req = dma_burst_len;
> >> +               ld_ctr1 |= FIELD_PREP(DMA_READ_COUNTER, dma_burst_count);
> >> +       }
> >> +
> >> +       if (spicc->rx_dma) {
> >> +               spicc->rx_dma += len;
> >> +               count_en |= DMA_WRITE_COUNTER_EN;
> >> +               rxfifo_thres = dma_burst_len;
> >> +               write_req = dma_burst_len;
> >> +               ld_ctr1 |= FIELD_PREP(DMA_WRITE_COUNTER, dma_burst_count);
> >> +       }
> >> +
> >> +       writel_relaxed(count_en, spicc->base + SPICC_LD_CNTL0);
> >> +       writel_relaxed(ld_ctr1, spicc->base + SPICC_LD_CNTL1);
> >> +       writel_relaxed(SPICC_DMA_ENABLE
> >> +                   | SPICC_DMA_URGENT
> >> +                   | FIELD_PREP(SPICC_TXFIFO_THRESHOLD_MASK, txfifo_thres)
> >> +                   | FIELD_PREP(SPICC_READ_BURST_MASK, read_req)
> >> +                   | FIELD_PREP(SPICC_RXFIFO_THRESHOLD_MASK, rxfifo_thres)
> >> +                   | FIELD_PREP(SPICC_WRITE_BURST_MASK, write_req),
> >> +                   spicc->base + SPICC_DMAREG);
> >> +}
> >> +
> >> +static irqreturn_t meson_spicc_dma_irq(struct meson_spicc_device *spicc)
> >> +{
> >> +       if (readl_relaxed(spicc->base + SPICC_DMAREG) & SPICC_DMA_ENABLE)
> >> +               return IRQ_HANDLED;
> >> +
> >> +       if (spicc->xfer_remain) {
> >> +               meson_spicc_setup_dma(spicc);
> >> +       } else {
> >> +               writel_bits_relaxed(SPICC_SMC, 0, spicc->base + SPICC_CONREG);
> >> +               writel_relaxed(0, spicc->base + SPICC_INTREG);
> >> +               writel_relaxed(0, spicc->base + SPICC_DMAREG);
> >> +               meson_spicc_dma_unmap(spicc, spicc->xfer);
> >> +               complete(&spicc->done);
> >> +       }
> >> +
> >> +       return IRQ_HANDLED;
> >> +}
> >> +
> >>   static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc)
> >>   {
> >>          return !!FIELD_GET(SPICC_TF,
> >> @@ -293,6 +463,9 @@ static irqreturn_t meson_spicc_irq(int irq, void *data)
> >>
> >>          writel_bits_relaxed(SPICC_TC, SPICC_TC, spicc->base + SPICC_STATREG);
> >>
> >> +       if (spicc->using_dma)
> >> +               return meson_spicc_dma_irq(spicc);
> >> +
> >>          /* Empty RX FIFO */
> >>          meson_spicc_rx(spicc);
> >>
> >> @@ -426,9 +599,6 @@ static int meson_spicc_transfer_one(struct spi_controller *host,
> >>
> >>          meson_spicc_reset_fifo(spicc);
> >>
> >> -       /* Setup burst */
> >> -       meson_spicc_setup_burst(spicc);
> >> -
> >>          /* Setup wait for completion */
> >>          reinit_completion(&spicc->done);
> >>
> >> @@ -442,11 +612,36 @@ static int meson_spicc_transfer_one(struct spi_controller *host,
> >>          /* Increase it twice and add 200 ms tolerance */
> >>          timeout += timeout + 200;
> >>
> >> -       /* Start burst */
> >> -       writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
> >> +       if (xfer->bits_per_word == 64) {
> >> +               int ret;
> >> +
> >> +               /* dma_burst_len 1 can't trigger a dma burst */
> >> +               if (xfer->len < 16)
> >> +                       return -EINVAL;
> >> +
> >> +               ret = meson_spicc_dma_map(spicc, xfer);
> >> +               if (ret) {
> >> +                       meson_spicc_dma_unmap(spicc, xfer);
> >> +                       dev_err(host->dev.parent, "dma map failed\n");
> >> +                       return ret;
> >> +               }
> >> +
> >> +               spicc->using_dma = true;
> >> +               spicc->xfer_remain = DIV_ROUND_UP(xfer->len, spicc->bytes_per_word);
> >> +               meson_spicc_setup_dma(spicc);
> >> +               writel_relaxed(SPICC_TE_EN, spicc->base + SPICC_INTREG);
> >> +               writel_bits_relaxed(SPICC_SMC, SPICC_SMC, spicc->base + SPICC_CONREG);
> >> +       } else {
> >> +               spicc->using_dma = false;
> >> +               /* Setup burst */
> >> +               meson_spicc_setup_burst(spicc);
> >>
> >> -       /* Enable interrupts */
> >> -       writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
> >> +               /* Start burst */
> >> +               writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
> >> +
> >> +               /* Enable interrupts */
> >> +               writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
> >> +       }
> >>
> >>          if (!wait_for_completion_timeout(&spicc->done, msecs_to_jiffies(timeout)))
> >>                  return -ETIMEDOUT;
> >> @@ -545,6 +740,14 @@ static int meson_spicc_setup(struct spi_device *spi)
> >>          if (!spi->controller_state)
> >>                  spi->controller_state = spi_controller_get_devdata(spi->controller);
> >>
> >> +       /* DMA works at 64 bits, the rest works on PIO */
> >> +       if (spi->bits_per_word != 8 &&
> >> +           spi->bits_per_word != 16 &&
> >> +           spi->bits_per_word != 24 &&
> >> +           spi->bits_per_word != 32 &&
> >> +           spi->bits_per_word != 64)
> >> +               return -EINVAL;
> >> +
> >>          return 0;
> >>   }
> >>
> >> @@ -853,10 +1056,6 @@ static int meson_spicc_probe(struct platform_device *pdev)
> >>          host->num_chipselect = 4;
> >>          host->dev.of_node = pdev->dev.of_node;
> >>          host->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LOOP;
> >> -       host->bits_per_word_mask = SPI_BPW_MASK(32) |
> >> -                                  SPI_BPW_MASK(24) |
> >> -                                  SPI_BPW_MASK(16) |
> >> -                                  SPI_BPW_MASK(8);
> >
> > This should not be removed. SPI_BPW_MASK(64) needs to be added.
> > Removing bits_per_word_mask causes other code to assume this is an
> > 8-bit only controller.
>
> SPI_BPW_MASK(64) doesn't not exist, it's only a 32bit field, removing it is fine,
> the check is done later.

The spi_is_bpw_supported function in include/linux/spi/spi.h checks
bits_per_word_mask.
Without this set, drm_mipi_dbi behavior is changed to 8-bit transfers.

Yeah, the 64 won't work. I didn't check the macro.

>
> Neil
>
> >
> >
> >
> >>          host->flags = (SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX);
> >>          host->min_speed_hz = spicc->data->min_speed_hz;
> >>          host->max_speed_hz = spicc->data->max_speed_hz;
> >>
> >> ---
> >> base-commit: 49807ed87851916ef655f72e9562f96355183090
> >> change-id: 20250408-spi-dma-c499f560d295
> >>
> >> Best regards,
> >> --
> >> Xianwei Zhao <xianwei.zhao@amlogic.com>
> >>
> >>
> >>
> >> _______________________________________________
> >> linux-amlogic mailing list
> >> linux-amlogic@lists.infradead.org
> >> http://lists.infradead.org/mailman/listinfo/linux-amlogic
>
Da Xue May 1, 2025, 7:34 p.m. UTC | #6
On Wed, Apr 30, 2025 at 9:33 AM Da Xue <da@lessconfused.com> wrote:
>
> On Wed, Apr 30, 2025 at 3:43 AM Neil Armstrong
> <neil.armstrong@linaro.org> wrote:
> >
> > On 30/04/2025 04:13, Da Xue wrote:
> > > On Mon, Apr 14, 2025 at 2:30 AM Xianwei Zhao via B4 Relay
> > > <devnull+xianwei.zhao.amlogic.com@kernel.org> wrote:
> > >>
> > >> From: Xianwei Zhao <xianwei.zhao@amlogic.com>
> > >>
> > >> Add DMA support for spicc driver.
> > >>
> > >> DMA works if the transfer meets the following conditions:
> > >> 1. 64 bits per word;
> > >> 2. The transfer length must be multiples of the dma_burst_len,
> > >>     and the dma_burst_len should be one of 8,7...2,
> > >>     otherwise, it will be split into several SPI bursts.
> > >>
> > >> Signed-off-by: Sunny Luo <sunny.luo@amlogic.com>
> > >> Signed-off-by: Xianwei Zhao <xianwei.zhao@amlogic.com>
> > >> ---
> > >> Changes in v2:
> > >> - Make formatting adjustments and code optimizations according to Neil's suggestions.
> > >> - Remove two special DMA trigger modes that are not fully implemented.
> > >> - Link to v1: https://lore.kernel.org/r/20250408-spi-dma-v1-1-3c38be62c09c@amlogic.com
> > >> ---
> > >>   drivers/spi/spi-meson-spicc.c | 241 ++++++++++++++++++++++++++++++++++++++----
> > >>   1 file changed, 220 insertions(+), 21 deletions(-)
> > >>
> > >> diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c
> > >> index df74ad5060f8..6b9137307533 100644
> > >> --- a/drivers/spi/spi-meson-spicc.c
> > >> +++ b/drivers/spi/spi-meson-spicc.c
> > >> @@ -21,18 +21,26 @@
> > >>   #include <linux/interrupt.h>
> > >>   #include <linux/reset.h>
> > >>   #include <linux/pinctrl/consumer.h>
> > >> +#include <linux/dma-mapping.h>
> > >>
> > >>   /*
> > >> - * The Meson SPICC controller could support DMA based transfers, but is not
> > >> - * implemented by the vendor code, and while having the registers documentation
> > >> - * it has never worked on the GXL Hardware.
> > >> - * The PIO mode is the only mode implemented, and due to badly designed HW :
> > >> - * - all transfers are cutted in 16 words burst because the FIFO hangs on
> > >> - *   TX underflow, and there is no TX "Half-Empty" interrupt, so we go by
> > >> - *   FIFO max size chunk only
> > >> - * - CS management is dumb, and goes UP between every burst, so is really a
> > >> - *   "Data Valid" signal than a Chip Select, GPIO link should be used instead
> > >> - *   to have a CS go down over the full transfer
> > >> + * There are two modes for data transmission: PIO and DMA.
> > >> + * When bits_per_word is 8, 16, 24, or 32, data is transferred using PIO mode.
> > >> + * When bits_per_word is 64, DMA mode is used by default.
> > >> + *
> > >> + * DMA achieves a transfer with one or more SPI bursts, each SPI burst is made
> > >> + * up of one or more DMA bursts. The DMA burst implementation mechanism is,
> > >> + * For TX, when the number of words in TXFIFO is less than the preset
> > >> + * reading threshold, SPICC starts a reading DMA burst, which reads the preset
> > >> + * number of words from TX buffer, then writes them into TXFIFO.
> > >> + * For RX, when the number of words in RXFIFO is greater than the preset
> > >> + * writing threshold, SPICC starts a writing request burst, which reads the
> > >> + * preset number of words from RXFIFO, then write them into RX buffer.
> > >> + * DMA works if the transfer meets the following conditions,
> > >> + * - 64 bits per word
> > >
> > > Just for clarification, DMA can only send 64-bit words due to hardware
> > > design, right?
> > > The bit-per-word in spi control register (CONREG) has no effect?
> > >
> > >> + * - The transfer length in word must be multiples of the dma_burst_len, and
> > >> + *   the dma_burst_len should be one of 8,7...2, otherwise, it will be split
> > >> + *   into several SPI bursts by this driver
> > >>    */
> > >>
> > >>   #define SPICC_MAX_BURST        128
> > >> @@ -128,6 +136,23 @@
> > >>
> > >>   #define SPICC_DWADDR   0x24    /* Write Address of DMA */
> > >>
> > >> +#define SPICC_LD_CNTL0 0x28
> > >> +#define VSYNC_IRQ_SRC_SELECT           BIT(0)
> > >> +#define DMA_EN_SET_BY_VSYNC            BIT(2)
> > >> +#define XCH_EN_SET_BY_VSYNC            BIT(3)
> > >> +#define DMA_READ_COUNTER_EN            BIT(4)
> > >> +#define DMA_WRITE_COUNTER_EN           BIT(5)
> > >> +#define DMA_RADDR_LOAD_BY_VSYNC                BIT(6)
> > >> +#define DMA_WADDR_LOAD_BY_VSYNC                BIT(7)
> > >> +#define DMA_ADDR_LOAD_FROM_LD_ADDR     BIT(8)
> > >> +
> > >> +#define SPICC_LD_CNTL1 0x2c
> > >> +#define DMA_READ_COUNTER               GENMASK(15, 0)
> > >> +#define DMA_WRITE_COUNTER              GENMASK(31, 16)
> > >> +#define DMA_BURST_LEN_DEFAULT          8
> > >> +#define DMA_BURST_COUNT_MAX            0xffff
> > >> +#define SPI_BURST_LEN_MAX      (DMA_BURST_LEN_DEFAULT * DMA_BURST_COUNT_MAX)
> > >> +
> > >
> > > LD_CNTL0 and LD_CNTL1 are not in this datasheet for GXL
> > > (S805X/S905X/S905D). Do they exist on these SoCs and are not
> > > documented?
> > >
> > >>   #define SPICC_ENH_CTL0 0x38    /* Enhanced Feature */
> > >>   #define SPICC_ENH_CLK_CS_DELAY_MASK    GENMASK(15, 0)
> > >>   #define SPICC_ENH_DATARATE_MASK                GENMASK(23, 16)
> > >> @@ -171,6 +196,9 @@ struct meson_spicc_device {
> > >>          struct pinctrl                  *pinctrl;
> > >>          struct pinctrl_state            *pins_idle_high;
> > >>          struct pinctrl_state            *pins_idle_low;
> > >> +       dma_addr_t                      tx_dma;
> > >> +       dma_addr_t                      rx_dma;
> > >> +       bool                            using_dma;
> > >>   };
> > >>
> > >>   #define pow2_clk_to_spicc(_div) container_of(_div, struct meson_spicc_device, pow2_div)
> > >> @@ -202,6 +230,148 @@ static void meson_spicc_oen_enable(struct meson_spicc_device *spicc)
> > >>          writel_relaxed(conf, spicc->base + SPICC_ENH_CTL0);
> > >>   }
> > >>
> > >> +static int meson_spicc_dma_map(struct meson_spicc_device *spicc,
> > >> +                              struct spi_transfer *t)
> > >> +{
> > >> +       struct device *dev = spicc->host->dev.parent;
> > >> +
> > >> +       if (!(t->tx_buf && t->rx_buf))
> > >> +               return -EINVAL;
> > >> +
> > >> +       t->tx_dma = dma_map_single(dev, (void *)t->tx_buf, t->len, DMA_TO_DEVICE);
> > >> +       if (dma_mapping_error(dev, t->tx_dma))
> > >> +               return -ENOMEM;
> > >> +
> > >> +       t->rx_dma = dma_map_single(dev, t->rx_buf, t->len, DMA_FROM_DEVICE);
> > >> +       if (dma_mapping_error(dev, t->rx_dma))
> > >> +               return -ENOMEM;
> > >> +
> > >> +       spicc->tx_dma = t->tx_dma;
> > >> +       spicc->rx_dma = t->rx_dma;
> > >> +
> > >> +       return 0;
> > >> +}
> > >> +
> > >> +static void meson_spicc_dma_unmap(struct meson_spicc_device *spicc,
> > >> +                                 struct spi_transfer *t)
> > >> +{
> > >> +       struct device *dev = spicc->host->dev.parent;
> > >> +
> > >> +       if (t->tx_dma)
> > >> +               dma_unmap_single(dev, t->tx_dma, t->len, DMA_TO_DEVICE);
> > >> +       if (t->rx_dma)
> > >> +               dma_unmap_single(dev, t->rx_dma, t->len, DMA_FROM_DEVICE);
> > >> +}
> > >> +
> > >> +/*
> > >> + * According to the remain words length, calculate a suitable spi burst length
> > >> + * and a dma burst length for current spi burst
> > >> + */
> > >> +static u32 meson_spicc_calc_dma_len(struct meson_spicc_device *spicc,
> > >> +                                   u32 len, u32 *dma_burst_len)
> > >> +{
> > >> +       u32 i;
> > >> +
> > >> +       if (len <= spicc->data->fifo_size) {
> > >> +               *dma_burst_len = len;
> > >> +               return len;
> > >> +       }
> > >> +
> > >> +       *dma_burst_len = DMA_BURST_LEN_DEFAULT;
> > >> +
> > >> +       if (len == (SPI_BURST_LEN_MAX + 1))
> > >> +               return SPI_BURST_LEN_MAX - DMA_BURST_LEN_DEFAULT;
> > >> +
> > >> +       if (len >= SPI_BURST_LEN_MAX)
> > >> +               return SPI_BURST_LEN_MAX;
> > >> +
> > >> +       for (i = DMA_BURST_LEN_DEFAULT; i > 1; i--)
> > >> +               if ((len % i) == 0) {
> > >> +                       *dma_burst_len = i;
> > >> +                       return len;
> > >> +               }
> > >> +
> > >> +       i = len % DMA_BURST_LEN_DEFAULT;
> > >> +       len -= i;
> > >> +
> > >> +       if (i == 1)
> > >> +               len -= DMA_BURST_LEN_DEFAULT;
> > >> +
> > >> +       return len;
> > >> +}
> > >> +
> > >> +static void meson_spicc_setup_dma(struct meson_spicc_device *spicc)
> > >> +{
> > >> +       unsigned int len;
> > >> +       unsigned int dma_burst_len, dma_burst_count;
> > >> +       unsigned int count_en = 0;
> > >> +       unsigned int txfifo_thres = 0;
> > >> +       unsigned int read_req = 0;
> > >> +       unsigned int rxfifo_thres = 31;
> > >> +       unsigned int write_req = 0;
> > >> +       unsigned int ld_ctr1 = 0;
> > >> +
> > >> +       writel_relaxed(spicc->tx_dma, spicc->base + SPICC_DRADDR);
> > >> +       writel_relaxed(spicc->rx_dma, spicc->base + SPICC_DWADDR);
> > >> +
> > >> +       /* Set the max burst length to support a transmission with length of
> > >> +        * no more than 1024 bytes(128 words), which must use the CS management
> > >> +        * because of some strict timing requirements
> > >> +        */
> > >> +       writel_bits_relaxed(SPICC_BURSTLENGTH_MASK, SPICC_BURSTLENGTH_MASK,
> > >> +                           spicc->base + SPICC_CONREG);
> > >> +
> > >> +       len = meson_spicc_calc_dma_len(spicc, spicc->xfer_remain,
> > >> +                                      &dma_burst_len);
> > >> +       spicc->xfer_remain -= len;
> > >> +       dma_burst_count = DIV_ROUND_UP(len, dma_burst_len);
> > >> +       dma_burst_len--;
> > >> +
> > >> +       if (spicc->tx_dma) {
> > >> +               spicc->tx_dma += len;
> > >> +               count_en |= DMA_READ_COUNTER_EN;
> > >> +               txfifo_thres = spicc->data->fifo_size - dma_burst_len;
> > >> +               read_req = dma_burst_len;
> > >> +               ld_ctr1 |= FIELD_PREP(DMA_READ_COUNTER, dma_burst_count);
> > >> +       }
> > >> +
> > >> +       if (spicc->rx_dma) {
> > >> +               spicc->rx_dma += len;
> > >> +               count_en |= DMA_WRITE_COUNTER_EN;
> > >> +               rxfifo_thres = dma_burst_len;
> > >> +               write_req = dma_burst_len;
> > >> +               ld_ctr1 |= FIELD_PREP(DMA_WRITE_COUNTER, dma_burst_count);
> > >> +       }
> > >> +
> > >> +       writel_relaxed(count_en, spicc->base + SPICC_LD_CNTL0);
> > >> +       writel_relaxed(ld_ctr1, spicc->base + SPICC_LD_CNTL1);
> > >> +       writel_relaxed(SPICC_DMA_ENABLE
> > >> +                   | SPICC_DMA_URGENT
> > >> +                   | FIELD_PREP(SPICC_TXFIFO_THRESHOLD_MASK, txfifo_thres)
> > >> +                   | FIELD_PREP(SPICC_READ_BURST_MASK, read_req)
> > >> +                   | FIELD_PREP(SPICC_RXFIFO_THRESHOLD_MASK, rxfifo_thres)
> > >> +                   | FIELD_PREP(SPICC_WRITE_BURST_MASK, write_req),
> > >> +                   spicc->base + SPICC_DMAREG);
> > >> +}
> > >> +
> > >> +static irqreturn_t meson_spicc_dma_irq(struct meson_spicc_device *spicc)
> > >> +{
> > >> +       if (readl_relaxed(spicc->base + SPICC_DMAREG) & SPICC_DMA_ENABLE)
> > >> +               return IRQ_HANDLED;
> > >> +
> > >> +       if (spicc->xfer_remain) {
> > >> +               meson_spicc_setup_dma(spicc);
> > >> +       } else {
> > >> +               writel_bits_relaxed(SPICC_SMC, 0, spicc->base + SPICC_CONREG);
> > >> +               writel_relaxed(0, spicc->base + SPICC_INTREG);
> > >> +               writel_relaxed(0, spicc->base + SPICC_DMAREG);
> > >> +               meson_spicc_dma_unmap(spicc, spicc->xfer);
> > >> +               complete(&spicc->done);
> > >> +       }
> > >> +
> > >> +       return IRQ_HANDLED;
> > >> +}
> > >> +
> > >>   static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc)
> > >>   {
> > >>          return !!FIELD_GET(SPICC_TF,
> > >> @@ -293,6 +463,9 @@ static irqreturn_t meson_spicc_irq(int irq, void *data)
> > >>
> > >>          writel_bits_relaxed(SPICC_TC, SPICC_TC, spicc->base + SPICC_STATREG);
> > >>
> > >> +       if (spicc->using_dma)
> > >> +               return meson_spicc_dma_irq(spicc);
> > >> +
> > >>          /* Empty RX FIFO */
> > >>          meson_spicc_rx(spicc);
> > >>
> > >> @@ -426,9 +599,6 @@ static int meson_spicc_transfer_one(struct spi_controller *host,
> > >>
> > >>          meson_spicc_reset_fifo(spicc);
> > >>
> > >> -       /* Setup burst */
> > >> -       meson_spicc_setup_burst(spicc);
> > >> -
> > >>          /* Setup wait for completion */
> > >>          reinit_completion(&spicc->done);
> > >>
> > >> @@ -442,11 +612,36 @@ static int meson_spicc_transfer_one(struct spi_controller *host,
> > >>          /* Increase it twice and add 200 ms tolerance */
> > >>          timeout += timeout + 200;
> > >>
> > >> -       /* Start burst */
> > >> -       writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
> > >> +       if (xfer->bits_per_word == 64) {
> > >> +               int ret;
> > >> +
> > >> +               /* dma_burst_len 1 can't trigger a dma burst */
> > >> +               if (xfer->len < 16)
> > >> +                       return -EINVAL;
> > >> +
> > >> +               ret = meson_spicc_dma_map(spicc, xfer);
> > >> +               if (ret) {
> > >> +                       meson_spicc_dma_unmap(spicc, xfer);
> > >> +                       dev_err(host->dev.parent, "dma map failed\n");
> > >> +                       return ret;
> > >> +               }
> > >> +
> > >> +               spicc->using_dma = true;
> > >> +               spicc->xfer_remain = DIV_ROUND_UP(xfer->len, spicc->bytes_per_word);
> > >> +               meson_spicc_setup_dma(spicc);
> > >> +               writel_relaxed(SPICC_TE_EN, spicc->base + SPICC_INTREG);
> > >> +               writel_bits_relaxed(SPICC_SMC, SPICC_SMC, spicc->base + SPICC_CONREG);
> > >> +       } else {
> > >> +               spicc->using_dma = false;
> > >> +               /* Setup burst */
> > >> +               meson_spicc_setup_burst(spicc);
> > >>
> > >> -       /* Enable interrupts */
> > >> -       writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
> > >> +               /* Start burst */
> > >> +               writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
> > >> +
> > >> +               /* Enable interrupts */
> > >> +               writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
> > >> +       }
> > >>
> > >>          if (!wait_for_completion_timeout(&spicc->done, msecs_to_jiffies(timeout)))
> > >>                  return -ETIMEDOUT;
> > >> @@ -545,6 +740,14 @@ static int meson_spicc_setup(struct spi_device *spi)
> > >>          if (!spi->controller_state)
> > >>                  spi->controller_state = spi_controller_get_devdata(spi->controller);
> > >>
> > >> +       /* DMA works at 64 bits, the rest works on PIO */
> > >> +       if (spi->bits_per_word != 8 &&
> > >> +           spi->bits_per_word != 16 &&
> > >> +           spi->bits_per_word != 24 &&
> > >> +           spi->bits_per_word != 32 &&
> > >> +           spi->bits_per_word != 64)
> > >> +               return -EINVAL;
> > >> +
> > >>          return 0;
> > >>   }
> > >>
> > >> @@ -853,10 +1056,6 @@ static int meson_spicc_probe(struct platform_device *pdev)
> > >>          host->num_chipselect = 4;
> > >>          host->dev.of_node = pdev->dev.of_node;
> > >>          host->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LOOP;
> > >> -       host->bits_per_word_mask = SPI_BPW_MASK(32) |
> > >> -                                  SPI_BPW_MASK(24) |
> > >> -                                  SPI_BPW_MASK(16) |
> > >> -                                  SPI_BPW_MASK(8);
> > >
> > > This should not be removed. SPI_BPW_MASK(64) needs to be added.
> > > Removing bits_per_word_mask causes other code to assume this is an
> > > 8-bit only controller.
> >
> > SPI_BPW_MASK(64) doesn't not exist, it's only a 32bit field, removing it is fine,
> > the check is done later.
>
> The spi_is_bpw_supported function in include/linux/spi/spi.h checks
> bits_per_word_mask.
> Without this set, drm_mipi_dbi behavior is changed to 8-bit transfers.

Hoping for some feedback from the SPI maintainers.
MIPI DBI use the function spi_is_bpw_supported in
drivers/gpu/drm/drm_mipi_dbi.c.
spi_is_bpw_supported checks against a 32-bit bit mask to validate if
the bpw is supported.
SPI transfers are also validated against this mask if it is set but
this mask cannot support 64-bit words.

This driver patch adds 64-bit word support but breaks MIPI DBI since
bits_per_word_mask is no longer set.
Should the u32 bits_per_word_mask be converted to an u64?

>
> Yeah, the 64 won't work. I didn't check the macro.
>
> >
> > Neil
> >
> > >
> > >
> > >
> > >>          host->flags = (SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX);
> > >>          host->min_speed_hz = spicc->data->min_speed_hz;
> > >>          host->max_speed_hz = spicc->data->max_speed_hz;
> > >>
> > >> ---
> > >> base-commit: 49807ed87851916ef655f72e9562f96355183090
> > >> change-id: 20250408-spi-dma-c499f560d295
> > >>
> > >> Best regards,
> > >> --
> > >> Xianwei Zhao <xianwei.zhao@amlogic.com>
> > >>
> > >>
> > >>
> > >> _______________________________________________
> > >> linux-amlogic mailing list
> > >> linux-amlogic@lists.infradead.org
> > >> http://lists.infradead.org/mailman/listinfo/linux-amlogic
> >
Mark Brown May 1, 2025, 8:32 p.m. UTC | #7
On Wed, Apr 30, 2025 at 09:43:40AM +0200, Neil Armstrong wrote:
> On 30/04/2025 04:13, Da Xue wrote:
> > On Mon, Apr 14, 2025 at 2:30 AM Xianwei Zhao via B4 Relay
> > <devnull+xianwei.zhao.amlogic.com@kernel.org> wrote:
> > > 
> > > From: Xianwei Zhao <xianwei.zhao@amlogic.com>
> > > 

Please delete unneeded context from mails when replying.  Doing this
makes it much easier to find your reply in the message, helping ensure
it won't be missed by people scrolling through the irrelevant quoted
material.
diff mbox series

Patch

diff --git a/drivers/spi/spi-meson-spicc.c b/drivers/spi/spi-meson-spicc.c
index df74ad5060f8..6b9137307533 100644
--- a/drivers/spi/spi-meson-spicc.c
+++ b/drivers/spi/spi-meson-spicc.c
@@ -21,18 +21,26 @@ 
 #include <linux/interrupt.h>
 #include <linux/reset.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/dma-mapping.h>
 
 /*
- * The Meson SPICC controller could support DMA based transfers, but is not
- * implemented by the vendor code, and while having the registers documentation
- * it has never worked on the GXL Hardware.
- * The PIO mode is the only mode implemented, and due to badly designed HW :
- * - all transfers are cutted in 16 words burst because the FIFO hangs on
- *   TX underflow, and there is no TX "Half-Empty" interrupt, so we go by
- *   FIFO max size chunk only
- * - CS management is dumb, and goes UP between every burst, so is really a
- *   "Data Valid" signal than a Chip Select, GPIO link should be used instead
- *   to have a CS go down over the full transfer
+ * There are two modes for data transmission: PIO and DMA.
+ * When bits_per_word is 8, 16, 24, or 32, data is transferred using PIO mode.
+ * When bits_per_word is 64, DMA mode is used by default.
+ *
+ * DMA achieves a transfer with one or more SPI bursts, each SPI burst is made
+ * up of one or more DMA bursts. The DMA burst implementation mechanism is,
+ * For TX, when the number of words in TXFIFO is less than the preset
+ * reading threshold, SPICC starts a reading DMA burst, which reads the preset
+ * number of words from TX buffer, then writes them into TXFIFO.
+ * For RX, when the number of words in RXFIFO is greater than the preset
+ * writing threshold, SPICC starts a writing request burst, which reads the
+ * preset number of words from RXFIFO, then write them into RX buffer.
+ * DMA works if the transfer meets the following conditions,
+ * - 64 bits per word
+ * - The transfer length in word must be multiples of the dma_burst_len, and
+ *   the dma_burst_len should be one of 8,7...2, otherwise, it will be split
+ *   into several SPI bursts by this driver
  */
 
 #define SPICC_MAX_BURST	128
@@ -128,6 +136,23 @@ 
 
 #define SPICC_DWADDR	0x24	/* Write Address of DMA */
 
+#define SPICC_LD_CNTL0	0x28
+#define VSYNC_IRQ_SRC_SELECT		BIT(0)
+#define DMA_EN_SET_BY_VSYNC		BIT(2)
+#define XCH_EN_SET_BY_VSYNC		BIT(3)
+#define DMA_READ_COUNTER_EN		BIT(4)
+#define DMA_WRITE_COUNTER_EN		BIT(5)
+#define DMA_RADDR_LOAD_BY_VSYNC		BIT(6)
+#define DMA_WADDR_LOAD_BY_VSYNC		BIT(7)
+#define DMA_ADDR_LOAD_FROM_LD_ADDR	BIT(8)
+
+#define SPICC_LD_CNTL1	0x2c
+#define DMA_READ_COUNTER		GENMASK(15, 0)
+#define DMA_WRITE_COUNTER		GENMASK(31, 16)
+#define DMA_BURST_LEN_DEFAULT		8
+#define DMA_BURST_COUNT_MAX		0xffff
+#define SPI_BURST_LEN_MAX	(DMA_BURST_LEN_DEFAULT * DMA_BURST_COUNT_MAX)
+
 #define SPICC_ENH_CTL0	0x38	/* Enhanced Feature */
 #define SPICC_ENH_CLK_CS_DELAY_MASK	GENMASK(15, 0)
 #define SPICC_ENH_DATARATE_MASK		GENMASK(23, 16)
@@ -171,6 +196,9 @@  struct meson_spicc_device {
 	struct pinctrl			*pinctrl;
 	struct pinctrl_state		*pins_idle_high;
 	struct pinctrl_state		*pins_idle_low;
+	dma_addr_t			tx_dma;
+	dma_addr_t			rx_dma;
+	bool				using_dma;
 };
 
 #define pow2_clk_to_spicc(_div) container_of(_div, struct meson_spicc_device, pow2_div)
@@ -202,6 +230,148 @@  static void meson_spicc_oen_enable(struct meson_spicc_device *spicc)
 	writel_relaxed(conf, spicc->base + SPICC_ENH_CTL0);
 }
 
+static int meson_spicc_dma_map(struct meson_spicc_device *spicc,
+			       struct spi_transfer *t)
+{
+	struct device *dev = spicc->host->dev.parent;
+
+	if (!(t->tx_buf && t->rx_buf))
+		return -EINVAL;
+
+	t->tx_dma = dma_map_single(dev, (void *)t->tx_buf, t->len, DMA_TO_DEVICE);
+	if (dma_mapping_error(dev, t->tx_dma))
+		return -ENOMEM;
+
+	t->rx_dma = dma_map_single(dev, t->rx_buf, t->len, DMA_FROM_DEVICE);
+	if (dma_mapping_error(dev, t->rx_dma))
+		return -ENOMEM;
+
+	spicc->tx_dma = t->tx_dma;
+	spicc->rx_dma = t->rx_dma;
+
+	return 0;
+}
+
+static void meson_spicc_dma_unmap(struct meson_spicc_device *spicc,
+				  struct spi_transfer *t)
+{
+	struct device *dev = spicc->host->dev.parent;
+
+	if (t->tx_dma)
+		dma_unmap_single(dev, t->tx_dma, t->len, DMA_TO_DEVICE);
+	if (t->rx_dma)
+		dma_unmap_single(dev, t->rx_dma, t->len, DMA_FROM_DEVICE);
+}
+
+/*
+ * According to the remain words length, calculate a suitable spi burst length
+ * and a dma burst length for current spi burst
+ */
+static u32 meson_spicc_calc_dma_len(struct meson_spicc_device *spicc,
+				    u32 len, u32 *dma_burst_len)
+{
+	u32 i;
+
+	if (len <= spicc->data->fifo_size) {
+		*dma_burst_len = len;
+		return len;
+	}
+
+	*dma_burst_len = DMA_BURST_LEN_DEFAULT;
+
+	if (len == (SPI_BURST_LEN_MAX + 1))
+		return SPI_BURST_LEN_MAX - DMA_BURST_LEN_DEFAULT;
+
+	if (len >= SPI_BURST_LEN_MAX)
+		return SPI_BURST_LEN_MAX;
+
+	for (i = DMA_BURST_LEN_DEFAULT; i > 1; i--)
+		if ((len % i) == 0) {
+			*dma_burst_len = i;
+			return len;
+		}
+
+	i = len % DMA_BURST_LEN_DEFAULT;
+	len -= i;
+
+	if (i == 1)
+		len -= DMA_BURST_LEN_DEFAULT;
+
+	return len;
+}
+
+static void meson_spicc_setup_dma(struct meson_spicc_device *spicc)
+{
+	unsigned int len;
+	unsigned int dma_burst_len, dma_burst_count;
+	unsigned int count_en = 0;
+	unsigned int txfifo_thres = 0;
+	unsigned int read_req = 0;
+	unsigned int rxfifo_thres = 31;
+	unsigned int write_req = 0;
+	unsigned int ld_ctr1 = 0;
+
+	writel_relaxed(spicc->tx_dma, spicc->base + SPICC_DRADDR);
+	writel_relaxed(spicc->rx_dma, spicc->base + SPICC_DWADDR);
+
+	/* Set the max burst length to support a transmission with length of
+	 * no more than 1024 bytes(128 words), which must use the CS management
+	 * because of some strict timing requirements
+	 */
+	writel_bits_relaxed(SPICC_BURSTLENGTH_MASK, SPICC_BURSTLENGTH_MASK,
+			    spicc->base + SPICC_CONREG);
+
+	len = meson_spicc_calc_dma_len(spicc, spicc->xfer_remain,
+				       &dma_burst_len);
+	spicc->xfer_remain -= len;
+	dma_burst_count = DIV_ROUND_UP(len, dma_burst_len);
+	dma_burst_len--;
+
+	if (spicc->tx_dma) {
+		spicc->tx_dma += len;
+		count_en |= DMA_READ_COUNTER_EN;
+		txfifo_thres = spicc->data->fifo_size - dma_burst_len;
+		read_req = dma_burst_len;
+		ld_ctr1 |= FIELD_PREP(DMA_READ_COUNTER, dma_burst_count);
+	}
+
+	if (spicc->rx_dma) {
+		spicc->rx_dma += len;
+		count_en |= DMA_WRITE_COUNTER_EN;
+		rxfifo_thres = dma_burst_len;
+		write_req = dma_burst_len;
+		ld_ctr1 |= FIELD_PREP(DMA_WRITE_COUNTER, dma_burst_count);
+	}
+
+	writel_relaxed(count_en, spicc->base + SPICC_LD_CNTL0);
+	writel_relaxed(ld_ctr1, spicc->base + SPICC_LD_CNTL1);
+	writel_relaxed(SPICC_DMA_ENABLE
+		    | SPICC_DMA_URGENT
+		    | FIELD_PREP(SPICC_TXFIFO_THRESHOLD_MASK, txfifo_thres)
+		    | FIELD_PREP(SPICC_READ_BURST_MASK, read_req)
+		    | FIELD_PREP(SPICC_RXFIFO_THRESHOLD_MASK, rxfifo_thres)
+		    | FIELD_PREP(SPICC_WRITE_BURST_MASK, write_req),
+		    spicc->base + SPICC_DMAREG);
+}
+
+static irqreturn_t meson_spicc_dma_irq(struct meson_spicc_device *spicc)
+{
+	if (readl_relaxed(spicc->base + SPICC_DMAREG) & SPICC_DMA_ENABLE)
+		return IRQ_HANDLED;
+
+	if (spicc->xfer_remain) {
+		meson_spicc_setup_dma(spicc);
+	} else {
+		writel_bits_relaxed(SPICC_SMC, 0, spicc->base + SPICC_CONREG);
+		writel_relaxed(0, spicc->base + SPICC_INTREG);
+		writel_relaxed(0, spicc->base + SPICC_DMAREG);
+		meson_spicc_dma_unmap(spicc, spicc->xfer);
+		complete(&spicc->done);
+	}
+
+	return IRQ_HANDLED;
+}
+
 static inline bool meson_spicc_txfull(struct meson_spicc_device *spicc)
 {
 	return !!FIELD_GET(SPICC_TF,
@@ -293,6 +463,9 @@  static irqreturn_t meson_spicc_irq(int irq, void *data)
 
 	writel_bits_relaxed(SPICC_TC, SPICC_TC, spicc->base + SPICC_STATREG);
 
+	if (spicc->using_dma)
+		return meson_spicc_dma_irq(spicc);
+
 	/* Empty RX FIFO */
 	meson_spicc_rx(spicc);
 
@@ -426,9 +599,6 @@  static int meson_spicc_transfer_one(struct spi_controller *host,
 
 	meson_spicc_reset_fifo(spicc);
 
-	/* Setup burst */
-	meson_spicc_setup_burst(spicc);
-
 	/* Setup wait for completion */
 	reinit_completion(&spicc->done);
 
@@ -442,11 +612,36 @@  static int meson_spicc_transfer_one(struct spi_controller *host,
 	/* Increase it twice and add 200 ms tolerance */
 	timeout += timeout + 200;
 
-	/* Start burst */
-	writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
+	if (xfer->bits_per_word == 64) {
+		int ret;
+
+		/* dma_burst_len 1 can't trigger a dma burst */
+		if (xfer->len < 16)
+			return -EINVAL;
+
+		ret = meson_spicc_dma_map(spicc, xfer);
+		if (ret) {
+			meson_spicc_dma_unmap(spicc, xfer);
+			dev_err(host->dev.parent, "dma map failed\n");
+			return ret;
+		}
+
+		spicc->using_dma = true;
+		spicc->xfer_remain = DIV_ROUND_UP(xfer->len, spicc->bytes_per_word);
+		meson_spicc_setup_dma(spicc);
+		writel_relaxed(SPICC_TE_EN, spicc->base + SPICC_INTREG);
+		writel_bits_relaxed(SPICC_SMC, SPICC_SMC, spicc->base + SPICC_CONREG);
+	} else {
+		spicc->using_dma = false;
+		/* Setup burst */
+		meson_spicc_setup_burst(spicc);
 
-	/* Enable interrupts */
-	writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
+		/* Start burst */
+		writel_bits_relaxed(SPICC_XCH, SPICC_XCH, spicc->base + SPICC_CONREG);
+
+		/* Enable interrupts */
+		writel_relaxed(SPICC_TC_EN, spicc->base + SPICC_INTREG);
+	}
 
 	if (!wait_for_completion_timeout(&spicc->done, msecs_to_jiffies(timeout)))
 		return -ETIMEDOUT;
@@ -545,6 +740,14 @@  static int meson_spicc_setup(struct spi_device *spi)
 	if (!spi->controller_state)
 		spi->controller_state = spi_controller_get_devdata(spi->controller);
 
+	/* DMA works at 64 bits, the rest works on PIO */
+	if (spi->bits_per_word != 8 &&
+	    spi->bits_per_word != 16 &&
+	    spi->bits_per_word != 24 &&
+	    spi->bits_per_word != 32 &&
+	    spi->bits_per_word != 64)
+		return -EINVAL;
+
 	return 0;
 }
 
@@ -853,10 +1056,6 @@  static int meson_spicc_probe(struct platform_device *pdev)
 	host->num_chipselect = 4;
 	host->dev.of_node = pdev->dev.of_node;
 	host->mode_bits = SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LOOP;
-	host->bits_per_word_mask = SPI_BPW_MASK(32) |
-				   SPI_BPW_MASK(24) |
-				   SPI_BPW_MASK(16) |
-				   SPI_BPW_MASK(8);
 	host->flags = (SPI_CONTROLLER_MUST_RX | SPI_CONTROLLER_MUST_TX);
 	host->min_speed_hz = spicc->data->min_speed_hz;
 	host->max_speed_hz = spicc->data->max_speed_hz;