[U-Boot] [PATCH v2 1/3] MX31: Add NAND SPL for i.MX31.

Magnus Lilja
2009-05-03T19:56:57Z

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@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@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@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 */
+ }
+}
+
--
1.5.6