u_boot移植(五)之分析uboot源码中nand flash操作

合集下载

u-boot_2010.6nandflash驱动彻底分析

u-boot_2010.6nandflash驱动彻底分析

u-boot_2010.6nandflash驱动彻底分析2017年11⽉13⽇15:37:34最近公司⼤裁员,闹的⼈⼼惶惶,不管怎么样,武装好⾃⼰才是硬道理,坚持学习,学会那些还没学会的。

今天虚拟机突然打不开了,吓了我⼀跳,因为代码都还没备份,⼀定得养成备份代码的习惯!好了,下⾯开始进⼊正题吧,nandflash驱动彻底分析底层驱动移植完后,执⾏nand 命令:nand read 0x30000000 0 0x2000nand read 的命令格式是 nand read addr off | partition size ,总结起来就是读到哪去?从哪读?读多⼤?返回的结果是:NAND read: device 0 offset 0x0, size 0x2000file is nand_util.c,fun is nand_read_skip_bad,line is 599,NAND read from offset 2000 failed -740 bytes read: ERROR读失败,给出读失败错误码 -74要分析失败原因,先分析函数执⾏流程执⾏nand read 命令后,其实是执⾏了nand_read_skip_bad(nand, off, &size,(u_char *)addr);跳过坏块读函数的参数简单明了,从哪读,读到哪去,读多少,以及⼀个公共句柄(包含nand的信息,例如有多少个块,块⼤⼩等) 1int nand_read_skip_bad(nand_info_t *nand, loff_t offset, size_t *length,2 u_char *buffer)3 {4int rval;5 size_t left_to_read = *length;6 size_t len_incl_bad;7 u_char *p_buffer = buffer;89 len_incl_bad = get_len_incl_bad (nand, offset, *length); /* 函数分析见2017.11.14笔记(往下翻) */1011if ((offset + len_incl_bad) > nand->size) {12 printf ("Attempt to read outside the flash area\n");13return -EINVAL;14 }1516if (len_incl_bad == *length) {17 rval = nand_read (nand, offset, length, buffer);18if (!rval || rval == -EUCLEAN)19return0;20 printf ("NAND read from offset %llx failed %d\n",21 offset, rval);22return rval;23 }2425while (left_to_read > 0) {26 size_t block_offset = offset & (nand->erasesize - 1);27 size_t read_length;2829 WATCHDOG_RESET ();3031if (nand_block_isbad (nand, offset & ~(nand->erasesize - 1))) {32 printf ("Skipping bad block 0x%08llx\n",33 offset & ~(nand->erasesize - 1));34 offset += nand->erasesize - block_offset;35continue;36 }3738if (left_to_read < (nand->erasesize - block_offset))39 read_length = left_to_read;40else41 read_length = nand->erasesize - block_offset;42/* read_length 最⼤不会超过 nand->erasesize (128K) */43/* nand_read 函数是⼀个按块读函数 */44 rval = nand_read (nand, offset, &read_length, p_buffer);45if (rval && rval != -EUCLEAN) {46 printf ("NAND read from offset %llx failed %d\n",47 offset, rval);48 *length -= left_to_read;49return rval;50 }5152 left_to_read -= read_length;53 offset += read_length;54 p_buffer += read_length;55 }5657return0;58 }先看get_len_incl_bad1static size_t get_len_incl_bad (nand_info_t *nand, loff_t offset,2const size_t length)3 {4 size_t len_incl_bad = 0;5 size_t len_excl_bad = 0;6 size_t block_len;78while (len_excl_bad < length) {9 block_len = nand->erasesize - (offset & (nand->erasesize - 1));1011if (!nand_block_isbad (nand, offset & ~(nand->erasesize - 1)))12 len_excl_bad += block_len;1314 len_incl_bad += block_len;15 offset += block_len;1617if (offset >= nand->size)18break;19 }2021return len_incl_bad;22 }想要看懂这个函数,先要理解offset的构成2017年11⽉14⽇10:00:40每天进步⼀点点nand->erasesize = 128K = 0x20000nand->erasesize - 1 = 0x1FFFFoffset & (nand->erasesize - 1) 保留低17位,清⾼位,因为nand的特性,所以是按块来统计长度block_len = nand->erasesize - offset & (nand->erasesize - 1) 作⽤是统计当前块要读的长度然后判断当前块是不是坏块,如果不是坏块,则让len_excl_bad += block_len,判断是否循环的标准是len_excl_bad < length 不论当前块是不是坏块,都让len_incl_bad += block_len,len_incl_bad 得到的是包含坏块的长度总结get_len_incl_bad函数的作⽤为:①如果偏移offset是从整块开始的,返回的结果是块的整数倍(不⽤看length,⽐如length是0x20001,则读两块)②如果偏移offset不是从整块开始的,返回的结果是块的整数倍+第⼀块要读的长度总之,返回的结果是按块补齐的再继续分析nand_read_skip_bad1if (len_incl_bad == *length) {2 rval = nand_read (nand, offset, length, buffer);3if (!rval || rval == -EUCLEAN)4return0;56return rval;7 }什么情况下,len_incl_bad == *length ?要读的内容⾥,没有坏块,且长度最后按块对齐(例如,从0x400开始读,读的长度为0x40000-0x400)如果不满⾜,则进⼊while循环读,在while⾥,按块读,先判断当前块是不是坏块,如果是则跳过,不是则读下⾯分析nand_read函数1/* 1.从哪读 2.读多少 3.读到哪去 */2static int nand_read(struct mtd_info *mtd, loff_t from, size_t len,3 size_t *retlen, uint8_t *buf)4 {5struct nand_chip *chip = mtd->priv;6int ret;78/* Do not allow reads past end of device */9if ((from + len) > mtd->size)10return -EINVAL;11if (!len)12return0;13/* 选中芯⽚ */14 nand_get_device(chip, mtd, FL_READING);1516 chip->ops.len = len;17 chip->ops.datbuf = buf;18 chip->ops.oobbuf = NULL;1920 ret = nand_do_read_ops(mtd, from, &chip->ops);2122 *retlen = chip->ops.retlen;23/* 取消选中芯⽚ */24 nand_release_device(mtd);2526return ret;27 }其实真正的读函数是nand_do_read_ops,从哪去,读多少,读到哪去都被装载在chip->ops结构体中再看nand_do_read_ops函数1/**2 * nand_do_read_ops - [Internal] Read data with ECC3 *4 * @mtd: MTD device structure5 * @from: offset to read from6 * @ops: oob ops structure7 *8 * Internal function. Called with chip held.9*/10static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,11struct mtd_oob_ops *ops)12 {13int chipnr, page, realpage, col, bytes, aligned;14struct nand_chip *chip = mtd->priv;15struct mtd_ecc_stats stats;16int blkcheck = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;17int sndcmd = 1;18int ret = 0;19 uint32_t readlen = ops->len;//128K 0x20000 根据输⼊的长度决定20 uint32_t oobreadlen = ops->ooblen; // oobreadlen = 021 uint8_t *bufpoi, *oob, *buf;2223 stats = mtd->ecc_stats;2425 chipnr = (int)(from >> chip->chip_shift);26 chip->select_chip(mtd, chipnr);27/* realpage 总页地址(⾼17位) */28 realpage = (int)(from >> chip->page_shift);//pageshift is 1129 page = realpage & chip->pagemask;//pagemask = 1ffff30/* col 低11位 */31 col = (int)(from & (mtd->writesize - 1));//writesize = 2048, 2047 = 0x7ff32/* 得到页地址和列地址 */33 buf = ops->datbuf;34 oob = ops->oobbuf;3536while(1) {37 bytes = min(mtd->writesize - col, readlen); //128K 0x20000 根据输⼊的长度决定38 aligned = (bytes == mtd->writesize); //aligned = 1;3940/* Is the current page in the buffer ? */41if (realpage != chip->pagebuf || oob) {42 bufpoi = aligned ? buf : chip->buffers->databuf;43/* 对齐与不对齐读到的缓冲是不⼀样的 */44if (likely(sndcmd)) {45 chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);46 sndcmd = 0;47 }4849/* Now read the page into the buffer */50if (unlikely(ops->mode == MTD_OOB_RAW))51 ret = chip->ecc.read_page_raw(mtd, chip,//nand_read_page_raw52 bufpoi, page);//从哪⾥读,读到哪⾥去,读多少53else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)54 ret = chip->ecc.read_subpage(mtd, chip, col, bytes, bufpoi);55/* nand_read_subpage */56else57 ret = chip->ecc.read_page(mtd, chip, bufpoi,//整页读 bufpoi = buf58 page); //nand_read_page_swecc59if (ret < 0)60break;6162/* Transfer not aligned data */63if (!aligned) {64if (!NAND_SUBPAGE_READ(chip) && !oob)65 chip->pagebuf = realpage;66 memcpy(buf, chip->buffers->databuf + col, bytes);67 }6869 buf += bytes;7071if (unlikely(oob)) {72/* Raw mode does data:oob:data:oob */73if (ops->mode != MTD_OOB_RAW) {74int toread = min(oobreadlen,75 chip->yout->oobavail);76if (toread) {77 oob = nand_transfer_oob(chip,78 oob, ops, toread);79 oobreadlen -= toread;80 }81 } else82 buf = nand_transfer_oob(chip,83 buf, ops, mtd->oobsize);84 }8586if (!(chip->options & NAND_NO_READRDY)) {87/*88 * Apply delay or wait for ready/busy pin. Do89 * this before the AUTOINCR check, so no90 * problems arise if a chip which does auto91 * increment is marked as NOAUTOINCR by the92 * board driver.93*/94if (!chip->dev_ready)95 udelay(chip->chip_delay);96else97 nand_wait_ready(mtd);98 }99 } else {100 memcpy(buf, chip->buffers->databuf + col, bytes);101 buf += bytes;102 }103104 readlen -= bytes;105106if (!readlen)107break;108109/* For subsequent reads align to page boundary. */110 col = 0;111/* Increment page address */112 realpage++;113114 page = realpage & chip->pagemask;115/* Check, if we cross a chip boundary */116if (!page) {117 chipnr++;118 chip->select_chip(mtd, -1);119 chip->select_chip(mtd, chipnr);120 }121122/* Check, if the chip supports auto page increment123 * or if we have hit a block boundary.124*/125if (!NAND_CANAUTOINCR(chip) || !(page & blkcheck))126 sndcmd = 1;127 }128129 ops->retlen = ops->len - (size_t) readlen;130if (oob)131 ops->oobretlen = ops->ooblen - oobreadlen;132133if (ret)134return ret;135136if (mtd->ecc_stats.failed - stats.failed)137return -EBADMSG;138139return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; 140 }。

uboot移植与源码分析总结(6)-Nand驱动

uboot移植与源码分析总结(6)-Nand驱动

uboot移植与源码分析总结(6)-Nand驱动uboot移植与源码分析总结(6)-Nand驱动2013-06-29 11:32:17分享:有关nand flash的特性描述,可以见我之前写的这篇文章《NandFlash结构与分析》。

从功能上来说,nand flash与norflash 并无太大差异,主要区别在于操作接口和方式。

Nand基于非sram总线接口,使用nand接口,所以一般需要mcu具有nand控制器才可与其连接。

在读取时,以页为单位;擦除和写入时,以块为单位。

将nand视作一个MTD设备uboot将nand视作一个mtd设备,所以使用mtd机制对nand 设备进行管理。

单个nand设备用nand_info_t来描述。

而nand_info_t实际上就是mtd结构。

最多支持的nand设备数CONFIG_SYS_MAX_NAND_DEVICE可由开发者自行配置。

不过一般目标板上只有一块Nand设备,所以取值通常为1。

但是仅使用mtd结构来描述不够,因为MTD只是一个通用的存储描述结构,而Nand设备特定的某些属性,如ECC布局等不能简单的添加到mtd结构中。

所以,uboot定义了nand_chip结构。

int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);void (*select_chip)(struct mtd_info *mtd, int chip);int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl);int (*init_size)(struct mtd_info *mtd, struct nand_chip *this, u8 *id_data);int (*dev_ready)(struct mtd_info *mtd);void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column,int page_addr);int(*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);void (*erase_cmd)(struct mtd_info *mtd, int page);int (*scan_bbt)(struct mtd_info *mtd);int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state,int status, int page);int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,const uint8_t *buf, int page, int cached, int raw);int chip_delay;unsigned int options;int page_shift;int phys_erase_shift;int bbt_erase_shift;int chip_shift;int numchips;uint64_t chipsize;然后,分配了CONFIG_SYS_MAX_NAND_DEVICE个nand_chip 结构。

U-boot烧内核到NandFlash步骤

U-boot烧内核到NandFlash步骤

U-boot烧内核到NandFlash步骤
1.把uImage内核镜象放到TFT服务器文件夹内。

2.配置Window下的IP地址:192.168.0.30 这个其实也是TFTP服务器的地址。

3.修改u-boot配置文件u-boot-1.3.4/include/configs/xyd2440.h
修改为:
4.保存文件,重新编译u-boot.把生成的u-boot文件烧录到Norflash中。

5.重新启动开发板,
6.内核的启动过程:(在2 的条件下)
#tftp 30000000 uImage ; 通过tftp 命令将uImage 文件下载到内存30000000 位置。

(#bootm 32000000 ; 跳到内存32000000 处运行)。

#nand erase
#nand write.jffs2 32000000 100000322b90 ; 将内存32000000 处的数据写到NAND 的0 地址处大小为322b90 322b90这个值是内核大小,是执行tftp 32000000 uImage ;命令后下载内核产生的值,根据实际情况修改。

100000这个值是内核在NandFlash起始地址,要和。

中#defind CONFIG_BOOTCOMMAND “nboot 0x32000000 0 100000; bootm 0x32000000”中的100000相同。

7.重启开发板,如果有以下信息表示已经成功烧录。

(启动linux的信息)。

移植笔记-:从Nand_flash启动的uboot

移植笔记-:从Nand_flash启动的uboot

到目前为此,这个版本的uboot已经加进了Nand驱动,可以使用Nand命令对nandflash进行读写。

但是还不能从Nand启动,只可以从Norflash启动或用tftp下载到sdram中然后go到该地址使用。

原因是目前移植的uboot还没有实现s3c2410支持的从Nand Flash启动机制需要的自拷贝功能。

下面介绍移植可以从Nand flash启动的uboot。

关于这里的Nand Boot概念,如果分析过vivi源码,明白vivi是如何从Nand跳转到sdram中继续执行以及copy_myself原理的话,再理解这里就比较简单了。

其实我们这里使uboot支持从nand启动的原理正是移植vivi里的copy_myself机制。

关于s3c2410从Nand Flash启动的原理可以查阅其他相关资料,不懂的话也没关系,不影响移植过程。

当然,我们的最终目的是编译一个可以直接烧录到nand中启动的uboot。

既然已经到这一步了,要实现nand启动已经很容易了。

只需要添加几行代码,大部分从vivi拷贝,只需修改少许地方。

过程如下:1.在board/smdk2410加入NAND Flash读函数,建立nand_read.c文件,加入如下内容(copy from vivi):(在vivi源码下的arch\s3c2410\nand_read.c)#include<config.h>#define __REGb(x)(*(volatile unsigned char*)(x))#define __REGi(x)(*(volatile unsigned int*)(x))#define NF_BASE 0x4e000000#define NFCONF __REGi(NF_BASE + 0x0)#define NFCMD __REGb(NF_BASE + 0x4)#define NFADDR __REGb(NF_BASE + 0x8)#define NFDATA __REGb(NF_BASE + 0xc)#define NFSTAT __REGb(NF_BASE + 0x10)#define BUSY 1inline void wait_idle(void){int i;while(!(NFSTAT & BUSY))for(i=0; i<10; i++);}#define NAND_SECTOR_SIZE 512#define NAND_BLOCK_MASK (NAND_SECTOR_SIZE - 1)/* low level nand read function */intnand_read_ll(unsigned char*buf,unsigned long start_addr,int size) {int i, j;if((start_addr & NAND_BLOCK_MASK)||(size & NAND_BLOCK_MASK)) {return-1;/* invalid alignment */}/* chip Enable */NFCONF &=~0x800;for(i=0; i<10; i++);for(i=start_addr; i <(start_addr + size);){/* READ0 */NFCMD = 0;/* Write Address */NFADDR = i & 0xff;NFADDR =(i >> 9)& 0xff;NFADDR =(i >> 17)& 0xff;NFADDR =(i >> 25)& 0xff;wait_idle();for(j=0; j < NAND_SECTOR_SIZE; j++, i++){*buf =(NFDATA & 0xff);buf++;}}/* chip Disable */NFCONF |= 0x800;/* chip disable */return 0;}2.修改cpu/arm920t/start.S文件(copy from vivi)(在vivi源码下的arch\s3c2410\Head.s。

u(boot中NANDflash的MTD驱动移植)-

u(boot中NANDflash的MTD驱动移植)-

u(boot中NANDflash的MTD驱动移植)-u-bootu-boot中的“与非”闪存的MTD驱动程序迁移移植了linux中的MTD 驱动程序源代码,以支持“与非”闪存擦除、刻录写入和读取驱动程序内存技术设备内存技术设备是Linux的一个子系统,用于访问闪存设备MTD的主要目的是简化新存储设备的驱动,并提供通用接口功能。

MTD驱动可以支持CFI接口的非闪存驱动和非闪存驱动。

众所周知,“与非”闪存的访问接口不像“非”闪存那样提供标准的CFI访问接口,但“与非”闪存制造商已经对不同品牌和型号的“与非”闪存芯片的访问接口制定了一些常规规定,如命令字、地址序列、命令序列、坏块标记位置、oob区域格式等。

值得注意的是,在工艺方面有两种类型的“与非”闪存:MLC和SLCMLC和SLC属于两种不同类型的NAND闪存SLC的全称是单级单元,即单级单元闪存,而MLC的全称是多级单元,即多级单元闪存。

它们的区别在于,SLC的每个单元只能存储一位数据,而MLC 的每个单元只能存储两位数据,MLC的数据密度是SLC的两倍。

就页容量而言,还有两种类型的与非:大页与非闪存(例如HY27UF082G2B)和小页与非闪存(例如K9F1G08U0A)这两种类型在页面容量、命令序列、地址序列、页面内访问和坏块识别方面非常不同,并且遵循不同的约定,因此在移植驱动程序时应该特别注意。

在下,以大页面NAND flash: HY27UF082G2B为例,介绍NAND flash 的一些基本情况,然后介绍MTD驱动程序的基本结构和流程分析。

最后,介绍了在u-boot中迁移MTD驱动程序的详细步骤:3 .4 . 1)nandflash的一些基本信息fl2400开发板上的NAND Flash芯片型号是现代HY27UF082G2B。

英特尔于1988年首次开发了或非闪存技术。

它最重要的特点是支持片上执行,彻底改变了EPROM和EEPROM主宰非易失性闪存世界的局面。

Nandflashuboot命令详解

Nandflashuboot命令详解

Nandflashuboot命令详解(转⾃)nand info & nand device显⽰flash的信息:DM365 :>nand infoDevice 0: NAND 32MiB 3,3V 8-bit, sector size 16 KiBDM365 :>nand deviceDevice 0: NAND 32MiB 3,3V 8-bitnand read(.oob) addr off size不管是读取data, 使⽤nand read,还是读取oob,使⽤命令nand read.oob,后⾯跟的地址addr,都是ram的地址,off指的是nand flash的地址, size:指要读取nand flash的数据⼤⼩,但是如果是读取oob, size不能超过⼀个page 的oob size,如果page size为512个字节, oob size就是16个字节.DM365 :>nand read 86000000 58000 100NAND read: device 0 offset 0x58000, size 0x100256 bytes read: OKDM365 :>md 86000000 4086000000: ea000012 e59ff014 e59ff014 e59ff014 ................…………860000f0: e1a0000d eb00022e 00000000 00000000 ................DM365 :>nand read.oob 86000000 58000 10NAND read: device 0 offset 0x58000, size 0x1016 bytes read: OKDM365 :>md 86000000 4086000000: ffffffff 2707ffff 33e316ad 44b2e1a1 .......'...3...D如果⼀次想读取完整的⼀个page 的值,包含oob,使⽤下⾯将的命令, nand dump.nand dump [addr] [size]调⽤过程: nand dump addr size (common/cmd_nand.c)==> nand_dump() ==> nand_read_raw();nand dump 不管你的size有多⼤,⾄少会dump出⼀个page的⼤⼩:SMDK2440 # nand dump 0 100Page 00000000 dump:12 00 00 ea 14 f0 9f e5 14 f0 9f e5 14 f0 9f e514 f0 9f e5 14 f0 9f e5 14 f0 9f e5 14 f0 9f e500 02 f8 33 60 02 f8 33 c0 02 f8 33 20 03 f8 3380 03 f8 33 e0 03 f8 33 40 04 f8 33 ef be ad de00 00 f8 33 00 00 f8 33 58 19 fa 33 34 6d fa 3300 00 0f e1 1f 00 c0 e3 d3 00 80 e3 00 f0 29 e1…………04 30 8c e5 fc 4d 00 eb 00 01 9f e5 f0 3c 00 eb02 0d 00 eb 41 42 00 eb f4 00 9f e5 00 40 98 e504 02 00 eb 0d 10 a0 e1 04 00 84 e5 40 20 a0 e3OOB:ff ff ff ff ff ff ff ffff ff ff ff ff ff ff ffff ff ff ff ff ff ff ffff ff ff ff ff ff ff ffff ff ff ff ff ff ff ff69 a6 ab 3c 33 cf 66 5aa7 cf f0 33 a6 96 97 3f0c c3 30 30 c3 cc 33 f3nand write - addr off size这个命令和nand read⼀样,只是⽅向是反的,是把ram的值写到 nand flash中,但是这个写只能将1改为0,不能将0写成1. 这个command 会⾃动skipping bad blocks。

Nand Flash启动模式下的Uboot移植

Nand Flash启动模式下的Uboot移植

Nand Flash启动模式下的Uboot移植
杨素秋
【期刊名称】《软件导刊》
【年(卷),期】2013(020)003
【摘要】BootLoader移植是嵌入式系统开发中的一个重要环节,而Uboot是一个支持多处理器多操作系统较为通用的BootLoader。

分析了Uboot的启动过程,并对s3c2440A如何支持驱动代码在外部NandFlash上的执行,以及在
s3c2440A平台上如何实现NandFlash启动模式下的Uboot移植进行了介绍。

具体操作和编译在fedora9操作系统和arm—linux-gcc4.3.2交叉编译下完成。

【总页数】4页(P33-36)
【作者】杨素秋
【作者单位】常州信息职业技术学院,江苏常州213164
【正文语种】中文
【中图分类】TP301
【相关文献】
1.基于S3C2440 Nand Flash启动模式的研究 [J], 韩金利
2.季底效应6月下旬主流NAND Flash颗粒合约价小跌约1%~6% [J],
3.VxWorks环境下NandFlash驱动程序设计研究 [J], 冉鹏;陈向
4.Nand Flash启动模式下的Uboot移植 [J], 杨素秋
5.嵌入式Linux下NAND flash上根文件系统的构建 [J], 程建
因版权原因,仅展示原文概要,查看原文内容请购买。

UBOOT中关于NAND FLASH的支持

UBOOT中关于NAND FLASH的支持

UBOOT中关于NAND FLASH的支持十分完善,从命令上可以看出来,关于NAND FLASH的操作专门有个子系统。

在驱动层面,UBOOT使用了MTD驱动规范,这个规范中对NAND FLASH的各种操作实现都很规范,一般来说只改写些少量的代码(相对于MTD 设备驱动来说)就可以支持起来。

大大减轻了移植难度。

一般来说,各个主控芯片的NAND FALSH驱动都存放于drivers/mtd/nand/目录之下,就S3C24X0系列芯片来说,UBOOT现在只提供了S3C2410的NAND FLASH驱动,而S3C2410和S3C2440在NAND FALSH模块方面是有一定的差别的,所以需要修改。

既然这里没有现成的驱动程序,就来实现一个吧,由于S3C2410和S3C2440同属于一个系列的芯片,所以就以S3C2410的驱动为模板写S3C2440的NAND FLASH驱动。

S3C2410的驱动(drivers/mtd/nand/s3c2410_nand.c)001#include <common.h>002003#include <nand.h>004#include <asm/arch/s3c24x0_cpu.h>005#include <asm/io.h>006007#define S3C2410_NFCONF_EN (1<<15)008#define S3C2410_NFCONF_512BYTE (1<<14)009#define S3C2410_NFCONF_4STEP (1<<13)010#define S3C2410_NFCONF_INITECC (1<<12)011#define S3C2410_NFCONF_nFCE (1<<11)012#define S3C2410_NFCONF_TACLS(x) ((x)<<8)013#define S3C2410_NFCONF_TWRPH0(x) ((x)<<4)014#define S3C2410_NFCONF_TWRPH1(x) ((x)<<0)015016#define S3C2410_ADDR_NALE 4017#define S3C2410_ADDR_NCLE 8018019#ifdef CONFIG_NAND_SPL020021/* in the early stage of NAND flash booting, printf() is not available */022#define printf(fmt, args...)023024static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)025{026int i;027struct nand_chip *this = mtd->priv;028029for (i = 0; i < len; i++)030buf[i] = readb(this->IO_ADDR_R);031}032#endif033034static void s3c2410_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)035{036struct nand_chip *chip = mtd->priv;037struct s3c2410_nand *nand = s3c2410_get_base_nand();038039debugX(1, "hwcontrol(): 0x%02x 0x%02x\n", cmd, ctrl);040041if (ctrl & NAND_CTRL_CHANGE)042{043ulong IO_ADDR_W = (ulong)nand;044045if (!(ctrl & NAND_CLE))046IO_ADDR_W |= S3C2410_ADDR_NCLE;047if (!(ctrl & NAND_ALE))048IO_ADDR_W |= S3C2410_ADDR_NALE;049050chip->IO_ADDR_W = (void *)IO_ADDR_W;051052if (ctrl & NAND_NCE)053writel(readl(&nand->NFCONF) & ~S3C2410_NFCONF_nFCE,054&nand->NFCONF);055else056writel(readl(&nand->NFCONF) | S3C2410_NFCONF_nFCE,057&nand->NFCONF);058}059060if (cmd != NAND_CMD_NONE)061writeb(cmd, chip->IO_ADDR_W);062}063064static int s3c2410_dev_ready(struct mtd_info *mtd)065{066struct s3c2410_nand *nand = s3c2410_get_base_nand();067debugX(1, "dev_ready\n");068return readl(&nand->NFSTAT) & 0x01;069}070071#ifdef CONFIG_S3C2410_NAND_HWECC072void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode)073{074struct s3c2410_nand *nand = s3c2410_get_base_nand();075debugX(1, "s3c2410_nand_enable_hwecc(%p, %d)\n", mtd, mode);076writel(readl(&nand->NFCONF) | S3C2410_NFCONF_INITECC, &nand->NFCONF);077}078079static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, 080u_char *ecc_code)081{082struct s3c2410_nand *nand = s3c2410_get_base_nand();083ecc_code[0] = readb(&nand->NFECC);084ecc_code[1] = readb(&nand->NFECC + 1);085ecc_code[2] = readb(&nand->NFECC + 2);086debugX(1, "s3c2410_nand_calculate_hwecc(%p,): 0x%02x 0x%02x 0x%02x\n", 087mtd , ecc_code[0], ecc_code[1], ecc_code[2]);088089return0;090}091092static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, 093u_char *read_ecc, u_char *calc_ecc)094{095if (read_ecc[0] == calc_ecc[0] &&096read_ecc[1] == calc_ecc[1] &&097read_ecc[2] == calc_ecc[2])098return0;099100printf("s3c2410_nand_correct_data: not implemented\n");101return -1;102}103#endif105int board_nand_init(struct nand_chip *nand)106{107u_int32_t cfg;108u_int8_t tacls, twrph0, twrph1;109struct s3c24x0_clock_power *clk_power = s3c24x0_get_base_clock_power();110struct s3c2410_nand *nand_reg = s3c2410_get_base_nand();111112debugX(1, "board_nand_init()\n");113114writel(readl(&clk_power->CLKCON) | (1 << 4), &clk_power->CLKCON);115116/* initialize hardware */117twrph0 = 3;118twrph1 = 0;119tacls = 0;120121cfg = S3C2410_NFCONF_EN;122cfg |= S3C2410_NFCONF_TACLS(tacls - 1);123cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);124cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);125writel(cfg, &nand_reg->NFCONF);126127/* initialize nand_chip data structure */128nand->IO_ADDR_R = nand->IO_ADDR_W = (void *)&nand_reg->NFDATA;129130nand->select_chip = NULL;131132/* read_buf and write_buf are default */133/* read_byte and write_byte are default */134#ifdef CONFIG_NAND_SPL135nand->read_buf = nand_read_buf;136#endif137138/* hwcontrol always must be implemented */139nand->cmd_ctrl = s3c2410_hwcontrol;140141nand->dev_ready = s3c2410_dev_ready;142143#ifdef CONFIG_S3C2410_NAND_HWECC144nand->ecc.hwctl = s3c2410_nand_enable_hwecc;145nand->ecc.calculate = s3c2410_nand_calculate_ecc;146nand->ecc.correct = s3c2410_nand_correct_data;147nand->ecc.mode = NAND_ECC_HW;148nand->ecc.size = CONFIG_SYS_NAND_ECCSIZE;149nand->ecc.bytes = CONFIG_SYS_NAND_ECCBYTES;150#else151nand->ecc.mode = NAND_ECC_SOFT;152#endif153154#ifdef CONFIG_S3C2410_NAND_BBT155nand->options = NAND_USE_FLASH_BBT;156#else157nand->options = 0;158#endif159160debugX(1, "end of nand_init\n");162return0;163 }可以看到在这个驱动中,为了让UBOOT在S3C2410下支持NAND FLASH实现了如下函数。

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

u_boot移植(五)之分析uboot源码中nand flash操作一、OneNand 和Nand Flash我们已经能从Nand Flash启动了,启动之后,大家会看到如下效果:可以看出,我们的uboot默认使用的是OneNand。

需要注意的是我们的FSC100上面是没有OneNand的,有的是K9F2G08U0B型号的NAND FLASH。

前面我们了解过Nor Flash 和Nand Flash,那OneNand Flash又是什么呢?二、uboot 源码中Nand Flash部分代码分析我们从Nand Flash初始化看起,打开lib_arm/board.c文件,为了紧抓主线,以下代码只列举出了主线代码。

可以看出,我们可以通过CONFIG_CMD_NAND和CONFIG_CMD_ONENAND两个宏来选择NAND FLASH初始化还是 ONENAND FLASH初始化。

uboot 中默认定义了宏CONFIG_CMD_ONENAND,所以选择的是ONENAND FLASH初始化。

我们的FSC100上面使用的是NAND FLASH,所以我们要定义CONFIG_CMD_NAND宏,取消CONFIG_CMD_ONENAND宏的定义。

嗯!先做个记录:修改include/configs/fsc100.h,定义宏CONFIG_CMD_NAND,取消宏CONFIG_CMD_ONENAND。

好了,接下我们看看nand_init()函数时如何实现的。

看以看出,这段代码调用根据CONFIG_SYS_MAX_NAND_DEVICE宏[默认没有定义]的值来决定系统中Nand Flash设备的个数。

接着调用nand_init_chip()函数完成Nand Flash初始化,然后计算出每块Nand Flash的大小。

最终会输出Nand Flash总的容量。

嗯!做个记录:修改include/configs/fsc100.h,定义宏CONFIG_SYS_MAX_NAND_DEVICE,值为1没有看明白的地方是给nand_init_chip()函数传递的参数,接下来我们来看看他们是如何定义的。

哈哈,终于到了让人头疼的地方了。

先来看看struct nand_chip和struct mtd_info两个结构体,这两个结构体的成员有很多很多,很多初学者一看到就晕头转向了,为了让大家不被吓到,我对这两个结构体的成员做了精简,只保留我们关心的成员,其它的都去掉了。

(1)struct nand_chip每个成员的注释如下:其中[BOARDSPECIFIC]标识的是需要我们自己填充的,它们跟我们开发板相关。

其中select_chip会被uboot源码默认赋值,但默认值一般都不能达到我们要求,所以需要我们自己填充。

总结如下:@IO_ADDR_R/W 用来记录SOC上面读取/写入数据的NFDATA寄存器地址@select_chip 用来记录决定是否选中Nand Flash芯片的函数@cmd_ctrl 用来记录发向Nand Flash发送命令或地址的函数 @dev_ready 用来记录探测Nand Flash是否就绪的函数@options 用来记录Nand Flash的位宽(2)struct mtd_info每个成员解释如下:@type 类型 : NOR Flash 、Nand Flash等@size 存储设备大小@name 设备名@erase 对设备进行擦除操作@read 对设备进行读操作@write 对设备进行写操作@priv 私有指针哥们,这两个结构体到底是用来干嘛的呀? 要想回到这个问题,还得从Linux 内核的MTD框架说起。

MTD,Memory Technology Device即内存技术设备,在Linux内核中,引入MTD层为NOR FLASH和NAND FLASH设备提供统一接口。

uboot中的MTD架构就是拷贝了内核的代码。

在MTD的架构中,在MTD层用一个数组 struct mtd_info*mtd_table[MAX_MTD_DEVICES]保存系统中所有的MTD设备。

每个MTD 设备利用 struct mtd_info 这个结构来描述,该结构中描述了存储设备的基本信息和具体操作所需要的内核函数。

用数据结构struct nand_chip来表示一个NAND FLASH芯片, 该结构体包含了关于Nand Flash的地址信息,读写方法,ECC模式,硬件控制等一系列底层机制。

总的来说,在MTD架构中,用struct mtd_info 描述一个Flash 型设备,这个结构体中提供了通用的操作Flash 型设备的函数接口。

例如:读、写、擦除等。

用struct nand_chip描述一个NAND FLASH芯片,这个结构体中提供具体对NAND FLASH设备的操作函数。

这样做的一个好处就是,就是规范了函数调用接口。

上层工程师调用通用的函数接口来操作Flash型设备,底层工程师提供具体函数接口来操作Flash型设备。

关于上、下两层如何关联,就由MTD层来做。

这种分层的思想,在Linux 内核中体现了淋漓尽致,大家一定要掌握这种思想。

好了,不管有没有理解,都不影响我们移植uboot,我们只需要关系我们需要修改的部分,其他的部分MTD框架都给我们做好了。

接着看代码吧!这段代码主要干了三件事情:(1)调用board_nand_ini()函数完成SOC的NAND Flash 控制器初始化,并且填充struct nand_chip结构体相应字段。

也可以理解为完成实际的开发板上的NAND Flash设备初始化。

(2)调用nand_scan()函数,扫描NAND Flash设备,并且填充structmtd_info结构体。

也可以理解为完成MTD设备初始化。

(3)调用add_mtd_device()函数,向系统添加mtd设备。

接下来,我们先来看看第一件事,初始化开发板上的NAND Flash设备。

显然,这个board_nand_ini()函数会随着开发板的不同实现就不同。

在uboot的源码中,并没有S5CPC100对应的NAND Flash设备初始化函数,但是有s3c2410芯片对应的NAND Flash设备初始化函数。

这两块SOC都是三星的产品,大同小异,我们先来分析一下s3c2410芯片对应的board_nand_init()函数的实现,然后编写S5PC100的board_nand_init()函数。

嗯!记录一下:需要在drivers/mtd/nand目录下添加s5pc100_nand.c文件,完成FSC100的NAND Flash设备初始化。

s3c2410_nand.c看以看到board_nand_init()函数,主要完成了一下几件事情:(1)初始化Nand Flash控制器,让其发出Nand Flash要求的时序。

(2)填充了struct nand_chip结构体的select_chip 成员值为NULL 、cmd_ctrl成员值为s3c2410_hwcontrol、dev_ready成员值为s3c2410_dev_ready。

还记得这些成员值都是用来干嘛的吗?回顾一下....(3)根据CONFIG_S3C2410_NAND_HWECC宏,决定Nand Flash设备使用硬件 ECC还是软件ECC。

关于s3c2410_hwcontrol、s3c2410_dev_ready函数时如何实现的,先不急,后面用到的时候再看。

接着来看看nand_scan函数时如何实现的。

这段代码很短,上面的注释是理解nand_scan函数主要功能最好资料,大家一定要读懂。

先来看看nand_scan_ident()函数,字面意思就是扫描Nand Flash设备的ID。

这段代码主要完成了,一下几件事情:(1)调用nand_set_defaults()函数,初始化了struct nand_chip 没有在board_nand_init()函数中初始化的成员例如:(2)调用nand_get_flash_type()函数,读取了当前开发板中NAND FLASH 设备ID。

我们来看看是如何实现的。

嗯,这段代码我精简了一下,如果全部贴出来,肯定会有很多人会说,"哥,我不干了,太吓人了"。

废话少说,还是看看这段代码主要干了什么。

从上面代码可以看出,这段代码中主要干了一下几件事情:<1>发送读取ID命令给当前开发板上的Nand Flash设备,然后读取设备ID<2>根据读取的NAND Flash设备ID ,在nand_flash_id数组中查询是否支持当前NAND FLASHnand_flash_id 在driver/mtd/nand/nand_ids.c文件中有定义,以下这个文件中的部分内容。

需要注意的是,如果这个数组中没有当前开发板NAND Flash的型号,大家只需要按照格式添加自己开发上的Nand Flash 设备到这个数组即可。

不知道到这个地方大家有没有晕掉,其实我一直跟学员将,有人带着分析代码是幸福的。

好了,接下来我们需要思考一下。

我们知道要想对Nand Flash操作,必须先向Nand Flash 发送命令、发送地址操作。

很明显这些操作是和具体SOC相关的。

在前面的代码中分析中,我们看到在向Nand Flash发送命令是通过chip->cmdfunc()进行的。

接下来,我们就由chip->cmdfunc()为出发点,看看在uboot中如何向Nand Flash设备发送命令、发送地址的。

三、分析如何Nand Flash设备发送命令、地址先来回顾一下chip->cmdfunc()函数,是什么时候赋值的。

可以看到,chip->cmdfunc赋值为nand_command函数。

我们来看看这个函数是如何实现的。

可以看出,最终是调用chip->cmd_ctrl实现的。

回顾一下chip->cmd_ctrl是什么时候赋值的。

嗯,在board_nand_init函数中,nand->cmd_ctrl =s3c2410_hwcontrol。

我们来看看s3c240_hwcontrol是如何实现的。

可以看出,如果s3c2410_hwcontrol 参数ctrl 中包含NAND_CLE则是向NFCMD寄存器中写入命令,如果ctrl中包含NAND_ALE则是向NFADDR 寄存器中写入地址。

嗯,做个记录:需要在drivers/mtd/nand目录下的5pc100_nand.c文件中实现s5pc100_hwcontrol函数中实现根据ctrl值是否含有NAND_CLE、NAND_ALE来决定是向NFCMD寄存器写值还是想NFADDR寄存器写值。

相关文档
最新文档