Skip to content

Commit

Permalink
zynqmp: spi: Added support for IO mode
Browse files Browse the repository at this point in the history
This patch added support for device tree "has-io-mode" flag.
This forced the driver to use IO mode instead of DMA. This
flag is necessary for UBIFS to operate correctly with SPI-NOR
devices.

Signed-off-by: Moon John C <John.Moon2@ngc.com>
Signed-off-by: Vipul Kumar <vipul.kumar@xilinx.com>
Signed-off-by: Michal Simek <michal.simek@xilinx.com>
  • Loading branch information
Moon John C authored and Michal Simek committed Apr 23, 2018
1 parent a79f0e8 commit 18f0c92
Showing 1 changed file with 112 additions and 10 deletions.
122 changes: 112 additions & 10 deletions drivers/spi/zynqmp_qspi.c
Expand Up @@ -42,6 +42,7 @@ DECLARE_GLOBAL_DATA_PTR;
#define ZYNQMP_QSPI_IXR_TXFULL_MASK 0x00000008 /* QSPI TX FIFO is full */
#define ZYNQMP_QSPI_IXR_RXNEMTY_MASK 0x00000010 /* QSPI RX FIFO Not Empty */
#define ZYNQMP_QSPI_IXR_GFEMTY_MASK 0x00000080 /* QSPI Generic FIFO Empty */
#define ZYNQMP_QSPI_IXR_GFNFULL_MASK 0x00000200 /* QSPI GENFIFO not full */
#define ZYNQMP_QSPI_IXR_ALL_MASK (ZYNQMP_QSPI_IXR_TXNFULL_MASK | \
ZYNQMP_QSPI_IXR_RXNEMTY_MASK)

Expand Down Expand Up @@ -84,6 +85,7 @@ DECLARE_GLOBAL_DATA_PTR;
#define ZYNQMP_QSPI_GFIFO_SELECT (1 << 0)

#define ZYNQMP_QSPI_FIFO_THRESHOLD 1
#define ZYNQMP_QSPI_GENFIFO_THRESHOLD 31

#define SPI_XFER_ON_BOTH 0
#define SPI_XFER_ON_LOWER 1
Expand Down Expand Up @@ -148,6 +150,7 @@ struct zynqmp_qspi_platdata {
u32 speed_hz;
unsigned int is_dual;
unsigned int tx_rx_mode;
unsigned int io_mode;
};

struct zynqmp_qspi_priv {
Expand All @@ -167,6 +170,7 @@ struct zynqmp_qspi_priv {
unsigned cs_change:1;
unsigned int dummy_bytes;
unsigned int tx_rx_mode;
unsigned int io_mode;
};

static u8 last_cmd;
Expand Down Expand Up @@ -219,6 +223,9 @@ static int zynqmp_qspi_ofdata_to_platdata(struct udevice *bus)
else
plat->is_dual = SF_DUAL_STACKED_FLASH;

plat->io_mode = fdtdec_get_bool(gd->fdt_blob, dev_of_offset(bus),
"has-io-mode");

offset = fdt_first_subnode(gd->fdt_blob, dev_of_offset(bus));

value = fdtdec_get_uint(gd->fdt_blob, offset, "spi-rx-bus-width", 1);
Expand Down Expand Up @@ -268,14 +275,21 @@ static void zynqmp_qspi_init_hw(struct zynqmp_qspi_priv *priv)
writel(ZYNQMP_QSPI_GFIFO_ALL_INT_MASK, &regs->idisr);
writel(ZYNQMP_QSPI_FIFO_THRESHOLD, &regs->txftr);
writel(ZYNQMP_QSPI_FIFO_THRESHOLD, &regs->rxftr);
writel(ZYNQMP_QSPI_GENFIFO_THRESHOLD, &regs->gqfthr);
writel(ZYNQMP_QSPI_GFIFO_ALL_INT_MASK, &regs->isr);
writel(0x0, &regs->enbr);

config_reg = readl(&regs->confr);
config_reg &= ~(ZYNQMP_QSPI_GFIFO_STRT_MODE_MASK |
ZYNQMP_QSPI_CONFIG_MODE_EN_MASK);
config_reg |= ZYNQMP_QSPI_CONFIG_DMA_MODE |
ZYNQMP_QSPI_GFIFO_WP_HOLD |
config_reg &= ~(ZYNQMP_QSPI_CONFIG_MODE_EN_MASK);
config_reg |= ZYNQMP_QSPI_GFIFO_WP_HOLD |
ZYNQMP_QSPI_DFLT_BAUD_RATE_DIV;
if (priv->io_mode) {
config_reg |= ZYNQMP_QSPI_GFIFO_STRT_MODE_MASK;
} else {
config_reg &= ~(ZYNQMP_QSPI_GFIFO_STRT_MODE_MASK);
config_reg |= ZYNQMP_QSPI_CONFIG_DMA_MODE;
}

writel(config_reg, &regs->confr);

writel(ZYNQMP_QSPI_ENABLE_ENABLE_MASK, &regs->enbr);
Expand Down Expand Up @@ -316,11 +330,24 @@ static void zynqmp_qspi_fill_gen_fifo(struct zynqmp_qspi_priv *priv,
u32 gqspi_fifo_reg)
{
struct zynqmp_qspi_regs *regs = priv->regs;
u32 reg;
u32 reg, config_reg, ier;

config_reg = readl(&regs->confr);
/* Manual start if needed */
if (config_reg & ZYNQMP_QSPI_GEN_FIFO_STRT_MOD) {
config_reg |= ZYNQMP_QSPI_STRT_GEN_FIFO;
writel(config_reg, &regs->confr);

/* Enable interrupts */
ier = readl(&regs->ier);
ier |= ZYNQMP_QSPI_IXR_ALL_MASK;
writel(ier, &regs->ier);
}

/* Wait until the fifo is not full to write the new command */
do {
reg = readl(&regs->isr);
} while (!(reg & ZYNQMP_QSPI_IXR_GFEMTY_MASK));
} while (!(reg & ZYNQMP_QSPI_IXR_GFNFULL_MASK));

writel(gqspi_fifo_reg, &regs->genfifo);
}
Expand Down Expand Up @@ -470,6 +497,7 @@ static int zynqmp_qspi_probe(struct udevice *bus)
priv->dma_regs = plat->dma_regs;
priv->is_dual = plat->is_dual;
priv->tx_rx_mode = plat->tx_rx_mode;
priv->io_mode = plat->io_mode;

if (priv->is_dual == -1) {
debug("%s: No QSPI device detected based on MIO settings\n",
Expand Down Expand Up @@ -511,7 +539,7 @@ static int zynqmp_qspi_set_mode(struct udevice *bus, uint mode)

static int zynqmp_qspi_fill_tx_fifo(struct zynqmp_qspi_priv *priv, u32 size)
{
u32 data;
u32 data, config_reg, ier;
u32 timeout = ZYNQMP_QSPI_TIMEOUT;
struct zynqmp_qspi_regs *regs = priv->regs;
u32 *buf = (u32 *)priv->tx_buf;
Expand All @@ -520,6 +548,17 @@ static int zynqmp_qspi_fill_tx_fifo(struct zynqmp_qspi_priv *priv, u32 size)
debug("TxFIFO: 0x%x, size: 0x%x\n", readl(&regs->isr),
size);

config_reg = readl(&regs->confr);
/* Manual start if needed */
if (config_reg & ZYNQMP_QSPI_GEN_FIFO_STRT_MOD) {
config_reg |= ZYNQMP_QSPI_STRT_GEN_FIFO;
writel(config_reg, &regs->confr);
/* Enable interrupts */
ier = readl(&regs->ier);
ier |= ZYNQMP_QSPI_IXR_ALL_MASK;
writel(ier, &regs->ier);
}

while (size && timeout) {
if (readl(&regs->isr) &
ZYNQMP_QSPI_IXR_TXNFULL_MASK) {
Expand Down Expand Up @@ -672,6 +711,66 @@ static int zynqmp_qspi_genfifo_fill_tx(struct zynqmp_qspi_priv *priv)
return ret;
}

static int zynqmp_qspi_start_io(struct zynqmp_qspi_priv *priv,
u32 gen_fifo_cmd, u32 *buf)
{
u32 len;
u32 actuallen = priv->len;
u32 config_reg, ier, isr;
u32 timeout = ZYNQMP_QSPI_TIMEOUT;
struct zynqmp_qspi_regs *regs = priv->regs;
u32 last_bits;
u32 *traverse = buf;

while (priv->len) {
len = zynqmp_qspi_calc_exp(priv, &gen_fifo_cmd);
/* If exponent bit is set, reset immediate to be 2^len */
if (gen_fifo_cmd & ZYNQMP_QSPI_GFIFO_EXP_MASK)
priv->bytes_to_receive = (1 << len);
else
priv->bytes_to_receive = len;
zynqmp_qspi_fill_gen_fifo(priv, gen_fifo_cmd);
debug("GFIFO_CMD_RX:0x%x\n", gen_fifo_cmd);
/* Manual start */
config_reg = readl(&regs->confr);
config_reg |= ZYNQMP_QSPI_STRT_GEN_FIFO;
writel(config_reg, &regs->confr);
/* Enable RX interrupts for IO mode */
ier = readl(&regs->ier);
ier |= ZYNQMP_QSPI_IXR_ALL_MASK;
writel(ier, &regs->ier);
while (priv->bytes_to_receive && timeout) {
isr = readl(&regs->isr);
if (isr & ZYNQMP_QSPI_IXR_RXNEMTY_MASK) {
if (priv->bytes_to_receive >= 4) {
*traverse = readl(&regs->drxr);
traverse++;
priv->bytes_to_receive -= 4;
} else {
last_bits = readl(&regs->drxr);
memcpy(traverse, &last_bits,
priv->bytes_to_receive);
priv->bytes_to_receive = 0;
}
timeout = ZYNQMP_QSPI_TIMEOUT;
} else {
udelay(1);
timeout--;
}
}

debug("buf:0x%lx, rxbuf:0x%lx, *buf:0x%x len: 0x%x\n",
(unsigned long)buf, (unsigned long)priv->rx_buf,
*buf, actuallen);
if (!timeout) {
printf("IO timeout: %d\n", readl(&regs->isr));
return -1;
}
}

return 0;
}

static int zynqmp_qspi_start_dma(struct zynqmp_qspi_priv *priv,
u32 gen_fifo_cmd, u32 *buf)
{
Expand Down Expand Up @@ -749,10 +848,13 @@ static int zynqmp_qspi_genfifo_fill_rx(struct zynqmp_qspi_priv *priv)
* Check if receive buffer is aligned to 4 byte and length
* is multiples of four byte as we are using dma to receive.
*/
if (!((unsigned long)priv->rx_buf & (ZYNQMP_QSPI_DMA_ALIGN - 1)) &&
!(actuallen % ZYNQMP_QSPI_DMA_ALIGN)) {
if ((!((unsigned long)priv->rx_buf & (ZYNQMP_QSPI_DMA_ALIGN - 1)) &&
!(actuallen % ZYNQMP_QSPI_DMA_ALIGN)) || priv->io_mode) {
buf = (u32 *)priv->rx_buf;
return zynqmp_qspi_start_dma(priv, gen_fifo_cmd, buf);
if (priv->io_mode)
return zynqmp_qspi_start_io(priv, gen_fifo_cmd, buf);
else
return zynqmp_qspi_start_dma(priv, gen_fifo_cmd, buf);
}

ALLOC_CACHE_ALIGN_BUFFER(u8, tmp, roundup(priv->len,
Expand Down

0 comments on commit 18f0c92

Please sign in to comment.