spi: zynqmp_gqspi: fix quad mode bus conflict
Starting the GENFIFO after each CMD and ADDR entry creates a gap
between the address phase (TX) and data phase (RX). For read commands
without dummy cycles, the controller is still driving DQ2/DQ3 HIGH
when the flash switches to output mode, causing a bus conflict.
Split the GENFIFO helper into a write-only variant and a
start-and-wait variant. Queue CMD, ADDR and dummy entries without
starting the GENFIFO. The data phase appends its entry and starts
the queued sequence, keeping the address-to-data transition
continuous.
Since genfifo_cmd() no longer triggers after each entry, operations
without a data phase have no trigger path. Call start_gen_fifo()
from exec_op() before deasserting chip select to execute the queued
CMD and ADDR entries.
Fixes: 22cca1730e ("spi: zynqmp_gqspi: Add support for ZynqMP qspi driver")
Signed-off-by: Suraj Kakade <suraj.hanumantkakade@amd.com>
Signed-off-by: Padmarao Begari <padmarao.begari@amd.com>
Signed-off-by: Michal Simek <michal.simek@amd.com>
Link: https://patch.msgid.link/20260709083757.1851446-1-suraj.hanumantkakade@amd.com
This commit is contained in:
committed by
Michal Simek
parent
40727c4f38
commit
93332d403a
@@ -283,16 +283,21 @@ static u32 zynqmp_qspi_genfifo_mode(u8 buswidth)
|
||||
}
|
||||
}
|
||||
|
||||
static void zynqmp_qspi_fill_gen_fifo(struct zynqmp_qspi_priv *priv,
|
||||
static void zynqmp_qspi_write_gen_fifo(struct zynqmp_qspi_priv *priv,
|
||||
u32 gqspi_fifo_reg)
|
||||
{
|
||||
struct zynqmp_qspi_regs *regs = priv->regs;
|
||||
u32 config_reg, ier;
|
||||
int ret = 0;
|
||||
|
||||
log_content("%s, GFIFO_CMD: 0x%X\n", __func__, gqspi_fifo_reg);
|
||||
|
||||
writel(gqspi_fifo_reg, ®s->genfifo);
|
||||
}
|
||||
|
||||
static int zynqmp_qspi_start_gen_fifo(struct zynqmp_qspi_priv *priv)
|
||||
{
|
||||
struct zynqmp_qspi_regs *regs = priv->regs;
|
||||
u32 config_reg, ier;
|
||||
int ret = 0;
|
||||
|
||||
config_reg = readl(®s->confr);
|
||||
/* Manual start if needed */
|
||||
@@ -310,6 +315,15 @@ static void zynqmp_qspi_fill_gen_fifo(struct zynqmp_qspi_priv *priv,
|
||||
if (ret)
|
||||
log_warning("%s, Timeout\n", __func__);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int zynqmp_qspi_fill_gen_fifo(struct zynqmp_qspi_priv *priv,
|
||||
u32 gqspi_fifo_reg)
|
||||
{
|
||||
zynqmp_qspi_write_gen_fifo(priv, gqspi_fifo_reg);
|
||||
|
||||
return zynqmp_qspi_start_gen_fifo(priv);
|
||||
}
|
||||
|
||||
static void zynqmp_qspi_chipselect(struct zynqmp_qspi_priv *priv, int is_on)
|
||||
@@ -573,7 +587,7 @@ static void zynqmp_qspi_genfifo_cmd(struct zynqmp_qspi_priv *priv)
|
||||
gen_fifo_cmd |= zynqmp_qspi_genfifo_mode(op->cmd.buswidth);
|
||||
gen_fifo_cmd |= GQSPI_GFIFO_TX;
|
||||
gen_fifo_cmd |= op->cmd.opcode;
|
||||
zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd);
|
||||
zynqmp_qspi_write_gen_fifo(priv, gen_fifo_cmd);
|
||||
|
||||
/* Send address */
|
||||
for (i = 0; i < op->addr.nbytes; i++) {
|
||||
@@ -584,7 +598,7 @@ static void zynqmp_qspi_genfifo_cmd(struct zynqmp_qspi_priv *priv)
|
||||
gen_fifo_cmd |= GQSPI_GFIFO_TX;
|
||||
gen_fifo_cmd |= addr;
|
||||
|
||||
zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd);
|
||||
zynqmp_qspi_write_gen_fifo(priv, gen_fifo_cmd);
|
||||
}
|
||||
|
||||
/* Send dummy */
|
||||
@@ -596,7 +610,7 @@ static void zynqmp_qspi_genfifo_cmd(struct zynqmp_qspi_priv *priv)
|
||||
gen_fifo_cmd &= ~(GQSPI_GFIFO_TX | GQSPI_GFIFO_RX);
|
||||
gen_fifo_cmd |= GQSPI_GFIFO_DATA_XFR_MASK;
|
||||
gen_fifo_cmd |= dummy_cycles;
|
||||
zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd);
|
||||
zynqmp_qspi_write_gen_fifo(priv, gen_fifo_cmd);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -644,7 +658,9 @@ static int zynqmp_qspi_genfifo_fill_tx(struct zynqmp_qspi_priv *priv)
|
||||
|
||||
while (priv->len) {
|
||||
len = zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd);
|
||||
zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd);
|
||||
ret = zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (gen_fifo_cmd & GQSPI_GFIFO_EXP_MASK)
|
||||
ret = zynqmp_qspi_fill_tx_fifo(priv, 1 << len);
|
||||
@@ -666,6 +682,7 @@ static int zynqmp_qspi_start_io(struct zynqmp_qspi_priv *priv,
|
||||
struct zynqmp_qspi_regs *regs = priv->regs;
|
||||
u32 last_bits;
|
||||
u32 *traverse = buf;
|
||||
int ret;
|
||||
|
||||
while (priv->len) {
|
||||
len = zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd);
|
||||
@@ -674,7 +691,9 @@ static int zynqmp_qspi_start_io(struct zynqmp_qspi_priv *priv,
|
||||
priv->bytes_to_receive = (1 << len);
|
||||
else
|
||||
priv->bytes_to_receive = len;
|
||||
zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd);
|
||||
ret = zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Manual start */
|
||||
config_reg = readl(®s->confr);
|
||||
@@ -741,7 +760,9 @@ static int zynqmp_qspi_start_dma(struct zynqmp_qspi_priv *priv,
|
||||
|
||||
while (priv->len) {
|
||||
zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd);
|
||||
zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd);
|
||||
ret = zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = wait_for_bit_le32(&dma_regs->dmaisr,
|
||||
@@ -886,6 +907,8 @@ static int zynqmp_qspi_exec_op(struct spi_slave *slave,
|
||||
ret = zynqmp_qspi_genfifo_fill_rx(priv);
|
||||
else if (op->data.dir == SPI_MEM_DATA_OUT)
|
||||
ret = zynqmp_qspi_genfifo_fill_tx(priv);
|
||||
else
|
||||
ret = zynqmp_qspi_start_gen_fifo(priv);
|
||||
|
||||
zynqmp_qspi_chipselect(priv, 0);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user