]> git.sur5r.net Git - u-boot/blobdiff - drivers/spi/cadence_qspi_apb.c
spi: cadence_qspi_apb: Support 32 bit AHB address
[u-boot] / drivers / spi / cadence_qspi_apb.c
index 00a115f3fba74e8f1937b2936886d4417cb5fd63..a8cc23f0b6e8a69ddee67c708cc331ee883c574b 100644 (file)
@@ -28,6 +28,7 @@
 #include <common.h>
 #include <asm/io.h>
 #include <asm/errno.h>
+#include <wait_bit.h>
 #include "cadence_qspi.h"
 
 #define CQSPI_REG_POLL_US                      (1) /* 1us */
 
 #define CQSPI_FIFO_WIDTH                       (4)
 
-/* Controller sram size in word */
-#define CQSPI_REG_SRAM_SIZE_WORD               (128)
-#define CQSPI_REG_SRAM_RESV_WORDS              (2)
-#define CQSPI_REG_SRAM_PARTITION_WR            (1)
-#define CQSPI_REG_SRAM_PARTITION_RD            \
-       (CQSPI_REG_SRAM_SIZE_WORD - CQSPI_REG_SRAM_RESV_WORDS)
 #define CQSPI_REG_SRAM_THRESHOLD_WORDS         (50)
 
 /* Transfer mode */
@@ -50,7 +45,6 @@
 #define CQSPI_INST_TYPE_QUAD                   (2)
 
 #define CQSPI_STIG_DATA_LEN_MAX                        (8)
-#define CQSPI_INDIRECTTRIGGER_ADDR_MASK                (0xFFFFF)
 
 #define CQSPI_DUMMY_CLKS_PER_BYTE              (8)
 #define CQSPI_DUMMY_BYTES_MAX                  (4)
 #define        CQSPI_REG_CONFIG                        0x00
 #define        CQSPI_REG_CONFIG_CLK_POL_LSB            1
 #define        CQSPI_REG_CONFIG_CLK_PHA_LSB            2
-#define        CQSPI_REG_CONFIG_ENABLE_MASK            (1 << 0)
-#define        CQSPI_REG_CONFIG_DIRECT_MASK            (1 << 7)
-#define        CQSPI_REG_CONFIG_DECODE_MASK            (1 << 9)
-#define        CQSPI_REG_CONFIG_XIP_IMM_MASK           (1 << 18)
+#define        CQSPI_REG_CONFIG_ENABLE_MASK            BIT(0)
+#define        CQSPI_REG_CONFIG_DIRECT_MASK            BIT(7)
+#define        CQSPI_REG_CONFIG_DECODE_MASK            BIT(9)
+#define        CQSPI_REG_CONFIG_XIP_IMM_MASK           BIT(18)
 #define        CQSPI_REG_CONFIG_CHIPSELECT_LSB         10
 #define        CQSPI_REG_CONFIG_BAUD_LSB               19
 #define        CQSPI_REG_CONFIG_IDLE_LSB               31
 #define        CQSPI_REG_IRQMASK                       0x44
 
 #define        CQSPI_REG_INDIRECTRD                    0x60
-#define        CQSPI_REG_INDIRECTRD_START_MASK         (1 << 0)
-#define        CQSPI_REG_INDIRECTRD_CANCEL_MASK        (1 << 1)
-#define        CQSPI_REG_INDIRECTRD_INPROGRESS_MASK    (1 << 2)
-#define        CQSPI_REG_INDIRECTRD_DONE_MASK          (1 << 5)
+#define        CQSPI_REG_INDIRECTRD_START_MASK         BIT(0)
+#define        CQSPI_REG_INDIRECTRD_CANCEL_MASK        BIT(1)
+#define        CQSPI_REG_INDIRECTRD_INPROGRESS_MASK    BIT(2)
+#define        CQSPI_REG_INDIRECTRD_DONE_MASK          BIT(5)
 
 #define        CQSPI_REG_INDIRECTRDWATERMARK           0x64
 #define        CQSPI_REG_INDIRECTRDSTARTADDR           0x68
 #define        CQSPI_REG_INDIRECTRDBYTES               0x6C
 
 #define        CQSPI_REG_CMDCTRL                       0x90
-#define        CQSPI_REG_CMDCTRL_EXECUTE_MASK          (1 << 0)
-#define        CQSPI_REG_CMDCTRL_INPROGRESS_MASK       (1 << 1)
+#define        CQSPI_REG_CMDCTRL_EXECUTE_MASK          BIT(0)
+#define        CQSPI_REG_CMDCTRL_INPROGRESS_MASK       BIT(1)
 #define        CQSPI_REG_CMDCTRL_DUMMY_LSB             7
 #define        CQSPI_REG_CMDCTRL_WR_BYTES_LSB          12
 #define        CQSPI_REG_CMDCTRL_WR_EN_LSB             15
 #define        CQSPI_REG_CMDCTRL_OPCODE_MASK           0xFF
 
 #define        CQSPI_REG_INDIRECTWR                    0x70
-#define        CQSPI_REG_INDIRECTWR_START_MASK         (1 << 0)
-#define        CQSPI_REG_INDIRECTWR_CANCEL_MASK        (1 << 1)
-#define        CQSPI_REG_INDIRECTWR_INPROGRESS_MASK    (1 << 2)
-#define        CQSPI_REG_INDIRECTWR_DONE_MASK          (1 << 5)
+#define        CQSPI_REG_INDIRECTWR_START_MASK         BIT(0)
+#define        CQSPI_REG_INDIRECTWR_CANCEL_MASK        BIT(1)
+#define        CQSPI_REG_INDIRECTWR_INPROGRESS_MASK    BIT(2)
+#define        CQSPI_REG_INDIRECTWR_DONE_MASK          BIT(5)
 
 #define        CQSPI_REG_INDIRECTWRWATERMARK           0x74
 #define        CQSPI_REG_INDIRECTWRSTARTADDR           0x78
@@ -198,128 +192,6 @@ static unsigned int cadence_qspi_apb_cmd2addr(const unsigned char *addr_buf,
        return addr;
 }
 
-static void cadence_qspi_apb_read_fifo_data(void *dest,
-       const void *src_ahb_addr, unsigned int bytes)
-{
-       unsigned int temp;
-       int remaining = bytes;
-       unsigned int *dest_ptr = (unsigned int *)dest;
-       unsigned int *src_ptr = (unsigned int *)src_ahb_addr;
-
-       while (remaining > 0) {
-               if (remaining >= CQSPI_FIFO_WIDTH) {
-                       *dest_ptr = readl(src_ptr);
-                       remaining -= CQSPI_FIFO_WIDTH;
-               } else {
-                       /* dangling bytes */
-                       temp = readl(src_ptr);
-                       memcpy(dest_ptr, &temp, remaining);
-                       break;
-               }
-               dest_ptr++;
-       }
-
-       return;
-}
-
-static void cadence_qspi_apb_write_fifo_data(const void *dest_ahb_addr,
-       const void *src, unsigned int bytes)
-{
-       unsigned int temp;
-       int remaining = bytes;
-       unsigned int *dest_ptr = (unsigned int *)dest_ahb_addr;
-       unsigned int *src_ptr = (unsigned int *)src;
-
-       while (remaining > 0) {
-               if (remaining >= CQSPI_FIFO_WIDTH) {
-                       writel(*src_ptr, dest_ptr);
-                       remaining -= sizeof(unsigned int);
-               } else {
-                       /* dangling bytes */
-                       memcpy(&temp, src_ptr, remaining);
-                       writel(temp, dest_ptr);
-                       break;
-               }
-               src_ptr++;
-       }
-
-       return;
-}
-
-/* Read from SRAM FIFO with polling SRAM fill level. */
-static int qspi_read_sram_fifo_poll(const void *reg_base, void *dest_addr,
-                       const void *src_addr,  unsigned int num_bytes)
-{
-       unsigned int remaining = num_bytes;
-       unsigned int retry;
-       unsigned int sram_level = 0;
-       unsigned char *dest = (unsigned char *)dest_addr;
-
-       while (remaining > 0) {
-               retry = CQSPI_REG_RETRY;
-               while (retry--) {
-                       sram_level = CQSPI_GET_RD_SRAM_LEVEL(reg_base);
-                       if (sram_level)
-                               break;
-                       udelay(1);
-               }
-
-               if (!retry) {
-                       printf("QSPI: No receive data after polling for %d times\n",
-                              CQSPI_REG_RETRY);
-                       return -1;
-               }
-
-               sram_level *= CQSPI_FIFO_WIDTH;
-               sram_level = sram_level > remaining ? remaining : sram_level;
-
-               /* Read data from FIFO. */
-               cadence_qspi_apb_read_fifo_data(dest, src_addr, sram_level);
-               dest += sram_level;
-               remaining -= sram_level;
-               udelay(1);
-       }
-       return 0;
-}
-
-/* Write to SRAM FIFO with polling SRAM fill level. */
-static int qpsi_write_sram_fifo_push(struct cadence_spi_platdata *plat,
-                               const void *src_addr, unsigned int num_bytes)
-{
-       const void *reg_base = plat->regbase;
-       void *dest_addr = plat->ahbbase;
-       unsigned int retry = CQSPI_REG_RETRY;
-       unsigned int sram_level;
-       unsigned int wr_bytes;
-       unsigned char *src = (unsigned char *)src_addr;
-       int remaining = num_bytes;
-       unsigned int page_size = plat->page_size;
-       unsigned int sram_threshold_words = CQSPI_REG_SRAM_THRESHOLD_WORDS;
-
-       while (remaining > 0) {
-               retry = CQSPI_REG_RETRY;
-               while (retry--) {
-                       sram_level = CQSPI_GET_WR_SRAM_LEVEL(reg_base);
-                       if (sram_level <= sram_threshold_words)
-                               break;
-               }
-               if (!retry) {
-                       printf("QSPI: SRAM fill level (0x%08x) not hit lower expected level (0x%08x)",
-                              sram_level, sram_threshold_words);
-                       return -1;
-               }
-               /* Write a page or remaining bytes. */
-               wr_bytes = (remaining > page_size) ?
-                                       page_size : remaining;
-
-               cadence_qspi_apb_write_fifo_data(dest_addr, src, wr_bytes);
-               src += wr_bytes;
-               remaining -= wr_bytes;
-       }
-
-       return 0;
-}
-
 void cadence_qspi_apb_controller_enable(void *reg_base)
 {
        unsigned int reg;
@@ -538,6 +410,9 @@ void cadence_qspi_apb_controller_init(struct cadence_spi_platdata *plat)
        /* Configure the remap address register, no remap */
        writel(0, plat->regbase + CQSPI_REG_REMAP);
 
+       /* Indirect mode configurations */
+       writel((plat->sram_size/2), plat->regbase + CQSPI_REG_SRAMPARTITION);
+
        /* Disable all interrupts */
        writel(0, plat->regbase + CQSPI_REG_IRQMASK);
 
@@ -697,13 +572,9 @@ int cadence_qspi_apb_indirect_read_setup(struct cadence_spi_platdata *plat,
                addr_bytes = cmdlen - 1;
 
        /* Setup the indirect trigger address */
-       writel(((u32)plat->ahbbase & CQSPI_INDIRECTTRIGGER_ADDR_MASK),
+       writel((u32)plat->ahbbase,
               plat->regbase + CQSPI_REG_INDIRECTTRIGGER);
 
-       /* Configure SRAM partition for read. */
-       writel(CQSPI_REG_SRAM_PARTITION_RD, plat->regbase +
-              CQSPI_REG_SRAMPARTITION);
-
        /* Configure the opcode */
        rd_reg = cmdbuf[0] << CQSPI_REG_RD_INSTR_OPCODE_LSB;
 
@@ -749,40 +620,84 @@ int cadence_qspi_apb_indirect_read_setup(struct cadence_spi_platdata *plat,
        return 0;
 }
 
+static u32 cadence_qspi_get_rd_sram_level(struct cadence_spi_platdata *plat)
+{
+       u32 reg = readl(plat->regbase + CQSPI_REG_SDRAMLEVEL);
+       reg >>= CQSPI_REG_SDRAMLEVEL_RD_LSB;
+       return reg & CQSPI_REG_SDRAMLEVEL_RD_MASK;
+}
+
+static int cadence_qspi_wait_for_data(struct cadence_spi_platdata *plat)
+{
+       unsigned int timeout = 10000;
+       u32 reg;
+
+       while (timeout--) {
+               reg = cadence_qspi_get_rd_sram_level(plat);
+               if (reg)
+                       return reg;
+               udelay(1);
+       }
+
+       return -ETIMEDOUT;
+}
+
 int cadence_qspi_apb_indirect_read_execute(struct cadence_spi_platdata *plat,
-       unsigned int rxlen, u8 *rxbuf)
+       unsigned int n_rx, u8 *rxbuf)
 {
-       unsigned int reg;
+       unsigned int remaining = n_rx;
+       unsigned int bytes_to_read = 0;
+       int ret;
 
-       writel(rxlen, plat->regbase + CQSPI_REG_INDIRECTRDBYTES);
+       writel(n_rx, plat->regbase + CQSPI_REG_INDIRECTRDBYTES);
 
        /* Start the indirect read transfer */
        writel(CQSPI_REG_INDIRECTRD_START_MASK,
               plat->regbase + CQSPI_REG_INDIRECTRD);
 
-       if (qspi_read_sram_fifo_poll(plat->regbase, (void *)rxbuf,
-                                    (const void *)plat->ahbbase, rxlen))
-               goto failrd;
+       while (remaining > 0) {
+               ret = cadence_qspi_wait_for_data(plat);
+               if (ret < 0) {
+                       printf("Indirect write timed out (%i)\n", ret);
+                       goto failrd;
+               }
+
+               bytes_to_read = ret;
+
+               while (bytes_to_read != 0) {
+                       bytes_to_read *= CQSPI_FIFO_WIDTH;
+                       bytes_to_read = bytes_to_read > remaining ?
+                                       remaining : bytes_to_read;
+                       /* Handle non-4-byte aligned access to avoid data abort. */
+                       if (((uintptr_t)rxbuf % 4) || (bytes_to_read % 4))
+                               readsb(plat->ahbbase, rxbuf, bytes_to_read);
+                       else
+                               readsl(plat->ahbbase, rxbuf, bytes_to_read >> 2);
+                       rxbuf += bytes_to_read;
+                       remaining -= bytes_to_read;
+                       bytes_to_read = cadence_qspi_get_rd_sram_level(plat);
+               }
+       }
 
-       /* Check flash indirect controller */
-       reg = readl(plat->regbase + CQSPI_REG_INDIRECTRD);
-       if (!(reg & CQSPI_REG_INDIRECTRD_DONE_MASK)) {
-               reg = readl(plat->regbase + CQSPI_REG_INDIRECTRD);
-               printf("QSPI: indirect completion status error with reg 0x%08x\n",
-                      reg);
+       /* Check indirect done status */
+       ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_INDIRECTRD,
+                          CQSPI_REG_INDIRECTRD_DONE_MASK, 1, 10, 0);
+       if (ret) {
+               printf("Indirect read completion error (%i)\n", ret);
                goto failrd;
        }
 
        /* Clear indirect completion status */
        writel(CQSPI_REG_INDIRECTRD_DONE_MASK,
               plat->regbase + CQSPI_REG_INDIRECTRD);
+
        return 0;
 
 failrd:
        /* Cancel the indirect read */
        writel(CQSPI_REG_INDIRECTRD_CANCEL_MASK,
               plat->regbase + CQSPI_REG_INDIRECTRD);
-       return -1;
+       return ret;
 }
 
 /* Opcode + Address (3/4 bytes) */
@@ -798,12 +713,9 @@ int cadence_qspi_apb_indirect_write_setup(struct cadence_spi_platdata *plat,
                return -EINVAL;
        }
        /* Setup the indirect trigger address */
-       writel(((u32)plat->ahbbase & CQSPI_INDIRECTTRIGGER_ADDR_MASK),
+       writel((u32)plat->ahbbase,
               plat->regbase + CQSPI_REG_INDIRECTTRIGGER);
 
-       writel(CQSPI_REG_SRAM_PARTITION_WR,
-              plat->regbase + CQSPI_REG_SRAMPARTITION);
-
        /* Configure the opcode */
        reg = cmdbuf[0] << CQSPI_REG_WR_INSTR_OPCODE_LSB;
        writel(reg, plat->regbase + CQSPI_REG_WR_INSTR);
@@ -820,48 +732,45 @@ int cadence_qspi_apb_indirect_write_setup(struct cadence_spi_platdata *plat,
 }
 
 int cadence_qspi_apb_indirect_write_execute(struct cadence_spi_platdata *plat,
-       unsigned int txlen, const u8 *txbuf)
+       unsigned int n_tx, const u8 *txbuf)
 {
-       unsigned int reg = 0;
-       unsigned int retry;
+       unsigned int page_size = plat->page_size;
+       unsigned int remaining = n_tx;
+       unsigned int write_bytes;
+       int ret;
 
        /* Configure the indirect read transfer bytes */
-       writel(txlen, plat->regbase + CQSPI_REG_INDIRECTWRBYTES);
+       writel(n_tx, plat->regbase + CQSPI_REG_INDIRECTWRBYTES);
 
        /* Start the indirect write transfer */
        writel(CQSPI_REG_INDIRECTWR_START_MASK,
               plat->regbase + CQSPI_REG_INDIRECTWR);
 
-       if (qpsi_write_sram_fifo_push(plat, (const void *)txbuf, txlen))
-               goto failwr;
-
-       /* Wait until last write is completed (FIFO empty) */
-       retry = CQSPI_REG_RETRY;
-       while (retry--) {
-               reg = CQSPI_GET_WR_SRAM_LEVEL(plat->regbase);
-               if (reg == 0)
-                       break;
-
-               udelay(1);
-       }
-
-       if (reg != 0) {
-               printf("QSPI: timeout for indirect write\n");
-               goto failwr;
-       }
+       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);
+
+               ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_SDRAMLEVEL,
+                                  CQSPI_REG_SDRAMLEVEL_WR_MASK <<
+                                  CQSPI_REG_SDRAMLEVEL_WR_LSB, 0, 10, 0);
+               if (ret) {
+                       printf("Indirect write timed out (%i)\n", ret);
+                       goto failwr;
+               }
 
-       /* Check flash indirect controller status */
-       retry = CQSPI_REG_RETRY;
-       while (retry--) {
-               reg = readl(plat->regbase + CQSPI_REG_INDIRECTWR);
-               if (reg & CQSPI_REG_INDIRECTWR_DONE_MASK)
-                       break;
-               udelay(1);
+               txbuf += write_bytes;
+               remaining -= write_bytes;
        }
 
-       if (!(reg & CQSPI_REG_INDIRECTWR_DONE_MASK)) {
-               printf("QSPI: indirect completion status error with reg 0x%08x\n",
-                      reg);
+       /* Check indirect done status */
+       ret = wait_for_bit("QSPI", plat->regbase + CQSPI_REG_INDIRECTWR,
+                          CQSPI_REG_INDIRECTWR_DONE_MASK, 1, 10, 0);
+       if (ret) {
+               printf("Indirect write completion error (%i)\n", ret);
                goto failwr;
        }
 
@@ -874,7 +783,7 @@ failwr:
        /* Cancel the indirect write */
        writel(CQSPI_REG_INDIRECTWR_CANCEL_MASK,
               plat->regbase + CQSPI_REG_INDIRECTWR);
-       return -1;
+       return ret;
 }
 
 void cadence_qspi_apb_enter_xip(void *reg_base, char xip_dummy)