1
0
mirror of git://projects.qi-hardware.com/xburst-tools.git synced 2025-04-21 12:27:27 +03:00
Files
xburst-tools/qiboot/src/cpu/s3c2442/nand_read.c
Dmitry Kurochkin 0ca1d89412 Bad magic when booting from NAND
Qi fails to boot from NAND. Qi booting from SD goes fine. Booting NAND
from NOR works as well.

I have found the problem. The error was because we check if the
current block is bad or the next one is bad. And skip the current
block even if only the next one is bad. This way we skip good block
before a bad one.

This patch fixes the bug.

Signed-off-by: Dmitry Kurochkin <dmitry.kurochkin@gmail.com>
2009-05-07 00:27:20 -05:00

157 lines
3.6 KiB
C

/*
* nand_read.c: Simple NAND read functions for booting from NAND
*
* This is used by cpu/arm920/start.S assembler code,
* and the board-specific linker script must make sure this
* file is linked within the first 4kB of NAND flash.
*
* Taken from GPLv2 licensed vivi bootloader,
* Copyright (C) 2002 MIZI Research, Inc.
*
* Author: Hwang, Chideok <hwang@mizi.com>
* Date : $Date: 2004/02/04 10:37:37 $
*
* u-boot integration and bad-block skipping (C) 2006 by OpenMoko, Inc.
* Author: Harald Welte <laforge@openmoko.org>
*/
/* NOTE this stuff runs in steppingstone context! */
/* the API refers to 512-byte blocks */
#include <qi.h>
#include "nand_read.h"
#define NAND_CMD_READ0 0
#define NAND_CMD_READSTART 0x30
#define __REGb(x) (*(volatile unsigned char *)(x))
#define __REGw(x) (*(volatile unsigned short *)(x))
#define __REGi(x) (*(volatile unsigned int *)(x))
#define NF_BASE 0x4e000000
#define NFCONF __REGi(NF_BASE + 0x0)
#define NFCONT __REGi(NF_BASE + 0x4)
#define NFCMD __REGb(NF_BASE + 0x8)
#define NFADDR __REGb(NF_BASE + 0xc)
#define NFDATA __REGb(NF_BASE + 0x10)
#define NFDATA16 __REGw(NF_BASE + 0x10)
#define NFSTAT __REGb(NF_BASE + 0x20)
#define NFSTAT_BUSY 1
#define nand_select() (NFCONT &= ~(1 << 1))
#define nand_deselect() (NFCONT |= (1 << 1))
#define nand_clear_RnB() (NFSTAT |= (1 << 2))
static inline void nand_wait(void)
{
int i;
while (!(NFSTAT & NFSTAT_BUSY))
for (i=0; i<10; i++);
}
/* configuration for 2440 with 2048byte sized flash */
#define NAND_5_ADDR_CYCLE
#define NAND_PAGE_SIZE 2048
#define BAD_BLOCK_OFFSET NAND_PAGE_SIZE
#define NAND_BLOCK_MASK (NAND_PAGE_SIZE - 1)
#define NAND_BLOCK_SIZE (NAND_PAGE_SIZE * 64)
int s3c2442_nand_is_bad_block(unsigned long block_index)
{
unsigned char data;
unsigned long page_num;
nand_select();
nand_clear_RnB();
page_num = block_index >> 2; /* addr / 2048 */
NFCMD = NAND_CMD_READ0;
NFADDR = BAD_BLOCK_OFFSET & 0xff;
NFADDR = (BAD_BLOCK_OFFSET >> 8) & 0xff;
NFADDR = page_num & 0xff;
NFADDR = (page_num >> 8) & 0xff;
NFADDR = (page_num >> 16) & 0xff;
NFCMD = NAND_CMD_READSTART;
nand_wait();
data = (NFDATA & 0xff);
if (data != 0xff)
return 1;
return 0;
}
static int nand_read_page_ll(unsigned char *buf, unsigned long block512, int blocks512)
{
unsigned short *ptr16 = (unsigned short *)buf;
unsigned int i, page_num;
unsigned int block_amount;
int blocks_possible = (3 - (block512 & 3)) + 1;
if (blocks512 > blocks_possible)
blocks512 = blocks_possible;
block_amount = (NAND_PAGE_SIZE / 4 / 2) * blocks512;
nand_clear_RnB();
NFCMD = NAND_CMD_READ0;
page_num = block512 >> 2; /* 512 block -> 2048 block */
/* Write Address */
NFADDR = 0;
NFADDR = (block512 & 3) << 1; /* which 512 block in 2048 */
NFADDR = page_num & 0xff;
NFADDR = (page_num >> 8) & 0xff;
NFADDR = (page_num >> 16) & 0xff;
NFCMD = NAND_CMD_READSTART;
nand_wait();
for (i = 0; i < block_amount; i++)
*ptr16++ = NFDATA16;
return blocks512;
}
/* low level nand read function */
int nand_read_ll(unsigned char *buf, unsigned long start_block512,
int blocks512)
{
int i, j;
/* chip Enable */
nand_select();
nand_clear_RnB();
for (i = 0; i < 10; i++)
;
while (blocks512 > 0) {
if (s3c2442_nand_is_bad_block(start_block512)) {
start_block512 += 4;
if (start_block512 >> 2 > BAD_BLOCK_OFFSET)
/* end of NAND */
return -1;
continue;
}
j = nand_read_page_ll(buf, start_block512, blocks512);
start_block512 += j;
buf += j << 9;
blocks512 -= j;
if (this_board->get_ui_keys)
if ((this_board->get_ui_keys)() & UI_ACTION_SKIPKERNEL) {
puts(" ** skipping \n");
return -3;
}
}
/* chip Disable */
nand_deselect();
return 0;
}