---
 board/sunxi/Makefile |   1 +
 board/sunxi/nand.c   | 252 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 253 insertions(+)
 create mode 100644 board/sunxi/nand.c

diff --git a/board/sunxi/Makefile b/board/sunxi/Makefile
index 1bde476..8b2e40e 100644
--- a/board/sunxi/Makefile
+++ b/board/sunxi/Makefile
@@ -9,6 +9,7 @@
 # SPDX-License-Identifier:     GPL-2.0+
 #
 obj-y  += board.o
+obj-$(CONFIG_NAND_SUNXI)       += nand.o
 obj-$(CONFIG_SUNXI_GMAC)       += gmac.o
 obj-$(CONFIG_A10_MID_1GB)      += dram_sun4i_360_1024_iow16.o
 obj-$(CONFIG_A10_OLINUXINO_LIME)       += dram_a10_olinuxino_lime.o
diff --git a/board/sunxi/nand.c b/board/sunxi/nand.c
new file mode 100644
index 0000000..11aa656
--- /dev/null
+++ b/board/sunxi/nand.c
@@ -0,0 +1,252 @@
+/*
+ * Copyright (c) 2014, Antmicro Ltd <www.antmicro.com>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <common.h>
+#include <config.h>
+#include <asm/io.h>
+#include <nand.h>
+
+/* minimal "boot0" style NAND support for Allwinner A20 */
+#define W32(a, b) (*(volatile unsigned int   *)(a)) = b
+#define R32(a)    (*(volatile unsigned int   *)(a))
+
+char ch(char c) {
+        if (c >= 'a' && c <= 'z') return c;
+        if (c >= 'A' && c <= 'Z') return c;
+        if (c >= '0' && c <= '9') return c;
+        return '.';
+}
+
+void print_mem(uint32_t addr, uint32_t len, uint32_t label) {
+        int i=0;
+        int j;
+        char buf[16];
+        for(i=0; i<len; i++) {
+                if(!(i%0x10)) {
+                        if(i) {
+                                printf(" |");
+                                for (j = 0; j < 16; j++) printf("%c", 
ch(buf[j]));
+                                printf("|\n");
+                        }
+                        printf(  "0x%p: ", (void *)(label+i));
+                } else {
+                        printf(" ");
+                }
+                buf[i % 16] =  *(char *)(addr+i);
+                printf("%02x", *(char *)(addr+i));
+        }
+        printf(" |");
+        for (j = 0; j < 16; j++) printf("%c", ch(buf[j]));
+        printf("|\n");
+}
+
+/* temporary buffer for read of 1024 bytes */
+#ifdef CONFIG_SPL_BUILD
+char temp_buf[0x400] __attribute__ ((aligned (0x10), section (".text#")));
+#else
+void *temp_buf = 0x0;
+#endif
+
+
+#define PORTC_BASE                      0x01c20800
+#define CCMU_BASE                       0x01c20000
+#define NANDFLASHC_BASE                 0x01c03000
+#define DMAC_BASE                       0x01c02000
+
+#define NANDFLASHC_CTL                  0x00000000
+#define NANDFLASHC_ST                   0x00000004
+#define NANDFLASHC_INT                  0x00000008
+#define NANDFLASHC_TIMING_CTL           0x0000000C
+#define NANDFLASHC_TIMING_CFG           0x00000010
+#define NANDFLASHC_ADDR_LOW             0x00000014
+#define NANDFLASHC_ADDR_HIGH            0x00000018
+#define NANDFLASHC_SECTOR_NUM           0x0000001C
+#define NANDFLASHC_CNT                  0x00000020
+#define NANDFLASHC_CMD                  0x00000024
+#define NANDFLASHC_RCMD_SET             0x00000028
+#define NANDFLASHC_WCMD_SET             0x0000002C
+#define NANDFLASHC_IO_DATA              0x00000030
+#define NANDFLASHC_ECC_CTL              0x00000034
+#define NANDFLASHC_ECC_ST               0x00000038
+#define NANDFLASHC_DEBUG                0x0000003C
+#define NANDFLASHC_ECC_CNT0             0x00000040
+#define NANDFLASHC_ECC_CNT1             0x00000044
+#define NANDFLASHC_ECC_CNT2             0x00000048
+#define NANDFLASHC_ECC_CNT3             0x0000004C
+#define NANDFLASHC_USER_DATA_BASE       0x00000050
+#define NANDFLASHC_EFNAND_STATUS        0x00000090
+#define NANDFLASHC_SPARE_AREA           0x000000A0
+#define NANDFLASHC_PATTERN_ID           0x000000A4
+#define NANDFLASHC_RAM0_BASE            0x00000400
+#define NANDFLASHC_RAM1_BASE            0x00000800
+
+
+void nand_set_clocks(void) {
+        W32(PORTC_BASE + 0x48, 0x22222222);
+        W32(PORTC_BASE + 0x4C, 0x22222222);
+        W32(PORTC_BASE + 0x50, 0x2222222);
+        W32(PORTC_BASE + 0x54, 0x2);
+        W32(PORTC_BASE + 0x5C, 0x55555555);
+        W32(PORTC_BASE + 0x60, 0x15555);
+        W32(PORTC_BASE + 0x64, 0x5140);
+        W32(PORTC_BASE + 0x68, 0x4016);
+
+        uint32_t val = R32(CCMU_BASE + 0x60);
+        W32(CCMU_BASE + 0x60, 0x2000 | val);
+
+        val = R32(CCMU_BASE + 0x80);
+        W32(CCMU_BASE + 0x80, val | 0x80000000 | 0x1);
+}
+
+volatile int inited = 0;
+void nand_init(void) {
+        inited = 1;
+        uint32_t val;
+        nand_set_clocks();
+        val = R32(NANDFLASHC_BASE + 0x00);
+        /* CTL = (1<<0 <-EN    1<<1 RESET) */
+        W32(NANDFLASHC_BASE + 0x00, val | 0x3);         /* enable and reset 
CTL */
+        do {
+                val = R32(NANDFLASHC_BASE + 0x00);
+                if (val & (1<<1)) break;
+        } while (1);
+}
+
+/* random seed used by linux */
+const uint16_t random_seed[128] = {
+        0x2b75, 0x0bd0, 0x5ca3, 0x62d1, 0x1c93, 0x07e9, 0x2162, 0x3a72,
+        0x0d67, 0x67f9, 0x1be7, 0x077d, 0x032f, 0x0dac, 0x2716, 0x2436,
+        0x7922, 0x1510, 0x3860, 0x5287, 0x480f, 0x4252, 0x1789, 0x5a2d,
+        0x2a49, 0x5e10, 0x437f, 0x4b4e, 0x2f45, 0x216e, 0x5cb7, 0x7130,
+        0x2a3f, 0x60e4, 0x4dc9, 0x0ef0, 0x0f52, 0x1bb9, 0x6211, 0x7a56,
+        0x226d, 0x4ea7, 0x6f36, 0x3692, 0x38bf, 0x0c62, 0x05eb, 0x4c55,
+        0x60f4, 0x728c, 0x3b6f, 0x2037, 0x7f69, 0x0936, 0x651a, 0x4ceb,
+        0x6218, 0x79f3, 0x383f, 0x18d9, 0x4f05, 0x5c82, 0x2912, 0x6f17,
+        0x6856, 0x5938, 0x1007, 0x61ab, 0x3e7f, 0x57c2, 0x542f, 0x4f62,
+        0x7454, 0x2eac, 0x7739, 0x42d4, 0x2f90, 0x435a, 0x2e52, 0x2064,
+        0x637c, 0x66ad, 0x2c90, 0x0bad, 0x759c, 0x0029, 0x0986, 0x7126,
+        0x1ca7, 0x1605, 0x386a, 0x27f5, 0x1380, 0x6d75, 0x24c3, 0x0f8e,
+        0x2b7a, 0x1418, 0x1fd1, 0x7dc1, 0x2d8e, 0x43af, 0x2267, 0x7da3,
+        0x4e3d, 0x1338, 0x50db, 0x454d, 0x764d, 0x40a3, 0x42e6, 0x262b,
+        0x2d2e, 0x1aea, 0x2e17, 0x173d, 0x3a6e, 0x71bf, 0x25f9, 0x0a5d,
+        0x7c57, 0x0fbe, 0x46ce, 0x4939, 0x6b17, 0x37bb, 0x3e91, 0x76db,
+};
+
+volatile uint32_t ecc_errors = 0;
+
+/* read 0x400 bytes from real_addr to temp_buf */
+void nand_read_block(unsigned int real_addr, int syndrome) {
+        uint32_t val;
+        if (!inited) {
+                nand_init();
+        }
+        memset((void*)temp_buf, 0, 0x400); /* clear temp_buf */
+
+        /* set CMD  */
+        W32(NANDFLASHC_BASE + NANDFLASHC_CMD, 0xC000FF);
+        do {
+                val = R32(NANDFLASHC_BASE + NANDFLASHC_ST);
+                if (val & (1<<1)) break;
+                udelay(1000);
+        } while (1);
+
+        uint32_t page = real_addr / (8 * 1024);
+        if (page > 0xFFFF) {
+                /* TODO: currently this is not supported */
+                printf("NAND: Reading from address >= %08X is not allowed.\n",
+                       0xFFFF * 8 * 1024);
+                return;
+        }
+
+        uint32_t shift = real_addr % (8*1024);
+        W32(0x1C03038, 0);
+
+        /* ECC_CTL, randomization */
+        if (syndrome) {
+                W32(0x1C03034, (0x4A80 << 16) | 0x0200 | 1 | (1<<3) | (1<<12));
+                /* shift every 1kB in syndrome */
+                shift += (shift / 0x400) * 0x2e;
+
+        } else {
+                W32(0x1C03034, (random_seed[page % 128] << 16) | 0x0200 | 1 | 
(1<<3) | (/*1*/ 1 <<12));
+        }
+
+        uint32_t addr = (page << 16) | shift;
+        val = R32(NANDFLASHC_BASE + NANDFLASHC_CTL);
+        W32(NANDFLASHC_BASE + NANDFLASHC_CTL, val | (1<<14) /*0x4001*/);
+
+        if(syndrome) {
+                W32(NANDFLASHC_BASE + NANDFLASHC_SPARE_AREA, 0x400);
+        } else {
+                uint32_t oob_offset = 0x2000  + (shift / 0x400) * 0x2e;
+                W32(NANDFLASHC_BASE + NANDFLASHC_SPARE_AREA, oob_offset);
+        }
+
+
+        /* DMAC */
+        W32(DMAC_BASE + 0x300, 0x0); /* clr dma cmd */
+        /* read from REG_IO_DATA */
+        W32(DMAC_BASE + 0x304, NANDFLASHC_BASE + NANDFLASHC_IO_DATA);
+        W32(DMAC_BASE + 0x308, (uint32_t)temp_buf); /* read to RAM */
+        W32(DMAC_BASE + 0x318, 0x7F0F);
+        W32(DMAC_BASE + 0x30C, 0x400); /* 1kB */
+        W32(DMAC_BASE + 0x300, 0x84000423);
+
+        W32(NANDFLASHC_BASE + NANDFLASHC_RCMD_SET, 0x00E00530);
+        W32(NANDFLASHC_BASE + NANDFLASHC_SECTOR_NUM, 1);
+        W32(NANDFLASHC_BASE + NANDFLASHC_ADDR_LOW, addr);
+        W32(NANDFLASHC_BASE + NANDFLASHC_ADDR_HIGH, 0);
+
+        /* CMD (PAGE READ) */
+        W32(NANDFLASHC_BASE + NANDFLASHC_CMD, 0x85EC0000 | (syndrome ? 
0x02000000 : 0x0));
+        do {                    /* wait for dma irq */
+                val = R32(NANDFLASHC_BASE + NANDFLASHC_ST);
+                if (val & (1<<2)) break;
+                udelay(1000);
+        } while (1);
+
+        do {                    /* make sure cmd is finished */
+                val = R32(DMAC_BASE + 300);
+                if (!(val & 0x80000000)) break;
+                udelay(1000);
+        } while (1);
+
+        if(R32(NANDFLASHC_BASE | NANDFLASHC_ECC_ST))
+                ecc_errors++;
+}
+
+int helper_load(uint32_t offs, unsigned int size, void *dest) {
+        uint32_t dst;
+        uint32_t count;
+        uint32_t adr = offs;
+        memset((void*)dest, 0x0, size); /* clean destination memory */
+        ecc_errors = 0;
+        for (dst = (uint32_t)dest; dst < ((uint32_t)dest+size); dst+=0x400) {
+                /* if < 0x400000 then syndrome read */
+                nand_read_block(adr, adr < 0x400000);
+                memcpy((void*)dst, (void*)temp_buf, 0x400);
+                adr += 0x400;
+
+                count = dst - (uint32_t)dest;
+                if(count &&
+                   ((!((count / 0x8000) % 80) && ((count % 0x8000) == 00))
+                    || ((count + 0x0400) >= size)))
+                        printf("\n");
+
+                if((count % 0x8000) == 0)
+                        printf(".");
+
+        }
+
+        return ecc_errors;
+}
+
+int nand_spl_load_image(uint32_t offs, unsigned int size, void *dest) {
+        helper_load(offs,size,dest);
+        return 0;
+}
+
+void nand_deselect(void) {}
-- 
2.2.1

-- 
You received this message because you are subscribed to the Google Groups 
"linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to