[U-Boot] [v3 (RFC)] Proposal to add NAND-boot support for Sunxi SPL

Hereby a v3 of the patches adding NAND support to SPL. Most noticably all the feedback has been addressed: tidy structs for NAND and DMA, timeouts on wait operations and more proper use of the CCU. Patches are marked RFC mostly because I'm still contemplating the implementation of a proper full NAND driver to be used with the existing UBI subsystem. This could mean some slight changes in structure to better accomodate code sharing between sunxi-nand for SPL and U-boot. If you wish you can merge the first three patches though, I expect them not to change any further. Thanks for earlier feedback, and don't hesitate to let me know if anything else could be improved. Cheers,
Roy

From: Daniel Kochmański dkochmanski@turtle-solutions.eu
This change is necessary to calculate correct checksum for NAND boot. Works both for MMC and NAND. Without it BROM rejects boot image as invalid (bad checksum). (Changes block size from 0x200 to 0x2000).
V2: Document decision in source too
Signed-off-by: Daniel Kochmański dkochmanski@turtle-solutions.eu Signed-off-by: Roy Spliet r.spliet@ultimaker.com Cc: Ian Campbell ijc@hellion.org.uk Cc: Hans De Goede hdegoede@redhat.com Reviewed-by: Hans de Goede hdegoede@redhat.com --- tools/mksunxiboot.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/tools/mksunxiboot.c b/tools/mksunxiboot.c index 0035f6e..3361251 100644 --- a/tools/mksunxiboot.c +++ b/tools/mksunxiboot.c @@ -65,7 +65,13 @@ int gen_check_sum(struct boot_file_head *head_p)
#define SUN4I_SRAM_SIZE 0x7600 /* 0x7748+ is used by BROM */ #define SRAM_LOAD_MAX_SIZE (SUN4I_SRAM_SIZE - sizeof(struct boot_file_head)) -#define BLOCK_SIZE 512 + +/* + * BROM (at least on A10 and A20) requires NAND-images to be explicitly aligned + * to a multiple of 8K, and rejects the image otherwise. MMC-images are fine + * with 512B blocks. To cater for both, align to the largest of the two. + */ +#define BLOCK_SIZE 0x2000
struct boot_img { struct boot_file_head header;

Signed-off-by: Roy Spliet r.spliet@ultimaker.com --- arch/arm/include/asm/arch-sunxi/dma.h | 16 +++++++ arch/arm/include/asm/arch-sunxi/dma_sun4i.h | 68 +++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+) create mode 100644 arch/arm/include/asm/arch-sunxi/dma.h create mode 100644 arch/arm/include/asm/arch-sunxi/dma_sun4i.h
diff --git a/arch/arm/include/asm/arch-sunxi/dma.h b/arch/arm/include/asm/arch-sunxi/dma.h new file mode 100644 index 0000000..e288f36 --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/dma.h @@ -0,0 +1,16 @@ +/* + * (C) Copyright 2015 Roy Spliet rspliet@ultimaker.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _SUNXI_DMA_H +#define _SUNXI_DMA_H + +#if defined(CONFIG_MACH_SUN4I) || defined(CONFIG_MACH_SUN7I) +#include <asm/arch/dma_sun4i.h> +#else +#error "DMA definition not available for this architecture" +#endif + +#endif /* _SUNXI_DMA_H */ diff --git a/arch/arm/include/asm/arch-sunxi/dma_sun4i.h b/arch/arm/include/asm/arch-sunxi/dma_sun4i.h new file mode 100644 index 0000000..778a04b --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/dma_sun4i.h @@ -0,0 +1,68 @@ +/* + * (C) Copyright 2015 Roy Spliet rspliet@ultimaker.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _SUNXI_DMA_SUN4I_H +#define _SUNXI_DMA_SUN4I_H + +struct sunxi_dma_cfg +{ + u32 ctl; /* 0x00 Control */ + u32 src_addr; /* 0x04 Source address */ + u32 dst_addr; /* 0x08 Destination address */ + u32 bc; /* 0x0C Byte counter */ + u32 res0[2]; + u32 ddma_para; /* 0x18 extra parameter (dedicated DMA only) */ + u32 res1; +}; + +struct sunxi_dma +{ + u32 irq_en; /* 0x000 IRQ enable */ + u32 irq_pend; /* 0x004 IRQ pending */ + u32 auto_gate; /* 0x008 auto gating */ + u32 res0[61]; + struct sunxi_dma_cfg ndma[8]; /* 0x100 Normal DMA */ + u32 res1[64]; + struct sunxi_dma_cfg ddma[8]; /* 0x300 Dedicated DMA */ +}; + +enum ddma_drq_type { + DDMA_DST_DRQ_SRAM = 0, + DDMA_SRC_DRQ_SRAM = 0, + DDMA_DST_DRQ_SDRAM = 1, + DDMA_SRC_DRQ_SDRAM = 1, + DDMA_DST_DRQ_PATA = 2, + DDMA_SRC_DRQ_PATA = 2, + DDMA_DST_DRQ_NAND = 3, + DDMA_SRC_DRQ_NAND = 3, + DDMA_DST_DRQ_USB0 = 4, + DDMA_SRC_DRQ_USB0 = 4, + DDMA_DST_DRQ_ETHERNET_MAC_TX = 6, + DDMA_SRC_DRQ_ETHERNET_MAC_RX = 7, + DDMA_DST_DRQ_SPI1_TX = 8, + DDMA_SRC_DRQ_SPI1_RX = 9, + DDMA_DST_DRQ_SECURITY_SYS_TX = 10, + DDMA_SRC_DRQ_SECURITY_SYS_RX = 11, + DDMA_DST_DRQ_TCON0 = 14, + DDMA_DST_DRQ_TCON1 = 15, + DDMA_DST_DRQ_MSC = 23, + DDMA_SRC_DRQ_MSC = 23, + DDMA_DST_DRQ_SPI0_TX = 26, + DDMA_SRC_DRQ_SPI0_RX = 27, + DDMA_DST_DRQ_SPI2_TX = 28, + DDMA_SRC_DRQ_SPI2_RX = 29, + DDMA_DST_DRQ_SPI3_TX = 30, + DDMA_SRC_DRQ_SPI3_RX = 31, +}; + +#define SUNXI_DMA_CTL_SRC_DRQ(a) ((a) & 0x1f) +#define SUNXI_DMA_CTL_MODE_IO (1 << 5) +#define SUNXI_DMA_CTL_SRC_DATA_WIDTH_32 (2 << 9) +#define SUNXI_DMA_CTL_DST_DRQ(a) (((a) & 0x1f) << 16) +#define SUNXI_DMA_CTL_DST_DATA_WIDTH_32 (2 << 25) +#define SUNXI_DMA_CTL_TRIGGER (1 << 31) + +#endif /* _SUNXI_DMA_SUN4I_H */

Signed-off-by: Roy Spliet r.spliet@ultimaker.com --- arch/arm/include/asm/arch-sunxi/clock_sun4i.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h index c28ee05..fe3e92f 100644 --- a/arch/arm/include/asm/arch-sunxi/clock_sun4i.h +++ b/arch/arm/include/asm/arch-sunxi/clock_sun4i.h @@ -39,7 +39,7 @@ struct sunxi_ccm_reg { u32 apb0_gate; /* 0x68 apb0 module clock gating */ u32 apb1_gate; /* 0x6c apb1 module clock gating */ u8 res4[0x10]; - u32 nand_sclk_cfg; /* 0x80 nand sub clock control */ + u32 nand0_clk_cfg; /* 0x80 nand sub clock control */ u32 ms_sclk_cfg; /* 0x84 memory stick sub clock control */ u32 sd0_clk_cfg; /* 0x88 sd0 clock control */ u32 sd1_clk_cfg; /* 0x8c sd1 clock control */

From: Daniel Kochmański dkochmanski@turtle-solutions.eu
V2: - Rename config option - Move to separate driver - fix DMA directly into RAM - Many readability upgrades - Drop R32 and W32 macros in favour of readl/writel respectively - Use standard port controller methods for pinctl - Make many NAND options semi-configurable
V3: - Use proper structs and definitions for DMA and NAND - Fix bug wrt. page size selection - Add time-out to waits
Signed-off-by: Roy Spliet r.spliet@ultimaker.com --- arch/arm/cpu/armv7/sunxi/board.c | 12 +- arch/arm/include/asm/arch-sunxi/gpio.h | 2 + arch/arm/include/asm/arch-sunxi/nand.h | 67 +++++++++ board/sunxi/Kconfig | 12 ++ board/sunxi/board.c | 16 ++ drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/sunxi_nand_spl.c | 268 +++++++++++++++++++++++++++++++++ include/configs/sun4i.h | 1 + include/configs/sun5i.h | 3 + include/configs/sun7i.h | 2 + include/configs/sun8i.h | 6 + include/configs/sunxi-common.h | 20 +++ 12 files changed, 408 insertions(+), 2 deletions(-) create mode 100644 arch/arm/include/asm/arch-sunxi/nand.h create mode 100644 drivers/mtd/nand/sunxi_nand_spl.c
diff --git a/arch/arm/cpu/armv7/sunxi/board.c b/arch/arm/cpu/armv7/sunxi/board.c index 6718ae2..70f413f 100644 --- a/arch/arm/cpu/armv7/sunxi/board.c +++ b/arch/arm/cpu/armv7/sunxi/board.c @@ -111,8 +111,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) { @@ -122,6 +124,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/arch/arm/include/asm/arch-sunxi/gpio.h b/arch/arm/include/asm/arch-sunxi/gpio.h index 59d8210..2b49616 100644 --- a/arch/arm/include/asm/arch-sunxi/gpio.h +++ b/arch/arm/include/asm/arch-sunxi/gpio.h @@ -156,6 +156,8 @@ enum sunxi_gpio_number { #define SUN4I_GPB_UART0 2 #define SUN5I_GPB_UART0 2
+#define SUNXI_GPC_NAND 2 + #define SUNXI_GPC_SDC2 3 #define SUN6I_GPC_SDC3 4
diff --git a/arch/arm/include/asm/arch-sunxi/nand.h b/arch/arm/include/asm/arch-sunxi/nand.h new file mode 100644 index 0000000..22844d8 --- /dev/null +++ b/arch/arm/include/asm/arch-sunxi/nand.h @@ -0,0 +1,67 @@ +/* + * (C) Copyright 2015 Roy Spliet rspliet@ultimaker.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _SUNXI_NAND_H +#define _SUNXI_NAND_H + +#include <linux/types.h> + +struct sunxi_nand +{ + u32 ctl; /* 0x000 Configure and control */ + u32 st; /* 0x004 Status information */ + u32 intr; /* 0x008 Interrupt control */ + u32 timing_ctl; /* 0x00C Timing control */ + u32 timing_cfg; /* 0x010 Timing configure */ + u32 addr_low; /* 0x014 Low word address */ + u32 addr_high; /* 0x018 High word address */ + u32 block_num; /* 0x01C Data block number */ + u32 data_cnt; /* 0x020 Data counter for transfer */ + u32 cmd; /* 0x024 NDFC commands */ + u32 rcmd_set; /* 0x028 Read command set for vendor NAND mem */ + u32 wcmd_set; /* 0x02C Write command set */ + u32 io_data; /* 0x030 IO data */ + u32 ecc_ctl; /* 0x034 ECC configure and control */ + u32 ecc_st; /* 0x038 ECC status and operation info */ + u32 efr; /* 0x03C Enhanced feature */ + u32 err_cnt0; /* 0x040 Corrected error bit counter 0 */ + u32 err_cnt1; /* 0x044 Corrected error bit counter 1 */ + u32 user_data[16]; /* 0x050[16] User data field */ + u32 efnand_st; /* 0x090 EFNAND status */ + u32 res0[3]; + u32 spare_area; /* 0x0A0 Spare area configure */ + u32 pat_id; /* 0x0A4 Pattern ID register */ + u32 rdata_sta_ctl; /* 0x0A8 Read data status control */ + u32 rdata_sta_0; /* 0x0AC Read data status 0 */ + u32 rdata_sta_1; /* 0x0B0 Read data status 1 */ + u32 res1[3]; + u32 mdma_addr; /* 0x0C0 MBUS DMA Address */ + u32 mdma_cnt; /* 0x0C4 MBUS DMA data counter */ +}; + +#define SUNXI_NAND_CTL_EN (1 << 0) +#define SUNXI_NAND_CTL_RST (1 << 1) +#define SUNXI_NAND_CTL_PAGE_SIZE(a) ((fls(a) - 11) << 8) +#define SUNXI_NAND_CTL_RAM_METHOD_DMA (1 << 14) + +#define SUNXI_NAND_ST_CMD_INT (1 << 1) +#define SUNXI_NAND_ST_DMA_INT (1 << 2) +#define SUNXI_NAND_ST_FIFO_FULL (1 << 3) + +#define SUNXI_NAND_CMD_ADDR_CYCLES(a) ((a - 1) << 16); +#define SUNXI_NAND_CMD_SEND_CMD1 (1 << 22) +#define SUNXI_NAND_CMD_WAIT_FLAG (1 << 23) +#define SUNXI_NAND_CMD_ORDER_INTERLEAVE 0 +#define SUNXI_NAND_CMD_ORDER_SEQ (1 << 25) + +#define SUNXI_NAND_ECC_CTL_ECC_EN (1 << 0) +#define SUNXI_NAND_ECC_CTL_PIPELINE (1 << 3) +#define SUNXI_NAND_ECC_CTL_BS_512B (1 << 5) +#define SUNXI_NAND_ECC_CTL_RND_EN (1 << 9) +#define SUNXI_NAND_ECC_CTL_MODE(a) ((a) << 12) +#define SUNXI_NAND_ECC_CTL_RND_SEED(a) ((a) << 16) + +#endif /* _SUNXI_NAND_H */ diff --git a/board/sunxi/Kconfig b/board/sunxi/Kconfig index a60d028..cf58d73 100644 --- a/board/sunxi/Kconfig +++ b/board/sunxi/Kconfig @@ -269,6 +269,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/board.c b/board/sunxi/board.c index d9f7691..d5bed30 100644 --- a/board/sunxi/board.c +++ b/board/sunxi/board.c @@ -22,6 +22,9 @@ #ifdef CONFIG_AXP221_POWER #include <axp221.h> #endif +#ifdef CONFIG_NAND_SUNXI +#include <nand.h> +#endif #include <asm/arch/clock.h> #include <asm/arch/cpu.h> #include <asm/arch/display.h> @@ -315,6 +318,19 @@ int board_mmc_init(bd_t *bis) } #endif
+void board_nand_init(void) +{ + unsigned int pin; + static u8 ports[] = CONFIG_NAND_SUNXI_GPC_PORTS; + + /* Configure AHB muxes to connect output pins with NAND controller */ + for (pin = 0; pin < 16; pin++) + sunxi_gpio_set_cfgpin(SUNXI_GPC(pin), SUNXI_GPC_NAND); + + for (pin = 0; pin < ARRAY_SIZE(ports); pin++) + sunxi_gpio_set_cfgpin(SUNXI_GPC(ports[pin]), SUNXI_GPC_NAND); +} + void i2c_init_board(void) { #ifdef CONFIG_I2C0_ENABLE diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 347ea62..a0cf4d5 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -73,5 +73,6 @@ obj-$(CONFIG_NAND_FSL_ELBC) += fsl_elbc_spl.o obj-$(CONFIG_NAND_FSL_IFC) += fsl_ifc_spl.o obj-$(CONFIG_NAND_MXC) += mxc_nand_spl.o obj-$(CONFIG_NAND_MXS) += mxs_nand_spl.o mxs_nand.o +obj-$(CONFIG_NAND_SUNXI) += sunxi_nand_spl.o
endif # drivers diff --git a/drivers/mtd/nand/sunxi_nand_spl.c b/drivers/mtd/nand/sunxi_nand_spl.c new file mode 100644 index 0000000..c3afdbb --- /dev/null +++ b/drivers/mtd/nand/sunxi_nand_spl.c @@ -0,0 +1,268 @@ +/* + * Copyright (c) 2014, Antmicro Ltd <www.antmicro.com> + * Copyright (c) 2015, Turtle Solutions <www.turtle-solutions.eu> + * Copyright (c) 2015, Roy Spliet rspliet@ultimaker.com + * + * SPDX-License-Identifier: GPL-2.0+ + * + * \todo Detect chip parameters (page size, ECC mode, randomisation...) + */ + +#include <common.h> +#include <config.h> +#include <asm/io.h> +#include <nand.h> +#include <asm/arch/cpu.h> +#include <asm/arch/clock.h> +#include <asm/arch/dma.h> +#include <asm/arch/nand.h> + +void +nand_init(void) +{ + struct sunxi_ccm_reg * const ccm = + (struct sunxi_ccm_reg *)SUNXI_CCM_BASE; + struct sunxi_nand * const nand = (struct sunxi_nand *)SUNXI_NFC_BASE; + u32 val; + + board_nand_init(); + + /* "un-gate" NAND clock and clock source + * This assumes that the clock was already correctly configured by + * BootROM */ + setbits_le32(&ccm->ahb_gate0, 0x00002000); + setbits_le32(&ccm->nand0_clk_cfg, 0x80000000); + + val = readl(&nand->ctl); + val |= SUNXI_NAND_CTL_RST; + writel(val, &nand->ctl); + + /* Wait until reset pin is deasserted */ + do { + val = readl(&nand->ctl); + if (!(val & SUNXI_NAND_CTL_RST)) + break; + } while (1); + + /** \todo Chip select, currently kind of static */ + val = readl(&nand->ctl); + val &= 0xf0fff0f2; + val |= SUNXI_NAND_CTL_EN; + val |= SUNXI_NAND_CTL_PAGE_SIZE(CONFIG_NAND_SUNXI_PAGE_SIZE); + writel(val, &nand->ctl); + + writel(0x100, &nand->timing_ctl); + writel(0x7ff, &nand->timing_cfg); + + /* reset CMD */ + val = SUNXI_NAND_CMD_SEND_CMD1 | SUNXI_NAND_CMD_WAIT_FLAG | + NAND_CMD_RESET; + writel(val, &nand->cmd); + do { + val = readl(&nand->st); + if (val & (1<<1)) + break; + udelay(1000); + } while (1); + + printf("Nand initialised\n"); +} + +int +nand_wait_timeout(u32 *reg, u32 mask, u32 val) +{ + unsigned long tmo = timer_get_us() + 1000000; /* 1s */ + + while ((readl(reg) & mask) != val) { + if (timer_get_us() > tmo) + return -ETIMEDOUT; + } + + return 0; +} + +/* random seed */ +static 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; + +static void +nand_config_ecc(struct sunxi_nand *nand, uint32_t page, int syndrome) +{ + static u8 strength[] = {16, 24, 28, 32, 40, 48, 56, 60, 64}; + int i; + uint32_t ecc_mode; + u32 ecc; + u16 seed = 0; + + for (i = 0; i < ARRAY_SIZE(strength); i++) { + if (CONFIG_NAND_SUNXI_ECC_STRENGTH == strength[i]) { + ecc_mode = i; + break; + } + } + + if (i == ARRAY_SIZE(strength)) { + printf("ECC strength unsupported\n"); + return; + } + + ecc = SUNXI_NAND_ECC_CTL_ECC_EN | + SUNXI_NAND_ECC_CTL_PIPELINE | + SUNXI_NAND_ECC_CTL_RND_EN | + SUNXI_NAND_ECC_CTL_MODE(ecc_mode); + + if (CONFIG_NAND_SUNXI_ECC_STEP == 512) + ecc |= SUNXI_NAND_ECC_CTL_BS_512B; + + if (syndrome) + seed = 0x4A80; + else + seed = random_seed[page % ARRAY_SIZE(random_seed)]; + + ecc |= SUNXI_NAND_ECC_CTL_RND_SEED(seed); + + writel(ecc, &nand->ecc_ctl); +} + +/* read CONFIG_NAND_SUNXI_ECC_STEP bytes from real_addr to temp_buf */ +void +nand_read_block(struct sunxi_nand *nand, phys_addr_t src, dma_addr_t dst, + int syndrome) +{ + struct sunxi_dma * const dma = (struct sunxi_dma *)SUNXI_DMA_BASE; + struct sunxi_dma_cfg * const dma_cfg = &dma->ddma[0]; + + uint32_t shift; + uint32_t page; + uint32_t addr; + uint32_t oob_offset; + uint32_t ecc_bytes; + u32 val; + u32 cmd; + + page = src / CONFIG_NAND_SUNXI_PAGE_SIZE; + if (page > 0xFFFF) { + /* TODO: currently this is not supported */ + printf("Reading from address >= %08X is not allowed.\n", + 0xFFFF * CONFIG_NAND_SUNXI_PAGE_SIZE); + return; + } + + shift = src % CONFIG_NAND_SUNXI_PAGE_SIZE; + writel(0, &nand->ecc_st); + + /* ECC_CTL, randomization */ + ecc_bytes = CONFIG_NAND_SUNXI_ECC_STRENGTH * + fls(CONFIG_NAND_SUNXI_ECC_STEP * 8); + ecc_bytes = DIV_ROUND_UP(ecc_bytes, 8); + ecc_bytes += (ecc_bytes & 1); /* Align to 2-bytes */ + ecc_bytes += 4; + + nand_config_ecc(nand, page, syndrome); + if (syndrome) { + /* shift every 1kB in syndrome */ + shift += (shift / CONFIG_NAND_SUNXI_ECC_STEP) * ecc_bytes; + oob_offset = CONFIG_NAND_SUNXI_ECC_STEP + shift; + } else { + oob_offset = CONFIG_NAND_SUNXI_PAGE_SIZE + + (shift / CONFIG_NAND_SUNXI_ECC_STEP) * ecc_bytes; + } + + addr = (page << 16) | shift; + + /* DMA */ + val = readl(&nand->ctl); + writel(val | SUNXI_NAND_CTL_RAM_METHOD_DMA, &nand->ctl); + + writel(oob_offset, &nand->spare_area); + + /* DMAC + * \todo Separate this into a tidy driver */ + writel(0x0, &dma->irq_en); /* clear dma interrupts */ + writel((uint32_t) &nand->io_data , &dma_cfg->src_addr); + writel(dst , &dma_cfg->dst_addr); + writel(0x00007F0F , &dma_cfg->ddma_para); + writel(CONFIG_NAND_SUNXI_ECC_STEP, &dma_cfg->bc); + + val = SUNXI_DMA_CTL_SRC_DRQ(DDMA_SRC_DRQ_NAND) | + SUNXI_DMA_CTL_MODE_IO | + SUNXI_DMA_CTL_SRC_DATA_WIDTH_32 | + SUNXI_DMA_CTL_DST_DRQ(DDMA_DST_DRQ_SDRAM) | + SUNXI_DMA_CTL_DST_DATA_WIDTH_32 | + SUNXI_DMA_CTL_TRIGGER; + writel(val, &dma_cfg->ctl); + + writel(0x00E00530, &nand->rcmd_set); + nand_wait_timeout(&nand->st, SUNXI_NAND_ST_FIFO_FULL, 0); + + writel(1 , &nand->block_num); + writel(addr, &nand->addr_low); + writel(0 , &nand->addr_high); + + /* CMD (PAGE READ) */ + cmd = 0x85E80000; + cmd |= SUNXI_NAND_CMD_ADDR_CYCLES(CONFIG_NAND_SUNXI_ADDR_CYCLES); + cmd |= (syndrome ? SUNXI_NAND_CMD_ORDER_SEQ : + SUNXI_NAND_CMD_ORDER_INTERLEAVE); + writel(cmd, &nand->cmd); + + if(nand_wait_timeout(&nand->st, SUNXI_NAND_ST_DMA_INT, + SUNXI_NAND_ST_DMA_INT)) { + printf("NAND timeout reading data\n"); + return; + } + + if(nand_wait_timeout(&dma_cfg->ctl, SUNXI_DMA_CTL_TRIGGER, 0)) { + printf("NAND timeout reading data\n"); + return; + } + + if (readl(&nand->ecc_st)) + ecc_errors++; +} + +int +nand_spl_load_image(uint32_t offs, unsigned int size, void *dest) +{ + struct sunxi_nand * const nand = (struct sunxi_nand *)SUNXI_NFC_BASE; + dma_addr_t dst_block; + dma_addr_t dst_end; + phys_addr_t addr = offs; + + dst_end = ((dma_addr_t) dest) + size; + + memset((void *)dest, 0x0, size); + ecc_errors = 0; + for (dst_block = (dma_addr_t) dest; dst_block < dst_end; + dst_block += CONFIG_NAND_SUNXI_ECC_STEP, + addr += CONFIG_NAND_SUNXI_ECC_STEP) { + /* syndrome read first 4MiB to match Allwinner BootROM */ + nand_read_block(nand, addr, dst_block, addr < 0x400000); + } + + if (ecc_errors) + printf("Error: %d ECC failures detected\n", ecc_errors); + return ecc_errors == 0; +} + +void +nand_deselect(void) +{} diff --git a/include/configs/sun4i.h b/include/configs/sun4i.h index ea079eb..a3c9408 100644 --- a/include/configs/sun4i.h +++ b/include/configs/sun4i.h @@ -18,6 +18,7 @@ #endif
#define CONFIG_SUNXI_USB_PHYS 3 +#define CONFIG_NAND_SUNXI_GPC_PORTS {16, 17, 18, 19, 20, 21, 22, 24}
/* * Include common sunxi configuration where most the settings are diff --git a/include/configs/sun5i.h b/include/configs/sun5i.h index d257659..8e13df5 100644 --- a/include/configs/sun5i.h +++ b/include/configs/sun5i.h @@ -19,6 +19,9 @@
#define CONFIG_SUNXI_USB_PHYS 2
+/* \todo A13 only defines port 19, whereas A10s requires each of these */ +#define CONFIG_NAND_SUNXI_GPC_PORTS {16, 17, 18, 19} + /* * Include common sunxi configuration where most the settings are */ diff --git a/include/configs/sun7i.h b/include/configs/sun7i.h index 56101a9..3d26ce8 100644 --- a/include/configs/sun7i.h +++ b/include/configs/sun7i.h @@ -24,6 +24,8 @@ #define CONFIG_ARMV7_SECURE_BASE SUNXI_SRAM_B_BASE #define CONFIG_TIMER_CLK_FREQ 24000000
+#define CONFIG_NAND_SUNXI_GPC_PORTS {16, 17, 18, 19, 20, 21, 22, 24} + /* * Include common sunxi configuration where most the settings are */ diff --git a/include/configs/sun8i.h b/include/configs/sun8i.h index 7111c63..cd33758 100644 --- a/include/configs/sun8i.h +++ b/include/configs/sun8i.h @@ -20,6 +20,12 @@
#define CONFIG_SUNXI_USB_PHYS 2
+#if defined(CONFIG_MACH_SUN8I_A23) +#define CONFIG_NAND_SUNXI_GPC_PORTS {16, 17, 18} +#elif defined(CONFIG_MACH_SUN8I_A33) +#define CONFIG_NAND_SUNXI_GPC_PORTS {16} +#endif + /* * Include common sunxi configuration where most the settings are */ diff --git a/include/configs/sunxi-common.h b/include/configs/sunxi-common.h index c8ebb54..cce0441 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 */ @@ -324,6 +326,24 @@ extern int soft_i2c_gpio_scl; #define CONFIG_ENV_IS_NOWHERE #endif
+#ifdef CONFIG_SPL_NAND_SUPPORT +#define CONFIG_NAND +#define CONFIG_SYS_NAND_SELF_INIT +#define CONFIG_NAND_SUNXI +#define CONFIG_CMD_SPL_WRITE_SIZE 0x000400 +#define CONFIG_SYS_NAND_U_BOOT_OFFS 0x008000 + +/* \todo Make these parameterisable in kernel config ? */ +#define CONFIG_NAND_SUNXI_PAGE_SIZE 8192 +#define CONFIG_NAND_SUNXI_ECC_STEP 1024 +#define CONFIG_NAND_SUNXI_ECC_STRENGTH 40 +#define CONFIG_NAND_SUNXI_ADDR_CYCLES 5 + +#ifndef CONFIG_NAND_SUNXI_GPC_PORTS +#error "No NAND GPC ports defined, NAND unsupported" +#endif +#endif /* CONFIG_SPL_NAND_SUPPORT */ + #define CONFIG_MISC_INIT_R #define CONFIG_SYS_CONSOLE_IS_IN_ENV
participants (1)
-
Roy Spliet