第三章第六节 NAND Flash驱动移植 进入正文之前,先跟大家说声抱歉,由于年底待处理的事情太多,陆陆续续忙碌了大概一个月,昨天晚上刚刚回到深圳,这段时间,我一会在关注大家的学习进展。废话我们不多说,进入主题,go。 NAND Flash硬件原理在U-Boot章节已经讲得很详细,这里就不再累赘讲解。直接进入Linux系统的NAND Flash驱动移植。 NAND Flash驱动很多工作linux内核已经完成,只需要稍作修改尽可使用。从三星官网,下载s3c_nand.c文件,并将其放入drivers/mtd/nand/中。如下所示: zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3/drivers/mtd/nand$ ls s3c* s3c2410.c s3c2410.o s3c_nand.c 需要将其编译进入内核,那么理所应当就该修改当前目录下(drivers/mtd/nand/)的Makefile,如下所示: zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3/drivers/mtd/nand$ gedit Makefile 在文件任何一个位置添加如下语句: obj-$(CONFIG_MTD_NAND_S3C) += s3c_nand.o 添加NAND Flash的配置选项,这样在make menuconfig时候便可随意选择是否需要NAND Flash驱动。涉及配置选项,需要修改的文件都是Kconfig。打开当前目录下(drivers/mtd/nand/)的Kconfig,在config MTD_NAND_S3C2410后面添加NandFlash选项,如下所示: config MTD_NAND_S3C tristate "NAND Flash support for S3C SoC" depends on (ARCH_S3C64XX || ARCH_S5P64XX || ARCH_S5PC1XX)&& MTD_NAND help This enables the NAND flash controller on the S3C. No board specfic support is done by this driver, eachboard must advertise a platform_device for the driver toattach. config MTD_NAND_S3C_DEBUG bool "S3C NAND driver debug" depends on MTD_NAND_S3C help Enable debugging of the S3C NAND driver config MTD_NAND_S3C_HWECC bool "S3C NAND Hardware ECC" depends on MTD_NAND_S3C help Enable the use of the S3C's internal ECC generatorwhen using NAND. Early versions of the chip have hadproblems with incorrect ECC generation, and if using these, the defaultof software ECC is preferable. If you lay down a device with the hardware ECC, thenyou will currently not be able to switch to software, as thereis no implementation for ECC method used by the S3C 由于s3c_nand.c文件许多寄存器还没有定义,则需要在arch/arm/plat-samsung/include/plat/regs_nand.h文件中添加相关的寄存器定义,如下所示: zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3/arch/arm/plat-samsung/include/plat$ gedit regs-nand.h 在文件的最后添加如下相关定义: #if 1 //zzq-> by 2013-3-16 /* for s3c_nand.c */ #define S3C_NFCONF S3C2410_NFREG(0x00) #define S3C_NFCONT S3C2410_NFREG(0x04) #define S3C_NFCMMD S3C2410_NFREG(0x08) #define S3C_NFADDR S3C2410_NFREG(0x0c) #define S3C_NFDATA8 S3C2410_NFREG(0x10) #define S3C_NFDATA S3C2410_NFREG(0x10) #define S3C_NFMECCDATA0 S3C2410_NFREG(0x14) #define S3C_NFMECCDATA1 S3C2410_NFREG(0x18) #define S3C_NFSECCDATA S3C2410_NFREG(0x1c) #define S3C_NFSBLK S3C2410_NFREG(0x20) #define S3C_NFEBLK S3C2410_NFREG(0x24) #define S3C_NFSTAT S3C2410_NFREG(0x28) #define S3C_NFMECCERR0 S3C2410_NFREG(0x2c) #define S3C_NFMECCERR1 S3C2410_NFREG(0x30) #define S3C_NFMECC0 S3C2410_NFREG(0x34) #define S3C_NFMECC1 S3C2410_NFREG(0x38) #define S3C_NFSECC S3C2410_NFREG(0x3c) #define S3C_NFMLCBITPT S3C2410_NFREG(0x40) #define S3C_NF8ECCERR0 S3C2410_NFREG(0x44) #define S3C_NF8ECCERR1 S3C2410_NFREG(0x48) #define S3C_NF8ECCERR2 S3C2410_NFREG(0x4c) #define S3C_NFM8ECC0 S3C2410_NFREG(0x50) #define S3C_NFM8ECC1 S3C2410_NFREG(0x54) #define S3C_NFM8ECC2 S3C2410_NFREG(0x58) #define S3C_NFM8ECC3 S3C2410_NFREG(0x5c) #define S3C_NFMLC8BITPT0 S3C2410_NFREG(0x60) #define S3C_NFMLC8BITPT1 S3C2410_NFREG(0x64) #define S3C_NFCONF_NANDBOOT (1<<31) #define S3C_NFCONF_ECCCLKCON (1<<30) #define S3C_NFCONF_ECC_MLC (1<<24) #define S3C_NFCONF_ECC_1BIT (0<<23) #define S3C_NFCONF_ECC_4BIT (2<<23) #define S3C_NFCONF_ECC_8BIT (1<<23) #define S3C_NFCONF_TACLS(x) ((x)<<12) #define S3C_NFCONF_TWRPH0(x) ((x)<<8) #define S3C_NFCONF_TWRPH1(x) ((x)<<4) #define S3C_NFCONF_ADVFLASH (1<<3) #define S3C_NFCONF_PAGESIZE (1<<2) #define S3C_NFCONF_ADDRCYCLE (1<<1) #define S3C_NFCONF_BUSWIDTH (1<<0) #define S3C_NFCONT_ECC_ENC (1<<18) #define S3C_NFCONT_LOCKTGHT (1<<17) #define S3C_NFCONT_LOCKSOFT (1<<16) #define S3C_NFCONT_8BITSTOP (1<<11) #define S3C_NFCONT_MECCLOCK (1<<7) #define S3C_NFCONT_SECCLOCK (1<<6) #define S3C_NFCONT_INITMECC (1<<5) #define S3C_NFCONT_INITSECC (1<<4) #define S3C_NFCONT_nFCE1 (1<<2) #define S3C_NFCONT_nFCE0 (1<<1) #define S3C_NFCONT_INITECC (S3C_NFCONT_INITSECC | S3C_NFCONT_INITMECC) #define S3C_NFSTAT_ECCENCDONE (1<<7) #define S3C_NFSTAT_ECCDECDONE (1<<6) #define S3C_NFSTAT_BUSY (1<<0) #define S3C_NFECCERR0_ECCBUSY (1<<31) //<-zzq #endif 在这段宏定义中的#if……#endif,是为了更好地添加与注释代码。 与u-boot中的NAND Flash一样,支持ECC校验。由于OK6410开发平台使用的NAND Flash芯片是:K9GAG08U0D,那么在nand_base.c文件中添加支持ECC校验,操作如下: zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3/drivers/mtd/nand$ gedit nand_base.c 添加如下: static struct nand_ecclayout nand_oob_218_128Bit = { .eccbytes = 104, .eccpos = { 24,25,26,27,28,29,30,31,32,33, 34,35,36,37,38,39,40,41,42,43, 44,45,46,47,48,49,50,51,52,53, 54,55,56,57,58,59,60,61,62,63, 64,65,66,67,68,69,70,71,72,73, 74,75,76,77,78,79,80,81,82,83, 84,85,86,87,88,89,90,91,92,93, 94,95,96,97,98,99,100,101,102,103, 104,105,106,107,108,109,110,111,112,113, 114,115,116,117,118,119,120,121,122,123, 124,125,126,127}, .oobfree = { { .offset = 2, .length = 22 } } }; ECC校验位有104位,即24~127。NAND Flash制造商规定0~1这2位为检测坏块位,2~23这22位为用户自定义检测位。 在int nand_scan_tail(struct mtd_info *mtd)函数中添加与之相对应的校验,如下所示: case 218: chip->ecc.layout = &nand_oob_218_128Bit; break; NAND Flash的驱动代码添加完成,在linux-3.8.3/arch/arm/mach-s3c64xx添加NAND Flash初始化,如下所示: static void __init ok6410_machine_init(void) { …… s3c_device_nand.name = "s3c6410-nand"; s3c_nand_set_platdata(&ok6410_nand_info); s3c_fb_set_platdata(&ok6410_lcd_pdata[features.lcd_index]); s3c24xx_ts_set_platdata(NULL); …… } 回到linux-3.8.3内核的根目录下,进行内核配置,如下所示: zhuzhaoqi@zhuzhaoqi-desktop:~/Linux/linux-3.8.3$ make menuconfig 内核配置如下所示操作: Device Drivers --> Memory Technology Device (MTD) support ---> NAND Device Support 进入NAND Device Support之后,取消NAND Flash support for Samsung S3C SoCs,选择 NAND Flash support for S3C SoC配置。如下所示: <> NAND Flash support for Samsung S3C SoCs <*> NAND Flash support for S3C SoC S3C NAND driver debug S3C NAND Hardware ECC 完成配置之后重新编译内核,通过TFTP进行调试,串口输出有一个错误,如下: S3C NAND Driver, (c) 2008 Samsung Electronics dev_id == 0xd5 select s3c_nand_oob_mlc ****Nandflash:ChipType= MLC ChipName=samsung-K9GAG08U0D************ S3C NAND Driver is using hardware ECC. NAND device: Manufacturer ID: 0xec, Chip ID: 0xd5 (Samsung NAND 2GiB 3,3V 8-bit), page size: 4096, OOB size: 218 Driver must set ecc.strength when using hardware ECC ------------[ cut here ]------------ kernel BUG at drivers/mtd/nand/nand_base.c:3382! Internal error: Oops - BUG: 0 [#1] ARM 从串口错误信息很容易可以知道错误的根源在linux3.8.3内核的drivers/mtd/nand/nand_base.c:3382,进入文件,找到根源,如下所示: if (mtd->writesize >= chip->ecc.size) { if (!chip->ecc.strength) { pr_warn("Driver must set ecc.strength when using hardware ECCn"); BUG(); } break; } 因为在内核配置的时候选择了硬件ECC校验,在执行上面代码的时候进入BUG(),而BUG()语句是一个死循环,内核进去之后无法出来。为了使调试通过,暂时先注释掉BUG()语句,使得内核不进入死循环。 重新编译内核,通过TFTP调试,串口输出信息为: …… ****Nandflash:ChipType= MLC ChipName=samsung-K9GAG08U0D************ S3C NAND Driver is using hardware ECC. NAND device: Manufacturer ID: 0xec, Chip ID: 0xd5 (Samsung NAND 2GiB 3,3V 8-bit), 2048MiB, page size: 4096, OOB size: 218 Driver must set ecc.strength when using hardware ECC Creating 4 MTD partitions on "NAND 2GiB 3,3V 8-bit": 0x000000000000-0x000000100000 : "Bootloader" 0x000000100000-0x000000600000 : "Kernel" 0x000000600000-0x00000ce00000 : "File System" 0x00000ce00000-0x000080000000 : "User" …… 从串口的输出,可以看到MTD分区信息和NAND Flash的驱动信息,从信息输出可知NAND Flash移植没有问题。 |