This patch adds the NAND SPL framework needed to boot i.MX31 boards from NAND. It has been tested on a i.MX31 PDK board with large page NAND. Small page NANDs should work as well, but this has not been tested. Note: The i.MX31 NFC uses a non-standard layout for large page NANDs, whether this is compatible with a particular setup depends on how the NAND device is programmed by the flash programmer (e.g. JTAG debugger). The patch is based on the work by Maxim Artamonov. Signed-off-by: Maxim Artamonov <scn1874@yandex.ru> Signed-off-by: Magnus Lilja <lilja.magnus@gmail.com>master
parent
df81238b3e
commit
40c642bc19
@ -0,0 +1,109 @@ |
||||
/*
|
||||
* |
||||
* (c) 2009 Magnus Lilja <lilja.magnus@gmail.com> |
||||
* |
||||
* See file CREDITS for list of people who contributed to this |
||||
* project. |
||||
* |
||||
* 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 |
||||
*/ |
||||
|
||||
#ifndef __FSL_NFC_H |
||||
#define __FSL_NFC_H |
||||
|
||||
/*
|
||||
* Register map and bit definitions for the Freescale NAND Flash |
||||
* Controller present in i.MX31 and other devices. |
||||
*/ |
||||
|
||||
struct fsl_nfc_regs { |
||||
u32 main_area0[128]; /* @0x000 */ |
||||
u32 main_area1[128]; |
||||
u32 main_area2[128]; |
||||
u32 main_area3[128]; |
||||
u32 spare_area0[4]; |
||||
u32 spare_area1[4]; |
||||
u32 spare_area2[4]; |
||||
u32 spare_area3[4]; |
||||
u32 reserved1[64 - 16 + 64 * 5]; |
||||
u16 bufsiz; /* @ 0xe00 */ |
||||
u16 reserved2; |
||||
u16 buffer_address; |
||||
u16 flash_add; |
||||
u16 flash_cmd; |
||||
u16 configuration; |
||||
u16 ecc_status_result; |
||||
u16 ecc_rslt_main_area; |
||||
u16 ecc_rslt_spare_area; |
||||
u16 nf_wr_prot; |
||||
u16 unlock_start_blk_add; |
||||
u16 unlock_end_blk_add; |
||||
u16 nand_flash_wr_pr_st; |
||||
u16 nand_flash_config1; |
||||
u16 nand_flash_config2; |
||||
}; |
||||
|
||||
/*
|
||||
* 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 /* __FSL_NFC_H */ |
@ -0,0 +1,259 @@ |
||||
/*
|
||||
* (C) Copyright 2009 |
||||
* Magnus Lilja <lilja.magnus@gmail.com> |
||||
* |
||||
* (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> |
||||
#include <fsl_nfc.h> |
||||
|
||||
static struct fsl_nfc_regs *nfc; |
||||
|
||||
static void nfc_wait_ready(void) |
||||
{ |
||||
uint32_t tmp; |
||||
|
||||
while (!(readw(&nfc->nand_flash_config2) & NFC_INT)) |
||||
; |
||||
|
||||
/* Reset interrupt flag */ |
||||
tmp = readw(&nfc->nand_flash_config2); |
||||
tmp &= ~NFC_INT; |
||||
writew(tmp, &nfc->nand_flash_config2); |
||||
} |
||||
|
||||
static void nfc_nand_init(void) |
||||
{ |
||||
/* unlocking RAM Buff */ |
||||
writew(0x2, &nfc->configuration); |
||||
|
||||
/* hardware ECC checking and correct */ |
||||
writew(NFC_ECC_EN, &nfc->nand_flash_config1); |
||||
} |
||||
|
||||
static void nfc_nand_command(unsigned short command) |
||||
{ |
||||
writew(command, &nfc->flash_cmd); |
||||
writew(NFC_CMD, &nfc->nand_flash_config2); |
||||
nfc_wait_ready(); |
||||
} |
||||
|
||||
static void nfc_nand_page_address(unsigned int page_address) |
||||
{ |
||||
unsigned int page_count; |
||||
|
||||
writew(0x00, &nfc->flash_cmd); |
||||
writew(NFC_ADDR, &nfc->nand_flash_config2); |
||||
nfc_wait_ready(); |
||||
|
||||
/* code only for 2kb flash */ |
||||
if (CONFIG_SYS_NAND_PAGE_SIZE == 0x800) { |
||||
writew(0x00, &nfc->flash_add); |
||||
writew(NFC_ADDR, &nfc->nand_flash_config2); |
||||
nfc_wait_ready(); |
||||
} |
||||
|
||||
page_count = CONFIG_SYS_NAND_SIZE / CONFIG_SYS_NAND_PAGE_SIZE; |
||||
|
||||
if (page_address <= page_count) { |
||||
page_count--; /* transform 0x01000000 to 0x00ffffff */ |
||||
do { |
||||
writew(page_address & 0xff, &nfc->flash_add); |
||||
writew(NFC_ADDR, &nfc->nand_flash_config2); |
||||
nfc_wait_ready(); |
||||
page_address = page_address >> 8; |
||||
page_count = page_count >> 8; |
||||
} while (page_count); |
||||
} |
||||
} |
||||
|
||||
static void nfc_nand_data_output(void) |
||||
{ |
||||
int i; |
||||
|
||||
/*
|
||||
* The NAND controller requires four output commands for |
||||
* large page devices. |
||||
*/ |
||||
for (i = 0; i < (CONFIG_SYS_NAND_PAGE_SIZE / 512); i++) { |
||||
writew(NFC_ECC_EN, &nfc->nand_flash_config1); |
||||
writew(i, &nfc->buffer_address); /* read in i:th buffer */ |
||||
writew(NFC_OUTPUT, &nfc->nand_flash_config2); |
||||
nfc_wait_ready(); |
||||
} |
||||
} |
||||
|
||||
static int nfc_nand_check_ecc(void) |
||||
{ |
||||
return readw(&nfc->ecc_status_result); |
||||
} |
||||
|
||||
static int nfc_read_page(unsigned int page_address, unsigned char *buf) |
||||
{ |
||||
int i; |
||||
u32 *src; |
||||
u32 *dst; |
||||
|
||||
writew(0, &nfc->buffer_address); /* read in first 0 buffer */ |
||||
nfc_nand_command(NAND_CMD_READ0); |
||||
nfc_nand_page_address(page_address); |
||||
|
||||
if (CONFIG_SYS_NAND_PAGE_SIZE == 0x800) |
||||
nfc_nand_command(NAND_CMD_READSTART); |
||||
|
||||
nfc_nand_data_output(); /* fill the main buffer 0 */ |
||||
|
||||
if (nfc_nand_check_ecc()) |
||||
return -1; |
||||
|
||||
src = &nfc->main_area0[0]; |
||||
dst = (u32 *)buf; |
||||
|
||||
/* main copy loop from NAND-buffer to SDRAM memory */ |
||||
for (i = 0; i < (CONFIG_SYS_NAND_PAGE_SIZE / 4); i++) { |
||||
writel(readl(src), dst); |
||||
src++; |
||||
dst++; |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int is_badblock(int pagenumber) |
||||
{ |
||||
int page = pagenumber; |
||||
u32 badblock; |
||||
u32 *src; |
||||
|
||||
/* Check the first two pages for bad block markers */ |
||||
for (page = pagenumber; page < pagenumber + 2; page++) { |
||||
writew(0, &nfc->buffer_address); /* read in first 0 buffer */ |
||||
nfc_nand_command(NAND_CMD_READ0); |
||||
nfc_nand_page_address(page); |
||||
|
||||
if (CONFIG_SYS_NAND_PAGE_SIZE == 0x800) |
||||
nfc_nand_command(NAND_CMD_READSTART); |
||||
|
||||
nfc_nand_data_output(); /* fill the main buffer 0 */ |
||||
|
||||
src = &nfc->spare_area0[0]; |
||||
|
||||
/*
|
||||
* IMPORTANT NOTE: The nand flash controller uses a non- |
||||
* standard layout for large page devices. This can |
||||
* affect the position of the bad block marker. |
||||
*/ |
||||
/* Get the bad block marker */ |
||||
badblock = readl(&src[CONFIG_SYS_NAND_BAD_BLOCK_POS / 4]); |
||||
badblock >>= 8 * (CONFIG_SYS_NAND_BAD_BLOCK_POS % 4); |
||||
badblock &= 0xff; |
||||
|
||||
/* bad block marker verify */ |
||||
if (badblock != 0xff) |
||||
return 1; /* potential bad block */ |
||||
} |
||||
|
||||
return 0; |
||||
} |
||||
|
||||
static int nand_load(unsigned int from, unsigned int size, unsigned char *buf) |
||||
{ |
||||
int i; |
||||
unsigned int page; |
||||
unsigned int maxpages = CONFIG_SYS_NAND_SIZE / |
||||
CONFIG_SYS_NAND_PAGE_SIZE; |
||||
|
||||
nfc = (void *)NFC_BASE_ADDR; |
||||
|
||||
nfc_nand_init(); |
||||
|
||||
/* Convert to page number */ |
||||
page = from / CONFIG_SYS_NAND_PAGE_SIZE; |
||||
i = 0; |
||||
|
||||
while (i < (size / CONFIG_SYS_NAND_PAGE_SIZE)) { |
||||
if (nfc_read_page(page, buf) < 0) |
||||
return -1; |
||||
|
||||
page++; |
||||
i++; |
||||
buf = buf + CONFIG_SYS_NAND_PAGE_SIZE; |
||||
|
||||
/*
|
||||
* Check if we have crossed a block boundary, and if so |
||||
* check for bad block. |
||||
*/ |
||||
if (!(page % CONFIG_SYS_NAND_PAGE_COUNT)) { |
||||
/*
|
||||
* Yes, new block. See if this block is good. If not, |
||||
* loop until we find i good block. |
||||
*/ |
||||
while (is_badblock(page)) { |
||||
page = page + CONFIG_SYS_NAND_PAGE_COUNT; |
||||
/* Check i we've reached the end of flash. */ |
||||
if (page >= maxpages) |
||||
return -1; |
||||
} |
||||
} |
||||
} |
||||
|
||||
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); |
||||
|
||||
nfc = (void *)NFC_BASE_ADDR; |
||||
|
||||
/*
|
||||
* CONFIG_SYS_NAND_U_BOOT_OFFS and CONFIG_SYS_NAND_U_BOOT_SIZE must |
||||
* be aligned to full pages |
||||
*/ |
||||
if (!nand_load(CONFIG_SYS_NAND_U_BOOT_OFFS, CONFIG_SYS_NAND_U_BOOT_SIZE, |
||||
(uchar *)CONFIG_SYS_NAND_U_BOOT_DST)) { |
||||
/* Copy from NAND successful, start U-boot */ |
||||
uboot = (void *)CONFIG_SYS_NAND_U_BOOT_START; |
||||
uboot(); |
||||
} else { |
||||
/* Unrecoverable error when copying from NAND */ |
||||
hang(); |
||||
} |
||||
} |
||||
|
||||
/*
|
||||
* Called in case of an exception. |
||||
*/ |
||||
void hang(void) |
||||
{ |
||||
/* Loop forever */ |
||||
while (1) ; |
||||
} |
Loading…
Reference in new issue