To reduce the CPU load in waiting for the OSPI internal SRAM to clear in
indirect mode, use the CQSPI_REG_IRQSTATUS register to check for the
indirect operation to complete. Enable the Indirect Operation Complete,
Transfer Watermark Breach and Underflow interrupt status bits and use
readl_poll_timeout() to poll for the Indirect Operation Complete bit to
get set.
The IRQ is not routed to the GIC, only the interrupt status bits of
the IP are polled.
The indirect operation completion and the indirect done status were
polled with a 10 ms timeout, which triggers "Indirect write timed out"
and "Indirect write clear completion error" errors on Versal NET.
Increase these timeouts to 500 ms, taking reference from the Linux
driver.
Fixes: 26da6353e171 ("mtd: cqspi: Simplify indirect write code")
Signed-off-by: Tejas Bhumkar <[email protected]>
Signed-off-by: Prasad Kummari <[email protected]>
Signed-off-by: Venkatesh Yadav Abbarapu <[email protected]>
Signed-off-by: Padmarao Begari <[email protected]>
---
drivers/spi/cadence_qspi.h | 14 ++++++++++++++
drivers/spi/cadence_qspi_apb.c | 28 ++++++++++++++++++++++------
2 files changed, 36 insertions(+), 6 deletions(-)
diff --git a/drivers/spi/cadence_qspi.h b/drivers/spi/cadence_qspi.h
index 6a8f9c58e4e..d97a4cbe9c1 100644
--- a/drivers/spi/cadence_qspi.h
+++ b/drivers/spi/cadence_qspi.h
@@ -203,6 +203,20 @@
(((readl((reg_base) + CQSPI_REG_SDRAMLEVEL)) >> \
CQSPI_REG_SDRAMLEVEL_WR_LSB) & CQSPI_REG_SDRAMLEVEL_WR_MASK)
+/* Interrupt status bits */
+#define CQSPI_REG_IRQ_UNDERFLOW BIT(1)
+#define CQSPI_REG_IRQ_IND_COMP BIT(2)
+#define CQSPI_REG_IRQ_WATERMARK BIT(6)
+
+#define CQSPI_IRQ_MASK_WR (CQSPI_REG_IRQ_IND_COMP
| \
+ CQSPI_REG_IRQ_WATERMARK
| \
+ CQSPI_REG_IRQ_UNDERFLOW)
+
+#define CQSPI_IRQ_STATUS_MASK GENMASK(16, 0)
+
+/* Indirect write timeout, same as CQSPI_TIMEOUT_MS in Linux */
+#define CQSPI_IND_WR_TIMEOUT_MS 500
+
struct cadence_spi_plat {
unsigned int max_hz;
void *regbase;
diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c
index dbfb7c1f592..e2a3ed3e018 100644
--- a/drivers/spi/cadence_qspi_apb.c
+++ b/drivers/spi/cadence_qspi_apb.c
@@ -31,6 +31,7 @@
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/errno.h>
+#include <linux/iopoll.h>
#include <wait_bit.h>
#include <spi.h>
#include <spi-mem.h>
@@ -857,6 +858,7 @@ cadence_qspi_apb_indirect_write_execute(struct
cadence_spi_priv *priv,
void *bounce_buf = NULL;
unsigned int write_bytes;
int ret;
+ u32 cr;
/*
* Use bounce buffer for non 32 bit aligned txbuf to avoid data
@@ -870,9 +872,15 @@ cadence_qspi_apb_indirect_write_execute(struct
cadence_spi_priv *priv,
bb_txbuf = bounce_buf;
}
- /* Configure the indirect read transfer bytes */
+ /* Configure the indirect write transfer bytes */
writel(n_tx, priv->regbase + CQSPI_REG_INDIRECTWRBYTES);
+ /* Clear all interrupts */
+ writel(CQSPI_IRQ_STATUS_MASK, priv->regbase + CQSPI_REG_IRQSTATUS);
+
+ /* Enable the write related interrupt status bits */
+ writel(CQSPI_IRQ_MASK_WR, priv->regbase + CQSPI_REG_IRQMASK);
+
/* Start the indirect write transfer */
writel(CQSPI_REG_INDIRECTWR_START,
priv->regbase + CQSPI_REG_INDIRECTWR);
@@ -891,9 +899,9 @@ cadence_qspi_apb_indirect_write_execute(struct
cadence_spi_priv *priv,
bb_txbuf + rounddown(write_bytes, 4),
write_bytes % 4);
- ret = wait_for_bit_le32(priv->regbase + CQSPI_REG_SDRAMLEVEL,
- CQSPI_REG_SDRAMLEVEL_WR_MASK <<
- CQSPI_REG_SDRAMLEVEL_WR_LSB, 0, 10, 0);
+ ret = readl_poll_timeout(priv->regbase + CQSPI_REG_IRQSTATUS,
+ cr, cr & CQSPI_REG_IRQ_IND_COMP,
+ CQSPI_IND_WR_TIMEOUT_MS * 1000);
if (ret) {
printf("Indirect write timed out (%i)\n", ret);
goto failwr;
@@ -905,19 +913,24 @@ cadence_qspi_apb_indirect_write_execute(struct
cadence_spi_priv *priv,
/* Check indirect done status */
ret = wait_for_bit_le32(priv->regbase + CQSPI_REG_INDIRECTWR,
- CQSPI_REG_INDIRECTWR_DONE, 1, 10, 0);
+ CQSPI_REG_INDIRECTWR_DONE, 1,
+ CQSPI_IND_WR_TIMEOUT_MS, 0);
if (ret) {
printf("Indirect write completion error (%i)\n", ret);
goto failwr;
}
+ /* Disable the interrupt status bits */
+ writel(0, priv->regbase + CQSPI_REG_IRQMASK);
+
/* Clear indirect completion status */
writel(CQSPI_REG_INDIRECTWR_DONE,
priv->regbase + CQSPI_REG_INDIRECTWR);
/* Check indirect done status */
ret = wait_for_bit_le32(priv->regbase + CQSPI_REG_INDIRECTWR,
- CQSPI_REG_INDIRECTWR_DONE, 0, 10, 0);
+ CQSPI_REG_INDIRECTWR_DONE, 0,
+ CQSPI_IND_WR_TIMEOUT_MS, 0);
if (ret) {
printf("Indirect write clear completion error (%i)\n", ret);
goto failwr;
@@ -933,6 +946,9 @@ cadence_qspi_apb_indirect_write_execute(struct
cadence_spi_priv *priv,
return 0;
failwr:
+ /* Disable the interrupt status bits */
+ writel(0, priv->regbase + CQSPI_REG_IRQMASK);
+
/* Cancel the indirect write */
writel(CQSPI_REG_INDIRECTWR_CANCEL,
priv->regbase + CQSPI_REG_INDIRECTWR);
--
2.34.1