spl: nand: sunxi: use PIO instead of DMA
authorMiquel Raynal <miquel.raynal@bootlin.com>
Wed, 28 Feb 2018 19:51:55 +0000 (20:51 +0100)
committerMaxime Ripard <maxime.ripard@bootlin.com>
Tue, 3 Apr 2018 10:11:43 +0000 (12:11 +0200)
SPL support was first written to support only the earlier generations of
Allwinner SoCs, and was only really enabled on the A13 / GR8. However,
those old SoCs had a DMA engine that has been replaced since the A31 by
another DMA controller that is no longer compatible.

Since the code directly uses that DMA controller, it cannot operate
properly on the later SoCs, while the NAND controller has not changed.

There's two paths forward, the first one would have been to add support
for that DMA controller too, the second to just remove the DMA usage
entirely and rely on PIO.

The later has been chosen because CPU overload at this stage is not an
issue and it makes the driver more generic, and easier to understand.

Signed-off-by: Miquel Raynal <miquel.raynal@bootlin.com>
Acked-by: Boris Brezillon <boris.brezillon@bootlin.com>
Signed-off-by: Maxime Ripard <maxime.ripard@bootlin.com>
drivers/mtd/nand/sunxi_nand_spl.c

index af1e964effe4244e5d484c9279284e943a64f565..7241e9a374ab0bb0533ce7c9e8932bdcec5f2ed0 100644 (file)
@@ -10,6 +10,7 @@
 #include <common.h>
 #include <config.h>
 #include <nand.h>
+#include <linux/ctype.h>
 
 /* registers */
 #define NFC_CTL                    0x00000000
 #define NFC_SEND_CMD3              (1 << 28)
 #define NFC_SEND_CMD4              (1 << 29)
 #define NFC_RAW_CMD                (0 << 30)
+#define NFC_ECC_CMD                (1 << 30)
 #define NFC_PAGE_CMD               (2 << 30)
 
 #define NFC_ST_CMD_INT_FLAG        (1 << 1)
 #define NFC_ST_DMA_INT_FLAG        (1 << 2)
+#define NFC_ST_CMD_FIFO_STAT       (1 << 3)
 
 #define NFC_READ_CMD_OFFSET         0
 #define NFC_RANDOM_READ_CMD0_OFFSET 8
 #define NFC_CMD_RNDOUT             0x05
 #define NFC_CMD_READSTART          0x30
 
-#define SUNXI_DMA_CFG_REG0              0x300
-#define SUNXI_DMA_SRC_START_ADDR_REG0   0x304
-#define SUNXI_DMA_DEST_START_ADDRR_REG0 0x308
-#define SUNXI_DMA_DDMA_BC_REG0          0x30C
-#define SUNXI_DMA_DDMA_PARA_REG0        0x318
-
-#define SUNXI_DMA_DDMA_CFG_REG_LOADING  (1 << 31)
-#define SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 (2 << 25)
-#define SUNXI_DMA_DDMA_CFG_REG_DDMA_DST_DRQ_TYPE_DRAM (1 << 16)
-#define SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 (2 << 9)
-#define SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO (1 << 5)
-#define SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC (3 << 0)
-
-#define SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC (0x0F << 0)
-#define SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE (0x7F << 8)
-
 struct nfc_config {
        int page_size;
        int ecc_strength;
@@ -268,86 +255,74 @@ static const int ecc_bytes[] = {32, 46, 54, 60, 74, 88, 102, 110, 116};
 static int nand_read_page(const struct nfc_config *conf, u32 offs,
                          void *dest, int len)
 {
-       dma_addr_t dst = (dma_addr_t)dest;
        int nsectors = len / conf->ecc_size;
        u16 rand_seed = 0;
-       u32 val;
-       int page;
-
-       page = offs / conf->page_size;
+       int oob_chunk_sz = ecc_bytes[conf->ecc_strength];
+       int page = offs / conf->page_size;
+       u32 ecc_st;
+       int i;
 
        if (offs % conf->page_size || len % conf->ecc_size ||
            len > conf->page_size || len < 0)
                return -EINVAL;
 
-       /* clear ecc status */
-       writel(0, SUNXI_NFC_BASE + NFC_ECC_ST);
-
        /* Choose correct seed if randomized */
        if (conf->randomize)
                rand_seed = random_seed[page % conf->nseeds];
 
-       writel((rand_seed << 16) | (conf->ecc_strength << 12) |
-               (conf->randomize ? NFC_ECC_RANDOM_EN : 0) |
-               (conf->ecc_size == 512 ? NFC_ECC_BLOCK_SIZE : 0) |
-               NFC_ECC_EN | NFC_ECC_PIPELINE | NFC_ECC_EXCEPTION,
-               SUNXI_NFC_BASE + NFC_ECC_CTL);
-
-       flush_dcache_range(dst, ALIGN(dst + conf->ecc_size, ARCH_DMA_MINALIGN));
-
-       /* SUNXI_DMA */
-       writel(0x0, SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); /* clr dma cmd */
-       /* read from REG_IO_DATA */
-       writel(SUNXI_NFC_BASE + NFC_IO_DATA,
-              SUNXI_DMA_BASE + SUNXI_DMA_SRC_START_ADDR_REG0);
-       /* read to RAM */
-       writel(dst, SUNXI_DMA_BASE + SUNXI_DMA_DEST_START_ADDRR_REG0);
-       writel(SUNXI_DMA_DDMA_PARA_REG_SRC_WAIT_CYC |
-              SUNXI_DMA_DDMA_PARA_REG_SRC_BLK_SIZE,
-              SUNXI_DMA_BASE + SUNXI_DMA_DDMA_PARA_REG0);
-       writel(len, SUNXI_DMA_BASE + SUNXI_DMA_DDMA_BC_REG0);
-       writel(SUNXI_DMA_DDMA_CFG_REG_LOADING |
-              SUNXI_DMA_DDMA_CFG_REG_DMA_DEST_DATA_WIDTH_32 |
-              SUNXI_DMA_DDMA_CFG_REG_DDMA_DST_DRQ_TYPE_DRAM |
-              SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_DATA_WIDTH_32 |
-              SUNXI_DMA_DDMA_CFG_REG_DMA_SRC_ADDR_MODE_IO |
-              SUNXI_DMA_DDMA_CFG_REG_DDMA_SRC_DRQ_TYPE_NFC,
-              SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0);
-
-       writel(nsectors, SUNXI_NFC_BASE + NFC_SECTOR_NUM);
-       writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
-       writel(NFC_DATA_TRANS | NFC_PAGE_CMD | NFC_DATA_SWAP_METHOD,
-              SUNXI_NFC_BASE + NFC_CMD);
-
-       if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_ST_DMA_INT_FLAG,
-                        DEFAULT_TIMEOUT_US)) {
-               printf("Error while initializing dma interrupt\n");
-               return -EIO;
-       }
-       writel(NFC_ST_DMA_INT_FLAG, SUNXI_NFC_BASE + NFC_ST);
+       /* Retrieve data from SRAM (PIO) */
+       for (i = 0; i < nsectors; i++) {
+               int data_off = i * conf->ecc_size;
+               int oob_off = conf->page_size + (i * oob_chunk_sz);
+               u8 *data = dest + data_off;
+
+               /* Clear ECC status and restart ECC engine */
+               writel(0, SUNXI_NFC_BASE + NFC_ECC_ST);
+               writel((rand_seed << 16) | (conf->ecc_strength << 12) |
+                      (conf->randomize ? NFC_ECC_RANDOM_EN : 0) |
+                      (conf->ecc_size == 512 ? NFC_ECC_BLOCK_SIZE : 0) |
+                      NFC_ECC_EN | NFC_ECC_EXCEPTION,
+                      SUNXI_NFC_BASE + NFC_ECC_CTL);
+
+               /* Move the data in SRAM */
+               nand_change_column(data_off);
+               writel(conf->ecc_size, SUNXI_NFC_BASE + NFC_CNT);
+               nand_exec_cmd(NFC_DATA_TRANS);
 
-       if (!check_value_negated(SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0,
-                                SUNXI_DMA_DDMA_CFG_REG_LOADING,
-                                DEFAULT_TIMEOUT_US)) {
-               printf("Error while waiting for dma transfer to finish\n");
-               return -EIO;
-       }
+               /*
+                * Let the ECC engine consume the ECC bytes and possibly correct
+                * the data.
+                */
+               nand_change_column(oob_off);
+               nand_exec_cmd(NFC_DATA_TRANS | NFC_ECC_CMD);
 
-       invalidate_dcache_range(dst,
-                               ALIGN(dst + conf->ecc_size, ARCH_DMA_MINALIGN));
+               /* Get the ECC status */
+               ecc_st = readl(SUNXI_NFC_BASE + NFC_ECC_ST);
 
-       val = readl(SUNXI_NFC_BASE + NFC_ECC_ST);
+               /* ECC error detected. */
+               if (ecc_st & 0xffff)
+                       return -EIO;
 
-       /* ECC error detected. */
-       if (val & 0xffff)
-               return -EIO;
+               /*
+                * Return 1 if the first chunk is empty (needed for
+                * configuration detection).
+                */
+               if (!i && (ecc_st & 0x10000))
+                       return 1;
 
-       /*
-        * Return 1 if the page is empty.
-        * We consider the page as empty if the first ECC block is marked
-        * empty.
-        */
-       return (val & 0x10000) ? 1 : 0;
+               /* Retrieve the data from SRAM */
+               memcpy_fromio(data, SUNXI_NFC_BASE + NFC_RAM0_BASE,
+                             conf->ecc_size);
+
+               /* Stop the ECC engine */
+               writel(readl(SUNXI_NFC_BASE + NFC_ECC_CTL) & ~NFC_ECC_EN,
+                      SUNXI_NFC_BASE + NFC_ECC_CTL);
+
+               if (data_off + conf->ecc_size >= len)
+                       break;
+       }
+
+       return 0;
 }
 
 static int nand_max_ecc_strength(struct nfc_config *conf)