[PATCH v1 2/4] spi: coreqspi: add xfer function for PolarFire SoC

Eoin Dickson Eoin.Dickson at microchip.com
Thu May 22 16:10:00 CEST 2025


From: Eoin Dickson <eoin.dickson at microchip.com>

Add xfer function to PolarFire SoC coreqspi driver. The read and write
operations are limited to one byte at a time instead of four as CMD18
(multiple block read) reads garbage when four byte ops are enabled.

Signed-off-by: Eoin Dickson <eoin.dickson at microchip.com>
---
 drivers/spi/microchip_coreqspi.c | 113 ++++++++++++++++++++++++++++++-
 1 file changed, 111 insertions(+), 2 deletions(-)

diff --git a/drivers/spi/microchip_coreqspi.c b/drivers/spi/microchip_coreqspi.c
index 5fe0c8e123..d3d7124fc0 100644
--- a/drivers/spi/microchip_coreqspi.c
+++ b/drivers/spi/microchip_coreqspi.c
@@ -17,6 +17,7 @@
 #include <linux/delay.h>
 #include <linux/types.h>
 #include <linux/sizes.h>
+#include <asm/gpio.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -98,6 +99,8 @@ DECLARE_GLOBAL_DATA_PTR;
 #define REG_X4_TX_DATA         (0x4c)
 #define REG_FRAMESUP           (0x50)
 
+#define MAX_CS_COUNT 1
+
 /**
  * struct mchp_coreqspi - Defines qspi driver instance
  * @regs:              Address of the QSPI controller registers
@@ -114,6 +117,7 @@ struct mchp_coreqspi {
 	u8 *rxbuf;
 	int tx_len;
 	int rx_len;
+	struct gpio_desc cs_gpios[MAX_CS_COUNT];
 };
 
 static void mchp_coreqspi_init_hw(struct mchp_coreqspi *qspi)
@@ -173,7 +177,7 @@ static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi, bool word)
 	while (qspi->tx_len >= 4) {
 		while (readl(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL)
 			;
-		data = *(u32 *)qspi->txbuf;
+		data = qspi->txbuf ? *((u32 *)qspi->txbuf) : 0xFF;
 		qspi->txbuf += 4;
 		qspi->tx_len -= 4;
 		writel(data, qspi->regs + REG_X4_TX_DATA);
@@ -185,7 +189,7 @@ static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi, bool word)
 	while (qspi->tx_len--) {
 		while (readl(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL)
 			;
-		data =  *qspi->txbuf++;
+		data = qspi->txbuf ? *qspi->txbuf++ : 0xFF;
 		writel(data, qspi->regs + REG_TX_DATA);
 	}
 }
@@ -472,6 +476,110 @@ static int mchp_coreqspi_probe(struct udevice *dev)
 	/* Init the mpfs qspi hw */
 	mchp_coreqspi_init_hw(qspi);
 
+	if (CONFIG_IS_ENABLED(DM_GPIO)) {
+		int i;
+
+		ret = gpio_request_list_by_name(dev, "cs-gpios", qspi->cs_gpios,
+							ARRAY_SIZE(qspi->cs_gpios), 0);
+
+		if (ret < 0) {
+			pr_err("Can't get %s gpios! Error: %d", dev->name, ret);
+			return ret;
+		}
+
+		for (i = 0; i < ARRAY_SIZE(qspi->cs_gpios); i++) {
+			if (!dm_gpio_is_valid(&qspi->cs_gpios[i]))
+				continue;
+			dm_gpio_set_dir_flags(&qspi->cs_gpios[i], GPIOD_IS_OUT);
+		}
+	}
+
+	u32 control = readl(qspi->regs + REG_CONTROL);
+
+	control |= (CONTROL_MASTER | CONTROL_ENABLE);
+	control &= ~CONTROL_CLKIDLE;
+	writel(control, qspi->regs + REG_CONTROL);
+
+	return 0;
+}
+
+static void mchp_coreqspi_cs_activate(struct udevice *dev)
+{
+	struct udevice *bus = dev_get_parent(dev);
+	struct mchp_coreqspi *qspi = dev_get_priv(bus);
+	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+	u32 cs = slave_plat->cs;
+
+	if (CONFIG_IS_ENABLED(DM_GPIO) && dm_gpio_is_valid(&qspi->cs_gpios[cs]))
+		dm_gpio_set_value(&qspi->cs_gpios[cs], 1);
+}
+
+static void mchp_coreqspi_cs_deactivate(struct udevice *dev)
+{
+	struct udevice *bus = dev_get_parent(dev);
+	struct mchp_coreqspi *qspi = dev_get_priv(bus);
+	struct dm_spi_slave_plat *slave_plat = dev_get_parent_plat(dev);
+	u32 cs = slave_plat->cs;
+
+	if (CONFIG_IS_ENABLED(DM_GPIO) && dm_gpio_is_valid(&qspi->cs_gpios[cs]))
+		dm_gpio_set_value(&qspi->cs_gpios[cs], 0);
+}
+
+static int mchp_coreqspi_xfer(struct udevice *dev, unsigned int bitlen,
+				const void *dout, void *din, unsigned long flags)
+{
+	struct udevice *bus = dev_get_parent(dev);
+	struct mchp_coreqspi *qspi = dev_get_priv(bus);
+	struct spi_slave *slave = dev_get_parent_priv(dev);
+	uint total_bytes = bitlen >> 3; /* fixed 8-bit word length */
+	u32 control, frames;
+
+	int err = 0;
+
+	err = mchp_coreqspi_wait_for_ready(slave);
+	if (err)
+		return err;
+
+	control = readl(qspi->regs + REG_CONTROL);
+	control &= ~(CONTROL_MODE12_MASK | CONTROL_MODE0);
+	writel(control, qspi->regs + REG_CONTROL);
+
+	frames = total_bytes & BYTESUPPER_MASK;
+	writel(frames, qspi->regs + REG_FRAMESUP);
+
+	frames |= FRAMES_FLAGBYTE;
+	writel(frames, qspi->regs + REG_FRAMES);
+
+	if (flags & SPI_XFER_BEGIN)
+		mchp_coreqspi_cs_activate(dev);
+
+	if (bitlen == 0)
+		goto out;
+
+	if (bitlen % 8) { // Non byte aligned SPI transfer
+		flags |= SPI_XFER_END;
+		goto out;
+	}
+
+	qspi->txbuf = (u8 *)dout;
+	qspi->rxbuf = (u8 *)din;
+
+	while (total_bytes) {
+		qspi->tx_len = 1;
+		qspi->rx_len = 1;
+		total_bytes--;
+
+		if (din) {
+			mchp_coreqspi_write_op(qspi, true);
+			mchp_coreqspi_read_op(qspi);
+		} else {
+			mchp_coreqspi_write_op(qspi, true);
+		}
+	}
+out:
+	if (flags & SPI_XFER_END)
+		mchp_coreqspi_cs_deactivate(dev);
+
 	return 0;
 }
 
@@ -484,6 +592,7 @@ static const struct spi_controller_mem_ops mchp_coreqspi_mem_ops = {
 static const struct dm_spi_ops mchp_coreqspi_ops = {
 	.claim_bus      = mchp_coreqspi_claim_bus,
 	.release_bus    = mchp_coreqspi_release_bus,
+	.xfer           = mchp_coreqspi_xfer,
 	.set_speed      = mchp_coreqspi_set_speed,
 	.set_mode       = mchp_coreqspi_set_mode,
 	.mem_ops        = &mchp_coreqspi_mem_ops,
-- 
2.34.1



More information about the U-Boot mailing list