回复: [PATCH 2/3] spi: spi-cadence: supports transmission with bits_per_word of 16 and 32

Jun Guo Jun.Guo at cixtech.com
Fri Oct 10 00:50:24 PDT 2025


On 9/30/2025 3:56 PM, Jun Guo wrote:
> The Cadence IP supports configurable FIFO data widths of 16 bits or
> 32 bits during integration. The default FIFO data width is 8 bits.
> If the chip design modifies the FIFO data width to 16 bits or 32 bits,
> the fifo-width property can be configured in the firmware (DT/ACPI)
> to enable the driver to support data transfers at the corresponding width.
>
> Signed-off-by: Jun Guo <jun.guo at cixtech.com>
> ---
>   drivers/spi/spi-cadence.c | 125 ++++++++++++++++++++++++++++++++++----
>   1 file changed, 112 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c
> index 5ae09b21d23a..e91c6afbece9 100644
> --- a/drivers/spi/spi-cadence.c
> +++ b/drivers/spi/spi-cadence.c
> @@ -109,9 +109,11 @@
>    * @rxbuf:          Pointer to the RX buffer
>    * @tx_bytes:               Number of bytes left to transfer
>    * @rx_bytes:               Number of bytes requested
> + * @n_bytes:         Number of bytes per word
>    * @dev_busy:               Device busy flag
>    * @is_decoded_cs:  Flag for decoder property set or not
>    * @tx_fifo_depth:  Depth of the TX FIFO
> + * @fifo_width:              Width of the FIFO
>    * @rstc:           Optional reset control for SPI controller
>    */
>   struct cdns_spi {
> @@ -120,16 +122,25 @@ struct cdns_spi {
>       struct clk *pclk;
>       unsigned int clk_rate;
>       u32 speed_hz;
> -     const u8 *txbuf;
> -     u8 *rxbuf;
> +     const void *txbuf;
> +     void *rxbuf;
>       int tx_bytes;
>       int rx_bytes;
> +     u8 n_bytes;
>       u8 dev_busy;
>       u32 is_decoded_cs;
>       unsigned int tx_fifo_depth;
> +     unsigned int fifo_width;
>       struct reset_control *rstc;
>   };
>
> +enum cdns_spi_frame_n_bytes {
> +     CDNS_SPI_N_BYTES_NULL = 0,
> +     CDNS_SPI_N_BYTES_U8 = 1,
> +     CDNS_SPI_N_BYTES_U16 = 2,
> +     CDNS_SPI_N_BYTES_U32 = 4
> +};
> +
>   /* Macros for the SPI controller read/write */
>   static inline u32 cdns_spi_read(struct cdns_spi *xspi, u32 offset)
>   {
> @@ -305,6 +316,78 @@ static int cdns_spi_setup_transfer(struct spi_device *spi,
>       return 0;
>   }
>
> +static u8 cdns_spi_n_bytes(struct spi_transfer *transfer)
> +{
> +     if (transfer->bits_per_word <= 8)
> +             return CDNS_SPI_N_BYTES_U8;
> +     else if (transfer->bits_per_word <= 16)
> +             return CDNS_SPI_N_BYTES_U16;
> +     else
> +             return CDNS_SPI_N_BYTES_U32;
> +}
> +
> +static inline void cdns_spi_reader(struct cdns_spi *xspi)
> +{
> +     u32 rxw = 0;
> +
> +     if (xspi->rxbuf && !IS_ALIGNED((uintptr_t)xspi->rxbuf, xspi->n_bytes)) {
> +             pr_err("%s: rxbuf address is not aligned for %d bytes\n",
> +                    __func__, xspi->n_bytes);
> +             return;
> +     }
> +
> +     rxw = cdns_spi_read(xspi, CDNS_SPI_RXD);
> +     if (xspi->rxbuf) {
> +             switch (xspi->n_bytes) {
> +             case CDNS_SPI_N_BYTES_U8:
> +                     *(u8 *)xspi->rxbuf = rxw;
> +                     break;
> +             case CDNS_SPI_N_BYTES_U16:
> +                     *(u16 *)xspi->rxbuf = rxw;
> +                     break;
> +             case CDNS_SPI_N_BYTES_U32:
> +                     *(u32 *)xspi->rxbuf = rxw;
> +                     break;
> +             default:
> +                     pr_err("%s invalid n_bytes %d\n", __func__,
> +                            xspi->n_bytes);
> +                     return;
> +             }
> +             xspi->rxbuf = (u8 *)xspi->rxbuf + xspi->n_bytes;
> +     }
> +}
> +
> +static inline void cdns_spi_writer(struct cdns_spi *xspi)
> +{
> +     u32 txw = 0;
> +
> +     if (xspi->txbuf && !IS_ALIGNED((uintptr_t)xspi->txbuf, xspi->n_bytes)) {
> +             pr_err("%s: txbuf address is not aligned for %d bytes\n",
> +                    __func__, xspi->n_bytes);
> +             return;
> +     }
> +
> +     if (xspi->txbuf) {
> +             switch (xspi->n_bytes) {
> +             case CDNS_SPI_N_BYTES_U8:
> +                     txw = *(u8 *)xspi->txbuf;
> +                     break;
> +             case CDNS_SPI_N_BYTES_U16:
> +                     txw = *(u16 *)xspi->txbuf;
> +                     break;
> +             case CDNS_SPI_N_BYTES_U32:
> +                     txw = *(u32 *)xspi->txbuf;
> +                     break;
> +             default:
> +                     pr_err("%s invalid n_bytes %d\n", __func__,
> +                            xspi->n_bytes);
> +                     return;
> +             }
> +             cdns_spi_write(xspi, CDNS_SPI_TXD, txw);
> +             xspi->txbuf = (u8 *)xspi->txbuf + xspi->n_bytes;
> +     }
> +}
> +
>   /**
>    * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO
>    * @xspi:   Pointer to the cdns_spi structure
> @@ -321,23 +404,14 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx)
>
>       while (ntx || nrx) {
>               if (nrx) {
> -                     u8 data = cdns_spi_read(xspi, CDNS_SPI_RXD);
> -
> -                     if (xspi->rxbuf)
> -                             *xspi->rxbuf++ = data;
> -
> +                     cdns_spi_reader(xspi);
>                       nrx--;
>               }
>
>               if (ntx) {
> -                     if (xspi->txbuf)
> -                             cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++);
> -                     else
> -                             cdns_spi_write(xspi, CDNS_SPI_TXD, 0);
> -
> +                     cdns_spi_writer(xspi);
>                       ntx--;
>               }
> -
>       }
>   }
>
> @@ -454,6 +528,10 @@ static int cdns_transfer_one(struct spi_controller *ctlr,
>       if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)
>               udelay(10);
>
> +     xspi->n_bytes = cdns_spi_n_bytes(transfer);
> +     xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes);
> +     xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes);
> +
>       cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0);
>
>       cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT);
> @@ -654,6 +732,27 @@ static int cdns_spi_probe(struct platform_device *pdev)
>       ctlr->mode_bits = SPI_CPOL | SPI_CPHA;
>       ctlr->bits_per_word_mask = SPI_BPW_MASK(8);
>
> +     if (!device_property_read_u32(&pdev->dev, "fifo-width",
> +                                   &xspi->fifo_width)) {
> +             switch (xspi->fifo_width) {
> +             case 8:
> +                     break;
> +             case 16:
> +                     ctlr->bits_per_word_mask |= SPI_BPW_MASK(16);
> +                     break;
> +             case 32:
> +                     ctlr->bits_per_word_mask |=
> +                             (SPI_BPW_MASK(16) | SPI_BPW_MASK(32));
> +                     break;
> +             default:
> +                     dev_warn(
> +                             &pdev->dev,
> +                             "Unsupported FIFO width: %u. Using default bit-width mask.\n",
> +                             xspi->fifo_width);
> +                     break;
> +             }
> +     }
> +
>       if (!spi_controller_is_target(ctlr)) {
>               ctlr->mode_bits |=  SPI_CS_HIGH;
>               ctlr->set_cs = cdns_spi_chipselect;
hi Mark, could you please take some time to review the current patch?
I look forward to your comments at your earliest convenience.

Best wishes,
Jun Guo



________________________________________
发件人: Jun Guo <jun.guo at cixtech.com>
发送时间: 2025年9月30日 15:56
收件人: Peter Chen; Fugang Duan; robh at kernel.org; krzk+dt at kernel.org; conor+dt at kernel.org; broonie at kernel.org
抄送: linux-spi at vger.kernel.org; michal.simek at amd.com; cix-kernel-upstream; linux-arm-kernel at lists.infradead.org; devicetree at vger.kernel.org; linux-kernel at vger.kernel.org; Jun Guo
主题: [PATCH 2/3] spi: spi-cadence: supports transmission with bits_per_word of 16 and 32

The Cadence IP supports configurable FIFO data widths of 16 bits or
32 bits during integration. The default FIFO data width is 8 bits.
If the chip design modifies the FIFO data width to 16 bits or 32 bits,
the fifo-width property can be configured in the firmware (DT/ACPI)
to enable the driver to support data transfers at the corresponding width.

Signed-off-by: Jun Guo <jun.guo at cixtech.com>
---
 drivers/spi/spi-cadence.c | 125 ++++++++++++++++++++++++++++++++++----
 1 file changed, 112 insertions(+), 13 deletions(-)

diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c
index 5ae09b21d23a..e91c6afbece9 100644
--- a/drivers/spi/spi-cadence.c
+++ b/drivers/spi/spi-cadence.c
@@ -109,9 +109,11 @@
  * @rxbuf:             Pointer to the RX buffer
  * @tx_bytes:          Number of bytes left to transfer
  * @rx_bytes:          Number of bytes requested
+ * @n_bytes:           Number of bytes per word
  * @dev_busy:          Device busy flag
  * @is_decoded_cs:     Flag for decoder property set or not
  * @tx_fifo_depth:     Depth of the TX FIFO
+ * @fifo_width:                Width of the FIFO
  * @rstc:              Optional reset control for SPI controller
  */
 struct cdns_spi {
@@ -120,16 +122,25 @@ struct cdns_spi {
        struct clk *pclk;
        unsigned int clk_rate;
        u32 speed_hz;
-       const u8 *txbuf;
-       u8 *rxbuf;
+       const void *txbuf;
+       void *rxbuf;
        int tx_bytes;
        int rx_bytes;
+       u8 n_bytes;
        u8 dev_busy;
        u32 is_decoded_cs;
        unsigned int tx_fifo_depth;
+       unsigned int fifo_width;
        struct reset_control *rstc;
 };

+enum cdns_spi_frame_n_bytes {
+       CDNS_SPI_N_BYTES_NULL = 0,
+       CDNS_SPI_N_BYTES_U8 = 1,
+       CDNS_SPI_N_BYTES_U16 = 2,
+       CDNS_SPI_N_BYTES_U32 = 4
+};
+
 /* Macros for the SPI controller read/write */
 static inline u32 cdns_spi_read(struct cdns_spi *xspi, u32 offset)
 {
@@ -305,6 +316,78 @@ static int cdns_spi_setup_transfer(struct spi_device *spi,
        return 0;
 }

+static u8 cdns_spi_n_bytes(struct spi_transfer *transfer)
+{
+       if (transfer->bits_per_word <= 8)
+               return CDNS_SPI_N_BYTES_U8;
+       else if (transfer->bits_per_word <= 16)
+               return CDNS_SPI_N_BYTES_U16;
+       else
+               return CDNS_SPI_N_BYTES_U32;
+}
+
+static inline void cdns_spi_reader(struct cdns_spi *xspi)
+{
+       u32 rxw = 0;
+
+       if (xspi->rxbuf && !IS_ALIGNED((uintptr_t)xspi->rxbuf, xspi->n_bytes)) {
+               pr_err("%s: rxbuf address is not aligned for %d bytes\n",
+                      __func__, xspi->n_bytes);
+               return;
+       }
+
+       rxw = cdns_spi_read(xspi, CDNS_SPI_RXD);
+       if (xspi->rxbuf) {
+               switch (xspi->n_bytes) {
+               case CDNS_SPI_N_BYTES_U8:
+                       *(u8 *)xspi->rxbuf = rxw;
+                       break;
+               case CDNS_SPI_N_BYTES_U16:
+                       *(u16 *)xspi->rxbuf = rxw;
+                       break;
+               case CDNS_SPI_N_BYTES_U32:
+                       *(u32 *)xspi->rxbuf = rxw;
+                       break;
+               default:
+                       pr_err("%s invalid n_bytes %d\n", __func__,
+                              xspi->n_bytes);
+                       return;
+               }
+               xspi->rxbuf = (u8 *)xspi->rxbuf + xspi->n_bytes;
+       }
+}
+
+static inline void cdns_spi_writer(struct cdns_spi *xspi)
+{
+       u32 txw = 0;
+
+       if (xspi->txbuf && !IS_ALIGNED((uintptr_t)xspi->txbuf, xspi->n_bytes)) {
+               pr_err("%s: txbuf address is not aligned for %d bytes\n",
+                      __func__, xspi->n_bytes);
+               return;
+       }
+
+       if (xspi->txbuf) {
+               switch (xspi->n_bytes) {
+               case CDNS_SPI_N_BYTES_U8:
+                       txw = *(u8 *)xspi->txbuf;
+                       break;
+               case CDNS_SPI_N_BYTES_U16:
+                       txw = *(u16 *)xspi->txbuf;
+                       break;
+               case CDNS_SPI_N_BYTES_U32:
+                       txw = *(u32 *)xspi->txbuf;
+                       break;
+               default:
+                       pr_err("%s invalid n_bytes %d\n", __func__,
+                              xspi->n_bytes);
+                       return;
+               }
+               cdns_spi_write(xspi, CDNS_SPI_TXD, txw);
+               xspi->txbuf = (u8 *)xspi->txbuf + xspi->n_bytes;
+       }
+}
+
 /**
  * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO
  * @xspi:      Pointer to the cdns_spi structure
@@ -321,23 +404,14 @@ static void cdns_spi_process_fifo(struct cdns_spi *xspi, int ntx, int nrx)

        while (ntx || nrx) {
                if (nrx) {
-                       u8 data = cdns_spi_read(xspi, CDNS_SPI_RXD);
-
-                       if (xspi->rxbuf)
-                               *xspi->rxbuf++ = data;
-
+                       cdns_spi_reader(xspi);
                        nrx--;
                }

                if (ntx) {
-                       if (xspi->txbuf)
-                               cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++);
-                       else
-                               cdns_spi_write(xspi, CDNS_SPI_TXD, 0);
-
+                       cdns_spi_writer(xspi);
                        ntx--;
                }
-
        }
 }

@@ -454,6 +528,10 @@ static int cdns_transfer_one(struct spi_controller *ctlr,
        if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)
                udelay(10);

+       xspi->n_bytes = cdns_spi_n_bytes(transfer);
+       xspi->tx_bytes = DIV_ROUND_UP(xspi->tx_bytes, xspi->n_bytes);
+       xspi->rx_bytes = DIV_ROUND_UP(xspi->rx_bytes, xspi->n_bytes);
+
        cdns_spi_process_fifo(xspi, xspi->tx_fifo_depth, 0);

        cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT);
@@ -654,6 +732,27 @@ static int cdns_spi_probe(struct platform_device *pdev)
        ctlr->mode_bits = SPI_CPOL | SPI_CPHA;
        ctlr->bits_per_word_mask = SPI_BPW_MASK(8);

+       if (!device_property_read_u32(&pdev->dev, "fifo-width",
+                                     &xspi->fifo_width)) {
+               switch (xspi->fifo_width) {
+               case 8:
+                       break;
+               case 16:
+                       ctlr->bits_per_word_mask |= SPI_BPW_MASK(16);
+                       break;
+               case 32:
+                       ctlr->bits_per_word_mask |=
+                               (SPI_BPW_MASK(16) | SPI_BPW_MASK(32));
+                       break;
+               default:
+                       dev_warn(
+                               &pdev->dev,
+                               "Unsupported FIFO width: %u. Using default bit-width mask.\n",
+                               xspi->fifo_width);
+                       break;
+               }
+       }
+
        if (!spi_controller_is_target(ctlr)) {
                ctlr->mode_bits |=  SPI_CS_HIGH;
                ctlr->set_cs = cdns_spi_chipselect;
--
2.34.1



More information about the linux-arm-kernel mailing list