2006. 6. 20. 23:38
U-Boot>nand write.yaffs : NAND Flash Filesystem : YAFFS HOW-TO (2nd)  

글쓴이 : jazz0719  조회 : 3216   날짜 : 2004/06/30 16:40:51

% style % -->
오랫만에 글올리는군요. 다들 잘 지내셨어요?



저번엔 U-Boot에 NAND fusing 관련하여, MTD기반으로 연동하는 방법을 올렸었죠 ?

(공구보드 U-Boot소스. 2410-09-bluesky-u-boot-1.0.0.tar.gz도 살짝 보니까

이런방식이더군요.)



이번에 고도리님 등등과 자체프로젝트(^^?)를 추진하고 있는데, YAFFS작업이 있어서

이 기회에 cmd_nand.c의 JFFS지원 루틴을 분석하여, U-Boot에서 YAFFS 이미지 fusing도

지원하도록 수정해보았습니다.



질문올라온거 보면 yaffs image를 U-Boot의 nand write[.jffs2] 명령을 이용하여

fusing 시도하는 분이 계시던데, 절대 안될겁니다.(^---^).

YAFFS 이미지포맷이 JFFS2등 다른 이미지파일과 다르고, Spare Area(OOB)의 처리가

되어야 하기 때문입니다.



아래 찬찬히 읽어보시면 이해되실거예요.

(보드 specific 코드 - 형욱보드..로 표기)



- 장형욱 씀.


NAND Flash Filesystem : YAFFS
yaffs를 지원하도록 리눅스커널 설정

mtd driver 패치 설치
MTD(Memory Technology Devices)는 임베디드 디바이스에서 고형체 파일시스템(solid state filesystem)을구성하는데 사용하는 플래시 메모리, RAM, 그리고 그 비슷한 다른 칩셋 등 메모리 장치로 yaffs도 mtd를 통해 연결됨
mtd관련모듈을 yaffs를 지원하도록 최신 버전으로 패치(안할경우 fs/yaffs 컴파일시 에러 발생)
http://www.linux-mtd.infradead.org/에서 mtd-snapshot...tar.gz2 최신버전 다운로드
[mtd]/patch> sh patchin.sh -c -j [linux] : kernel mtd모듈 패치
-c : symbolic link를 만들지 않고 직접 커널소스로 mtd copy
-j : jffs2 지원

변경내용 : [linux]/drivers/mtd/, [linux]/include/linux/mtd/

NAND 관련 driver 코드 작성(형욱보드_nand.c)
falinux사이트에서 관련소스를 다운로드(http://www.falinux.com/download/data/kernel.zip)
[linux]/drivers/mtd/nand/ez_x5.c를 pwpro_nand.c로 바꾸고 아래와 같이 수정
: 하드웨어 관련 컨트롤 함수, IO 주소공간 지정, NAND 파티션 테이블 작성 등.
NAND 플래시 상 MTD 파티션 구분
Creating 3 MTD partitions on "NAND 16MiB 3,3V":
mtd0,mtdblock0 : 0x00004000-0x00190000 (1584K) : "Linux Kernel Partition"
mtd1,mtdblock1 : 0x00190000-0x00640000 (4800K) : "Root Filesystem(YAFFS) Partition"
mtd2,mtdblock2 : 0x00640000-0x00fa0000 (9600K) : "Configuration Partition"


소스

/*
*  drivers/mtd/nand/형욱보드_nand.c
*
*  Copyright (C) 2003 JANG, Hyung-Wook (jazz0719@dreamwiz.com)
*
*  Support board : 형욱보드
*/

#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
/* modified by hwjang. */
#include <asm-ppc/delay.h>
#include <asm-ppc/processor.h>

#define PWPRO_NAND_BASE                0xf0000000
#define PWPRO_NAND_BASE_D0            PWPRO_NAND_BASE
#define PWPRO_NAND_ACCESS_START            (0x000)    // not use
#define PWPRO_NAND_DATA                    (0x00000)
#define PWPRO_NAND_CMD                     (0x00001)
#define PWPRO_NAND_ADDR                    (0x00002)
#define PWPRO_NAND_ACCESS_END              (0x300) // not use
#define NAND_MAXCHIP                (1)
static struct mtd_info *pwpro_nand_mtd = NULL;    

#define SZ_1K                           (1024)
#define SZ_1M                 (1024*1024)
#define __PWPRO_GPIO

/* modified by hwjang. add NAND Flash IO address mapping. */

#define FLASH_BASE_ADDR     0xF0000000
/*
#define FLASH_DATA_REG      *(volatile unsigned char*)(FLASH_BASE_ADDR + 0x00000)
#define FLASH_CMD_REG       *(volatile unsigned char*)(FLASH_BASE_ADDR + 0x00001)
#define FLASH_ADDR_REG      *(volatile unsigned char*)(FLASH_BASE_ADDR + 0x00002)
#define FLASH_CS0_SET       *(volatile unsigned char*)(FLASH_BASE_ADDR + 0x80000)
#define FLASH_CS0_CLR       *(volatile unsigned char*)(FLASH_BASE_ADDR + 0xA0000)
*/
#define FLASH_DATA_REG      (FLASH_BASE_ADDR + 0x00000)
#define FLASH_CMD_REG       (FLASH_BASE_ADDR + 0x00001)
#define FLASH_ADDR_REG      (FLASH_BASE_ADDR + 0x00002)
#define FLASH_CS0_SET       (FLASH_BASE_ADDR + 0x80000)
#define FLASH_CS0_CLR       (FLASH_BASE_ADDR + 0xA0000)

#if defined(__PWPRO_GPIO)               /* Clocking and Chip Control */
#define DCRN_CPC0_CGCR0     0x0b1       /* Clock Generation Control Register 0 */

#define GPIO0_OR        0xEF600700
#define GPIO0_TCR       0xEF600704
#define GPIO0_ODR       0xEF600714
#define GPIO0_IR        0xEF60071C

#define GPIO_MASK_NAND_FLASH_MEM (1<<(31-15))

#define gpio_init()     (*(volatile unsigned int*)GPIO0_TCR = 0x10000000)
#define gpio_read()     (*(volatile unsigned int*)GPIO0_IR)
#define gpio_write(n)   (*(volatile unsigned int*)GPIO0_OR = (n))

static void _flash_gpio_init(void)
{
        unsigned long temp;
        unsigned int  val;
                                                                                                
        temp = mfdcr(DCRN_CPC0_CGCR0);
        temp |= 0x00200000;
        mtdcr(DCRN_CPC0_CGCR0, temp);
                                                                                                
        val = (*(volatile unsigned int*) GPIO0_TCR);
        val &= ~(GPIO_MASK_NAND_FLASH_MEM);
        (*(volatile unsigned int*) GPIO0_TCR) = val;
                                                                                                
        val = (*(volatile unsigned int*) GPIO0_ODR);
        val &= ~(GPIO_MASK_NAND_FLASH_MEM);
        (*(volatile unsigned int*) GPIO0_ODR) = val;
                                                                                                
        val  = gpio_read();
        val &= ~GPIO_MASK_NAND_FLASH_MEM;
        gpio_write(val);
}
#endif /* __PWPRO_GPIO */

/*
* Define partitions for flash device
*/
const static struct mtd_partition partition_info[] =
{
    {
        name  : "형욱보드 Linux Kernel Partition",
        offset: 0x00004000,
        size  : 1584*SZ_1K
    },
    {
        name  : "형욱보드 Root Filesystem(YAFFS) Partition",
        offset: 0x00190000,
        size  : 4800*SZ_1K
    },
    {
        name  : "형욱보드 Configuration Partition",
        offset: 0x00640000,
        size  : 9600*SZ_1K
    }
};
#define PWPRO_NAND_NUM_PARTITIONS     3


/*
*    hardware specific access to control-lines
*/
/* void pwpro_nand0_hwcontrol(int cmd) */
void pwpro_nand0_hwcontrol(struct mtd_info *mtd, int cmd)
{
/*    int dummy; */

    switch(cmd)
    {
/*
    case NAND_CTL_SETNCE: dummy = readb(EZ_NAND_BASE_D0 + EZ_NAND_DATA)      ; break;
    case NAND_CTL_CLRNCE: dummy = readb(EZ_NAND_BASE_D0 + EZ_NAND_ACCESS_END); break;
*/  
    case NAND_CTL_SETNCE:
        writeb(0, FLASH_CS0_SET);         /* chip enable */
        writeb(NAND_CMD_RESET, FLASH_CMD_REG);     /* reset */
        udelay(20);                 /* delay */
        break;
    case NAND_CTL_CLRNCE:
        break;
    default:
        break;
    }
}

/*
* Send command to NAND device
*/
void pwpro_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
{
    register struct nand_chip *this = mtd->priv;
    register unsigned long NAND_IO_ADDR = this->IO_ADDR_W;

    // Write out the command to the device.
    if (command != NAND_CMD_SEQIN)
    {
        writeb (command, NAND_IO_ADDR + PWPRO_NAND_CMD );
    }  
    else
    {
        if (mtd->oobblock == 256 && column >= 256)
        {
            column -= 256;
            writeb (NAND_CMD_READOOB, NAND_IO_ADDR + PWPRO_NAND_CMD );
            writeb (NAND_CMD_SEQIN  , NAND_IO_ADDR + PWPRO_NAND_CMD );
        }
        else if (mtd->oobblock == 512 && column >= 256)
        {
            if (column < 512)
            {
                column -= 256;
                writeb (NAND_CMD_READ1, NAND_IO_ADDR + PWPRO_NAND_CMD);
                writeb (NAND_CMD_SEQIN, NAND_IO_ADDR + PWPRO_NAND_CMD);
            }
            else
            {
                column -= 512;
                writeb (NAND_CMD_READOOB, NAND_IO_ADDR + PWPRO_NAND_CMD);
                writeb (NAND_CMD_SEQIN  , NAND_IO_ADDR + PWPRO_NAND_CMD);
            }
        }
        else
        {
            writeb (NAND_CMD_READ0      , NAND_IO_ADDR + PWPRO_NAND_CMD);
            writeb (NAND_CMD_SEQIN      , NAND_IO_ADDR + PWPRO_NAND_CMD);
        }
    }

    // Serially input address
    if (column != -1 || page_addr != -1)
    {
        if (column != -1) writeb (column, NAND_IO_ADDR + PWPRO_NAND_ADDR);
        if (page_addr != -1)
        {
            writeb ((unsigned char) (page_addr & 0xff), NAND_IO_ADDR + PWPRO_NAND_ADDR);
            writeb ((unsigned char) ((page_addr >> 8) & 0xff), NAND_IO_ADDR + PWPRO_NAND_ADDR);
            // One more address cycle for higher density devices
            if (mtd->size & 0x0c000000)
            {
               writeb ((unsigned char) ((page_addr >> 16) & 0x0f), NAND_IO_ADDR + PWPRO_NAND_ADDR);
            }
        }  
    }
  
    switch (command)
    {
          
    case NAND_CMD_PAGEPROG:
    case NAND_CMD_ERASE1:
    case NAND_CMD_ERASE2:
    case NAND_CMD_SEQIN:
    case NAND_CMD_STATUS:
        return;

    case NAND_CMD_RESET:
        if( this->dev_ready )    break;
        writeb (NAND_CMD_STATUS, NAND_IO_ADDR + PWPRO_NAND_CMD);
        while ( !(readb (this->IO_ADDR_R) & 0x40));
        return;

    default:
        if (!this->dev_ready)
        {
            udelay (this->chip_delay);
            return;
        }  
    }
  
/*    while (!this->dev_ready()); */
    while (!this->dev_ready);
  
}

/*
* Main initialization routine
*/
int __init pwpro_nand_init (void)
{
    struct nand_chip *this;
  
    //  Allocate memory for MTD device structure and private data
    pwpro_nand_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL);
    if (!pwpro_nand_mtd)
    {
        printk ("Unable to allocate 형욱보드 MTD device structure.\n");
        return -ENOMEM;
    }
    // Get pointer to private data
    this = (struct nand_chip *) (&pwpro_nand_mtd[1]);

    // Initialize structures
    memset((char *) pwpro_nand_mtd, 0, sizeof(struct mtd_info));
    memset((char *) this, 0, sizeof(struct nand_chip));

    // Link the private data with the MTD structure
    pwpro_nand_mtd->priv = this;

    // Set address of NAND IO lines
    this->IO_ADDR_R = PWPRO_NAND_BASE_D0 + PWPRO_NAND_DATA;
    this->IO_ADDR_W = PWPRO_NAND_BASE_D0 + PWPRO_NAND_DATA;
    // Set address of hardware control function
    this->hwcontrol = pwpro_nand0_hwcontrol;
    // Set commamd function
    this->cmdfunc = pwpro_nand_command ;
    // 15 us command delay time */
    this->chip_delay = 15;      

//    this->eccmode = NAND_ECC_SOFT;
//    pwpro_nand_mtd->oobinfo.useecc = -1;

#if defined(__PWPRO_GPIO)
    _flash_gpio_init();
#endif /* __POWATCH_GPIO */
  
    // Scan to find existence of the device
/*    if (nand_scan (pwpro_nand_mtd)) */
    if (nand_scan (pwpro_nand_mtd, NAND_MAXCHIP))
    {
        kfree (pwpro_nand_mtd);
        return -ENXIO;
    }
    // Allocate memory for internal data buffer
    this->data_buf = kmalloc (sizeof(u_char) * (pwpro_nand_mtd->oobblock + pwpro_nand_mtd->oobsize), GFP_KERNEL);
    if (!this->data_buf)
    {
        printk ("Unable to allocate NAND data buffer for 형욱보드-NAND.\n");
        kfree (pwpro_nand_mtd);
        return -ENOMEM;
    }
    // Register the partitions
    add_mtd_partitions(pwpro_nand_mtd, partition_info, PWPRO_NAND_NUM_PARTITIONS);
    // Return happy
    return 0;
}
module_init(pwpro_nand_init);

/*
* Clean up routine
*/
#ifdef MODULE
static void __exit pwpro_nand_cleanup (void)
{
    struct nand_chip *this = (struct nand_chip *) &pwpro_nand_mtd[0];

    // Unregister the device
    del_mtd_device (pwpro_nand_mtd);

    // Free internal data buffer
    kfree (this->data_buf);

    // Free the MTD device structure
    kfree (pwpro_nand_mtd);
}
module_exit(pwpro_nand_cleanup);
#endif

MODULE_LICENSE("GPL");
MODULE_AUTHOR("JANG, Hyung-Wook <jazz0719@dreamwiz.com");
MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on 형욱보드");



pwpro_nand.c를 커널컴파일 때 추가할 수 있도록 설정
[linux]/drivers/mtd/nand/Config.in 에 추가
dep_tristate '  NAND Device Support' CONFIG_MTD_NAND $CONFIG_MTD
if [ "$CONFIG_MTD_NAND" = "y" -o "$CONFIG_MTD_NAND" = "m" ]; then
   bool '    NAND Flash Device on 형욱보드' CONFIG_MTD_NAND_PWPRO
fi
[linux]/drivers/mtd/nand/Makefile 에 추가
obj-$(CONFIG_MTD_NAND_PWPRO)    += pwpro_nand.o


NAND Flash에 접근할 수 있도록 IO 주소공간을 커널에 등록
[linux]/arch/ppc/platforms/walnut.c 에 다음내용을 추가 (위에서 이미 등록됨. 확인)
void __init
board_io_mapping(void)
{
/* modified by hwjang */
        ...
        /* CS1 - NAND FLASH */
        io_block_mapping( 0xF0000000, 0xF0000000, 0x00100000, _PAGE_IO );
        ...
}
NAND ECC 경고메세지 관련
YAFFS는 MTD/NAND driver의 ECC기능을 쓰지않고, YAFFS에서 제공하는 것을 쓰므로 이로인한 아래의 경고메세지를 주석처리한다

[linux]/drivers/mtd/nand/nand.c 수정
case NAND_ECC_NONE:
  // printk(KERNEL_WARNING ...)


yaffs를 kernel fs에 포함

http://www.aleph1.co.uk/armlinux/projects/yaffs/index.html(yaffs소스),
http://www.toby-churchill.org/(ballon_yaffs소스) 다운로드


[linux]/fs/yaffs 생성.

yaffs소스에서 [linux]/fs/yaffs아래에 devextras.h yaffs_fs.c yaffs_guts.c yaffs_guts.h
yaffs_mtdif.cyaffs_mtdif.h yaffsinterface.h, yportenv.h, yaffs_ecc.h, yaffs_ecc.c 를 copy


[linux]/fs/yaffs에 [ballon_yaffs]/Makefile을 copy하고 아래와 같이 수정
obj-y := yaffs_fs.o yaffs_guts.o yaffs_mtdif.o yaffs_ecc.o
[linux]/fs/Config.in의 앞부분에 아래 추가하여 커널 컴파일 configuration시 yaffs 선택할 수 있도록 함
tristate "Yaffs filesystem on NAND" CONFIG_YAFFS_FS
(document에서는 CONFIG_MTD_NAND가 정의되어있을때면 설정가능하도록 되어있으나,
실제 nand flash에 접근할때는 mtd가아닌 yaffs의 ecc, verify write기능을 쓰기 때문에 이렇게 설정해야함


[linxux]fs/Makefile에 추가
subdir-$(CONFIG_YAFFS_FS)       += yaffs


컴파일 에러 발생시 추가 수정내용 (옵션)
[linux]/include/linux/serialP.h 수정 : async_icount 구조체 정의 관련
/* modified by hwjang. for yaffs filesystem support */
/* #if (LINUX_VERSION_CODE < 0x020300) */
#if (LINUX_VERSION_CODE >= 0x020300)
/* Unfortunate, but Linux 2.2 needs async_icount defined here and
* it got moved in 2.3 */
#include <linux/serial.h>
#endif


[linux]/include/linux/mtd/mtd.h 추가 : 2.5버전이하에서 사용하는 BUG_ON함수가 previewkit 소스에는 제공하지 않으므로 2.5이상 버전의 커널 소스트리의 bug.h파일을 [linux]/include/asm-ppc/bug.h 로 copy
#include <asm-ppc/bug.h>




kernel에 mtd, yaffs가 적용되도록 환경설정

Load configuration : arc/ppc/deconfig 한후 아래 내용 추가

Loadable module support
  [*] Kernel module loader : /sbin/insmod yaff.o하지 않아도 커널이 자동적으로 yaff module을 등록시키도록 하기 위함.

General setup
  [*] Default bootloader kernel arguments
    Initial kernel command string: "console=ttyS0,9600 console=tty0 root=/dev/mtdblock0"
    : 루트파일시스템을 플래시에 구성된 yaffs파일시스템으로 mtd를 통해 연결. 아래 참고.

Memory Technolygy Devices(MTD)
  <*> Memory Technology Device (MTD) support
  [*] Debugging(verbocity=0)
  <*> MTD partitioning support
  <*> Direct char Device access to MTD device
  <*> Caching block device access to MTD devices
  NAND Flash Device Drivers
    <*> NAND Device Support
    [*] NAND Flash Device on 형욱보드
    [*] Verify NAND page writes

Fliesystem
  <*> Yaffs filesystem on NAND

Save as poswatch_pro.menuconfig


[linux]>make clea
[linux]>make dep
[linux]>make uImage
-> yaffs 플래시파일시스템 지원 u-boot용 커널 생성
   : [linux]/arch/ppc/boot/images/vmlinux.UBoot
[linux]>make zImage
-> Poswatch openbios용 압축커널이미지(arch/ppc/boot/images/zImage.treeboot) 생성(http://www.geocrawler.com/archives/3/8358/2002/1/50/7622546/)


U-Boot를 yaffs를 지원하여 Stand-alone으로 동작하도록 수정
include/configs/형욱보드.h
mtd partition 0으로부터 리눅스 커널 이미지를 램으로 로드하여, 부팅.
/* autoboot command */
#define CONFIG_BOOTCOMMAND      "nand read 200000 4000 (vmlinux.UBoot사이즈);bootm 200000"

mtd partition 1의 yaffs.image를 root filesystem으로 인식.
#define CONFIG_BOOTARGS           "root=/dev/mtdblock1 rw"


yaffs rootfs 구성


directory 구성
yaffs : working dir
- rootfs_yaffs : 위에서 구성한 rootfs를 copy ( cp -dprR 이용)


$ cd rootfs_yaffs


mknod dev/mtd0 c 90 0
mknod dev/mtd1 c 90 1
mknod dev/mtdblock0 b 31 0
mknod dev/mtdblock1 b 31 1


$ vi etc/fstab
/dev/mtdblock1    / yaffs default 0 0


아래의 설정은 http://doc.kldp.org/KoreanDoc/html/Boot_Process-KLDP/inittabfile.html 참조.


vi etc/inittab
::sysinit:/etc/init.d/rcS 추가 (이상하게 앞의 si 키워드 없애야 동작함. 향후 검토)


vi etc/init.d/rcS 수정(맨앞)
/sbin/insmod yaffs.o : menuconfig에서 Loadable support>kernel module loader선택했다면 제외할것.
mount -t yaffs /dev/mtdblock1 / -> flash의 yaffs파일시스템이 /로 마운트


yaffs 이미지 작성.


yaffs/utils에서 yaffs 이미지 작성용 툴을 제공
mkyaffsimage : root filesystem 디렉토리로부터 NAND flash용 yaffs 이미지 작성
mkyaffs : flash erase


yaffs compile
yaffs/Makefile, yaffs/utils/Makefile을 아래와 같이 수정
KERNELDIR = [linux]
MAKETOOLS =
...
# USE_RAM_FOR_TEST = -DCONFIG_YAFFS_RAM_ENABLED


yaffs$ make
yaffs/utils$ make
http://62.49.201.250/balloon/releases/utils/mkyaffsimage 다운로드  
$ mv mkyaffsimage mkyaffsimage.ballon
Ihttp://www.intrinsyc.com/support/I-Linux/405-cube/cerfcube405ep.htm#misc/cd.htm으로부터 mkyaffsimage 다운로드
$ mv mkyaffsimage mkyaffsimage.cerf


$ mkyaffsimage.cerf rootfs_yaffs yaffs.image convert: NAND flash용 yaffs 이미지(yaffs.image)생성


U-Boot에 YAFFS 이미지 지원 NAND flash 처리루틴 추가 (cmd_nand.c 기반)
NAND flash 관련 명령어는 common/cmd_nand.c에서 정의됨
common/cmd_nand.c
U_BOOT_CMD(
        nand,   5,      1,      do_nand,
        "nand    - NAND sub-system\n",
        "info  - show available NAND devices\n"
        "nand device [dev] - show or set current device\n"
        "nand read[.jffs2[s]]  addr off size\n"
        "nand write[.jffs2] addr off size - read/write `size' bytes starting\n"
        "    at offset `off' to/from memory address `addr'\n"
        "nand erase [clean] [off size] - erase `size' bytes from\n"
        "    offset `off' (entire device if not specified)\n"
        "nand bad - show bad blocks\n"
        "nand read.oob addr off size - read out-of-band data\n"
        "nand write.oob addr off size - write out-of-band data\n"
);
nand 관련 명령어 셋 컴파일되도록 설정

$ vi common/Makefile
COBJS = cmd_nand.o 추가
$ vi include/configs/WALNUT405.h : CFG_CMD_NAND 추가

#define CONFIG_COMMANDS        (CONFIG_CMD_DFL  | \
                                CFG_CMD_PCI     | \
                                CFG_CMD_IRQ     | \
                                CFG_CMD_NAND )
board/.../형욱보드.c
/* modified by hwjang : NAND initialization */
#if (CONFIG_COMMANDS & CFG_CMD_NAND)
extern ulong
nand_probe(ulong physadr);

void
nand_init(void)
{
#define FLASH_BASE_ADDR     0xF0000000
        ulong totlen = 0;

        printf ("Probing at 0x%.8x\n", FLASH_BASE_ADDR);
        totlen = nand_probe (FLASH_BASE_ADDR);

        printf ("%4lu MB\n", totlen >>20);
}
#endif /* (CONFIG_COMMANDS & CFG_CMD_NAND) */




$ vi include/configs/형욱보드.h : 각시스템의 설정에 맞게 각 매크로를 define해주세요.
/* modified by hwjang */
/*-----------------------------------------------------------------------
* NAND-FLASH stuff
*-----------------------------------------------------------------------
*/
#define CONFIG_NAND
#define CONFIG_YAFFS                    /* use yaffs filesystem on nand flash */

#define CFG_NAND_BASE                   0xF0000000
#define DCRN_CPC0_CGCR0                 0x0b1
#define GPIO0_TCR_ADDR                  0xEF600704
#define GPIO0_ODR_ADDR                  0xEF600718
#define GPIO0_IR_ADDR                   0xEF60071C

#define GPIO_MASK_NAND_FLASH_MEM        (1<<(31-15))

#define gpio_read()                     (*(volatile unsigned int*)GPIO0_IR)
#define gpio_write(n)                   (*(volatile unsigned int*)GPIO0_ODR_ADDR = (n))

#define CFG_MAX_NAND_DEVICE     1       /* Max number of NAND devices           */
#define SECTORSIZE 512

#define ADDR_COLUMN 1
#define ADDR_PAGE 2
#define ADDR_COLUMN_PAGE 3

#define NAND_ChipID_UNKNOWN     0x00
#define NAND_MAX_FLOORS 1
#define NAND_MAX_CHIPS 1

#define CFG_NAND_CE_SET         (CFG_NAND_BASE + 0x00080000)
#define CFG_NAND_CE_CLR         (CFG_NAND_BASE + 0x000A0000)
#define CFG_NAND_CMD_REG        (CFG_NAND_BASE + 0x00000001)
#define CFG_NAND_ADDR_REG       (CFG_NAND_BASE + 0x00000002)

#define NAND_DISABLE_CE(nand)    do { *(volatile __u8 *)(CFG_NAND_CE_CLR) = 0;} while(0)
#define NAND_ENABLE_CE(nand)     do { *(volatile __u8 *)(CFG_NAND_CE_SET) = 0;} while(0)
#define NAND_CTL_CLRALE(nandptr) do { ; } while(0)
#define NAND_CTL_SETALE(nandptr) do { ; } while(0)
#define NAND_CTL_CLRCLE(nandptr) do { ; } while(0)
#define NAND_CTL_SETCLE(nandptr) do { ; } while(0)
#define NAND_WAIT_READY(nand)    while ((gpio_read() & GPIO_MASK_NAND_FLASH_MEM) ? 0:1)

#define WRITE_NAND_COMMAND(d, adr) do{ *(volatile __u8 *)(CFG_NAND_CMD_REG) = (__u8)(d); } while(0)
#define WRITE_NAND_ADDRESS(d, adr) do{ *(volatile __u8 *)(CFG_NAND_ADDR_REG) = (__u8)(d); } while(0)
#define WRITE_NAND(d, adr) ( writeb(d, adr) )
#define READ_NAND(adr) ( readb(adr) )


$ vi lib_ppc/board.c : flash 초기화 안해줌(수정 가능)
/* modified by hwjang : no flash initialization */
#if 0
/* #if !defined(CFG_NO_FLASH) */
        puts ("FLASH: ");

        if ((flash_size = flash_init ()) > 0) {


YAFFS 이미지  Fusing 루틴 추가 (cmd_nand.c)
#  nand write.yaffs (yaffs.image가 로딩된 ram addr) (NAND flash offset) (yaffs.image 사이즈)
가 동작하도록 명령어 셋과 처리루틴 추가
YAFFS의 경우 U-Boot에서 지원하는 JFFS2와 동작하는 상황이 다르며, 아래사항을 처리해야 함
Page Write 시 OOB영역 처리 : yaffs.image는 data(1st/2nd half array, 512bytes) + oob(spare array, 16bytes)의 연속으로 구성되며, JFFS2와 다르게  spare area(OOB영역)에 yaffs.image의 값을 쓸 수 있도록 수정

ECC disable : YAFFS는 NAND_ECC_NONE모드
size 문제 : (yaffs.image 사이즈)는 oob영역까지 포함된 사이즈, U-Boot에서는 data영역만의 사이즈 기반(즉 FLASH address기반)으로 처리하므로, yaffs의 경우 이를 적당히 조절해야 함. 처리하지 않을 경우 의도보다 FLASH 영역을 넘어서 잘못된 값을 Fusing. bad block 발생.
nand write.yaffs 명령어셋 추가
#define NANDRW_YAFFS    0x08
nand write.yaffs 명령을 인식하고, nand_write_yaffs()를 호출하도록 설정.
int do_nand (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
...
                else if (cmdtail && !strncmp(cmdtail, ".yaffs", 2))
                        cmd |= NANDRW_YAFFS;
...
}
/* cmd: 0: NANDRW_WRITE                 write, fail on bad block
*      1: NANDRW_READ                  read, fail on bad block
*      2: NANDRW_WRITE | NANDRW_JFFS2  write, skip bad blocks
*      3: NANDRW_READ | NANDRW_JFFS2   read, data all 0xff for bad blocks
*      7: NANDRW_READ | NANDRW_JFFS2 | NANDRW_JFFS2_SKIP read, skip bad blocks
*      8: NANDRW_WRITE | NANDRW_YAFFS  write, yaffs // hwjang
*/
static int nand_rw (struct nand_chip* nand, int cmd,
            size_t start, size_t len,
            size_t * retlen, u_char * buf)
{
/* size관련처리 */
int pages_per_block = (nand->erasesize) / (nand->oobblock);
if (cmd & NANDRW_YAFFS) {
// len passed is yaffs image size (including oob_data).
// modified to size except oob_data to work properly in u-boot
        int num_pages_yaffs = len / (nand->oobblock + nand->oobsize);
        len = num_pages_yaffs << nand->page_shift;
}
..
             else if ((cmd == (NANDRW_WRITE | NANDRW_JFFS2)) || (cmd == (NANDRW_WRITE | NANDRW_YAFFS))) {
             //else if (cmd == (NANDRW_WRITE | NANDRW_JFFS2)) {
                                        /* skip bad block */
                                        start += erasesize;
                                        continue;
                                }
...
    else if (cmd & NANDRW_YAFFS) {
        ret = nand_write_yaffs (nand, start,
                   min(len, eblk + erasesize - start),
                   &n, (u_char*)buf, NULL);

        // hwjang :     yaffs image contains oob_data ( size : 16 Bytes in each page )
        //              so buf pointer should be increased to point at data_buf of the next block.
        buf += (pages_per_block << 4);

    }

}
nand->data_buf[]에 OOB영역까지 포함한 yaffs.image의 값을 할당하고, OOB영역까지 page programming되도록 설정
static int nand_write_yaffs (struct nand_chip* nand, size_t to, size_t len,
                           size_t * retlen, const u_char * buf, u_char * ecc_code)
{
        int i, page, col, cnt, ret = 0;
//hwjang
int pages_per_block = (nand->erasesize) / (nand->oobblock);

        /* Do not allow write past end of device */
        if ((to + len) > nand->totlen) {
                printf ("%s: Attempt to write past end of page\n", __FUNCTION__);
                return -1;
        }

        /* Shift to get page */
        page = ((int) to) >> nand->page_shift;

        /* Get the starting column */
        col = to & (nand->oobblock - 1);

        /* Initialize return length value */
        *retlen = 0;

        /* Select the NAND device */
#ifdef CONFIG_OMAP1510
        archflashwp(0,0);
#endif
        NAND_ENABLE_CE(nand);  /* set pin low */

        /* Check the WP bit */
        NanD_Command(nand, NAND_CMD_STATUS);
        if (!(READ_NAND(nand->IO_ADDR) & 0x80)) {
                printf ("%s: Device is write protected!!!\n", __FUNCTION__);
                ret = -1;
                goto out;
        }

        /* Loop until all data is written */
// hwjang :     yaffs image contains oob_data ( size : 16 Bytes in each page )
//              so additional length should be checked.
        while (*retlen < len + (pages_per_block << 4)) {

                /* Invalidate cache, if we write to this page */
                if (nand->cache_page == page)
                        nand->cache_page = -1;

                /* Write data into buffer */
                if ((col + len) >= nand->oobblock)
// hwjang :     write including oob_data in yaffs image
                        for (i = col, cnt = 0; i < nand->oobblock + nand->oobsize; i++, cnt++)
                                nand->data_buf[i] = buf[(*retlen + cnt)];
                else
                        for (i = col, cnt = 0; cnt < (len - *retlen); i++, cnt++)
                                nand->data_buf[i] = buf[(*retlen + cnt)];

                /* We use the same function for write and writev !) */
                ret = nand_write_page_yaffs (nand, page, col, i, NULL);

                if (ret)
                        goto out;

                /* Next data start at page boundary */
                col = 0;

                /* Update written bytes count */
                *retlen += cnt;

                /* Increment page address */
                page++;
        }

        /* Return happy */
        *retlen = len;

out:
        /* De-select the NAND device */
        NAND_DISABLE_CE(nand);  /* set pin high */
#ifdef CONFIG_OMAP1510
        archflashwp(0,1);
#endif
        return ret;
}


OOB영역에 접근하는 ECC관련코드를 제거(nand_write_page()와 비교해볼것).
timing 설정(아래참조)
static int nand_write_page_yaffs (struct nand_chip *nand,
                            int page, int col, int last, u_char * ecc_code)
{
        int i;
        unsigned long nandptr = nand->IO_ADDR;

        /* Prepad for partial page programming !!! */
        for (i = 0; i < col; i++)
                nand->data_buf[i] = 0xff;

        /* Postpad for partial page programming !!! oob is already padded */
        for (i = last; i < nand->oobblock; i++)
                nand->data_buf[i] = 0xff;

        /* Send command to begin auto page programming */
        NanD_Command(nand, NAND_CMD_READ0);
        NanD_Command(nand, NAND_CMD_SEQIN);
        NanD_Address(nand, ADDR_COLUMN_PAGE, (page << nand->page_shift) + col);

        /* Write out complete page of data */
        for (i = 0; i < (nand->oobblock + nand->oobsize); i++)
                WRITE_NAND(nand->data_buf[i], nand->IO_ADDR);

        /* Send command to actually program the data */
        NanD_Command(nand, NAND_CMD_PAGEPROG);

udelay(300); // hwjang

        NanD_Command(nand, NAND_CMD_STATUS);
#ifdef NAND_NO_RB
        { u_char ret_val;

          do{
                ret_val = READ_NAND(nandptr);   /* wait till ready */
          } while((ret_val & 0x40) != 0x40);
        }
#endif
        /* See if device thinks it succeeded */
        if (READ_NAND(nand->IO_ADDR) & 0x01) {
                printf ("%s: Failed write, page 0x%08x, ", __FUNCTION__, page);
                return -1;
        }

#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
        /*
         * The NAND device assumes that it is always writing to
         * a cleanly erased page. Hence, it performs its internal
         * write verification only on bits that transitioned from
         * 1 to 0. The device does NOT verify the whole page on a
         * byte by byte basis. It is possible that the page was
         * not completely erased or the page is becoming unusable
         * due to wear. The read with ECC would catch the error
         * later when the ECC page check fails, but we would rather
         * catch it early in the page write stage. Better to write
         * no data than invalid data.
         */

        /* Send command to read back the page */
        if (col < nand->eccsize)
                NanD_Command(nand, NAND_CMD_READ0);
        else
                NanD_Command(nand, NAND_CMD_READ1);
        NanD_Address(nand, ADDR_COLUMN_PAGE, (page << nand->page_shift) + col);

udelay(9); // hwjang

        /* Loop through and verify the data */
        for (i = col; i < last; i++) {
               if (nand->data_buf[i] != readb (nand->IO_ADDR)) {
                        printf ("%s: Failed write verify, page 0x%08x ", __FUNCTION__, page);
                        return -1;
                }
        }
#endif
        return 0;
}


형욱보드 설정에 맞춘 wait timing 세팅  
하드웨어 측정 결과
구분
하드웨어 R/B pin Busy timing
소스 설정값(udelay 적용값)

write
t_PROG = 180 us
300 us

erase
t_BER = 2000 us
2500 us

read
t_R = 7.20 us
9 us


$ vi common/cmd_nand.c
static int nand_read_ecc(struct nand_chip *nand, size_t start, size_t len,
                 size_t * retlen, u_char *buf, u_char *ecc_code)
{
...
                /* Send the read command */
                NanD_Command(nand, NAND_CMD_READ0);
                NanD_Address(nand, ADDR_COLUMN_PAGE, (page << nand->page_shift) + col);

udelay(9); // hwjang

                /* Read in a page + oob data */
                NanD_ReadBuf(nand, nand->data_buf, nand->oobblock + nand->oobsize);
...
}

static int nand_write_page (struct nand_chip *nand,
                            int page, int col, int last, u_char * ecc_code)
{
...
        /* Write out complete page of data */
        for (i = 0; i < (nand->oobblock + nand->oobsize); i++)
                WRITE_NAND(nand->data_buf[i], nand->IO_ADDR);

        /* Send command to actually program the data */
        NanD_Command(nand, NAND_CMD_PAGEPROG);

udelay(300); // hwjang

        NanD_Command(nand, NAND_CMD_STATUS);
...
}

static int nand_read_oob(struct nand_chip* nand, size_t ofs, size_t len,
                         size_t * retlen, u_char * buf)
{
...
        if (nand->page256 && ofs + len > (ofs | 0x7) + 1) {
                len256 = (ofs | 0x7) + 1 - ofs;
                NanD_ReadBuf(nand, buf, len256);

                NanD_Command(nand, NAND_CMD_READOOB);
                NanD_Address(nand, ADDR_COLUMN_PAGE, ofs & (~0x1ff));
        }

udelay(9); // hwjang

        NanD_ReadBuf(nand, &buf[len256], len - len256);
...
}

static int nand_write_oob(struct nand_chip* nand, size_t ofs, size_t len,
                  size_t * retlen, const u_char * buf)
{
...
        /* treat crossing 8-byte OOB data for 2M x 8bit devices */
        /* Note: datasheet says it should automaticaly wrap to the */
        /*       next OOB block, but it didn't work here. mf.      */
        if (nand->page256 && ofs + len > (ofs | 0x7) + 1) {
                len256 = (ofs | 0x7) + 1 - ofs;
                for (i = 0; i < len256; i++)
                        WRITE_NAND(buf[i], nandptr);

                NanD_Command(nand, NAND_CMD_PAGEPROG);

udelay(300);

                NanD_Command(nand, NAND_CMD_STATUS);
...
        for (i = len256; i < len; i++)
                WRITE_NAND(buf[i], nandptr);

        NanD_Command(nand, NAND_CMD_PAGEPROG);

udelay(300); // hwjang

        NanD_Command(nand, NAND_CMD_STATUS);
...
}

static int nand_erase(struct nand_chip* nand, size_t ofs, size_t len, int clean)
{
...
        while(len) {
                /*mychip = &nand->chips[shr(ofs, nand->chipshift)];*/
                mychip = &nand->chips[ofs >> nand->chipshift];

                /* always check for bad block first, genuine bad blocks
                 * should _never_  be erased.
                 */
                if (ALLOW_ERASE_BAD_DEBUG || !check_block(nand, ofs)) {
                        /* Select the NAND device */
                        NAND_ENABLE_CE(nand);  /* set pin low */

                        NanD_Command(nand, NAND_CMD_ERASE1);
                        NanD_Address(nand, ADDR_PAGE, ofs);
                        NanD_Command(nand, NAND_CMD_ERASE2);
udelay(2500); // hwjang
                        NanD_Command(nand, NAND_CMD_STATUS);
...
}
NAND FLASH로부터 STAND-ALONE으로 동작
# tftpboot 200000 /tftpboot/vmlinux.UBoot
# tftpboot 400000 /tftpboot/yaffs.image
# nand erase (nand시작번지) (nand 사이즈)
# nand write 200000 4000 (vmlinux.UBoot 사이즈)
# nand write.yaffs 400000 190000 (yaffs.image 사이즈)
# reset
(재부팅)
# nand read 200000 4000 (vmlinux.UBoot사이즈);bootm 200000 실행되고

(콘솔메시지)
U-Boot 1.1.1 (Jun 30 2004 - 15:21:21)

CPU:   IBM PowerPC 405GPr Rev. B at 266.500 MHz (PLB=133, OPB=66, EBC=66 MHz)
       PCI sync clock at 33 MHz, internal PCI arbiter enabled
       16 kB I-Cache 16 kB D-Cache
Board: ### No HW ID - assuming WALNUT405
I2C:   ready
DRAM:  32 MB
*** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
NAND:Probing at 0xf0000000
  16 MB
Hit any key to stop autoboot:  0
=> tftpboot 200000 /tftpboot/uImage
ENET Speed is 10 Mbps - HALF duplex connection
TFTP from server 192.168.1.210; our IP address is 192.168.1.200
Filename '/tftpboot/uImage'.
Load address: 0x200000
Loading: #################################################################
         #################################################################
         ############
done
Bytes transferred = 722310 (b0586 hex)
=> tftpboot 400000 /tftpboot/yaffs.image
ENET Speed is 10 Mbps - HALF duplex connection
TFTP from server 192.168.1.210; our IP address is 192.168.1.200
Filename '/tftpboot/yaffs.image'.
Load address: 0x400000
Loading: #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         #################################################################
         ############################################################
done
Bytes transferred = 3631056 (3767d0 hex)
=> nand write 200000 4000 b0586

NAND write: device 0 offset 16384, size 722310 ...  722310 bytes written: OK
=> nand write.yaffs 400000 190000 3767d0

NAND write: device 0 offset 1638400, size 3631056 ...  3521024 bytes written: OK
=> nand bad

Device 0 bad blocks:
0x00000000

=> reset

U-Boot 1.1.1 (Jun 30 2004 - 15:21:21)

CPU:   IBM PowerPC 405GPr Rev. B at 266.500 MHz (PLB=133, OPB=66, EBC=66 MHz)
       PCI sync clock at 33 MHz, internal PCI arbiter enabled
       16 kB I-Cache 16 kB D-Cache
Board: ### No HW ID - assuming WALNUT405
I2C:   ready
DRAM:  32 MB
*** Warning - bad CRC, using default environment

In:    serial
Out:   serial
Err:   serial
NAND:Probing at 0xf0000000
  16 MB
Hit any key to stop autoboot:  0

NAND read: device 0 offset 16384, size 2097152 ...  2097152 bytes read: OK
## Booting image at 00200000 ...
   Image Name:   Linux-2.4.24-pre2
   Image Type:   PowerPC Linux Kernel Image (gzip compressed)
   Data Size:    722246 Bytes = 705.3 kB
   Load Address: 00000000
   Entry Point:  00000000
   Verifying Checksum ... OK
   Uncompressing Kernel Image ... OK

...(중간생략)

NAND device: Manufacturer ID: 0xec, Chip ID: 0x73 (Samsung NAND 16MiB 3,3V)
Creating 3 MTD partitions on "NAND 16MiB 3,3V":
0x00004000-0x00190000 : "형욱보드 Linux Kernel Partition"
mtd: Giving out device 0 to 형욱보드 Linux Kernel Partition
0x00190000-0x00640000 : "형욱보드 Root Filesystem(YAFFS) Partition"
mtd: Giving out device 1 to 형욱보드 Root Filesystem(YAFFS) Partition
0x00640000-0x00fa0000 : "형욱보드 Configuration Partition"
mtd: Giving out device 2 to 형욱보드 Configuration Partition

...(중간생략)

yaffs: dev is 7937 name is "1f:01"
VFS: Mounted root (yaffs filesystem).
Freeing unused kernel memory: 100k init
serial console detected.  Disabling virtual terminals.
init started:  BusyBox v0.60.2 (2002.12.19-10:50+0000) multi-call binary

(none) login: root

$ ls
$ cd /tmp
$ ls
$ cat abcd > test.txt
cat: abcd: No such file or directory
$ echo abcd > test.txt
$ ls
test.tx