
This patch adds the NAND SPL framework needed to boot i.MX31 boards from NAND.
The patch is based on the work by Maxim Artamonov <scn1874 at yandex.ru > (which was signed-off-by him).
Signed-off-by: Magnus Lilja lilja.magnus@gmail.com --- cpu/arm1136/start.S | 29 +++-- include/asm-arm/arch-mx31/mx31-regs.h | 90 +++++++++++++ nand_spl/nand_boot_mx31.c | 223 +++++++++++++++++++++++++++++++++ 3 files changed, 331 insertions(+), 11 deletions(-) create mode 100644 nand_spl/nand_boot_mx31.c
diff --git a/cpu/arm1136/start.S b/cpu/arm1136/start.S index e622338..9bbbaf1 100644 --- a/cpu/arm1136/start.S +++ b/cpu/arm1136/start.S @@ -1,6 +1,9 @@ /* * armboot - Startup Code for OMP2420/ARM1136 CPU-core * + * + * Copyright (c) 2008 Maxim Artamonov, <scn1874 at yandex.ru> + * * Copyright (c) 2004 Texas Instruments r-woodruff2@ti.com * * Copyright (c) 2001 Marius Gröger mag@sysgo.de @@ -32,7 +35,7 @@ #include <version.h> .globl _start _start: b reset -#ifdef CONFIG_ONENAND_IPL +#if defined(CONFIG_ONENAND_IPL) || defined(CONFIG_NAND_SPL) ldr pc, _hang ldr pc, _hang ldr pc, _hang @@ -156,9 +159,9 @@ relocate: /* relocate U-Boot to RAM */ adr r0, _start /* r0 <- current position of code */ ldr r1, _TEXT_BASE /* test if we run from flash or RAM */ cmp r0, r1 /* don't reloc during debug */ -#ifndef CONFIG_ONENAND_IPL +#if !defined(CONFIG_ONENAND_IPL) && !defined(CONFIG_NAND_SPL) beq stack_setup -#endif /* CONFIG_ONENAND_IPL */ +#endif /* !CONFIG_ONENAND_IPL && !CONFIG_NAND_SPL*/
ldr r2, _armboot_start ldr r3, _bss_start @@ -175,7 +178,7 @@ copy_loop: /* Set up the stack */ stack_setup: ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */ -#ifdef CONFIG_ONENAND_IPL +#if defined(CONFIG_ONENAND_IPL) || defined (CONFIG_NAND_SPL) sub sp, r0, #128 /* leave 32 words for abort-stack */ #else sub r0, r0, #CONFIG_SYS_MALLOC_LEN /* malloc area */ @@ -184,14 +187,14 @@ stack_setup: sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif sub sp, r0, #12 /* leave 3 words for abort-stack */ -#endif /* CONFIG_ONENAND_IPL */ +#endif /* CONFIG_ONENAND_IPL || CONFIG_NAND_SPL*/
clear_bss: ldr r0, _bss_start /* find start of bss segment */ ldr r1, _bss_end /* stop here */ mov r2, #0x00000000 /* clear */
-#ifndef CONFIG_ONENAND_IPL +#if !defined(CONFIG_ONENAND_IPL) && !defined(CONFIG_NAND_SPL) clbss_l:str r2, [r0] /* clear loop... */ add r0, r0, #4 cmp r0, r1 @@ -200,12 +203,15 @@ clbss_l:str r2, [r0] /* clear loop... */
ldr pc, _start_armboot
+#ifdef CONFIG_NAND_SPL +_start_armboot: .word nand_boot +#else #ifdef CONFIG_ONENAND_IPL _start_armboot: .word start_oneboot #else _start_armboot: .word start_armboot -#endif - +#endif /* CONFIG_ONENAND_IPL */ +#endif /* CONFIG_NAND_SPL */
/* ************************************************************************* @@ -244,7 +250,7 @@ cpu_init_crit: mov lr, ip /* restore link */ mov pc, lr /* back to my caller */
-#ifndef CONFIG_ONENAND_IPL +#if !defined(CONFIG_ONENAND_IPL) && !defined(CONFIG_NAND_SPL) /* ************************************************************************* * @@ -357,12 +363,12 @@ cpu_init_crit: .macro get_fiq_stack @ setup FIQ stack ldr sp, FIQ_STACK_START .endm -#endif /* CONFIG_ONENAND_IPL */ +#endif /* !CONFIG_ONENAND_IPL && !CONFIG_NAND_SPL*/
/* * exception handlers */ -#ifdef CONFIG_ONENAND_IPL +#if defined(CONFIG_ONENAND_IPL) || defined(CONFIG_NAND_SPL) .align 5 do_hang: ldr sp, _TEXT_BASE /* use 32 words about stack */ @@ -436,3 +442,4 @@ arm1136_cache_flush: mcr p15, 0, r1, c7, c5, 0 @ invalidate I cache mov pc, lr @ back to caller #endif /* CONFIG_ONENAND_IPL */ + diff --git a/include/asm-arm/arch-mx31/mx31-regs.h b/include/asm-arm/arch-mx31/mx31-regs.h index a8a05c8..3d811d7 100644 --- a/include/asm-arm/arch-mx31/mx31-regs.h +++ b/include/asm-arm/arch-mx31/mx31-regs.h @@ -194,4 +194,94 @@ #define CS5_BASE 0xB6000000 #define PCMCIA_MEM_BASE 0xC0000000
+/* + * NAND controller + */ +#define NFC_BASE_ADDR 0xB8000000 + +/* + * Addresses for NFC registers + */ +#define NFC_BUF_SIZE (NFC_BASE_ADDR + 0xE00) +#define NFC_BUF_ADDR (NFC_BASE_ADDR + 0xE04) +#define NFC_FLASH_ADDR (NFC_BASE_ADDR + 0xE06) +#define NFC_FLASH_CMD (NFC_BASE_ADDR + 0xE08) +#define NFC_CONFIG (NFC_BASE_ADDR + 0xE0A) +#define NFC_ECC_STATUS_RESULT (NFC_BASE_ADDR + 0xE0C) +#define NFC_RSLTMAIN_AREA (NFC_BASE_ADDR + 0xE0E) +#define NFC_RSLTSPARE_AREA (NFC_BASE_ADDR + 0xE10) +#define NFC_WRPROT (NFC_BASE_ADDR + 0xE12) +#define NFC_UNLOCKSTART_BLKADDR (NFC_BASE_ADDR + 0xE14) +#define NFC_UNLOCKEND_BLKADDR (NFC_BASE_ADDR + 0xE16) +#define NFC_NF_WRPRST (NFC_BASE_ADDR + 0xE18) +#define NFC_CONFIG1 (NFC_BASE_ADDR + 0xE1A) +#define NFC_CONFIG2 (NFC_BASE_ADDR + 0xE1C) + +/* + * Addresses for NFC RAM BUFFER Main area 0 + */ +#define MAIN_AREA0 (NFC_BASE_ADDR + 0x000) +#define MAIN_AREA1 (NFC_BASE_ADDR + 0x200) +#define MAIN_AREA2 (NFC_BASE_ADDR + 0x400) +#define MAIN_AREA3 (NFC_BASE_ADDR + 0x600) + +/* + * Addresses for NFC SPARE BUFFER Spare area 0 + */ +#define SPARE_AREA0 (NFC_BASE_ADDR + 0x800) +#define SPARE_AREA1 (NFC_BASE_ADDR + 0x810) +#define SPARE_AREA2 (NFC_BASE_ADDR + 0x820) +#define SPARE_AREA3 (NFC_BASE_ADDR + 0x830) + +/* + * Set INT to 0, FCMD to 1, rest to 0 in NFC_CONFIG2 Register for Command + * operation + */ +#define NFC_CMD 0x1 + +/* + * Set INT to 0, FADD to 1, rest to 0 in NFC_CONFIG2 Register for Address + * operation + */ +#define NFC_ADDR 0x2 + +/* + * Set INT to 0, FDI to 1, rest to 0 in NFC_CONFIG2 Register for Input + * operation + */ +#define NFC_INPUT 0x4 + +/* + * Set INT to 0, FDO to 001, rest to 0 in NFC_CONFIG2 Register for Data + * Output operation + */ +#define NFC_OUTPUT 0x8 + +/* + * Set INT to 0, FD0 to 010, rest to 0 in NFC_CONFIG2 Register for Read ID + * operation + */ +#define NFC_ID 0x10 + +/* + * Set INT to 0, FDO to 100, rest to 0 in NFC_CONFIG2 Register for Read + * Status operation + */ +#define NFC_STATUS 0x20 + +/* + * Set INT to 1, rest to 0 in NFC_CONFIG2 Register for Read Status + * operation + */ +#define NFC_INT 0x8000 + +#define NFC_SP_EN (1 << 2) +#define NFC_ECC_EN (1 << 3) +#define NFC_INT_MSK (1 << 4) +#define NFC_BIG (1 << 5) +#define NFC_RST (1 << 6) +#define NFC_CE (1 << 7) +#define NFC_ONE_CYCLE (1 << 8) + #endif /* __ASM_ARCH_MX31_REGS_H */ + diff --git a/nand_spl/nand_boot_mx31.c b/nand_spl/nand_boot_mx31.c new file mode 100644 index 0000000..d698d2a --- /dev/null +++ b/nand_spl/nand_boot_mx31.c @@ -0,0 +1,223 @@ +/* + * (C) Copyright 2008 + * Maxim Artamonov, <scn1874 at yandex.ru> + * + * (C) Copyright 2006-2008 + * Stefan Roese, DENX Software Engineering, sr at denx.de. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ +#include <common.h> +#include <nand.h> +#include <asm-arm/arch/mx31-regs.h> +#include <asm/io.h> + +static void mx31_wait_ready(void) +{ + while (1) { + if (readw(NFC_CONFIG2) & NFC_INT) { + uint32_t tmp; + /* Reset interrupt flag */ + tmp = readw(NFC_CONFIG2); + tmp &= ~NFC_INT; + writew(tmp, NFC_CONFIG2); + break; + } + } +} + +static void mx31_nand_init(void) +{ + /* unlocking RAM Buff */ + writew(0x2, NFC_CONFIG); + + /* hardware ECC checking and correct */ + writew(NFC_ECC_EN, NFC_CONFIG1); +} + +static void mx31_nand_command(unsigned short command) +{ + writew(command, NFC_FLASH_CMD); + writew(NFC_CMD, NFC_CONFIG2); + mx31_wait_ready(); +} + +static void mx31_nand_page_address(unsigned int page_address) +{ + unsigned int page_count; + + writew(0x00, NFC_FLASH_ADDR); + writew(NFC_ADDR, NFC_CONFIG2); + mx31_wait_ready(); + + /* code only for 2kb flash */ + if (CFG_NAND_PAGE_SIZE == 0x800) { + writew(0x00, NFC_FLASH_ADDR); + writew(NFC_ADDR, NFC_CONFIG2); + mx31_wait_ready(); + } + + page_count = CFG_NAND_CHIP_SIZE / CFG_NAND_PAGE_SIZE; + + if (page_address <= page_count) { + page_count--; /* transform 0x01000000 to 0x00ffffff */ + do { + writew(page_address & 0xff, NFC_FLASH_ADDR); + writew(NFC_ADDR, NFC_CONFIG2); + mx31_wait_ready(); + page_address = page_address >> 8; + page_count = page_count >> 8; + } while (page_count); + } +} + +static void mx31_nand_data_output(void) +{ + int i; + + /* + * The NAND controller requires four output commands for + * large page devices. + */ + for (i = 0; i < (CFG_NAND_PAGE_SIZE / 512); i++) { + writew(NFC_ECC_EN, NFC_CONFIG1); + writew(i, NFC_BUF_ADDR); /* read in i:th buffer */ + writew(NFC_OUTPUT, NFC_CONFIG2); + mx31_wait_ready(); + } +} + +static int mx31_nand_check_ecc(void) +{ + unsigned short ecc_status_register; + + ecc_status_register = readw(NFC_ECC_STATUS_RESULT); + + if (ecc_status_register != 0) + return 1; /* error */ + return 0; +} + +static int mx31_read_page(unsigned int page_address, unsigned char *buf) +{ + int i; + volatile u32 *p1; + volatile u32 *p2; + u32 a; + + writew(0, NFC_BUF_ADDR); /* read in first 0 buffer */ + mx31_nand_command(NAND_CMD_READ0); + mx31_nand_page_address(page_address); + + if (CFG_NAND_CHIP_SIZE >= 0x08000000) + mx31_nand_command(NAND_CMD_READSTART); + + mx31_nand_data_output(); /* fill the main buffer 0 */ + + if (mx31_nand_check_ecc()) + return -1; + + p1 = (u32 *)MAIN_AREA0; + p2 = (u32 *)buf; + + /* main copy loop from NAND-buffer to SDRAM memory */ + for (i = 0; i < (CFG_NAND_PAGE_SIZE / 4); i++) { + *p2 = *p1; + p1++; + p2++; + } + + p1 = (u32 *)SPARE_AREA0; + + /* it is hardware specific code for 8-bit 512B NAND-flash spare area */ + p1++; + a = *p1; + a = (a & 0x0000ff00) >> 8; + + if (a != 0xff) /* bad block marker verify */ + return 1; /* potential bad block */ + + return 0; +} + +static int nand_load(unsigned int from, unsigned int size, unsigned char *buf) +{ + int i, bb; + + mx31_nand_init(); + + /* convert from to page number */ + from = from / CFG_NAND_PAGE_SIZE; + + i = 0; + + while (i < (size/CFG_NAND_PAGE_SIZE)) { + if ((from * CFG_NAND_PAGE_SIZE) >= CFG_NAND_CHIP_SIZE) + return 2; /* memory segment violation */ + + bb = mx31_read_page(from, buf); + + if (bb < 0) + return -1; + + /* checking first page of each block */ + /* if this page has bb marker, then skip whole block */ + if ((!(from % CFG_NAND_PAGES_PER_BLOCK)) && bb) { + from = from + CFG_NAND_PAGES_PER_BLOCK; + } else { + i++; + from++; + buf = buf + CFG_NAND_PAGE_SIZE; + } + } + + return 0; +} + +/* + * The main entry for NAND booting. It's necessary that SDRAM is already + * configured and available since this code loads the main U-Boot image + * from NAND into SDRAM and starts it from there. + */ +void nand_boot(void) +{ + __attribute__((noreturn)) void (*uboot)(void); + + /* CFG_NAND_U_BOOT_OFFS and CFG_NAND_U_BOOT_SIZE must */ + /* be aligned to full pages */ + if (!nand_load(CFG_NAND_U_BOOT_OFFS, CFG_NAND_U_BOOT_SIZE, + (uchar *)CFG_NAND_U_BOOT_DST)) { + /* Copy from NAND successful, start U-boot */ + uboot = (void *)CFG_NAND_U_BOOT_START; + uboot(); + } else { + /* Unrecoverable error when copying from NAND */ + while (1) { + /* Loop forever */ + } + } +} + +/* + * Called from start.S in case of an exception. + */ +void hang(void) +{ + while (1) { + /* Loop forever */ + } +} +