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实现了如下函数。

移植从Nand_flash启动的uboot移植

移植从Nand_flash启动的uboot移植

最详细的了,如果你正在搞这个,可以参考一下的。

加入Nand flash驱动的支持,可以在uboot命令行下操作Nand flash.但还未能从Nand flash启动,只能在Nor flash 内运行.支持从Nandflash启动会在下节介绍。

下面描述详细步骤:1. 打开Nandflash驱动支持要使uboot支持nand驱动,需要在smdk2410.h中将CF G_CMD_NAND部分注释取消,打开nandflash功能。

修改如下:2. 添加nand_init()函数Uboot对SMDK2410板的NAND Flash初始化部分没有写即在lib_arm/board.c中的start_armboot函数中有这么一句:因为前面打开了CFG_CMD_NAND这个宏,uboot编译时会去查询nand_init()这个函数。

而在board/smdk2410目录下任何源文件中都没有定义nand_init这个函数。

所以需要我们补充这个函数以及这个函数涉及的底层操作。

这里我们可以参考VCMA9板的nand_init函数,VCMA9板是一款用S3C2410的板子,因此这部分操作和SMDK2410 Demo Board很相似。

大部分代码可以照搬。

分析VCM9板的nand_init()函数,发现需要拷贝如下内容:首先将board/mpl/vcma9/vcma9.h中下面代码拷贝到common/cmd_nand.c中do_nand函数前面。

NFCE_HIGH} NFCE_STATE;static inline void NF_Conf(u16 conf){S3C2410_NAND *const nand = S3C2410_GetBase_NAND(); nand->NFCONF = conf;}static inline void NF_Cmd(u8 cmd){S3C2410_NAND *const nand = S3C2410_GetBase_NAND();nand->NFCMD = cmd;}static inline void NF_CmdW(u8 cmd){NF_Cmd(cmd);udelay(1);}static inline void NF_Addr(u8 addr){S3C2410_NAND *const nand = S3C2410_GetBase_NAND();nand->NFADDR = addr;}static inline void NF_SetCE(NFCE_STATE s){S3C2410_NAND *const nand = S3C2410_GetBase_NAND();switch(s){case NFCE_LOW:nand->NFCONF &=~(1<<11);break;case NFCE_HIGH:nand->NFCONF |=(1<<11);break;}}static inline void NF_WaitRB(void){S3C2410_NAND *const nand = S3C2410_GetBase_NAND();while(!(nand->NFSTAT &(1<<0)));}static inline void NF_Write(u8 data){S3C2410_NAND *const nand = S3C2410_GetBase_NAND(); nand->NFDATA = data;}static inline u8 NF_Read(void){S3C2410_NAND *const nand = S3C2410_GetBase_NAND();return(nand->NFDATA);}static inline void NF_Init_ECC(void){S3C2410_NAND *const nand = S3C2410_GetBase_NAND();nand->NFCONF |=(1<<12);}static inline u32 NF_Read_ECC(void){S3C2410_NAND *const nand = S3C2410_GetBase_NAND();return(nand->NFECC);}#endif/* dongas - support nand driver - end */再接着将board/mpl/vcma9/vcma9.c中下面代码拷贝到common/cmd_nand.c中来。

最新Uboot移植步骤 6:NandFlash读操作

最新Uboot移植步骤 6:NandFlash读操作

最新Uboot移植步骤6:NandFlash读操作前面在调试时把NAND相关的宏CONFIG_CMD_NAND注释掉了一些,现在去重新打开:编译,看看会出什么错误:先看第一个error:72行显示一个不完整的类型看这个nand的定义:发现nand是2410_nand的结构体,但我们未定义CONFIG_S3C2410这个宏,我们现在要使用2440,正好下面有,我们直接使用2440的结构体好了:这个文件都是给2410用的,我们直接拷贝一份,改名为2440,SI文件添加一下:修改宏定义:修改Makefile:先看看nand是如何操作的,着nand相关函数:看nand_init函数:可见这里就可以识别nand并输出nand的大小了,先看运行哪个函数:可见CONFIG_SYS_NAND_SELF_INIT宏未定义,所以运行nand_init_chip函数:看单板初始化函数:把2410修改为2440:发现s3c2440_get_base_nand函数未定义,我们先搜下2410的定义,改为2440的定义:修改发现S3C2440_NAND_BASE未定义,那我们找到2410定义的地方自己仿照定义2440下:修改初始化时序:看这个函数,我们把2410都改成2440:修改这里:看到nfcont,我们还要设置下这个:现在board_nand_init函数修改完了,开始修改nand_scan函数:观察之后,发现要修改如下(具体为何这样修改,参考NAND驱动开发视频,现在我还没看,无法详述):编译,下载:有错误:搜索nand_wait_ready,发现再我们自己的init.c函数里也定义了,把我们的改一下:再编译下载:发现NAND已经可以识别!把uboot从nor拷贝到nand:验证:数据都相同,现在重启开发板从nand启动:这时从NAND启动,Nor无法识别,所以显示0KB!成功!。

最新Uboot移植步骤 5:NorFlash

最新Uboot移植步骤 5:NorFlash

最新Uboot移植步骤5:NorFlash显示Flash:***failed***,说明norflash未识别,我们搜索“Flash:”进入第一个查看找到这个判断条件,如果flash_size>0则输出flash大小,否则输出*** failed ***### ERROR ### Please RESET the board ###其中hang函数导致程序无法继续向下执行,我们只实现了nand启动肯定在这会卡住,所以我们不用这个hang 函数,直接输出flash未识别的信息就好了,改动如下:现在来找norflash未识别的原因,进入flash_init函数看见这样一段代码可知,有2个函数可以检测flash的大小如果flash_detect_legacy函数不行再使用flash_get_size函数,先进入flash_detect_legacy函数看下,其结构如下:该函数有2个,使用哪一个由宏CONFIG_FLASH_CFI_LEGACY决定,搜索该宏:很明显,前面我们都是使用该函数进行大小检测的,而该函数无法识别flash,那我们使用新方法进行检测,进入新方法查看:发现有很多可用调试信息,我们看看如何起用这些调试信息:发现只要定义了_DEBUG即可启用调试信息,我们定义该宏:在文件开始发现注释:我们直接定义DEBUG即可,配置,编译,下载到板子norflash:重新上电从norflash启动,输出如下:我们查看JEDEC PROBE:从哪来查看norflash手册,看读取的设备ID是否正确可以看到输出的厂家设备ID是正确的,说明下面这个函数读取正确那就是函数出现错误,我们进入该函数查看:通过注释就可看到该函数是把我们读取的ID与这里的jedec_table[]数组里的ID进行比较,看这个数组:该数组里存有各种型号的设备参数,而我们的不在其中,我们自己添加:发现这里有个和我们类似的,拷贝代码用下:添加后修改成我们自己的:***************************************************************************************** 现在先说一下,这些参数都如何得到的并修改:.mfr_id = 0x001c001c,厂家ID,.dev_id = 0x2249,设备ID.name = "EON EN29LV160A",名字自己取.uaddr = {[1] = MTD_UADDR_0x0555_0x02AA /* x16 */},解锁地址,如图:NorFlash读ID时,前3周期是写数据,第4周期是读数据,先在555地址写AA,再在2AA地址写55,再在555地址写90,这时解锁成功,就可以从地址100读取厂家ID是1C,设备ID也是如此。

u_boo移植(六)之支持Nand Flash操作

u_boo移植(六)之支持Nand Flash操作

u_boo移植(六)之支持Nand Flash操作通过上一节uboot中Nand Flash操作的部分源码分析我们可以知道,如果想在uboot中驱动我们自己开发板上的Nand Flash设备,只需要完成以下几件事情就可以了。

1.修改include/configs/fsc100.h,定义宏CONFIG_CMD_NAND,取消宏CONFIG_CMD_ONENAND2.修改include/configs/fsc100.h,定义宏CONFIG_SYS_MAX_NAND_DEVICE,值为13.需要在drivers/mtd/nand目录下添加s5pc100_nand.c文件,完成FSC100的NAND Flash设备初始化(1)初始化Nand Flash硬件接线到SOC上的GPIO口(2)初始化SOC的Nand Flash控制器(3)实现s5pc100_hwcontrol函数中实现根据ctrl值是否含有NAND_CLE、NAND_ALE来决定是向NFCMD寄存器写值还是向NFADDR寄存器写值(4)实现s5pc00_select_chip函数,完成对Nand Flash设备片选和取消片选(5)实现s5pc100_dev_ready函数,完成对Nand Flash设备进行是否忙碌探测关于如何对Nand Flash进行读、写、擦除、坏块管理等操作,uboot源码中的MTD框架已经帮我们做好了。

好了,我们已经知道需要干什么了,接下来就开始干活吧!一、修改include/configs/fsc100.h文件,添加和取消相应的宏在fsc100.h文件里面,添加以下代码:二、drivers/mtd/nand目录下添加s5pc100_nand.c文件大家可以将此目录下的s3c2410_nand.c拷贝成s5pc100_nand.c,然后进行修改。

下面给出给我修改的代码:需要注意的是,不同的开发板配置不一样,流程都一样,说白了就是操作的寄存器不一样,大家可以根据自己开发板实际情况自行修改。

UBOOT从NAND FLASH启动分析

UBOOT从NAND FLASH启动分析

UBOOT从NAND FLASH启动分析UBOOT从NAND FLASH启动分析在分析启动代码之前先看一下S3C2440的NAND启动:在配置NAND启动模式之后,S3C2440上电会先将NAND中的0x0 - 0x1000共4096字节的数据拷贝到位于Bank0中的Boot Internal SRAM上Bank0如下图:可以看出Boot Internal SRAM为4KB大小,也正是因为Boot Internal SRAM只有4KB 大小,所以只能从NAND中拷贝4K的内容= 3= 这个Boot Internal SRAM是配置为NAND FLASH启动模式才有的这4K内容是什么呢?~ 这就要看Uboot的镜像文件中是如何进行连接的了~连接脚本在board/smdk2440/u-boot.lds中,如下SECTIONS{. = 0x00000000;. = ALIGN(4);.text :{cpu/arm920t/start.o (.text)cpu/arm920t/s3c24x0/nand_read.o (.text)*(.text)}. = ALIGN(4);.rodata : { *(.rodata) }. = ALIGN(4);.data : { *(.data) }. = ALIGN(4);.got : { *(.got) }. = .;__u_boot_cmd_start = .;.u_boot_cmd : { *(.u_boot_cmd) }__u_boot_cmd_end = .;. = ALIGN(4);__bss_start = .;.bss : { *(.bss) }_end = .;}.text为代码段,可以看出cpu/arm920t/start.o在代码段的最前面,所以会先执行start.o 中的代码连接完成后的镜像文件的前4K如下cpu/arm920t/start.o(.text).text 0x33f80000 0x4e0 cpu/arm920t/start.o0x33f80050 IRQ_STACK_START0x33f80048 _bss_start0x33f8004c _bss_end0x33f80044 _armboot_start0x33f80000 _start0x33f80054 FIQ_STACK_STARTcpu/arm920t/s3c24x0/nand_read.o(.text).text 0x33f804e0 0x1b8 cpu/arm920t/s3c24x0/nand_read.o0x33f804e0 nand_read_ll*(.text).text 0x33f80698 0x64 board/smdk2440/libsmdk2440.a(lowlevel_init.o)0x33f8069c lowlevel_init.text 0x33f806fc 0x280 cpu/arm920t/libarm920t.a(interrupts.o)0x33f80934 do_fiq0x33f80880 do_undefined_instruction0x33f80744 show_regs0x33f80958 do_irq0x33f80728 bad_mode0x33f808c8 do_prefetch_abort0x33f8070c disable_interrupts0x33f80910 do_not_used0x33f808ec do_data_abort0x33f808a4 do_software_interrupt0x33f806fc enable_interrupts.text 0x33f8097c 0x250 cpu/arm920t/s3c24x0/libs3c24x0.a(interrupts.o)0x33f80aa4 set_timer0x33f80a20 reset_timer0x33f8097c interrupt_init0x33f80ba0 get_tbclk0x33f80a90 get_timer0x33f809f0 reset_timer_masked0x33f80a24 get_timer_masked0x33f80ab4 udelay0x33f80b10 udelay_masked0x33f80bac reset_cpu0x33f80b8c get_ticks.text 0x33f80bcc 0x150 cpu/arm920t/s3c24x0/libs3c24x0.a(speed.o)0x33f80c4c get_HCLK0x33f80cec get_PCLK0x33f80c44 get_FCLK0x33f80d14 get_UCLK.text 0x33f80d1c 0x1e8 cpu/arm920t/s3c24x0/libs3c24x0.a(cmd_s3c24xx.o) 0x33f80d8c do_s3c24xx.text 0x33f80f04 0xdc cpu/arm920t/s3c24x0/libs3c24x0.a(serial.o)0x33f80f04 serial_setbrg0x33f80fa8 serial_tstc0x33f80f80 serial_putc0x33f80f58 serial_init0x33f80fb8 serial_puts0x33f80f68 serial_getc.text 0x33f80fe0 0x140 lib_arm/libarm.a(_divsi3.o)0x33f80fe0 __divsi3如何设置从0x33f80000开始呢?~这是链接的时候指定的在根目录下面的config.mk中有下面一句LDFLAGS += -Bstatic -T $(LDSCRIPT) -Ttext $(TEXT_BASE) $(PLATFORM_LDFLAGS)关键就是其中的-Ttext $(TEXT_BASE),这句指明了代码段的起始地址而TEXT_BASE在board/smdk2440/config.mk中定义TEXT_BASE = 0x33F8 0000 为什么是0x33F8 0000呢?~这是将NAND中Uboot拷贝到RAM中的起始地址,所以在代码拷贝到RAM之前不能使用绝对地址来寻址数据,只能用相对地址在以下将用虚拟地址来指Uboot在RAM中的地址,也就是0x33F8 0000现在来看代码cpu/arm920t/start.S_start: ;异常处理向量表b start_codeldr pc, _undefined_instruction ;未定义指令异常:0x00000004ldr pc, _software_interrupt ;软中断异常:0x00000008ldr pc, _prefetch_abort ;预取异常:0x0000000Cldr pc, _data_abort ;数据异常:0x00000010ldr pc, _not_used ;未使用:0x00000014ldr pc, _irq ;外部中断请求IRQ:0x00000018ldr pc, _fiq ;快束中断请求FIQ:0x0000001Cb start_code在虚拟地址0x33F8 0000处, 拷贝到Boot Internal SRAM后则位于0x0处,所以b start_code是第一条执行的指令,start_code在cpu/arm920t/start.S中代码如下://读取CPSR寄存器的内容到R0mrs r0,cpsr//清除R0中的0 - 4 这5个位后保存到R0中//也就是清除用户模式位bic r0,r0,#0x1f//置R0的0 1 4 6 7 位为真//也就是选择SVC模式,同时IRQ和FIQ被禁止,处理器处于ARM状态//关闭中断和快速中断orr r0,r0,#0xd3//将R0中的值保存到CPSR上msr cpsr,r0# define pWTCON 0x53000000 ;看门狗控制寄存器WTCON# define INTMSK 0x4A000008 ;中断屏蔽寄存器INTMSK# define INTSUBMSK 0x4A00001C ;辅助中断屏蔽寄存器,由于外设中断源太多,要用此寄存器屏蔽剩余的中断源# define LOCKTIME 0x4c000000 ;PLL锁定时间计数寄存器# define MPLLCON 0x4c000004 ;主时钟锁相环控制寄存器# define UPLLCON 0x4c000008# define CLKDIVN 0x4C000014 ;时钟分频寄存器/* clock divisor register */# define INTSUBMSK_val 0xffff# define MPLLCON_val ((184 12) + (2 4) + 2) /*406M*/# define UPLLCON_val ((60 12) + (4 4) + 2) /* 47M */# define CLKDIVN_val 7 /* FCLK:HCLK:PCLK = 1:3:6 */# define CAMDIVN 0x4C000018//取得看门狗寄存器的地址ldr r0, =pWTCON//将R1寄存器清0mov r1, #0x0//将看门狗寄存器清0,即将看门狗禁止,包括定时器定时,溢出中断及溢出复位等str r1, [r0]/** mask all IRQs by setting all bits in the INTMR - default*///设R1寄存器为0xFFFF FFFFmov r1, #0xffffffff//读取中断屏蔽寄存器的地址ldr r0, =INTMSK//将中断屏蔽寄存器中的位全设1,屏蔽所有中断str r1, [r0]//# define INTSUBMSK_val 0xffff//设R1寄存器为0xFFFFldr r1, =INTSUBMSK_val//读取辅助中断屏蔽寄存器的地址ldr r0, =INTSUBMSK//将辅助中断屏蔽寄中的11个中断信号屏蔽掉,本人觉得INTSUBMS_val应设成7ff str r1, [r0]//# define LOCKTIME 0x4c000000//读取PLL锁频计数器寄存器地址到R0中ldr r0,=LOCKTIME//将R1设为0x00FF FFFFldr r1,=0xffffff//M_LTIME为最大的0xFFF//U_LTIME为最大的0xFFFstr r1,[r0] ;0xfff=4096>1800,远远满足锁定要求/* FCLK:HCLK:PCLK = 1:2:4 *//* default FCLK is 120 MHz ! *///# define CLKDIVN 0x4C000014 /* clock divisor register *///读取时钟分频寄存器的地址ldr r0, =CLKDIVN//# define CLKDIVN_val 7 /* FCLK:HCLK:PCLK = 1:3:6 *///将R1设为0x7mov r1, #CLKDIVN_va//PDIVN - 1: PCLK has the clock same as the HCLK/2.//HDIVN - 11 : HCLK = FCLK/3 when CAMDIVN[8] = 0.// HCLK = FCLK/6 when CAMDIVN[8] = 1.str r1, [r0]/* Make sure we get FCLK:HCLK:PCLK = 1:3:6 *///# define CAMDIVN 0x4C000018//读取摄像头时钟分频寄存器的地址ldr r0, =CAMDIVN//将R1设为0mov r1, #0//将摄像头时钟分频寄存器清0str r1, [r0]/* Clock asynchronous mode *///MRC p15, 0, Rd, c1, c0, 0 ; read control register//读取控制寄存器中的值到R1中mrc p15, 0, r1, c1, c0, 0 ;将协处理器p15的寄存器c1和c0的值传到arm处理器的R1寄存器中//31 iA bit Asynchronous clock select//30 nF bit notFastBus selectorr r1, r1, #0xc0000000 ;将最高两位置1//MCR p15, 0, Rd, c1, c0, 0 ; write control register//将R1中的值写到控制寄存器中mcr p15, 0, r1, c1, c0, 0 将arm的寄存器R1的32位数据传到协处理器p15的两个16位寄存器c1和c0//# define UPLLCON 0x4c000008//读取UPLL设置寄存器的地址到R0中ldr r0,=UPLLCON//# define UPLLCON_val ((60ldr r1,=UPLLCON_val//将R1中的值写入UPLL设置寄存器中str r1,[r0]//ARM920T为5级流水线,需要至少5个周期来让指令生效nopnopnopnopnopnopnopnop//读取MPLL设置寄存器的地址到R0中ldr r0,=MPLLCON//# define MPLLCON_val ((184ldr r1,=MPLLCON_val//将R1中的值写入MPLL设置寄存器中str r1,[r0]#define GPJCON 0x560000D0#define GPJDAT 0x560000D4#define GPJUP 0x560000D8//跳转到cpu_init_crit处执行//并将下一条指令的地址写入LR寄存器中bl cpu_init_critcpu_init_crit在cpu/arm920t/start.S中代码如下:cpu_init_crit:/** flush v4 I/D caches*///将R0寄存器置0mov r0, #0//Invalidate ICache and DCache SBZ MCR p15,0,Rd,c7,c7,0 //禁止指令和数据cachemcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache *///Invalidate TLB(s) SBZ MCR p15,0,Rd,c8,c7,0mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB *//** disable MMU stuff and caches*///MRC p15, 0, Rd, c1, c0, 0 ; read control registermrc p15, 0, r0, c1, c0, 0//清除[8] [9] [13] 这3个位//8 - System protection//9 - ROM protection//13 - Base location of exception registers - 0 = Low addresses = 0x00000000. bic r0, r0, #0x00002300 // clear bits 13, 9:8 (--V- --RS)//清除[0] [1] [2] [7] 这4个位// 0 - MMU enable - 0 = MMU disabled.// 1 - Alignment fault enable - 0 = Fault checking disabled.// 2 - DCache enable - 0 = DCache disabled.// 7 - Endianness - 0 = Little-endian operation.bic r0, r0, #0x00000087 // clear bits 7, 2:0 (B--- -CAM)//设置位[1]为真// 1 - Alignment fault enable - 1 = Fault checking enabled.orr r0, r0, #0x00000002 // set bit 2 (A) Align//设置位[12]为真//12 - ICache enable - 1 = ICache enabled.orr r0, r0, #0x00001000 // set bit 12 (I) I-Cache//MCR p15, 0, Rd, c1, c0, 0 ; write control registermcr p15, 0, r0, c1, c0, 0//将返回地址保存到IP中mov ip, lr//跳转到lowlevel_init中执行bl lowlevel_initcpu_init_crit在cpu/arm920t/start.S中代码如下:.globl lowlevel_init//读取下面标号为SMRDATA处的地址到R0中ldr r0, =SMRDATA//读取上面标号为_TEXT_BASE处的地址内容到R1中//也就是取得TEXT_BASE的值到R1中ldr r1, _TEXT_BASE//计算SMRDATA的相对地址保存到R0中//SMRDATA为虚拟地址,而TEXT_BASE为虚拟地址的起始地址//而现在Uboot的起始地址并不为虚拟地址//TEXT_BASE为0x33F8 0000,SMRDATA为0x33F8 06C8//而现在程序运行在起始地址为0x0000 0000的地方//所以需要计算以0x0000 0000为标准的相对地址sub r0, r0, r1//取得带宽与等待状态控制寄存器地址到R1中ldr r1, =BWSCON /* Bus Width Status Controller *///一共需要设置13个寄存器,每个寄存器4字节add r2, r0, #13*40://读取R0所指的项的值到R3中后R0自加4字节ldr r3, [r0], #4//将R3中的值保存到R1所指的地址中后R1自加4字节str r3, [r1], #4//比较R0和R2是否相等,相等则说明13个寄存器全部设置完毕cmp r2, r0//不等则跳转到上面标号为0处的地址继续执行bne 0b//跳回到返回地址中继续执行mov pc, lr.ltorg/* the literal pools origin */SMRDATA:.word(0+(B1_BWSCON4)+(B2_BWSCON8)+(B3_BWSCON12)+(B4_BWSCON16)+( B5_BWSCON20)+(B6_BWSCON24)+(B7_BWSCON28)).word((B0_Tacs13)+(B0_Tcos11)+(B0_T acc8)+(B0_Tcoh6)+(B0_T ah4)+(B0_T acp2) +(B0_PMC)).word((B1_Tacs13)+(B1_Tcos11)+(B1_T acc8)+(B1_Tcoh6)+(B1_T ah4)+(B1_T acp2) +(B1_PMC)).word((B2_Tacs13)+(B2_Tcos11)+(B2_T acc8)+(B2_Tcoh6)+(B2_T ah4)+(B2_T acp2) +(B2_PMC)).word((B3_Tacs13)+(B3_Tcos11)+(B3_T acc8)+(B3_Tcoh6)+(B3_T ah4)+(B3_T acp2) +(B3_PMC)).word((B4_Tacs13)+(B4_Tcos11)+(B4_T acc8)+(B4_Tcoh6)+(B4_T ah4)+(B4_T acp2) +(B4_PMC)).word((B5_Tacs13)+(B5_Tcos11)+(B5_T acc8)+(B5_Tcoh6)+(B5_T ah4)+(B5_T acp2) +(B5_PMC)).word ((B6_MT15)+(B6_Trcd2)+(B6_SCAN)).word ((B7_MT15)+(B7_Trcd2)+(B7_SCAN)).word ((REFEN23)+(TREFMD22)+(Trp20)+(Trc18)+(Tchr16)+REFCNT).word 0x32.word 0x30.word 0x30执行mov pc, lr后将返回到cpu_init_crit中剩下来还有2条指令//恢复返回地址到LRmov lr, ip//跳转到返回地址mov pc, lr执行完毕之后将返回到start_code中执行接下来的代码代码如下://#define GPJCON 0x560000D0//取得J端口控制寄存器的地址到R0中LDR R0, = GPJCON//将R1设置为0x1 5555LDR R1, = 0x15555//将R1中的值保存到J端口控制寄存器//GPJ0 - 01 - Output//GPJ1 - 01 - Output//GPJ2 - 01 - Output//GPJ3 - 01 - Output//GPJ4 - 01 - OutputSTR R1, [R0]//#define GPJUP 0x560000D8//取得J端口上拉功能寄存器的地址到R0中LDR R0, = GPJUP//将R1设置为0x1FLDR R1, = 0x1f//将R1中的值保存到J端口上拉功能寄存器//禁止GPJ0 - GPJ4的上拉功能STR R1, [R0]//#define GPJDAT 0x560000D4//取得J端口数据寄存器的地址到R0中LDR R0, = GPJDAT//将R1设为0x0LDR R1, = 0x00//将R1中的值保存到J端口数据寄存器//将J端口数据寄存器清0STR R1, [R0]//下面是NAND数据拷贝过程//relocate:copy_myself://#define S3C2440_NAND_BASE 0x4E000000//取得Nand Flash设置寄存器的地址mov r1, #S3C2440_NAND_BASE//将R2设为0xFFF0ldr r2, =0xfff0 // initial value tacls=3,rph0=7,rph1=7 //#define oNFCONF 0x00//读取Nand Flash设置寄存器中的值到R3中ldr r3, [r1, #oNFCONF]//将R3或上R2后保存到R3中orr r3, r3, r2//将R3中的值保存到Nand Flash设置寄存器中//TWRPH0 - 111 - Duration = HCLK * (TWRPH0 + 1) //TACLS - 11 - Duration = HCLK * TACLSstr r3, [r1, #oNFCONF]//#define oNFCONT 0x04//读取Nand Flash控制寄存器中的值到R3中ldr r3, [r1, #oNFCONT]//将R3的[0]位置1orr r3, r3, #1 // enable nand controller//将R3中的值保存到Nand Flash控制寄存器中//Mode - 1:Nand Flash Controller Enablestr r3, [r1, #oNFCONT]//读取虚拟起始地址到R0中ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot *///预留malloc所需要的空间sub r0, r0, #CFG_MALLOC_LEN /* malloc area *///预留bdinfo所需要的空间sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo *///预留中断和快速中断向量表空间sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)//预留12字节给中断栈sub sp, r0, #12 /* leave 3 words for abort-stack */// copy u-boot to RAM//读取虚拟起始地址到R0中,作为目标地址ldr r0, _TEXT_BASE//将R1设为0,作为源地址mov r1, #0x0//将UBOOT大小的值保存在R2中,作为数据大小mov r2, #CFG_UBOOT_SIZE//跳转到nand_read_ll处执行//并将下一条指令的地址保存在LR中bl nand_read_llnand_read_ll的原型为int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size) 之前设置的R0 R1 R2为它的3个参数R0 - bufR1 - start_addrR2 - sizenand_read_ll的代码在cpu/arm920t/s3c24x0/nand_read.c中int nand_read_ll(unsigned char *buf, unsigned long start_addr, int size) {int i, j;//检测源地址和大小是否在NandFlash的边界上if ((start_addr & NAND_BLOCK_MASK) || (size & NAND_BLOCK_MASK)) //不在边界上则返回-1表示出错return -1; /* invalid alignment *//* chip Enable */// #define nand_select() (NFCONT &= ~(1//置NAND Flash控制寄存器中除Reg_nCE外所有的位为1//Reg_nCE - NAND FLASH Memory nFCE signal control//0 - Force nFCE to low (Enable chip select)nand_select();// #define nand_clear_RnB() (NFSTAT |= (1//置NAND Flash操作状态寄存器中的RnB_TransDetect位为1//When RnB low to high transition is occurred, this value set and issue interrupt if enabled.//To clear this value write '1'//1: RnB transition is detectednand_clear_RnB();for (i=0; i10; i++);//从源地址的首地址开始历便所要拷贝的数据大小for (i=start_addr; i (start_addr + size);){//检测地址是否在NAND Flash的边界上if (start_addr % NAND_BLOCK_SIZE == 0){//检测是否为坏块if (is_bad_block(i)){/* Bad block *///向后延伸一个存储块i += NAND_BLOCK_SIZE;size += NAND_BLOCK_SIZE;//跳到下一块continue;}}j = nand_read_page_ll(buf, i);//指向下一块i += j;buf += j;// LED_FLASH();}/* chip Disable */// #define nand_deselect() (NFCONT |= (1//置Reg_nCE位为1//NAND Flash Memory nFCE signal control//1: Force nFCE to High(Disable chip select)nand_deselect();return 0;}nand_read_ll将Uboot从NAND中拷贝到RAM中拷贝完成后将返回到start_code接下来的代码如下://检测R0是否为0,R0为nand_read_ll的返回值tst r0, #0x0//为0则说明无错,跳转到ok_nand_read处执行beq ok_nand_readok_nand_read://将R0设为0mov r0, #0//ldr r1, =0x33f00000//将R1设为虚拟地址起始处ldr r1, _TEXT_BASE//检测0x400个字节mov r2, #0x400 // 4 bytes * 1024 = 4K-bytesgo_next://读取R0处地址的数据到R3中//然后R0自加4字节ldr r3, [r0], #4//读取R1处地址的数据到R4中//然后R1自加4字节ldr r4, [r1], #4//比较R3和R4的数据是否相等//也就是检测Boot Internal SRAM和RAM中的数据是否相等//以保证数据无错teq r3, r4//不等则跳转到notmatchbne notmatch//相等则R2自减4subs r2, r2, #4//当R2为0则跳转到done_nand_readbeq done_nand_read//R2不为0则跳转回go_next继续检测bne go_nextdone_nand_read:LDR R0, = GPJDATLDR R1, = 0x2STR R1, [R0]stack_setup://读取虚拟起始地址到R0中ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */ //预留malloc所需要的空间sub r0, r0, #CFG_MALLOC_LEN /* malloc area *///预留bdinfo所需要的空间sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo *///预留中断和快速中断向量表空间sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)//预留12字节给中断栈sub sp, r0, #12 /* leave 3 words for abort-stack */clear_bss://读取BSS段的起始地址ldr r0, _bss_start /* find start of bss segment *///读取BSS段的结束地址ldr r1, _bss_end /* stop here *///将R2设为0x0mov r2, #0x00000000 /* clear */clbss_l://将R2中的值保存在R0所指的地址str r2, [r0] /* clear loop... *///R0自加4字节add r0, r0, #4//比较R0和R1是否相等cmp r0, r1//不等则说明清0还没结束ble clbss_lLDR R0, = GPJDATLDR R1, = 0x1STR R1, [R0]//跳转到start_armboot处执行ldr pc, _start_armboot_start_armboot: .word start_armboot这里start_armboot是一个绝对地址,在朗成所修改的这个Uboot中为0x33F8 13F4 执行ldr pc, _start_armboot之后将会跳到RAM中的绝对地址继续执行整理了一个流程图,分为3个存储器:1 Boot Internal SRAM , 接在BANK0,起始地址为0x02 RAM , 接在BANK6,起始地址为0x3000 00003 NAND FLASH,为单独寻址流程如下图:红字为流程序号:1. 首先将NAND FLASH中的前0x1000字节内容拷贝到Boot Internal SRAM中2. 从Boot Internal SRAM的0x0地址处开始执行指令3. 将Uboot从Flash拷贝到RAM中4. 执行ldr pc, _start_armboot从Boot Internal SRAM中跳转到RAM中的绝对地址0x33F8 13F4处继续执行。

u-boot移植5--从nandflash启动甄选

u-boot移植5--从nandflash启动甄选

u-boot移植5--从nandflash启动(优选.)Uboot移植5--支持从nand flash启动同时支持norflash和nandflash启动方法一:在时钟修改方法一条件下:修改cpu/arm920t/start.S/*#ifndef CONFIG_SKIP_RELOCATE_UBOOTrelocate: */ /* relocate U-Boot to RAM */adr r0, _start /* r0 <- current position of code */ldr r1, _TEXT_BASE /* test if we run from flash or RAM */cmp r0, r1 /* don't reloc during debug */beq stack_setup/*check code position*/ldr r0,=48000000ldr r0,[r0]ands r0,r0,#6bne nor_boot#define LENGTH_UBOOT 0x60000#define NAND_CTL_BASE 0x4e000000/*offset*/#define oNFCONF 0x00#define oNFCONT 0x04#define oNFCMD 0x08#define oNFSTAT 0x20nand_boot:mov r1,#NAND_CTL_BASEldr r2,=((7<<12)|(7<<8)|(7<<4))str r2,[r1,#oNFCONF]ldr r2,[r1,#oNFCONF]ldr r2,=((1<<4)|(0<<1)|(1<<0))str r2,[r1,#oNFCONT]ldr r2,[r1,#oNFCONT]ldr r2,=(0x6)str r2,[r1,#oNFSTAT]ldr r2,[r1,#oNFSTAT]mov r2,#0xffstr r2,[r1,#oNFCMD]mov r3,#0nand1:add r3,r3,#0x1cmp r3,#0xablt nand1nand2:ldr r2,[r1,#oNFSTAT]tst r2,#0x4beq nand2ldr r2,[r1,#oNFCONT]orr r2,r2,#0x2str r2,[r1,#oNFCONT]ldr sp, DW_STACK_STARTmov fp, #0ldr r0, =TEXT_BASEmov r1, #0x0mov r2, #LENGTH_UBOOTbl nand_read_lltst r0, #0x0beq ok_nand_readbad_nand_read:loop2:b loop2ok_nand_read:mov r0, #0ldr r1, =TEXT_BASEmov r2, #0x400go_next:ldr r3, [r0], #4ldr r4, [r1], #4teq r3, r4bne notmatchsubs r2, r2, #4beq stack_setupbne go_nextnotmatch:loop3:b loop3nor_boot:adr r0, _start /* r0 <- current position of code */ldr r1, _TEXT_BASE /* test if we run from flash or RAM */ldr r2, _armboot_startldr r3, _bss_startsub r2, r3, r2 /* r2 <- size of armboot */add r2, r0, r2 /* r2 <- source end address */copy_loop:ldmia r0!, {r3-r10} /* copy from source address [r0] */stmia r1!, {r3-r10} /* copy to target address [r1] */cmp r0, r2 /* until source end addreee [r2] */ble copy_loop/* Set up the stack */接着在board/mao2440下新建文件nand_boot.c,代码如下。

u-boot nandflash 烧写

u-boot nandflash  烧写

u-boot中NAND Flash操作及烧写芯片内部存储布局及存储操作特点: K9F1208 64MB一片Nand flash为一个设备(device), 其数据存储分层为:1 (Device) = 4096 (Blocks)1 (Block) -= 32 (Pages/Rows) 页与行是相同的意思,叫法不一样1 (Page) = 528 (Bytes) = 数据块大小(512Bytes) + OOB 块大小(16Bytes)根据芯片规格,计算需要擦除的块大小,按上述指令操作,可以升级u-boot。

NAND操作指令指令 Flash内地址擦除长度nand erase 0x100000 0x200000指令内存中地址 Flash内地址写入长度nand write 0x20000000 0x100000 0x200000nand read 0x20000000 0x100000 0x200000注意写入长度的计算,要以块为单位,计算字节数量。

如:u-boot.bin大小为:114028 bytes一个NAND数据块的容量:32 ×512 = 16384 bytes需要擦除NAND数据块的数量:114028÷16384 = 6.96 ≈ 7 (往上取整)转为擦除NAND数据的长度:16384 ×7 = 114688 bytes (十进制)=0x1_C000 写入以页page为单位,一页大小为512B如何升级bootloader(以下以烧写u-boot.bin为实例)1、用tftp下载u-boot.bin到内存,如0x3300_0000tftp 33000000 u-boot.bin2、擦除uboot分区1Mnand erase 0x0 0x00100000 // 1M的bootloader分区3、写入NAND中nand write 0x33000000 0x0 0x1C000 // u-boot.bin实际大小需用的block (注:如果板子为空白,用sjf2410工具,通过并口,烧写到NAND的0x0地址即可)如何烧写linux kernel1、 tftp下载uImage2.6.14到内存,如0x3400_0000tftp 34000000 uImage2.6.142、擦除kernel分区3Mnand erase 00100000 003000003、写入NAND中nand write 34000000 00100000 00300000如何烧写文件系统fs4、 tftp下载rootfs.cramfs到内存,如0x3400_0000tftp 34000000 rootfs.cramfs5、擦除root分区40Mnand erase 00400000 028000006、写入NAND中nand write 34000000 00400000 (实际大小,往前取512B的整数倍)奇怪,3400_0000已经是64M内存的上限边界,为什么居然还可以使用??2011-5-17 yang 好像3400_0000已经自动映射到 3000_0000的。

ubootmmc命令详解Nandflashuboot命令详解

ubootmmc命令详解Nandflashuboot命令详解

ubootmmc命令详解Nandflashuboot命令详解
md指令
uboot下输⼊指令md,会提⽰md的⽤法,memory display,即内存显⽰。

b:8位
w:16位
l:32位(默认值)
⽰例:
上述0x10000000是要显⽰内存的那个地址,后⾯的数字是输出的个数,注意是16进制的。

直接输⼊md来显⽰,默认是以32位为单位输出。

mw指令
uboot下输⼊指令mw,会提⽰md的⽤法,memory write,即向内存中写⼊数据。

b\w\l的意思同上,address是要写⼊内存的地址,value是要写⼊的值,count是从address开始要写⼊多少个数,这些都是16进制数。

⽰例:
⽰例
在0x10000000地址内存的起始位置写⼊0x12345678,打印出内存的输出。

发现低位0x78是写在低地址0x10000000,⾼位0x12是写在⾼地址0x10000003上,也就是常说的⼩端模式(little endian),简单记忆:低低模式,即低位在低地址。

从NandFlash启动U-BOOT

从NandFlash启动U-BOOT

从NandFlash启动U-BOOT从Nand Flash启动U-BOOT的基本原理前4K的问题如果S3C2410被配置成从Nand Flash启动(配置由硬件工程师在电路板设置), S3C2410的Nand Flash控制器有一个特殊的功能,在S3C2410上电后,Nand Flash控制器会自动的把Nand Flash上的前4K数据搬移到4K内部RAM中,并把0x00000000设置内部RAM的起始地址,CPU从内部RAM的0x00000000位置开始启动。

这个过程不需要程序干涉。

程序员需要完成的工作,是把最核心的启动程序放在Nand Flash的前4K中。

启动程序的安排由于Nand Flash控制器从Nand Flash中搬移到内部RAM的代码是有限的,所以在启动代码的前4K里,我们必须完成S3C2410的核心配置以及把启动代码(U-BOOT)剩余部分搬到RAM中运行。

u-boot源码不支持从nand flash启动,可是s3c2410支持从nand flash启动,开发板(sbc-2410x)加电后s3c2410将nand flash 的前4k(保存有u-boot的部分功能--拷贝功能--把nand flash中的内容拷贝到SDRAM)拷贝到sram(s3c2410芯片内的sram)。

这就需要修改u-boot源码,增加u-boot的功能: 使u-boot在得到执行权后能够将其自身拷贝到开发板上SDRAM中,以便处理器能够执行u-boot .Nand Flash的命令、地址、数据都通过I/O口发送,管脚复用,这样做做的好处是,可以明显减少NAND FLASH的管脚数目,将来如果设计者想将NAND FLASH更换为更高密度、更大容量的,也不必改动电路板。

NAND FLASH不能够执行程序,本人总结其原因如下:1. NAND FLASH本身是连接到了控制器上而不是系统总线上。

CPU启动后是要取指令执行的,如果是SROM、NOR FLASH 等之类的,CPU 发个地址就可以取得指令并执行,NAND FLASH不行,因为NAND FLASH 是管脚复用,它有自己的一套时序,这样CPU无法取得可以执行的代码,也就不能初始化系统了。

  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寄存器写值。

相关文档
最新文档