由于NandFlash硬件升级比较快,公司去年一直在使用三星的K9GAG08U0D,现在MLC NandFlash 升级到了第二代,K9GAG08U0D 很快就会处在停产的状态,未雨绸缪,公司选型了K9GAG08U0E 来替代原有的NandFlash芯片。起初。本以为把新Nandflash ID信息增加到Uboot 和 Linux内核的NandFlash ID 列表文件中即可,也就是Uboot 和Linux源码中的nand_ids.c
文件中,但是仔细看了 K9GAG08U0E 的DataSheet,发现自己的想法错了。
下面记录了NandFlash更换时面临的几个问题,及解决方法:
第一个问题:ID 冲突
K9GAG08U0E 的 Product ID与 K9GAG08U0D 的Product ID 是相同的,都是 0xd5,而在Uboot 代码和Linux内核代码中,是根据ID信息来获取或者计算NandFlash的页大小和块大小这些关键信息的。
现在这两款的 Flash ID相同,而Page,OOB ,Block大小都是不同的。 K9GAG08U0D Page,OOB,Block 关键参数:
而K9GAG08U0E 的 Page,OOB,Block 关键参数:
可见他们是不同的,U0E 每页大小是U0D的两倍。
针对相同ID的两款不同Flash是怎样区分的呢?仔细看DataSheet,发现了可以快速区分两种设备的方法:
U0D Page=4K,U0D Page=8K,我们只需判断Read第四个字节的第0位和第1位即可。
下面列出关键代码段:
//这里必须有下面两行代码,因为U0E的DataSheet要求系统上电后第一条指令必须是NAND_CMD_RESET,而U0D没有这要求,如果不发送这条指令是无法读取到NandFlash的任何信息的,因为这个问题查找了很长时间,在这里提醒给大家。
// K9GAG08U0E must add below codes
{
s3c_nand_hwcontrol(0, NAND_CMD_RESET, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
s3c_nand_device_ready(0);
}
s3c_nand_hwcontrol(0, NAND_CMD_READID, NAND_NCE | NAND_CLE | NAND_CTRL_CHANGE);
s3c_nand_hwcontrol(0, 0x00, NAND_CTRL_CHANGE | NAND_NCE | NAND_ALE);
s3c_nand_hwcontrol(0, 0x00, NAND_NCE | NAND_ALE);
s3c_nand_hwcontrol(0, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
s3c_nand_device_ready(0);
tmp = readb(nand->IO_ADDR_R); /* Maf. ID */
// printf("Manufactor ID:%x\n",tmp);
tmp = dev_id = readb(nand->IO_ADDR_R); /* Device ID */
//printf("dev_id ID:%x \n",dev_id);
for (i = 0; nand_flash_ids[i].name != NULL; i++)
{
if (tmp == nand_flash_ids[i].id) {
type = &nand_flash_ids[i];
break;
}
nand->cellinfo = readb(nand->IO_ADDR_R);/* 3rd byte */
tmp = readb(nand->IO_ADDR_R);/* 4th byte */
int childType=tmp & 0x03; //Page size
// printf("dev_id=%x,childType=%x \n",dev_id,childType);
if(dev_id == 0xd5 && childType==0x01) //U0D
{
}else if(dev_id == 0xd5 && childType==0x02) //U0E
{
}
这样针对不同的NandFlash做不同的初始化了,以上代码来自uboot1.1.6/cpu/s3c64xx/nand.c 文件中的void board_nand_init(struct nand_chip *nand) 函数。
在上一篇中我们重点介绍了K9GAG08U0D 和 K9GAG08U0E的区别,除了这些外还有一个需要重点关注的地方,下面我们详细讲解。
Uboot从NandFlash启动第一步就是把NandFlash的前8K代码拷贝到s3c6410内部的SRAM中,然后运行,这8K的代码会从NandFlash中把完整的Uboot代码拷贝到6410开发板片外内存中,我这里使用的是256M的DDR,Uboot的存放地址为0x5FE00000,虚拟地址为
0xCFE00000.
注意:关键的8K代码不是在U0D(每页4K)的前两页存放,也不是在U0E(每页8K)的第一页存放,而是在U0E或者U0D的 前四页中存放的,前四页分为每页2K,总共8K,这是由s3c6410芯片本身所规定的。这8K字节外后面的数据就是按实 际的页数存放了,U0D 每页存放4K字节,U0E每页存放8K字节。
s3c6410支持从SD卡启动,从SD卡启动后就可以把Uboot.bin文件写入NandFlash了,可以认真的看一下Uboot 里面 的 nand write.uboot 命令是如何把数据写入NandFlash前4页 的。写入成功后就可以从NandFlash启动Uboot。
nand write.uboot 的关键代码 位于uboot1.1.6/common/cmd_nand.c文件中:
if (!read && s != NULL && (!strcmp(s, ".uboot")) && nand->writesize == 4096) //U0D
{
size=4096;
nand_write(nand, off, &size, (u_char *)addr);
off+=4096;
addr+=2048;
nand_write(nand, off, &size, (u_char *)addr);
off+=4096;
addr+=2048;
nand_write(nand, off, &size, (u_char *)addr);
off+=4096;
addr+=2048;
nand_write(nand, off, &size, (u_char *)addr);
off+=4096;
addr+=2048;
size=1024*1024-4*4096;
ret = nand_write(nand, off, &size, (u_char *)addr);
}else if(!read && s != NULL && (!strcmp(s, ".uboot")) && nand->writesize == 8192) //U0E
{
size=8192;
ret=nand_write(nand, off, &size, (u_char *)addr);
off+=8192;
addr+=2048;
ret=nand_write(nand, off, &size, (u_char *)addr);
off+=8192;
addr+=2048;
ret=nand_write(nand, off, &size, (u_char *)addr);
off+=8192;
addr+=2048;
ret=nand_write(nand, off, &size, (u_char *)addr);
off+=8192;
addr+=2048;
//写剩余的页面,这里的Uboot占用1M的NandFlash地址空间,在U0E里面占用128个页面(128Page=1M),上面已经写了四个页面了,这里写剩余的124个页,124个页面足够存放Uboot的有效数据了。
size=1024*1024-4*8192;
ret = nand_write(nand, off, &size, (u_char *)addr);
}
现在我们重点要看 Uboot代码,uboot1.1.6/cpu/s3c64xx/nand_cp.c 文件:
//该函数在Uboot.bin前8K代码中,这个函数实现把Uboot从Nandflash拷贝到外部内存中,是Uboot从Nandflash启动的关键地方
int copy_uboot_to_ram (void)
{
int large_block = 0;
int i;
vu_char id;
NAND_ENABLE_CE();
NFCMD_REG=NAND_CMD_RESET;
NF_TRANSRnB();
NFCMD_REG = NAND_CMD_READID;
NFADDR_REG = 0x00;
NF_TRANSRnB();
/* wait for a while */
for (i=0; i<200; i++);
int factory = NFDATA8_REG;
id = NFDATA8_REG;
int cellinfo=NFDATA8_REG;
int tmp= NFDATA8_REG;
int childType=tmp & 0x03; //Page size
if (id > 0x80)
{
large_block = 1;
}
if(id == 0xd5 && childType==0x01 ) //K9GAG08U0D
{
large_block = 2;
}else if(id == 0xd5 && childType==0x02 ) //K9GAG08U0D
{
large_block = 3;
}
/* read NAND Block.
* 128KB ->240KB because of U-Boot size increase. by scsuh
* So, read 0x3c000 bytes not 0x20000(128KB).
*/
return nandll_read_blocks(CFG_PHY_UBOOT_BASE, 0x3c000, large_block);
}
static int nandll_read_blocks (ulong dst_addr, ulong size, int large_block)
{
uchar *buf = (uchar *)dst_addr;
int i;
uint page_shift = 9;
if (large_block==1)
page_shift = 11;
if(large_block==2)
page_shift = 12;
if(large_block==3)
page_shift =13;
if(large_block == 2) // K9GAG08U0D
{
/* Read pages */
for (i = 0; i < 4; i++, buf+=(1<<(page_shift-1)))
{
nandll_read_page(buf, i, large_block);
}
/* Read pages */
for (i = 4; i < (0x3c000>>page_shift); i++, buf+=(1<<page_shift))
{
nandll_read_page(buf, i, large_block);
}
}else if(large_block == 3) //K9GAG08U0E
{
/* Read pages */
for (i = 0; i < 4; i++, buf+=(1<<(page_shift-2)))
{
nandll_read_page(buf, i, large_block);
}
/* Read pages */
for (i = 4; i < (0x3c000>>page_shift); i++, buf+=(1<<page_shift))
{
nandll_read_page(buf, i, large_block);
}
}
else
{
for (i = 0; i < (0x3c000>>page_shift); i++, buf+=(1<<page_shift))
{
nandll_read_page(buf, i, large_block);
}
}
return 0;
}
static int nandll_read_page (uchar *buf, ulong addr, int large_block)
{
int i;
int page_size = 512;
if (large_block==1)
page_size = 2048;
if (large_block==2)
page_size = 4096;
if(large_block==3)
page_size = 8192;
NAND_ENABLE_CE();
NFCMD_REG = NAND_CMD_READ0;
/* Write Address */ //5 个寻址周期,参看下图
NFADDR_REG = 0;
if (large_block)
NFADDR_REG = 0;
NFADDR_REG = (addr) & 0xff;
NFADDR_REG = (addr >> 8) & 0xff;
NFADDR_REG = (addr >> 16) & 0xff;
if (large_block)
NFCMD_REG = NAND_CMD_READSTART;
NF_TRANSRnB(); //等待NandFlash 状态引脚可读。
/* for compatibility(2460). u32 cannot be used. by scsuh */
for(i=0; i < page_size; i++)
{
*buf++ = NFDATA8_REG;
}
NAND_DISABLE_CE();
return 0;
}
寻址周期图:
具体的说明请参考K9GAG08U0E DataSheet 第9页。
这里用两片文章写了s3c6410支持K9GAG08U0E 的关键地方,当然还有一些具体的细节了,比如给关键结构体 nand chip, mtd_device 等赋值Page大小,Block大小,OOB大小的操作,硬件ECC 8bit 纠错等功能,当然这些不是K9GAG08U0E特有的功能,K9GAG08U0D也需要这样的操作,这里就不详细的介绍了。
希望这两篇文章对需要了解NandFlash如何通过软件工作的朋友有参考价值。
s3c6410 开发板Linux系统支持 K9GAG08U0E的方法的更多相关文章
-
SecureCRT访问开发板linux系统
前言: 最近在用OK6410开发板跑linux系统,经常在终端上敲一些指令,无奈开发板屏幕太小用起来非常不方便,所以使用终端一款能运行在windows上的软件与开发板连接,直接在电脑上操作开发板了,这 ...
-
【嵌入式开发】写入开发板Linux系统-模型S3C6410
笔者 : 万境绝尘 转载请著名出处 最终拿到板子了, 嵌入式开发正式开启. 板子型号 : 三星 S3C6410 基于ARM11, 指令集基于arm6指令集; 为毛不是 Cortext A9的板子; 烧 ...
-
[嵌入式开发板]iTOP-4412开发板linux 系统存储空间的修改
平台:iTOP-4412开发板 这里我们以修改成 1G 存储空间为例来讲解修改方法, 如果需要改 成其他大小的存储空间,参照此方法修改即可. 首先连接好 iTOP-4412 开发板的调试串口到 pc ...
-
iTOP-4412开发板---Linux系统学习下载步骤
本文转自迅为论坛:http://www.topeetboard.com 1.cd /home/topeet/Linux-simple/console 下建立.c文件 2. 编译命令,就在此目录下 # ...
-
迅为开发板4412开发板-ANROID系统的烧写方法分享
详情了解: http://topeetboard.com 更多了解:https://arm-board.taobao.com 一.OTG接口烧写方式 通过该方式可以烧写 Android4.0.3 ...
-
2440开发板linux系统移植3G拨号上网收发短信(三)
一.用text查看模式 下面的“发”是指我敲的命令,“收”是指回车后显示的信息包括其他接收的信息. ~ >: microcom -s 115200 /dev/ttyUSB1 发:at 收:OK ...
-
linux 3.4.103 内核移植到 S3C6410 开发板 移植失败 (问题总结,日本再战!)
linux 3.4.103 内核移植到 S3C6410 开发板 这个星期差点儿就搭在这里面了,一開始感觉非常不值得,移植这样的浪费时间的事情.想立刻搞定,然后安安静静看书 & coding. ...
-
搭建S3C6410开发板的测试环境
因为ARM架构的开发板可基于X86架构的PC在CPU指令以及二进制上都有所不同,而且如果linux驱动需要访问硬件,这些硬件很难在PC上模拟,所以就需要我们在带有这些硬件的开发板上进行调试和测试. ...
-
S3C6410开发板开发环境的搭建
本节主要介绍了S3C6410开发板及OK6410开发板.OK6410开发板是基于ARM11处理器的S3C6410,采用“核心版+底板”结构 主要步骤如下:. OK6410开发板自带一个串口,PC也需要 ...
随机推荐
-
利用layer的mask属性实现逐渐揭示的动画效果
github上又看到个不错的动画(https://github.com/rounak/RJImageLoader),如图: 所以就想来自己实现以下 不试不知道,这个动画还真不是看上去那么简单,我自己想 ...
-
AX 2012 在Grid 中添加image标识状态
refer to :http://kiwiaxguy.blogspot.hk/2013/10/displaying-image-on-form-grid-in.html
-
FreeMarker语法知识
FreeMarker的模板文件并不比HTML页面复杂多少,FreeMarker模板文件主要由如下4个部分组成:1,文本:直接输出的部分2,注释:<#-- ... -->格式部分,不会输出3 ...
-
Eclipse 环境安装和配置优化
1.下载相应的eclipse版本. 官方下载地址:http://www.eclipse.org/downloads/ juno版本的64位下载地址:http://www.eclipse.o ...
-
转:php页面静态化之真静态
之前的缓存和现在所说的页面静态化都是为网站提速的,如果是访问量小的小型网站可能不需要但是如果网站规模或访问量很大,那么页面静态化和缓存就会体现出各自的价值了下面先来了解下页面静态化的分类 页面静态化的 ...
-
nodejs笔记2——请求路由
对于不同的URL请求,服务器应该有不同的反应.我们要为路由提供请求的URL和其他需要的GET及POST参数,随后路由需要根据这些数据来执行相应的代码.我们需要的所有数据都会包含在request对象中, ...
-
Ioc在重构代码中的应用
最近lz在写抓工商公式系统(http://www.gsxt.gov.cn/index.html)的爬虫,其中的难点就是在怎么过极验验证码,搞的我不要不要的!如下: 简直是各种坑,被搞的死去活来以后还是 ...
-
express 遇到问题 - Error: Can&#39;t set headers after they are sent
在node配合mongodb,链接数据库,查询所有数据2步操作,都有响应数据返回是会出现 Error: Can't set headers after they are sent,的报错 故障原因:引 ...
-
构造函数constructor 与析构函数destructor(五)
我们知道当调用默认拷贝构造函数时,一个对象对另一个对象初始化时,这时的赋值时逐成员赋值.这就是浅拷贝,当成员变量有指针时,浅拷贝就会在析构函数那里出现问题.例如下面的例子: //test.h #ifn ...
-
(转)mysql5.7 根据二进制文件mysqlbinlog恢复数据库 Linux
原文:http://blog.csdn.net/qq_15058425/article/details/61196085 1.开始mysqlbinlog日志功能 先找打my.cnf文件的位置: 2.编 ...