u-boot-2010.03在LT2440上的移植详解 (三)

2019-07-13 02:16发布

u-boot-2010.03在LT2440上的移植详解 (三) 郑重声明,这系列文章改写自博客园 黄刚先生的《嵌入式Linux之我行——u-boot-2009.08在2440上的移植详解》 转载时请注明出处 文章出处:http://www.lt-net.cn 编译系统 Ubuntu10.04 交叉编译器 arm-linux-gcc 4.3.3 硬件设备 LT2440开发板   测试软件 u-boot-2010.03 依赖库 无 uboot下载地址:http://ftp.denx.de/pub/u-boot/u-boot-2010.03.tar.bz2

本次移植在u-boot-2010.03原有功能的基础上增加如下特性:

  1.支持2KB  page Nand Flash读写
  2.支持Nand/Nor Flash启动自动识别
  3.支持DM9000AEP 10M/100M自适应网卡
  4.支持yaffs文件系统烧写
  5.支持USB下载功能
  6.支持一键式菜单
  7.支持启动Logo
  8.支持ubifs(待续)

上接:u-boot-2010.03在LT2440上的移植详解 (二)
5)准备进入u-boot的第二阶段(在u-boot中添加对我们开发板上Nand Flash的支持)  
目前u-boot中还没有对2440上Nand Flash的支持,也就是说要想u-boot从Nand Flash上启动得自己去实现了。
首先我们实现nand flash 读写驱动,需要实现两个驱动,一个是在LT2440开发板启动时读取数据,还有一个是进入U-boot后的对nandflash的读写。
①启动时的nandflash驱动,放到在  board/samsung/lt2440/目录下,内容如下:
# gedit board/samsung/lt2440/nand_read.c #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_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;
};

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_S3C2440) || defined(CONFIG_S3C2442)
for (i = 0; i < (nand->page_size>>1); i++) {
  *ptr16 = NFDATA16;
  ptr16++;
}
#endif
return nand->page_size;
}
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[];
unsigned char b128MB=0;
/* 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;
  b128MB=0;
// 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;
  b128MB=1;
// 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_S3C2440_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;
}
修改board/samsung/lt2440/目录下Makefile,增加nand_read.c的编译支持
# gedit board/samsung/lt2440/Makefile COBJS := lt2440.o  nand_read.o
SOBJS := lowlevel_init.o

u-boot 启动后的nandflash驱动程序如下
#  gedit drivers/mtd/nand/s3c2440_nand.c #include #include
#include
#include
#define NF_BASE  0x4e000000
#if defined(CONFIG_S3C2440)
#define S3C2410_NFCONT_EN          (1<<0)
#define S3C2410_NFCONT_INITECC     (1<<4)
#define S3C2410_NFCONT_nFCE        (1<<1)
#define S3C2410_NFCONT_MAINECCLOCK (1<<5)
#define S3C2410_NFCONF_TACLS(x)    ((x)<<12)
#define S3C2410_NFCONF_TWRPH0(x)   ((x)<<8)
#define S3C2410_NFCONF_TWRPH1(x)   ((x)<<4)
#define S3C2410_ADDR_NALE 0x08
#define S3C2410_ADDR_NCLE 0x0c
#endif
ulong IO_ADDR_W = NF_BASE; #ifdef CONFIG_NAND_SPL/* in the early stage of NAND flash booting, printf() is not available */
#define printf(fmt, args...)
static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i = 0; i < len; i++)
  buf = readb(this->IO_ADDR_R);
}
#endif
static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
// struct nand_chip *chip = mtd->priv;
struct s3c2410_nand *nand = s3c2410_get_base_nand();
debugX(1, "hwcontrol(): 0x%02x 0x%02x/n", cmd, ctrl); if (ctrl & NAND_CTRL_CHANGE) {
  IO_ADDR_W = (ulong)nand;
  if (!(ctrl & NAND_CLE))
   IO_ADDR_W |= S3C2410_ADDR_NCLE;
  if (!(ctrl & NAND_ALE))
   IO_ADDR_W |= S3C2410_ADDR_NALE;
//  chip->IO_ADDR_W = (void *)IO_ADDR_W;
#if defined(CONFIG_S3C2440)
  if (ctrl & NAND_NCE)
   writel(readl(&nand->NFCONT) & ~S3C2410_NFCONT_nFCE,
          &nand->NFCONT);
  else
   writel(readl(&nand->NFCONT) | S3C2410_NFCONT_nFCE,
          &nand->NFCONT);
}
#endif
if (cmd != NAND_CMD_NONE)
  writeb(cmd, (void *)IO_ADDR_W);
}
static int s3c2410_dev_ready(struct mtd_info *mtd)
{
struct s3c2410_nand *nand = s3c2410_get_base_nand();
debugX(1, "dev_ready/n");
return readl(&nand->NFSTAT) & 0x01;
}
#ifdef CONFIG_S3C2410_NAND_HWECC
void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct s3c2410_nand *nand = s3c2410_get_base_nand();
debugX(1, "s3c2410_nand_enable_hwecc(%p, %d)/n", mtd, mode);

#if defined(CONFIG_S3C2440)
writel(readl(&nand->NFCONT) | S3C2410_NFCONT_INITECC, &nand->NFCONT);
#endif
}
static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
          u_char *ecc_code)
{
struct s3c2410_nand *nand = s3c2410_get_base_nand();
ecc_code[0] = readb(&nand->NFECC);
ecc_code[1] = readb(&nand->NFECC + 1);
ecc_code[2] = readb(&nand->NFECC + 2);
debugX(1, "s3c2410_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x/n",
        mtd , ecc_code[0], ecc_code[1], ecc_code[2]);
return 0;
}
static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
         u_char *read_ecc, u_char *calc_ecc)
{
if (read_ecc[0] == calc_ecc[0] &&
     read_ecc[1] == calc_ecc[1] &&
     read_ecc[2] == calc_ecc[2])
  return 0;
printf("s3c2410_nand_correct_data: not implemented/n");
return -1;
}
#endif
int board_nand_init(struct nand_chip *nand)
{
u_int32_t cfg;
u_int8_t tacls, twrph0, twrph1;
struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();
struct s3c2410_nand *nand_reg = s3c2410_get_base_nand();
debugX(1, "board_nand_init()/n"); writel(readl(&clk_power->CLKCON) | (1 << 4), &clk_power->CLKCON);
#if defined(CONFIG_S3C2440)
twrph0 = 3;
twrph1 = 0;
tacls = 0;
cfg = 0;
cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
writel(cfg, &nand_reg->NFCONF);
cfg = (0<<13)|(0<<12)|(0<<10)|(0<<9)|(0<<8)|(0<<6)|(0<<5)|(1<<4)|(0<<1)|(1<<0);
writel(cfg, &nand_reg->NFCONT);
/* initialize nand_chip data structure */
nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)&nand_reg->NFDATA;
#endif
nand->select_chip = NULL; /* read_buf and write_buf are default */
/* read_byte and write_byte are default */
#ifdef CONFIG_NAND_SPL
nand->read_buf = nand_read_buf;
#endif
/* hwcontrol always must be implemented */
nand->cmd_ctrl = s3c2410_hwcontrol;
nand->dev_ready = s3c2410_dev_ready;#ifdef CONFIG_S3C2410_NAND_HWECC
nand->ecc.hwctl = s3c2410_nand_enable_hwecc;
nand->ecc.calculate = s3c2410_nand_calculate_ecc;
nand->ecc.correct = s3c2410_nand_correct_data;
nand->ecc.mode = NAND_ECC_HW;
nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;
nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;
#else
nand->ecc.mode = NAND_ECC_SOFT;
#endif
#ifdef CONFIG_S3C2410_NAND_BBT
nand->options = NAND_USE_FLASH_BBT;
#else
nand->options = 0;
#endif
debugX(1, "end of nand_init/n"); return 0;
}
修改drivers/mtd/nand/目录下Makefile,增加s3c2440_nand.c的编译支持
# gedit drivers/mtd/nand/Makefile COBJS-$(CONFIG_NAND_S3C2440) += s3c2440_nand.o   //修改第48行为这样
然后修改 lt2440.h ,增加如下全局宏定义
# gedit   include/configs/lt2440.h #define CONFIG_CMD_NAND #define CONFIG_S3C2440_NAND_SKIP_BAD 1/* NAND flash settings */
#if defined(CONFIG_CMD_NAND)
#define CONFIG_NAND_S3C2410
#define CONFIG_NAND_S3C2440
#define CONFIG_SYS_NAND_BASE            0x4E000000 //Nand配置寄存器基地址
#define CONFIG_SYS_MAX_NAND_DEVICE      1
#define CONFIG_MTD_NAND_VERIFY_WRITE    1
#define CONFIG_SYS_64BIT_VSPRINTF 1
#endif
其次,修改cpu/arm920t/start.S这个文件,使u-boot从Nand Flash启动,在上一节中提过,u-boot默认是从Nor Flash启动的。修改部分如下:
# gedit cpu/arm920t/start.S //#define CONFIG_SKIP_LOWLEVEL_INIT   //之前定义了这个宏定义,现在需要从nandflash启动,注释掉
#ifndef CONFIG_SKIP_LOWLEVEL_INIT
bl cpu_init_crit
#endif
      
b   nandboot
#ifndef CONFIG_SKIP_RELOCATE_UBOOT
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         */
beq stack_setup
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2  /* r2 <- size of armboot            */
add r2, r0, r2  /* r2 <- source end address         */
copy_loop:
ldmia r0!, {r3-r10}  /* copy from source address [r0]    */
stmia r1!, {r3-r10}  /* copy to   target address [r1]    */
cmp r0, r2   /* until source end addreee [r2]    */
ble copy_loop
#endif /* CONFIG_SKIP_RELOCATE_UBOOT */

nandboot:
#define oNFCONF 0x00
#define oNFCONT 0x04
#define oNFCMD 0x08
#define oNFSTAT 0x20
#define NAND_CTL_BASE  0x4E000000  //Nand Flash?????????÷?ùμ??·
@ 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, #0x60000
bl nand_read_ll
tst r0, #0x0
        beq    stack_setup //ok_nand_read
bad_nand_read:
        loop2: b loop2    //infinite loop

/* Set up the stack          */
stack_setup:

············································
···········································
_start_armboot: .word start_armboot
#define STACK_BASE 0x33f00000
#define STACK_SIZE 0x10000
.align 2
DW_STACK_START: .word STACK_BASE+STACK_SIZE-4


/*
*************************************************************************
*
* CPU_init_critical registers
之前修改了链接地址,现在改回原来的
# gedit
board/samsung/lt2440/config.mk
修改 TEXT_BASE = 0x33F80000
由于s3c2410和s3c2440 nandflash 控制器不一样,需要修改控制寄存器定义
# gedit include/asm-arm/arch-s3c24x0/s3c24x0.h /* NAND FLASH (see S3C2410 manual chapter 6)
struct s3c2410_nand {
u32 NFCONF;
u32 NFCMD;
u32 NFADDR;
u32 NFDATA;
u32 NFSTAT;
u32 NFECC;
};*/
#if defined (CONFIG_S3C2440)
/* NAND FLASH (see S3C2440 manual chapter 6) */
struct s3c2410_nand {
u32 NFCONF;
u32 NFCONT;
u32 NFCMD;
u32 NFADDR;
u32 NFDATA;
u32 NFMECCD0;
u32 NFMECCD1;
u32 NFSECCD;
u32 NFSTAT;
u32 NFESTAT0;
u32 NFESTAT1;
u32 NFMECC0;
u32 NFMECC1;
u32 NFSECC;
u32 NFSBLK;
u32 NFEBLK;
};
#endif
     还有一个重要的地方要修改,在cpu/arm920t/u-boot.lds中,这个u-boot启动连接脚本文件决定了u-boot运行的入口地址,以及各个段的存储位置,这也是链接定位的作用。添加下面两行代码的主要目的是防止编译器把我们自己添加的用于nandboot的子函数放到4K之后,否则是无法启动的。如下: .text :
{
    cpu
/arm920t/start.o    (.text)
board/samsung/lt2440/lowlevel_init.o (.text)
    board
/samsung/lt2440/nand_read.o (.text)

*(.text)
}

我们还需要把环境变量保存到NandFlash中,前面的教程环境变量是保存在NorFlash中的。作如下修改,把环境变量保存到中NandFlash:
#  gedit   include/configs/lt2440.h // #define        CONFIG_ENV_IS_IN_FLASH        1 // #define CONFIG_ENV_SIZE                0x10000        /* Total Size of Environment Sector */
#define CONFIG_ENV_IS_IN_NAND  1  //将环境变量保存到NandFlash #define CONFIG_ENV_OFFSET      0x60000 //将环境变量保存到nand中的0x60000位置 #define CONFIG_ENV_SIZE        0x20000 /* Total Size of Environment Sector */
重新编译,使用原有开发板u-boot ,按下 u 键,烧写u-boot到nandflash  ,切换按键从NandFlash启动:
U-Boot 2010.03 (12鏈?08 2010 - 18:34:33)

DRAM:  64 MB
## Unknown FLASH on Bank 1 - Size = 0x00000000 = 0 MB
Flash:  0 kB
NAND:  256 MiB
*** Warning - bad CRC or NAND, using default environment
//   执行saveenv 命令,上面这个警告就没了
In:    serial
Out:   serial
Err:   serial
Net:   CS8900-0
LT2440 #
LT2440 # nand  info
Device 0: NAND 256MiB 3,3V 8-bit, sector size 128 KiB
LT2440 # nand
nand - NAND sub-system
Usage:
nand info - show available NAND devices
nand device [dev] - show or set current device
nand read - addr off|partition size
nand write - addr off|partition size
    read/write 'size' bytes starting at offset 'off'
    to/from memory address 'addr', skipping bad blocks.

nand erase [clean] [off size] - erase 'size' bytes from
    offset 'off' (entire device if not specified)
nand bad - show bad blocks
nand dump[.oob] off - dump page
nand scrub - really clean NAND erasing bad blocks (UNSAFE)
nand markbad off [...] - mark bad block(s) at offset (UNSAFE)
nand biterr off - make a bit error at offset (UNSAFE)
LT2440 #
//   从上面可以看出已经可以操作nandflash了,并且可以从NandFlash启动了


下接:u-boot-2010.03在LT2440上的移植详解 (四)