From 0eeed461b7b86e3b822984b1c266316ac70ccf69 Mon Sep 17 00:00:00 2001
From: vauban353 <vauban353@gmail.com>
Date: Sun, 6 Aug 2023 09:37:40 +0100
Subject: [PATCH 5/8] Microchip QSPI: Add regular transfers.

---
 drivers/spi/spi-microchip-core-qspi.c | 226 +++++++++++++++++++++++++-
 1 file changed, 223 insertions(+), 3 deletions(-)

diff --git a/drivers/spi/spi-microchip-core-qspi.c b/drivers/spi/spi-microchip-core-qspi.c
index 33c19b98b..5f1623ac4 100644
--- a/drivers/spi/spi-microchip-core-qspi.c
+++ b/drivers/spi/spi-microchip-core-qspi.c
@@ -1,3 +1,4 @@
+
 // SPDX-License-Identifier: (GPL-2.0)
 /*
  * Microchip coreQSPI QSPI controller driver
@@ -117,10 +118,10 @@ struct mchp_coreqspi {
 	struct completion data_completion;
 	struct mutex op_lock; /* lock access to the device */
 	u8 *txbuf;
-	u8 *rxbuf;
+	volatile u8 *rxbuf;
 	int irq;
-	int tx_len;
-	int rx_len;
+	volatile int tx_len;
+	volatile int rx_len;
 };
 
 static int mchp_coreqspi_set_mode(struct mchp_coreqspi *qspi, const struct spi_mem_op *op)
@@ -222,6 +223,68 @@ static inline void mchp_coreqspi_write_op(struct mchp_coreqspi *qspi, bool word)
 	}
 }
 
+static inline void mchp_coreqspi_write_read_op(struct mchp_coreqspi *qspi, bool last)
+{
+	u32 frames, control, data;
+	qspi->rx_len = qspi->tx_len;
+
+	control = readl_relaxed(qspi->regs + REG_CONTROL);
+	control |= CONTROL_FLAGSX4;
+	writel_relaxed(control, qspi->regs + REG_CONTROL);
+
+	while (qspi->tx_len >= 4) {
+		while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL)
+			;
+
+		data = *(u32 *)qspi->txbuf;
+		qspi->txbuf += 4;
+		qspi->tx_len -= 4;
+		writel_relaxed(data, qspi->regs + REG_X4_TX_DATA);
+
+		if (qspi->rx_len >= 8) {
+			if (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXAVAILABLE) {
+				data = readl_relaxed(qspi->regs + REG_X4_RX_DATA);
+				*(u32 *)qspi->rxbuf = data;
+				qspi->rxbuf += 4;
+				qspi->rx_len -= 4;
+			}
+		}
+	}
+
+	if (!last ) {
+		while (qspi->rx_len >= 4) {
+			while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY)
+				;
+			data = readl_relaxed(qspi->regs + REG_X4_RX_DATA);
+			*(u32 *)qspi->rxbuf = data;
+			qspi->rxbuf += 4;
+			qspi->rx_len -= 4;
+		}
+	}
+
+	if (qspi->tx_len) {
+		control &= ~CONTROL_FLAGSX4;
+		writel_relaxed(control, qspi->regs + REG_CONTROL);
+
+
+		while (qspi->tx_len--) {
+			while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_TXFIFOFULL)
+				;
+			data =  *qspi->txbuf++;
+			writel_relaxed(data, qspi->regs + REG_TX_DATA);
+		}
+		if (!last) {
+			while (qspi->rx_len--) {
+				while (readl_relaxed(qspi->regs + REG_STATUS) & STATUS_RXFIFOEMPTY)
+					;
+				data = readl_relaxed(qspi->regs + REG_RX_DATA);
+				*qspi->rxbuf++ = (data & 0xFF);
+			}
+		}
+	}
+}
+
+
 static void mchp_coreqspi_enable_ints(struct mchp_coreqspi *qspi)
 {
 	u32 mask = IEN_TXDONE |
@@ -497,6 +560,160 @@ static const struct spi_controller_mem_ops mchp_coreqspi_mem_ops = {
 	.exec_op = mchp_coreqspi_exec_op,
 };
 
+static int mchp_coreqspi_transfer_one_message(struct spi_controller *ctlr,
+					struct spi_message *m)
+{
+	struct mchp_coreqspi *qspi = spi_controller_get_devdata(ctlr);
+	struct spi_transfer *t = NULL;
+	u32 control, frames, status;
+	u32 total_bytes, cmd_bytes = 0, idle_cycles = 0;
+	int ret;
+	bool quad = false, dual = false;
+	bool keep_cs = false;
+
+	mutex_lock(&qspi->op_lock);
+	ret = readl_poll_timeout(qspi->regs + REG_STATUS, status,
+				 (status & STATUS_READY), 0,
+				 TIMEOUT_MS);
+	if (ret) {
+		dev_err(&ctlr->dev,
+			"Timeout waiting on QSPI ready.\n");
+		return -ETIMEDOUT;
+	}
+
+	ret = mchp_coreqspi_setup_clock(qspi, m->spi);
+	if (ret)
+		goto error;
+
+	if (m->spi->cs_gpiod) {
+		if (m->spi->mode & SPI_CS_HIGH) {
+			gpiod_set_value(m->spi->cs_gpiod, 0);
+		} else {
+			gpiod_set_value(m->spi->cs_gpiod, 1);
+		}
+	}
+
+	control = readl_relaxed(qspi->regs + REG_CONTROL);
+	control &= ~(CONTROL_MODE12_MASK |
+		     CONTROL_MODE0);
+	writel_relaxed(control, qspi->regs + REG_CONTROL);
+
+	reinit_completion(&qspi->data_completion);
+
+	/* Check the total bytes and command bytes */
+	list_for_each_entry(t, &m->transfers, transfer_list) {
+		total_bytes += t->len;
+		if ((!cmd_bytes) && !(t->tx_buf && t->rx_buf))
+			cmd_bytes = t->len;
+		if (!t->rx_buf)
+			cmd_bytes = total_bytes;
+		if (t->tx_nbits == SPI_NBITS_QUAD || t->rx_nbits == SPI_NBITS_QUAD)
+			quad = true;
+		else if (t->tx_nbits == SPI_NBITS_DUAL || t->rx_nbits == SPI_NBITS_DUAL)
+			dual = true;
+	}
+
+	control = readl_relaxed(qspi->regs + REG_CONTROL);
+	if (quad) {
+		control |= (CONTROL_MODE0 | CONTROL_MODE12_EX_RW);
+	} else if (dual) {
+		control &= ~CONTROL_MODE0;
+		control |= CONTROL_MODE12_FULL;
+	} else {
+		control &= ~(CONTROL_MODE12_MASK |
+			     CONTROL_MODE0);
+	}
+
+	writel_relaxed(control, qspi->regs + REG_CONTROL);
+	frames = total_bytes & BYTESUPPER_MASK;
+	writel_relaxed(frames, qspi->regs + REG_FRAMESUP);
+	frames = total_bytes & BYTESLOWER_MASK;
+	frames |= cmd_bytes << FRAMES_CMDBYTES_SHIFT;
+	frames |= idle_cycles << FRAMES_IDLE_SHIFT;
+	control = readl_relaxed(qspi->regs + REG_CONTROL);
+	if (control & CONTROL_MODE12_MASK)
+		frames |= (1 << FRAMES_SHIFT);
+
+	frames |= FRAMES_FLAGWORD;
+	writel_relaxed(frames, qspi->regs + REG_FRAMES);
+
+	list_for_each_entry(t, &m->transfers, transfer_list) {
+
+		if ((t->tx_buf) && (t->rx_buf)){
+			bool last = false;
+			qspi->txbuf = (u8 *)t->tx_buf;
+			qspi->rxbuf = (u8 *)t->rx_buf;
+			qspi->tx_len = t->len;
+			if (list_is_last(&t->transfer_list, &m->transfers))
+				last = true;
+			mchp_coreqspi_write_read_op(qspi, last);
+		} else if (t->tx_buf) {
+			qspi->txbuf = (u8 *)t->tx_buf;
+			qspi->tx_len = t->len;
+			mchp_coreqspi_write_op(qspi, true);
+		} else {
+			qspi->rxbuf = (u8 *)t->rx_buf;
+			qspi->rx_len = t->len;
+		}
+
+		if (t->cs_change) {
+			if (list_is_last(&t->transfer_list,
+					 &m->transfers)) {
+				keep_cs = true;
+			} else {
+				if (!t->cs_off) {
+//					gpiod_set_value(m->spi->cs_gpiod, 0);
+					if (m->spi->mode & SPI_CS_HIGH) {
+						gpiod_set_value(m->spi->cs_gpiod, 0);
+					} else {
+						gpiod_set_value(m->spi->cs_gpiod, 1);
+					}
+				}
+//				_spi_transfer_cs_change_delay(m, t);
+				if (!list_next_entry(t, transfer_list)->cs_off) {
+//					spi_set_cs(m->spi, true, false);
+					if (m->spi->mode & SPI_CS_HIGH) {
+//						gpiod_set_value(m->spi->cs_gpiod, 0);
+						gpiod_set_value(m->spi->cs_gpiod, 1);
+					} else {
+//						gpiod_set_value(m->spi->cs_gpiod, 1);
+						gpiod_set_value(m->spi->cs_gpiod, 0);
+					}
+//					gpiod_set_value(m->spi->cs_gpiod, 1);
+				}
+			}
+		} else if (!list_is_last(&t->transfer_list, &m->transfers) &&
+			   t->cs_off != list_next_entry(t, transfer_list)->cs_off) {
+//			spi_set_cs(m->spi, t->cs_off, false);
+					if (m->spi->mode & SPI_CS_HIGH) {
+						gpiod_set_value(m->spi->cs_gpiod, !t->cs_off);
+					} else {
+						gpiod_set_value(m->spi->cs_gpiod, !t->cs_off);
+					}
+		}
+
+
+
+	}
+
+	mchp_coreqspi_enable_ints(qspi);
+
+	if (!wait_for_completion_timeout(&qspi->data_completion, msecs_to_jiffies(1000)))
+		ret = -ETIMEDOUT;
+
+	m->actual_length = total_bytes;
+
+error:
+	if (ret != 0 || !keep_cs)
+		gpiod_set_value(m->spi->cs_gpiod, 0);
+
+	m->status = ret;
+	spi_finalize_current_message(ctlr);
+	mutex_unlock(&qspi->op_lock);
+	mchp_coreqspi_disable_ints(qspi);
+	return ret;
+}
+
 static int mchp_coreqspi_probe(struct platform_device *pdev)
 {
 	struct spi_controller *ctlr;
@@ -550,6 +767,9 @@ static int mchp_coreqspi_probe(struct platform_device *pdev)
 	ctlr->mode_bits = SPI_CPOL | SPI_CPHA | SPI_RX_DUAL | SPI_RX_QUAD |
 			  SPI_TX_DUAL | SPI_TX_QUAD;
 	ctlr->dev.of_node = np;
+	ctlr->transfer_one_message = mchp_coreqspi_transfer_one_message;
+	ctlr->num_chipselect = 2;
+	ctlr->use_gpio_descriptors = true;
 
 	ret = devm_spi_register_controller(&pdev->dev, ctlr);
 	if (ret) {
-- 
2.39.2