1. 임베디드리눅스장비에서램디스크를이용하여루트파일시스템을구현하였을경우에는보드동작중에파일로기록된내용이전원이꺼짐과동시에소실된다. 기록된내용을영구저장하기위해서는일반적으로플래시메모리에기록하여야한다. 플래시메모리를리눅스의루트파일시스템으로사용하기위해서는 MTD (Memory Technology Device ) 블록디바이스드라이버를사용하여야한다. 타겟보드는 NAND 플래시기반의보드이다. 보조기억장치로써 NOR 형플래시를사용하지않고 NAND 형플래시를사용한다. 타겟보드에서 NAND 기반의플래시를사용하게된이유는기존이지보드의영향이매우크다. 첫번째, 보드의생산량이많아질경우민감해지는보드단가형플래시보다 NOR형플래시가월등히비싸다. 부분에서 NAND 두번째, 쓰기속도의문제이다. NOR 형플래시의읽기속도는일반 ROM 의속도로 사용되어이익을얻을수있지만쓰기속도는너무느리게동작한다. 또 NAND에비해서읽기속도도전반적인속도평균을보면그리빠른편이라볼수없다. 세번째, 현장에서실제파일입출력을사용했을때의외로 NOR 플래시시스템에문제가발생함을알수있다. 그래서본사의개발팀은여러난상토론끝에 NAND 플래시시스템을쓰기로결정했다. 이 NAND 플래시시스템을파일시스템으로사용하기위해서는다음의두가지가있다 MTD + JFFS2 방식 MTD + YAFFS 방식 이중에서자체실험결과 NAND 시스템에서는 JFFS2 방식이무척불합리하다는것으로판단되어최종적으로 YAFFS 시스템으로결정이되었다.
아직완전한검증이되지않은시스템이라는점이문제이기는하나본사의자체시험결과문제점발생을찾지못했다. 굳이문제점을지적하라면동일크기의데이터를기록함에있어서사용량이 JFFS2 보다많이소모된다. 그러나이점은읽기속도와쓰기속도의향상으로충분히보상되고도남으며또한 NAND의단가가매우낮기때문에별문제가되지않을것으로본다. 현재제공되고있는커널소스는 MTD와 JFFS2 그리고 YAFFS에포함되어있다. 그러나본문서의커널패치에서는이부분이빠져있는데이유는그과정이복잡하기때문이다. 그래서따로이렇게한장을할애하여커널에 NAND시스템을위한 을구현에대하여설명하고있다. 우선진행하기이전에 MTD 에대해서간단하게살펴보고자한다. MTD MTD는디바이스드라이버이다. 이디바이스드라이버가지원하려했던초기모델은 DOC( Disk On Chip ) 이다. 그후임베디드시스템의플래시를지원했으며현재의형태가되었다. MTD 관련공식사이트는 http://www.linux-mtd.infradead.org/ 이다다음으로접속하면 mtd관련파일을얻을수있다. ftp://ftp.uk.linux.org/pub/people/dwmw2/mtd/cvs/ MTD가지원하는플래시는여러형태가있는데그중우리의관심은 NAND 이다. 기존 ARM을지원하는커널 2.6.8-rc2에서는이 MTD에대한내용이포함되어있다. 따라서이것을그대로사용하기로한다. MTD는두가지형태의디바이스드라이버를지원하는데캐랙터형과블럭형이다. 캐랙터형은플래시자체를접근가능하게하는부분이구현되어있다. 그러므로플래시를직접지우거나할경우에는캐랙터형으로접근하여야한다.
장치파일을만들때주번호는 90으로시작하며짝수의부번호는읽기쓰기가가능하고홀수의번호는읽기만가능하다. 이렇게구별한이유는아마도플래시를보호하기위한것으로보인다. 그러나타겟보드에서램디스크에미리만들어놓은것은읽기쓰기가가능한짝수부번호이다. 다음은이에대한내용을보여주는화면이다. 블럭형은플래시를파일시스템에서접근하게한것이다. 당연히이장치파일은응용프로그램에서접근하면곤란하다. 특별한경우가아니면접근하지말아야한다. 블럭형장치파일의일반적인사용은 mount 명령을사용할때사용된다. 다음은이에대한내용을보여주는화면이다.
1.1. MTD 에 NAND 지원패치 MTD 최신본 (?) 을커널에패치했다면다음에 NAND를지원하게 MTD를수정하여야한다. NAND 의종류가몇가지안되고자랑스럽게도한국의삼성 NAND가전세계를지배했어도 NAND의컨트롤방식은보드마다다르다. 그러므로이에대한패치를수행하여야한다. 또한가지는 NAND의 ECC 문제때문에끊임없이경고메세지를발생하는문제가있는데이를수정하여야한다../drivers/mtd/nand/nand_base.c 를수정한다. printk 문을주석처리를하면된다. nand_write_page 함수중에서다음라인을주석처리한다. [ 수정부분 #1] // printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n"); nand_read_ecc 함수중에서다음라인을주석처리한다. [ 수정부분 #2] // printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n"); nand_scan 함수중에서다음라인을주석처리한다. [ 수정부분 #3] // printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended!!\n");
./drivers/mtd/nand/ez_s2410.c 작성 타겟보드에추가되는 nand를지원하기위한루틴을작성해야하는데소스는제공된 CD의 /sw/mtd/ez_s2410.c 에있으며, 내용을작성하지않고복사하여사용하면된다. 다음은소스의내용이다. /* * drivers/mtd/nand/ez_s2410.c * * Copyright (C) 2003 You youngchang (frog@falinux.com) * * Support board - EZ-S2410 */ #include <linux/slab.h> #include <linux/module.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/partitions.h> #include <linux/string.h> #include <linux/config.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/compiler.h> #include <linux/netdevice.h> #include <linux/etherdevice.h> #include <linux/init.h> #include <linux/pci.h> #include <linux/delay.h> #include <linux/ethtool.h> #include <linux/mii.h> #include <linux/if_vlan.h> #include <linux/crc32.h> #include <linux/in.h> #include <linux/ip.h> #include <linux/tcp.h> #include <linux/udp.h> #include <asm/io.h> #include <asm/uaccess.h> #define EZ_NAND_BASE #define EZ_NAND_CTRL #define EZ_NAND_DATA #define EZ_NAND_CMD #define EZ_NAND_ADDR #define EZ_NAND_STATE S3C2410_VA_NAND (EZ_NAND_BASE+0x0) (EZ_NAND_BASE+0xC) (EZ_NAND_BASE+0x4) (EZ_NAND_BASE+0x8) (EZ_NAND_BASE+0x10) #define EZ_NAND_nFCE (1<<11) static struct mtd_info *ez_s2410_nand_mtd = NULL; // 정보테이블 #define SZ_1M (1024*1024) #define SZ_1K (1024) static char *cmdline_par;
/* * Define partitions for flash device */ #ifdef CONFIG_MTD_PARTITIONS static struct mtd_partition partition_info[] =.name = "mtdblock0 Kernel partition",.offset = 0x40000,.size = (2*SZ_1M) - 0x40000,,.name = "mtdblock1 Ramdisk partition",.offset = 0x400000,.size = 5*SZ_1M,.name = "mtdblock2(yaffs) Data partition 0",.offset = 0x700000,.size = 57*SZ_1M ; #define EZ_S2410_NAND_NUM_PARTITIONS 3 #endif // 커널커맨드라인을파싱한다. static void fixup_partition_info( void ) char *delim_ = ","; int argc; char *argv[256]; char *tok; int size[3]; argc = 0; argv[argc] = NULL; for (tok = strsep( &cmdline_par, delim_); tok; tok = strsep( &cmdline_par, delim_)) argv[argc++] = tok; if ( argc == EZ_S2410_NAND_NUM_PARTITIONS ) size[0] = simple_strtoul( argv[0],null,0 ); size[1] = simple_strtoul( argv[1],null,0 ); size[2] = simple_strtoul( argv[2],null,0 ); if ( ( size[0] > 0 ) && ( size[1] > 0 ) && ( size[2] > 0 ) ) partition_info[0].offset = 0x40000; partition_info[0].size = (size[0]*sz_1m) - partition_info[0].offset; partition_info[1].offset = size[0]*sz_1m; partition_info[1].size = size[1]*sz_1m; partition_info[2].offset = (size[0]+size[1])*sz_1m; partition_info[2].size = size[2]*sz_1m;
/* * hardware specific access to control-lines */ void ez_s2410_nand0_hwcontrol(struct mtd_info *mtd, int cmd) unsigned short dummy; switch(cmd) case NAND_CTL_SETNCE: writew( readw(ez_nand_ctrl) & (~EZ_NAND_nFCE), EZ_NAND_CTRL ); break; case NAND_CTL_CLRNCE: writew( readw(ez_nand_ctrl) EZ_NAND_nFCE, EZ_NAND_CTRL ); break; /* * Send command to NAND device */ void ez_s2410_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) writew (command, EZ_NAND_CMD ); else if (mtd->oobblock == 256 && column >= 256) column -= 256; writew (NAND_CMD_READOOB, EZ_NAND_CMD ); writew (NAND_CMD_SEQIN, EZ_NAND_CMD ); else if (mtd->oobblock == 512 && column >= 256) if (column < 512) column -= 256; writew (NAND_CMD_READ1, EZ_NAND_CMD); writew (NAND_CMD_SEQIN, EZ_NAND_CMD); else column -= 512; writew (NAND_CMD_READOOB, EZ_NAND_CMD); writew (NAND_CMD_SEQIN, EZ_NAND_CMD); else writew (NAND_CMD_READ0, EZ_NAND_CMD); writew (NAND_CMD_SEQIN, EZ_NAND_CMD); // Serially input address if (column!= -1 page_addr!= -1) if (column!= -1) writeb (column, EZ_NAND_ADDR); if (page_addr!= -1) writew ((unsigned char) (page_addr & 0xff), EZ_NAND_ADDR);
writew ((unsigned char) ((page_addr >> 8) & 0xff), EZ_NAND_ADDR); // One more address cycle for higher density devices if (mtd->size & 0x0c000000) writew ((unsigned char) ((page_addr >> 16) & 0x0f), EZ_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, EZ_NAND_CMD); while (!(readb (EZ_NAND_STATE) & 0x1)); return; default: if (!this->dev_ready) udelay (this->chip_delay); return; while (!this->dev_ready(mtd)); int ez_s2410_device_ready(struct mtd_info *mtd) return (readb (EZ_NAND_STATE) & 0x1)? 1 : 0; /* * Main initialization routine */ //int init ez_s2410_nand_init (void) static int init ez_s2410_nand_init (void) struct nand_chip *this; // Allocate memory for MTD device structure and private data ez_s2410_nand_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL); if (!ez_s2410_nand_mtd) printk ("Unable to allocate EZ-S2410-NAND MTD device structure. n"); return -ENOMEM; // Get pointer to private data this = (struct nand_chip *) (&ez_s2410_nand_mtd[1]); // Initialize structures memset((char *) ez_s2410_nand_mtd, 0, sizeof(struct mtd_info)); memset((char *) this, 0, sizeof(struct nand_chip)); // Link the private data with the MTD structure ez_s2410_nand_mtd->priv = this;
// Set address of NAND IO lines this->io_addr_r = EZ_NAND_DATA; this->io_addr_w = EZ_NAND_DATA; // Set address of hardware control function this->hwcontrol = ez_s2410_nand0_hwcontrol; this->dev_ready = ez_s2410_device_ready; // Set commamd function this->cmdfunc = ez_s2410_nand_command ; // 15 us command delay time this->chip_delay = 15; this->eccmode = NAND_ECC_SOFT; ez_s2410_nand_mtd->oobsize = 16; // Scan to find existence of the device if (nand_scan (ez_s2410_nand_mtd,1)) kfree (ez_s2410_nand_mtd); return -ENXIO; // 커널커맨드에서정보를얻는다 fixup_partition_info(); // Register the partitions add_mtd_partitions(ez_s2410_nand_mtd, partition_info, EZ_S2410_NAND_NUM_PARTITIONS); // Return happy return 0; module_init(ez_s2410_nand_init); /* * Clean up routine */ #ifdef MODULE static void exit ez_s2410_nand_cleanup (void) struct nand_chip *this = (struct nand_chip *) &ez_s2410_nand_mtd[0]; // Unregister the device del_mtd_device (ez_s2410_nand_mtd); // Free internal data buffer kfree (this->data_buf); // Free the MTD device structure kfree (ez_s2410_nand_mtd); module_exit(ez_s2410_nand_cleanup); #endif static int init nandpart_setup(char *s) cmdline_par = s; return 1; setup("nandparts=", nandpart_setup); MODULE_AUTHOR("You Youngchang, Jang Hyung-Gi <frog@falinux.com"); MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on EZ-S2410-NAND board"); MODULE_LICENSE("GPL");
./drivers/mtd/nand/kconfig 파일에다음내용을추가한다. config MTD_NAND_EZ_S2410 tristate "NAND Flash device for S3C2410 on EZ-S2410 board" depends on ARM && ARCH_S3C2410 && MTD_NAND help If you had to ask, you don't have one. Say 'N'. [ 수정전 ] [ 수정후 ]
./drivers/mtd/nand/makefile 수정 타겟보드를지원하기위하여항목을추가한다. obj-$(config_mtd_nand_ez_s2410) += ez_s2410.o [ 수정전 ] [ 수정후 ]
1.2. 커널옵션및커널패치 모든내용을수정했다면커널옵션에 MTD 에관련된것을설정해야한다. 다음은설정에대한화면이다. # make menuconfig
위와같이설정이끝났다면저장한다. 이제커널을컴파일한다. # make dep # make clean # make zimage 타겟보드에커널을다운로딩하고부팅한다. 이때부팅중에다음과같은파티션정보메세지가나오면정상이다. 타겟보드는아래와같이 3 개의파티션으로나누어져있다. /dev/mtd0, /dev/mtdblock0 : 커널이미지파티션 /dev/mtd1, /dev/mtdblock1 : 램디스크이미지파티션 /dev/mtd2, /dev/mtdblock2 : 일반영역파티션
이중에응용프로그램에서사용할수있는것은다음과같다. /dev/mtd2 /dev/mtdblock2 일단정상적인지시험하기위해서일반영역을지워보자 타겟에넣어져있는램디스크이미지안에는 eraseall 이라는 MTD 의삭제유틸리티가있다. $./eraseall /dev/mtd2 다음은타겟보드에서의실행화면이다. 이유틸리티를사용하지않고부트로드에서삭제할수도있다. 위의유틸리티를사용할경우에는디폴트로마운트되어있는것을해지하고지워야한다. 따라서부트로드에서 NAND 파티션을지우는것을추천한다.