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
글쓴이 : 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