From: Moon John C Date: Fri, 13 Apr 2018 07:40:46 +0000 (+0530) Subject: zynqmp: spi: Added support for IO mode X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=18f0c925017bc9f1b899e4b29d6d50e99ee7ba7e;p=thirdparty%2Fu-boot.git zynqmp: spi: Added support for IO mode 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 Signed-off-by: Vipul Kumar Signed-off-by: Michal Simek --- diff --git a/drivers/spi/zynqmp_qspi.c b/drivers/spi/zynqmp_qspi.c index b0a67f48f44..9d4719d87c2 100644 --- a/drivers/spi/zynqmp_qspi.c +++ b/drivers/spi/zynqmp_qspi.c @@ -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) @@ -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 @@ -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 { @@ -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; @@ -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); @@ -268,14 +275,21 @@ static void zynqmp_qspi_init_hw(struct zynqmp_qspi_priv *priv) writel(ZYNQMP_QSPI_GFIFO_ALL_INT_MASK, ®s->idisr); writel(ZYNQMP_QSPI_FIFO_THRESHOLD, ®s->txftr); writel(ZYNQMP_QSPI_FIFO_THRESHOLD, ®s->rxftr); + writel(ZYNQMP_QSPI_GENFIFO_THRESHOLD, ®s->gqfthr); writel(ZYNQMP_QSPI_GFIFO_ALL_INT_MASK, ®s->isr); + writel(0x0, ®s->enbr); config_reg = readl(®s->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, ®s->confr); writel(ZYNQMP_QSPI_ENABLE_ENABLE_MASK, ®s->enbr); @@ -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(®s->confr); + /* Manual start if needed */ + if (config_reg & ZYNQMP_QSPI_GEN_FIFO_STRT_MOD) { + config_reg |= ZYNQMP_QSPI_STRT_GEN_FIFO; + writel(config_reg, ®s->confr); + + /* Enable interrupts */ + ier = readl(®s->ier); + ier |= ZYNQMP_QSPI_IXR_ALL_MASK; + writel(ier, ®s->ier); + } + + /* Wait until the fifo is not full to write the new command */ do { reg = readl(®s->isr); - } while (!(reg & ZYNQMP_QSPI_IXR_GFEMTY_MASK)); + } while (!(reg & ZYNQMP_QSPI_IXR_GFNFULL_MASK)); writel(gqspi_fifo_reg, ®s->genfifo); } @@ -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", @@ -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; @@ -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(®s->isr), size); + config_reg = readl(®s->confr); + /* Manual start if needed */ + if (config_reg & ZYNQMP_QSPI_GEN_FIFO_STRT_MOD) { + config_reg |= ZYNQMP_QSPI_STRT_GEN_FIFO; + writel(config_reg, ®s->confr); + /* Enable interrupts */ + ier = readl(®s->ier); + ier |= ZYNQMP_QSPI_IXR_ALL_MASK; + writel(ier, ®s->ier); + } + while (size && timeout) { if (readl(®s->isr) & ZYNQMP_QSPI_IXR_TXNFULL_MASK) { @@ -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(®s->confr); + config_reg |= ZYNQMP_QSPI_STRT_GEN_FIFO; + writel(config_reg, ®s->confr); + /* Enable RX interrupts for IO mode */ + ier = readl(®s->ier); + ier |= ZYNQMP_QSPI_IXR_ALL_MASK; + writel(ier, ®s->ier); + while (priv->bytes_to_receive && timeout) { + isr = readl(®s->isr); + if (isr & ZYNQMP_QSPI_IXR_RXNEMTY_MASK) { + if (priv->bytes_to_receive >= 4) { + *traverse = readl(®s->drxr); + traverse++; + priv->bytes_to_receive -= 4; + } else { + last_bits = readl(®s->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(®s->isr)); + return -1; + } + } + + return 0; +} + static int zynqmp_qspi_start_dma(struct zynqmp_qspi_priv *priv, u32 gen_fifo_cmd, u32 *buf) { @@ -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,