[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