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

Jun Guo posted 3 patches 3 months, 1 week ago
[PATCH v3 2/3] spi: spi-cadence: supports transmission with bits_per_word of 16 and 32
Posted by Jun Guo 3 months, 1 week ago
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>
---
 drivers/spi/spi-cadence.c | 106 +++++++++++++++++++++++++++++++++-----
 1 file changed, 93 insertions(+), 13 deletions(-)

diff --git a/drivers/spi/spi-cadence.c b/drivers/spi/spi-cadence.c
index 5ae09b21d23a..47054da630d0 100644
--- a/drivers/spi/spi-cadence.c
+++ b/drivers/spi/spi-cadence.c
@@ -109,6 +109,7 @@
  * @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 */ }
 };
-- 
2.34.1
Re: [PATCH v3 2/3] spi: spi-cadence: supports transmission with bits_per_word of 16 and 32
Posted by Rodrigo Alencar 3 weeks, 3 days ago
On 25/10/31 03:30PM, Jun Guo wrote:
> 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>

...

> +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_write(xspi, CDNS_SPI_TXD, txw) needs to be called regardless of xspi->txbuf.
Otherwise, there will be no clock for the read operation to work.
This is a bug I am seeing on a Zedboard running on 6.19

kind regards,

Rodrigo Alencar
Re: [PATCH v3 2/3] spi: spi-cadence: supports transmission with bits_per_word of 16 and 32
Posted by Mark Brown 3 weeks, 3 days ago
On Wed, Jan 14, 2026 at 02:14:35PM +0000, Rodrigo Alencar wrote:
> On 25/10/31 03:30PM, Jun Guo wrote:

> > +	if (xspi->txbuf) {
> > +		switch (xspi->n_bytes) {
> > +		case CDNS_SPI_N_BYTES_U8:
> > +			txw = *(u8 *)xspi->txbuf;
> > +			break;

> > +		cdns_spi_write(xspi, CDNS_SPI_TXD, txw);
> > +		xspi->txbuf = (u8 *)xspi->txbuf + xspi->n_bytes;
> > +	}

> cdns_spi_write(xspi, CDNS_SPI_TXD, txw) needs to be called regardless of xspi->txbuf.
> Otherwise, there will be no clock for the read operation to work.
> This is a bug I am seeing on a Zedboard running on 6.19

So the device needs to be flagged as SPI_CONTROLLER_MUST_TX?
Re: [PATCH v3 2/3] spi: spi-cadence: supports transmission with bits_per_word of 16 and 32
Posted by Rodrigo Alencar 3 weeks, 3 days ago
On 26/01/14 02:16PM, Mark Brown wrote:
> On Wed, Jan 14, 2026 at 02:14:35PM +0000, Rodrigo Alencar wrote:
> > On 25/10/31 03:30PM, Jun Guo wrote:
> 
> > > +	if (xspi->txbuf) {
> > > +		switch (xspi->n_bytes) {
> > > +		case CDNS_SPI_N_BYTES_U8:
> > > +			txw = *(u8 *)xspi->txbuf;
> > > +			break;
> 
> > > +		cdns_spi_write(xspi, CDNS_SPI_TXD, txw);
> > > +		xspi->txbuf = (u8 *)xspi->txbuf + xspi->n_bytes;
> > > +	}
> 
> > cdns_spi_write(xspi, CDNS_SPI_TXD, txw) needs to be called regardless of xspi->txbuf.
> > Otherwise, there will be no clock for the read operation to work.
> > This is a bug I am seeing on a Zedboard running on 6.19
> 
> So the device needs to be flagged as SPI_CONTROLLER_MUST_TX?

yes, It works if

ctlr->flags = SPI_CONTROLLER_MUST_TX;

is added to the probe function. At least for my case handling with "xlnx,zynq-spi-r1p6"

kind regards,

Rodrigo Alencar
Re: [PATCH v3 2/3] spi: spi-cadence: supports transmission with bits_per_word of 16 and 32
Posted by Jun Guo 3 weeks, 3 days ago
hi Rodrigo,

   I'm very sorry for introducing this issue. Thank you for your 
reminder. I will immediately submit a patch to add 
SPI_CONTROLLER_MUST_TX to the flags during probe to resolve this problem.

On 1/15/2026 12:09 AM, Rodrigo Alencar wrote:
> [You don't often get email from 455.rodrigo.alencar@gmail.com. Learn why this is important at https://aka.ms/LearnAboutSenderIdentification ]
> 
> EXTERNAL EMAIL
> 
> On 26/01/14 02:16PM, Mark Brown wrote:
>> On Wed, Jan 14, 2026 at 02:14:35PM +0000, Rodrigo Alencar wrote:
>>> On 25/10/31 03:30PM, Jun Guo wrote:
>>
>>>> + if (xspi->txbuf) {
>>>> +         switch (xspi->n_bytes) {
>>>> +         case CDNS_SPI_N_BYTES_U8:
>>>> +                 txw = *(u8 *)xspi->txbuf;
>>>> +                 break;
>>
>>>> +         cdns_spi_write(xspi, CDNS_SPI_TXD, txw);
>>>> +         xspi->txbuf = (u8 *)xspi->txbuf + xspi->n_bytes;
>>>> + }
>>
>>> cdns_spi_write(xspi, CDNS_SPI_TXD, txw) needs to be called regardless of xspi->txbuf.
>>> Otherwise, there will be no clock for the read operation to work.
>>> This is a bug I am seeing on a Zedboard running on 6.19
>>
>> So the device needs to be flagged as SPI_CONTROLLER_MUST_TX?
> 
> yes, It works if
> 
> ctlr->flags = SPI_CONTROLLER_MUST_TX;
> 
> is added to the probe function. At least for my case handling with "xlnx,zynq-spi-r1p6"
> 
> kind regards,
> 
> Rodrigo Alencar
>