You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
276 lines
7.7 KiB
Diff
276 lines
7.7 KiB
Diff
1 year ago
|
From 0777b8ed4d5994b906dcfd06fa9c626b6905d655 Mon Sep 17 00:00:00 2001
|
||
|
From: vauban353 <vauban353@gmail.com>
|
||
|
Date: Sun, 6 Aug 2023 09:37:40 +0100
|
||
|
Subject: [PATCH] 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 33c19b98b9e2..2cce6526a507 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.25.1
|
||
|
|