]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
spi: spi-cadence: supports transmission with bits_per_word of 16 and 32
authorJun Guo <jun.guo@cixtech.com>
Fri, 31 Oct 2025 07:30:02 +0000 (15:30 +0800)
committerMark Brown <broonie@kernel.org>
Thu, 13 Nov 2025 18:56:49 +0000 (18:56 +0000)
The default FIFO data width of the Cadence SPI IP is 8 bits, but
the hardware supports configurations of 16 bits and 32 bits.
This patch enhances the driver to support communication with both
16-bits and 32-bits FIFO data widths.

Signed-off-by: Jun Guo <jun.guo@cixtech.com>
Link: https://patch.msgid.link/20251031073003.3289573-3-jun.guo@cixtech.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/spi/spi-cadence.c

index 5ae09b21d23a58b2d85fb73ea13569b1e7429fbf..47054da630d0f1b73ba4b4ffc0cc383fddeded1a 100644 (file)
  * @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
@@ -120,16 +121,24 @@ 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;
        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 +314,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 +402,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 +526,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 +730,9 @@ 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 (of_device_is_compatible(pdev->dev.of_node, "cix,sky1-spi-r1p6"))
+               ctlr->bits_per_word_mask |= SPI_BPW_MASK(16) | SPI_BPW_MASK(32);
+
        if (!spi_controller_is_target(ctlr)) {
                ctlr->mode_bits |=  SPI_CS_HIGH;
                ctlr->set_cs = cdns_spi_chipselect;
@@ -797,6 +876,7 @@ static const struct dev_pm_ops cdns_spi_dev_pm_ops = {
 
 static const struct of_device_id cdns_spi_of_match[] = {
        { .compatible = "xlnx,zynq-spi-r1p6" },
+       { .compatible = "cix,sky1-spi-r1p6" },
        { .compatible = "cdns,spi-r1p6" },
        { /* end of table */ }
 };