Unverified Commit 4e00135b authored by Jun Guo's avatar Jun Guo Committed by Mark Brown
Browse files

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: default avatarJun Guo <jun.guo@cixtech.com>
Link: https://patch.msgid.link/20251031073003.3289573-3-jun.guo@cixtech.com


Signed-off-by: default avatarMark Brown <broonie@kernel.org>
parent 55b5d192
Loading
Loading
Loading
Loading
+93 −13
Original line number Diff line number Diff line
@@ -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 */ }
};