diff mbox

[U-Boot,v2,3/8] spi: cadence_qspi_apb: Use 32 bit indirect write transaction when possible

Message ID F1518C073E9A0747B7EF185F3C3140B73E5259C7@eadc-w-mabprd02.ad.gd-ais.com
State Rejected
Delegated to: Jagannadha Sutradharudu Teki
Headers show

Commit Message

Jason Rush March 1, 2017, 4:36 p.m. UTC
From: Vignesh R <vigneshr@ti.com>

According to Section 11.15.4.9.2 Indirect Write Controller of K2G SoC
TRM SPRUHY8D[1], the external master is only permitted to issue 32-bit
data interface writes until the last word of an indirect transfer
otherwise indirect writes is known to fails sometimes. So, make sure
that QSPI indirect writes are 32 bit sized except for the last write.

[1]www.ti.com/lit/ug/spruhy8d/spruhy8d.pdf

Signed-off-by: Vignesh R <vigneshr@ti.com>
Signed-off-by: Jason A. Rush <jason.rush@gd-ms.com>
---
Changed in v2:
 - changed reg name to match current reg names

 drivers/spi/cadence_qspi_apb.c | 41 +++++++++++++++++++++++++++++++----------
 1 file changed, 31 insertions(+), 10 deletions(-)
diff mbox

Patch

diff --git a/drivers/spi/cadence_qspi_apb.c b/drivers/spi/cadence_qspi_apb.c
index df6a91fc9f..907c6edf0a 100644
--- a/drivers/spi/cadence_qspi_apb.c
+++ b/drivers/spi/cadence_qspi_apb.c
@@ -722,23 +722,44 @@  int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat,
 {
 	unsigned int page_size = plat->page_size;
 	unsigned int remaining = n_tx;
-	unsigned int write_bytes;
+	unsigned int write_bytes = 0;
 	int ret;
 
+	/* Handle non-4-byte aligned access to avoid data abort. */
+	if ((uintptr_t) txbuf % 4) {
+		write_bytes = n_tx > 3 ? 4 - ((uintptr_t)txbuf % 4) : n_tx;
+		/* Configure the indirect read transfer bytes */
+		writel(write_bytes, plat->regbase + CQSPI_REG_INDIRECTWRBYTES);
+		/* Start the indirect write transfer */
+		writel(CQSPI_REG_INDIRECTWR_START,
+		       plat->regbase + CQSPI_REG_INDIRECTWR);
+		writesb(plat->ahbbase, txbuf, write_bytes);
+		txbuf += write_bytes;
+		remaining -= write_bytes;
+		if (remaining > 0) {
+			u32 start_addr = readl(plat->regbase +
+					       CQSPI_REG_INDIRECTWRSTARTADDR);
+			start_addr += write_bytes;
+			writel(start_addr, plat->regbase +
+			       CQSPI_REG_INDIRECTWRSTARTADDR);
+		}
+	}
+
 	/* Configure the indirect read transfer bytes */
-	writel(n_tx, plat->regbase + CQSPI_REG_INDIRECTWRBYTES);
+	writel(n_tx - write_bytes, plat->regbase + CQSPI_REG_INDIRECTWRBYTES);
 
-	/* Start the indirect write transfer */
-	writel(CQSPI_REG_INDIRECTWR_START,
-	       plat->regbase + CQSPI_REG_INDIRECTWR);
+	if (remaining > 0)
+		writel(CQSPI_REG_INDIRECTWR_START,
+		       plat->regbase + CQSPI_REG_INDIRECTWR);
 
 	while (remaining > 0) {
 		write_bytes = remaining > page_size ? page_size : remaining;
-		/* Handle non-4-byte aligned access to avoid data abort. */
-		if (((uintptr_t)txbuf % 4) || (write_bytes % 4))
-			writesb(plat->ahbbase, txbuf, write_bytes);
-		else
-			writesl(plat->ahbbase, txbuf, write_bytes >> 2);
+
+		writesl(plat->ahbbase, txbuf, write_bytes >> 2);
+		if (write_bytes % 4)
+			writesb(plat->ahbbase,
+				txbuf + rounddown(write_bytes, 4),
+				write_bytes % 4);
 
 		ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_SDRAMLEVEL,
 				   CQSPI_REG_SDRAMLEVEL_WR_MASK <<