]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - drivers/mtd/nand/sunxi_nand_spl.c
sunxi_nand_spl: Properly config page-size in the nand ctl register
[karo-tx-uboot.git] / drivers / mtd / nand / sunxi_nand_spl.c
index 46654e4ef66a884468ea9eaa2d02e349794ef703..872cedfd5e8237200167e91259b663a641a448b7 100644 (file)
@@ -5,9 +5,10 @@
  * SPDX-License-Identifier:     GPL-2.0+
  */
 
+#include <asm/arch/clock.h>
+#include <asm/io.h>
 #include <common.h>
 #include <config.h>
-#include <asm/io.h>
 #include <nand.h>
 
 /* registers */
@@ -41,6 +42,8 @@
 #define NFC_CTL_EN                 (1 << 0)
 #define NFC_CTL_RESET              (1 << 1)
 #define NFC_CTL_RAM_METHOD         (1 << 14)
+#define NFC_CTL_PAGE_SIZE_MASK     (0xf << 8)
+#define NFC_CTL_PAGE_SIZE(a)       ((fls(a) - 11) << 8)
 
 
 #define NFC_ECC_EN                 (1 << 0)
@@ -153,6 +156,8 @@ void nand_init(void)
 {
        uint32_t val;
 
+       board_nand_init();
+
        val = readl(SUNXI_NFC_BASE + NFC_CTL);
        /* enable and reset CTL */
        writel(val | NFC_CTL_EN | NFC_CTL_RESET,
@@ -162,76 +167,42 @@ void nand_init(void)
                                 NFC_CTL_RESET, MAX_RETRIES)) {
                printf("Couldn't initialize nand\n");
        }
+
+       /* reset NAND */
+       writel(NFC_SEND_CMD1 | NFC_WAIT_FLAG | NAND_CMD_RESET,
+              SUNXI_NFC_BASE + NFC_CMD);
+
+       if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_CMD_INT_FLAG,
+                        MAX_RETRIES)) {
+               printf("Error timeout waiting for nand reset\n");
+               return;
+       }
 }
 
 static void nand_read_page(unsigned int real_addr, dma_addr_t dst,
                           int syndrome, uint32_t *ecc_errors)
 {
        uint32_t val;
-       int ecc_off = 0;
+       int i, ecc_off = 0;
        uint16_t ecc_mode = 0;
        uint16_t rand_seed;
        uint32_t page;
        uint16_t column;
        uint32_t oob_offset;
+       static const u8 strengths[] = { 16, 24, 28, 32, 40, 48, 56, 60, 64 };
 
-       switch (CONFIG_NAND_SUNXI_SPL_ECC_STRENGTH) {
-       case 16:
-               ecc_mode = 0;
-               ecc_off = 0x20;
-               break;
-       case 24:
-               ecc_mode = 1;
-               ecc_off = 0x2e;
-               break;
-       case 28:
-               ecc_mode = 2;
-               ecc_off = 0x32;
-               break;
-       case 32:
-               ecc_mode = 3;
-               ecc_off = 0x3c;
-               break;
-       case 40:
-               ecc_mode = 4;
-               ecc_off = 0x4a;
-               break;
-       case 48:
-               ecc_mode = 4;
-               ecc_off = 0x52;
-               break;
-       case 56:
-               ecc_mode = 4;
-               ecc_off = 0x60;
-               break;
-       case 60:
-               ecc_mode = 4;
-               ecc_off = 0x0;
-               break;
-       case 64:
-               ecc_mode = 4;
-               ecc_off = 0x0;
-               break;
-       default:
-               ecc_mode = 0;
-               ecc_off = 0;
-       }
-
-       if (ecc_off == 0) {
-               printf("Unsupported ECC strength (%d)!\n",
-                      CONFIG_NAND_SUNXI_SPL_ECC_STRENGTH);
-               return;
+       for (i = 0; i < ARRAY_SIZE(strengths); i++) {
+               if (CONFIG_NAND_SUNXI_SPL_ECC_STRENGTH == strengths[i]) {
+                       ecc_mode = i;
+                       break;
+               }
        }
 
-       /* set CMD  */
-       writel(NFC_SEND_CMD1 | NFC_WAIT_FLAG | NAND_CMD_RESET,
-              SUNXI_NFC_BASE + NFC_CMD);
-
-       if (!check_value(SUNXI_NFC_BASE + NFC_ST, NFC_CMD_INT_FLAG,
-                        MAX_RETRIES)) {
-               printf("Error while initilizing command interrupt\n");
-               return;
-       }
+       /* HW ECC always request ECC bytes for 1024 bytes blocks */
+       ecc_off = DIV_ROUND_UP(CONFIG_NAND_SUNXI_SPL_ECC_STRENGTH * fls(8 * 1024), 8);
+       /* HW ECC always work with even numbers of ECC bytes */
+       ecc_off += (ecc_off & 1);
+       ecc_off += 4; /* prepad */
 
        page = real_addr / CONFIG_NAND_SUNXI_SPL_PAGE_SIZE;
        column = real_addr % CONFIG_NAND_SUNXI_SPL_PAGE_SIZE;
@@ -256,16 +227,17 @@ static void nand_read_page(unsigned int real_addr, dma_addr_t dst,
        val = readl(SUNXI_NFC_BASE + NFC_CTL);
        writel(val | NFC_CTL_RAM_METHOD, SUNXI_NFC_BASE + NFC_CTL);
 
-       if (syndrome) {
-               writel(CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE,
-                      SUNXI_NFC_BASE + NFC_SPARE_AREA);
-       } else {
+       if (!syndrome) {
                oob_offset = CONFIG_NAND_SUNXI_SPL_PAGE_SIZE
                        + (column / CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE)
                        * ecc_off;
                writel(oob_offset, SUNXI_NFC_BASE + NFC_SPARE_AREA);
        }
 
+       flush_dcache_range(dst,
+                          ALIGN(dst + CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE,
+                                ARCH_DMA_MINALIGN));
+
        /* SUNXI_DMA */
        writel(0x0, SUNXI_DMA_BASE + SUNXI_DMA_CFG_REG0); /* clr dma cmd */
        /* read from REG_IO_DATA */
@@ -311,6 +283,10 @@ static void nand_read_page(unsigned int real_addr, dma_addr_t dst,
                return;
        }
 
+       invalidate_dcache_range(dst,
+                       ALIGN(dst + CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE,
+                             ARCH_DMA_MINALIGN));
+
        if (readl(SUNXI_NFC_BASE + NFC_ECC_ST))
                (*ecc_errors)++;
 }
@@ -320,6 +296,9 @@ int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
        void *current_dest;
        uint32_t ecc_errors = 0;
 
+       clrsetbits_le32(SUNXI_NFC_BASE + NFC_CTL, NFC_CTL_PAGE_SIZE_MASK,
+                       NFC_CTL_PAGE_SIZE(CONFIG_NAND_SUNXI_SPL_PAGE_SIZE));
+
        for (current_dest = dest;
                        current_dest < (dest + size);
                        current_dest += CONFIG_NAND_SUNXI_SPL_ECC_PAGE_SIZE) {
@@ -331,4 +310,16 @@ int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest)
        return ecc_errors ? -1 : 0;
 }
 
-void nand_deselect(void) {}
+void nand_deselect(void)
+{
+       struct sunxi_ccm_reg *const ccm =
+               (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+
+       clrbits_le32(&ccm->ahb_gate0, (CLK_GATE_OPEN << AHB_GATE_OFFSET_NAND0));
+#ifdef CONFIG_MACH_SUN9I
+       clrbits_le32(&ccm->ahb_gate1, (1 << AHB_GATE_OFFSET_DMA));
+#else
+       clrbits_le32(&ccm->ahb_gate0, (1 << AHB_GATE_OFFSET_DMA));
+#endif
+       clrbits_le32(&ccm->nand0_clk_cfg, CCM_NAND_CTRL_ENABLE | AHB_DIV_1);
+}