
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@turtle-solutions.eu Cc: Ian Campbell ijc@hellion.org.uk Cc: Hans De Goede hdegoede@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