spi: spi-cadence: supports transmission with bits_per_word of 16 and 32

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>
This commit is contained in:
Jun Guo 2025-10-31 15:30:02 +08:00 committed by Mark Brown
parent 55b5d192ba
commit 4e00135b2d
No known key found for this signature in database
GPG Key ID: 24D68B725D5487D0
1 changed files with 93 additions and 13 deletions

View File

@ -109,6 +109,7 @@
* @rxbuf: Pointer to the RX buffer * @rxbuf: Pointer to the RX buffer
* @tx_bytes: Number of bytes left to transfer * @tx_bytes: Number of bytes left to transfer
* @rx_bytes: Number of bytes requested * @rx_bytes: Number of bytes requested
* @n_bytes: Number of bytes per word
* @dev_busy: Device busy flag * @dev_busy: Device busy flag
* @is_decoded_cs: Flag for decoder property set or not * @is_decoded_cs: Flag for decoder property set or not
* @tx_fifo_depth: Depth of the TX FIFO * @tx_fifo_depth: Depth of the TX FIFO
@ -120,16 +121,24 @@ struct cdns_spi {
struct clk *pclk; struct clk *pclk;
unsigned int clk_rate; unsigned int clk_rate;
u32 speed_hz; u32 speed_hz;
const u8 *txbuf; const void *txbuf;
u8 *rxbuf; void *rxbuf;
int tx_bytes; int tx_bytes;
int rx_bytes; int rx_bytes;
u8 n_bytes;
u8 dev_busy; u8 dev_busy;
u32 is_decoded_cs; u32 is_decoded_cs;
unsigned int tx_fifo_depth; unsigned int tx_fifo_depth;
struct reset_control *rstc; 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 */ /* Macros for the SPI controller read/write */
static inline u32 cdns_spi_read(struct cdns_spi *xspi, u32 offset) 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; 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 * cdns_spi_process_fifo - Fills the TX FIFO, and drain the RX FIFO
* @xspi: Pointer to the cdns_spi structure * @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) { while (ntx || nrx) {
if (nrx) { if (nrx) {
u8 data = cdns_spi_read(xspi, CDNS_SPI_RXD); cdns_spi_reader(xspi);
if (xspi->rxbuf)
*xspi->rxbuf++ = data;
nrx--; nrx--;
} }
if (ntx) { if (ntx) {
if (xspi->txbuf) cdns_spi_writer(xspi);
cdns_spi_write(xspi, CDNS_SPI_TXD, *xspi->txbuf++);
else
cdns_spi_write(xspi, CDNS_SPI_TXD, 0);
ntx--; 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) if (cdns_spi_read(xspi, CDNS_SPI_ISR) & CDNS_SPI_IXR_TXFULL)
udelay(10); 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_process_fifo(xspi, xspi->tx_fifo_depth, 0);
cdns_spi_write(xspi, CDNS_SPI_IER, CDNS_SPI_IXR_DEFAULT); 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->mode_bits = SPI_CPOL | SPI_CPHA;
ctlr->bits_per_word_mask = SPI_BPW_MASK(8); 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)) { if (!spi_controller_is_target(ctlr)) {
ctlr->mode_bits |= SPI_CS_HIGH; ctlr->mode_bits |= SPI_CS_HIGH;
ctlr->set_cs = cdns_spi_chipselect; 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[] = { static const struct of_device_id cdns_spi_of_match[] = {
{ .compatible = "xlnx,zynq-spi-r1p6" }, { .compatible = "xlnx,zynq-spi-r1p6" },
{ .compatible = "cix,sky1-spi-r1p6" },
{ .compatible = "cdns,spi-r1p6" }, { .compatible = "cdns,spi-r1p6" },
{ /* end of table */ } { /* end of table */ }
}; };