[U-Boot] [PATCH 2/5] nand: sunxi: Add support for booting from internal NAND memory
Daniel Kochmański
dkochmanski at turtle-solutions.eu
Wed Apr 29 17:02:58 CEST 2015
Adds minimal DMA NAND driver for booting from NAND internal
memory. New config option SPL_NAND_SUPPORT is created for sunxi board,
which enables introduced driver and sets ENV_IS_NOWHERE (instead of
ENV_IS_IN_MMC).
NAND driver utilizes DMA interface to flash controller, which supports
randomization. Reading from both syndrome and non-syndrom partitions
is supported. Writing to flash isn't implemented for time being.
Signed-off-by: Daniel Kochmański <dkochmanski at turtle-solutions.eu>
Cc: Ian Campbell <ijc at hellion.org.uk>
Cc: Hans De Goede <hdegoede at redhat.com>
---
arch/arm/cpu/armv7/sunxi/board.c | 12 ++-
board/sunxi/Kconfig | 12 +++
board/sunxi/Makefile | 1 +
board/sunxi/nand.c | 219 +++++++++++++++++++++++++++++++++++++++
common/spl/spl_nand.c | 13 +--
include/configs/sunxi-common.h | 9 ++
6 files changed, 258 insertions(+), 8 deletions(-)
create mode 100644 board/sunxi/nand.c
diff --git a/arch/arm/cpu/armv7/sunxi/board.c b/arch/arm/cpu/armv7/sunxi/board.c
index c1b4cf5..ba81557 100644
--- a/arch/arm/cpu/armv7/sunxi/board.c
+++ b/arch/arm/cpu/armv7/sunxi/board.c
@@ -112,8 +112,10 @@ void s_init(void)
#ifdef CONFIG_SPL_BUILD
/* The sunxi internal brom will try to loader external bootloader
* from mmc0, nand flash, mmc2.
- * Unfortunately we can't check how SPL was loaded so assume
- * it's always the first SD/MMC controller
+ *
+ * Unfortunately we can't check how SPL was loaded so assume it's
+ * always the first SD/MMC controller, unless it was explicitly
+ * stated that SPL is on nand flash.
*/
u32 spl_boot_device(void)
{
@@ -123,6 +125,12 @@ u32 spl_boot_device(void)
* enabled build. It has many restrictions and can only boot over USB.
*/
return BOOT_DEVICE_BOARD;
+#elif defined(CONFIG_SPL_NAND_SUPPORT)
+ /*
+ * This is compile time configuration informing SPL, that it
+ * was loaded from nand flash.
+ */
+ return BOOT_DEVICE_NAND;
#else
/*
* When booting from the SD card, the "eGON.BT0" signature is expected
diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig
index 88e3358..1a30684 100644
--- a/board/sunxi/Kconfig
+++ b/board/sunxi/Kconfig
@@ -239,6 +239,18 @@ config MMC_SUNXI_SLOT_EXTRA
slot or emmc on mmc1 - mmc3. Setting this to 1, 2 or 3 will enable
support for this.
+config SPL_NAND_SUPPORT
+ bool "SPL/NAND mode support"
+ depends on SPL
+ default n
+ ---help---
+ This enables support for booting from NAND internal
+ memory. U-Boot SPL doesn't detect where is it load from,
+ therefore this option is needed to properly load image from
+ flash. Option also disables MMC functionality on U-Boot due to
+ initialization errors encountered, when both controllers are
+ enabled.
+
config USB0_VBUS_PIN
string "Vbus enable pin for usb0 (otg)"
default ""
diff --git a/board/sunxi/Makefile b/board/sunxi/Makefile
index 43766e0..7ad7412 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_SUNXI_AHCI) += ahci.o
obj-$(CONFIG_MACH_SUN4I) += dram_sun4i_auto.o
diff --git a/board/sunxi/nand.c b/board/sunxi/nand.c
new file mode 100644
index 0000000..e449da2
--- /dev/null
+++ b/board/sunxi/nand.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 2014, Antmicro Ltd <www.antmicro.com>
+ * Copyright (c) 2015, Turtle Solutions <www.turtle-solutions.eu>
+ *
+ * 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))
+
+/* 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);
+}
+
+int initialized = 0;
+void nand_init(void)
+{
+ initialized = 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 */
+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,
+};
+
+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 (!initialized)
+ 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("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<<12)));
+ }
+
+ uint32_t addr = (page << 16) | shift;
+ val = R32(NANDFLASHC_BASE + NANDFLASHC_CTL);
+ W32(NANDFLASHC_BASE + NANDFLASHC_CTL, val | (1<<14));
+
+ 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); /* clear 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 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;
+ }
+ 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)
+{}
diff --git a/common/spl/spl_nand.c b/common/spl/spl_nand.c
index b8c369d..9d59fbb 100644
--- a/common/spl/spl_nand.c
+++ b/common/spl/spl_nand.c
@@ -74,24 +74,25 @@ void spl_nand_load_image(void)
#endif
#ifdef CONFIG_NAND_ENV_DST
nand_spl_load_image(CONFIG_ENV_OFFSET,
- sizeof(*header), (void *)header);
+ sizeof(*header), (void *)header);
spl_parse_image_header(header);
nand_spl_load_image(CONFIG_ENV_OFFSET, spl_image.size,
- (void *)spl_image.load_addr);
+ (void *)spl_image.load_addr);
#ifdef CONFIG_ENV_OFFSET_REDUND
nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND,
- sizeof(*header), (void *)header);
+ sizeof(*header), (void *)header);
spl_parse_image_header(header);
nand_spl_load_image(CONFIG_ENV_OFFSET_REDUND, spl_image.size,
- (void *)spl_image.load_addr);
+ (void *)spl_image.load_addr);
#endif
#endif
/* Load u-boot */
nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,
- sizeof(*header), (void *)header);
+ sizeof(*header), (void *)header);
spl_parse_image_header(header);
nand_spl_load_image(CONFIG_SYS_NAND_U_BOOT_OFFS,
- spl_image.size, (void *)(unsigned long)spl_image.load_addr);
+ spl_image.size,
+ (void *)(unsigned long)spl_image.load_addr);
nand_deselect();
}
#endif
diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h
index 365d9a5..b40cdd3 100644
--- a/include/configs/sunxi-common.h
+++ b/include/configs/sunxi-common.h
@@ -106,8 +106,10 @@
#define CONFIG_CMD_MMC
#define CONFIG_MMC_SUNXI
#define CONFIG_MMC_SUNXI_SLOT 0
+#if !defined(CONFIG_SPL_NAND_SUPPORT)
#define CONFIG_ENV_IS_IN_MMC
#define CONFIG_SYS_MMC_ENV_DEV 0 /* first detected MMC controller */
+#endif /* CONFIG_SPL_NAND_SUPPORT */
#endif
/* 4MB of malloc() pool */
@@ -323,6 +325,13 @@ extern int soft_i2c_gpio_scl;
#define CONFIG_ENV_IS_NOWHERE
#endif
+#ifdef CONFIG_SPL_NAND_SUPPORT
+#define CONFIG_NAND
+#define CONFIG_NAND_SUNXI
+#define CONFIG_CMD_SPL_WRITE_SIZE 0x000400
+#define CONFIG_SYS_NAND_U_BOOT_OFFS 0x008000
+#endif /* CONFIG_SPL_NAND_SUPPORT */
+
#define CONFIG_MISC_INIT_R
#define CONFIG_SYS_CONSOLE_IS_IN_ENV
--
2.3.6
More information about the U-Boot
mailing list