]> git.ipfire.org Git - thirdparty/u-boot.git/commitdiff
zynqmp: spi: Added support for IO mode
authorMoon John C <John.Moon2@ngc.com>
Fri, 13 Apr 2018 07:40:46 +0000 (13:10 +0530)
committerMichal Simek <michal.simek@xilinx.com>
Mon, 23 Apr 2018 06:57:39 +0000 (08:57 +0200)
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>
drivers/spi/zynqmp_qspi.c

index b0a67f48f4450feb434e84dea6c5807f6faaeca1..9d4719d87c29db682c6758d855d8f63cde43b9c6 100644 (file)
@@ -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, &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);
@@ -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);
 }
@@ -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(&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) {
@@ -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)
 {
@@ -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,