回复: [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