Chinaunix首页 | 论坛 | 博客
  • 博客访问: 819778
  • 博文数量: 117
  • 博客积分: 2583
  • 博客等级: 少校
  • 技术积分: 1953
  • 用 户 组: 普通用户
  • 注册时间: 2008-12-06 22:58
个人简介

Coder

文章分类
文章存档

2013年(1)

2012年(10)

2011年(12)

2010年(77)

2009年(13)

2008年(4)

分类: 嵌入式

2010-03-13 21:26:05

第六步、从NAND Flash启动的支持

移植的重要关键部分是是代码的拷贝,从nand拷贝到sdram。从编程的角度看,nor flash是存储设备,而nandI/O外设,对它们的底层操作有本质的不同。u-boot中对ARM的支持部分,没有支持nand启动的代码,而只有 nor flash的,重定位u-boot的那部分代码只适用于nor flash,而不适用于nand整个启动代码参考了vivi的代码。因此,将vivinand_read.c(支持S3C2440vivi,此文件中的读nand函数是直接操作nand flash的,与u-boot自带,适合用于启动时用。)拷入/board/samsung/mini2440目录下。并修改目录下的makefile,使nand_read.c被编译。

 

COBJS  := nand_read.o mini2440.o  flash.o

 

Nand_read.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

 * Date  : $Date: 2004/02/04 10:37:37 $

 *

 * u-boot integration and bad-block skipping (C) 2006 by OpenMoko, Inc.

 * Author: Harald Welte

 */

 

#include

#include

 

 

#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

#if defined(CONFIG_S3C2410)

#define NFCONF       __REGi(NF_BASE + 0x0)

#define NFCMD     __REGb(NF_BASE + 0x4)

#define NFADDR       __REGb(NF_BASE + 0x8)

#define NFDATA       __REGb(NF_BASE + 0xc)

#define NFSTAT       __REGb(NF_BASE + 0x10)

#define NFSTAT_BUSY  1

#define nand_select()    (NFCONF &= ~0x800)

#define nand_deselect()  (NFCONF |= 0x800)

#define nand_clear_RnB() do {} while (0)

#elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)

#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))

#endif

 

static inline void nand_wait(void)

{

    int i;

 

    while (!(NFSTAT & NFSTAT_BUSY))

       for (i=0; i<10; i++);

}

 

struct boot_nand_t {

    int page_size;

    int block_size;

    int bad_block_offset;

//  unsigned long size;

};

 

#if 0

#if defined(CONFIG_S3C2410) || defined(CONFIG_MINI2440)

/* configuration for 2410 with 512byte sized flash */

#define NAND_PAGE_SIZE      512

#define BAD_BLOCK_OFFSET 5

#define NAND_BLOCK_MASK     (NAND_PAGE_SIZE - 1)

#define NAND_BLOCK_SIZE     0x4000

#else

/* 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)

#endif

 

/* compile time failure in case of an invalid configuration */

#if defined(CONFIG_S3C2410) && (NAND_PAGE_SIZE != 512)

#error "S3C2410 does not support nand page size != 512"

#endif

#endif

 

static int is_bad_block(struct boot_nand_t * nand, unsigned long i)

{

    unsigned char data;

    unsigned long page_num;

 

    nand_clear_RnB();

    if (nand->page_size == 512) {

       NFCMD = NAND_CMD_READOOB; /* 0x50 */

       NFADDR = nand->bad_block_offset & 0xf;

       NFADDR = (i >> 9) & 0xff;

       NFADDR = (i >> 17) & 0xff;

       NFADDR = (i >> 25) & 0xff;

    } else if (nand->page_size == 2048) {

       page_num = i >> 11; /* addr / 2048 */

       NFCMD = NAND_CMD_READ0;

       NFADDR = nand->bad_block_offset & 0xff;

       NFADDR = (nand->bad_block_offset >> 8) & 0xff;

       NFADDR = page_num & 0xff;

       NFADDR = (page_num >> 8) & 0xff;

       NFADDR = (page_num >> 16) & 0xff;

       NFCMD = NAND_CMD_READSTART;

    } else {

       return -1;

    }

    nand_wait();

    data = (NFDATA & 0xff);

    if (data != 0xff)

       return 1;

 

    return 0;

}

 

static int nand_read_page_ll(struct boot_nand_t * nand, unsigned char *buf, unsigned long addr)

{

    unsigned short *ptr16 = (unsigned short *)buf;

    unsigned int i, page_num;

 

    nand_clear_RnB();

 

    NFCMD = NAND_CMD_READ0;

 

    if (nand->page_size == 512) {

       /* Write Address */

       NFADDR = addr & 0xff;

       NFADDR = (addr >> 9) & 0xff;

       NFADDR = (addr >> 17) & 0xff;

       NFADDR = (addr >> 25) & 0xff;

    } else if (nand->page_size == 2048) {

       page_num = addr >> 11; /* addr / 2048 */

       /* Write Address */

       NFADDR = 0;

       NFADDR = 0;

       NFADDR = page_num & 0xff;

       NFADDR = (page_num >> 8) & 0xff;

       NFADDR = (page_num >> 16) & 0xff;

       NFCMD = NAND_CMD_READSTART;

    } else {

       return -1;

    }

    nand_wait();

 

#if defined(CONFIG_S3C2410)

    for (i = 0; i < nand->page_size; i++) {

       *buf = (NFDATA & 0xff);

       buf++;

    }

#elif defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442)

    for (i = 0; i < (nand->page_size>>1); i++) {

       *ptr16 = NFDATA16;

       ptr16++;

    }

#endif

 

    return nand->page_size;

}

 

static unsigned short nand_read_id()

{

    unsigned short res = 0;

    NFCMD = NAND_CMD_READID;

    NFADDR = 0;

    res = NFDATA;

    res = (res << 8) | NFDATA;

    return res;

}

 

extern unsigned int dynpart_size[];

 

/* low level nand read function */

int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size)

{

    int i, j;

    unsigned short nand_id;

    struct boot_nand_t nand;

 

    /* chip Enable */

    nand_select();

    nand_clear_RnB();

   

    for (i = 0; i < 10; i++)

       ;

    nand_id = nand_read_id();

    if (0) { /* dirty little hack to detect if nand id is misread */

       unsigned short * nid = (unsigned short *)0x31fffff0;

       *nid = nand_id;

    }  

 

       if (nand_id == 0xec76 ||    /* Samsung K91208 */

           nand_id == 0xad76 ) {   /*Hynix HY27US08121A*/

       nand.page_size = 512;

       nand.block_size = 16 * 1024;

       nand.bad_block_offset = 5;

    //  nand.size = 0x4000000;

    } else if (nand_id == 0xecf1 || /* Samsung K9F1G08U0B */

          nand_id == 0xecda ||  /* Samsung K9F2G08U0B */

          nand_id == 0xecd3 )   { /* Samsung K9K8G08 */

       nand.page_size = 2048;

       nand.block_size = 128 * 1024;

       nand.bad_block_offset = nand.page_size;

    //  nand.size = 0x8000000;

    } else {

       return -1; // hang

    }

    if ((start_addr & (nand.block_size-1)) || (size & ((nand.block_size-1))))

       return -1; /* invalid alignment */

 

    for (i=start_addr; i < (start_addr + size);) {

#ifdef CONFIG_S3C2410_NAND_SKIP_BAD

       if (i & (nand.block_size-1)== 0) {

           if (is_bad_block(&nand, i) ||

               is_bad_block(&nand, i + nand.page_size)) {

              /* Bad block */

              i += nand.block_size;

              size += nand.block_size;

              continue;

           }

       }

#endif

       j = nand_read_page_ll(&nand, buf, i);

       i += j;

       buf += j;

    }

 

    /* chip Disable */

    nand_deselect();

 

    return 0;

}

 

/cpu/arm920t/start.s中之前阶段中为了从内存中启动而屏蔽掉的语句恢复。然后,删除或注释掉原来的 relocate 部分,用nand启动相关部分来替代。这段代码中调用的读取Nand函数nand_read_ll正是在nand_read.c中实现的。

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

    bl  cpu_init_crit

#endif

 

/*********************** CHECK_CODE_POSITION *************************/

    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         */

    beq stack_setup

/**************************** NAND_BOOT ******************************/

 

#define LENGTH_UBOOT 0x40000

#define NAND_CTL_BASE 0x4E000000

 

#ifdef CONFIG_S3C2440

/* Offset */

#define oNFCONF 0x00

#define oNFCONT 0x04

#define oNFCMD 0x08

#define oNFSTAT 0x20

 

    @ reset NAND

    mov r1, #NAND_CTL_BASE

    ldr r2, =( (7<<12)|(7<<8)|(7<<4)|(0<<0) )

    str r2, [r1, #oNFCONF]

    ldr r2, [r1, #oNFCONF]

   

    ldr r2, =( (1<<4)|(0<<1)|(1<<0) )   @ Active low CE Control

    str r2, [r1, #oNFCONT]

    ldr r2, [r1, #oNFCONT]

   

    ldr r2, =(0x6) @ RnB Clear

    str r2, [r1, #oNFSTAT]

    ldr r2, [r1, #oNFSTAT]

   

    mov r2, #0xff  @ RESET command

    strb   r2, [r1, #oNFCMD]

   

    mov r3, #0 @ wait

nand1:

    add r3, r3, #0x1

    cmp r3, #0xa

    blt nand1

 

nand2:

    ldr r2, [r1, #oNFSTAT]   @ wait ready

    tst r2, #0x4

    beq nand2

   

   

    ldr r2, [r1, #oNFCONT]

    orr r2, r2, #0x2  @ Flash Memory Chip Disable

    str r2, [r1, #oNFCONT]

   

    @ get read to call C functions (for nand_read())

    ldr sp, DW_STACK_START   @ setup stack pointer

    mov fp, #0 @ no previous frame, so fp=0

 

    @ copy U-Boot to RAM

    ldr r0, =TEXT_BASE

    mov r1, #0x0

    mov r2, #LENGTH_UBOOT

    bl  nand_read_ll

    tst r0, #0x0

    beq ok_nand_read

 

bad_nand_read:

loop2:

    b   loop2  @ infinite loop

ok_nand_read:

    @ verify

    mov r0, #0

    ldr r1, =TEXT_BASE

    mov r2, #0x400 @ 4 bytes * 1024 = 4K-bytes

go_next:

    ldr r3, [r0], #4

    ldr r4, [r1], #4

    teq r3, r4

    bne notmatch

    subs   r2, r2, #4

    beq stack_setup

    bne go_next

 

notmatch:

loop3:

    b   loop3  @ infinite loop

#endif

还要定义为了调用nand_read_ll而初始化的栈所用的变量DW_STACK_START

_start_armboot:   .word start_armboot

#define STACK_BASE 0x33f00000

#define STACK_SIZE 0x10000

    .align 2

DW_STACK_START:   .word  STACK_BASE+STACK_SIZE-4

同时,在/board/samsung/mini2440/lowlevel_init.s中,根据开发板电路,对内存相关的几个寄存器定义进行调整。

s3c2440的两种启动方式,nor nand 启动在原理上有些不同,若从nor 启动,就是直接从nor flash0地址开始执行(flash 映射到bank0),可以执行flash的内部的任意地址的内容。而从nand启动,是在运行之前,由CPU自动地拷贝nand的前4K的内容到内部的 4K RAM中,再从内部的这个RAM中的0地址开始执行。所以我们必须保证在跳到SDARM运行之前的所有代码(cpu/arm920t/start.Sboard/Samsung/mini2440/lowlevel_init.Sboard/Samsung/mini2440/nand_read.c),都在前4K范围之内。

     而通过查看编译后u-boot根目录下的u-boot.map文件可知,lowlevel_init.o并不在前4K的范围:

 .text 

0x33f948e0       0x9c board/samsung/ok2440v3/libok2440v3.a(lowlevel_init.o)
   0x33f948e4               lowlevel_init  // (u-boot
的起始地址为0x33f80000)

所以我们要修改/cpu/arm920t/u-boot.lds文件,使lowlevel_init.o在前4K范围:

    .text :
    {
        cpu/arm920t/start.o    (.text)
        board/samsung/mini2440/lowlevel_init.o (.text)

board/samsung/mini2440/nand_read.o (.text)
        *(.text)
    }

 
修改后,重新编译,u-boot烧入nand flash,能成功从nand启动了。


主要参考资料:

移植U-Boot-2008.10到友善之臂 mini2440
http://blog.chinaunix.net/u3/96581/showart_1934276.html
移植 u-boot2009.08OK2440V3开发板(1)---方向与方法



阅读(2407) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~