norflash 是一种xip(eXecute In Place结构,类似于sdram),读的速度自然比nandflash快很多,可以在norflash上执行相关程序,但是不能执行写操作(要发一些列写操作命令才能执行)。
以s3c2440为例,讲述norflash操作及启动过程。norflash接在片选0上,从cpu看到的地址0启动。
然后了解硬件原理图连接:
可以看到norflash 是16位数据总线,cpu每读一次读取16位数据。
cpu和norflash连接方法:注意cpu的A0没有连接(因为norfalsh数据线是16位的,cpu发一个地址,norfalsh会一次返回16bit数据)。ARM处理器的每个地址对应的是一个BYTE 的数据单元,而 16-BIT 的 FLASH 的每个地址对应的是一个HALF-WORD(16-BIT)的数据单元.为了保持匹配,所以必须错位连接.这样,从ARM处理器发送出来的地址信号的最低位A0对16-BIT FLASH来说就被屏蔽掉了。
1.实现浏览norflash信息,如厂家ID,设备ID,regions,blocks(或者sections,一个regions含有多个blocks)。
注意linux driver分为两种确定flash类型方式:
jedec:通过命令获取flash厂家id和设备ID,并和开始已经定义好的数组进行比较。来获取flash类型。
cfi:(common flash interface):通过特殊命令获取flash类型,大小等信息。注意下面地址是相对于norflash来看的。
void do_scan_nor_flash(void)
{
char str[4];
unsigned int size;
int regions, i;
int region_info_base;
int block_addr, blocks, block_size, j;
int cnt;
int vendor, device;
/* 打印厂家ID、设备ID */
nor_cmd(0x555, 0xaa); /* 解锁 */
nor_cmd(0x2aa, 0x55);
nor_cmd(0x555, 0x90); /* read id */
vendor = nor_dat(0);
device = nor_dat(1);
nor_cmd(0, 0xf0); /* reset */
nor_cmd(0x55, 0x98); /* 进入cfi模式 */
str[0] = nor_dat(0x10);
str[1] = nor_dat(0x11);
str[2] = nor_dat(0x12);
str[3] = '\0';
printf("str = %s\n\r", str);
/* 打印容量 */
size = 1<<(nor_dat(0x27));
printf("vendor id = 0x%x, device id = 0x%x, nor size = 0x%x, %dM\n\r", vendor, device, size, size/(1024*1024));
/* 打印各个扇区的起始地址 */
/* 名词解释:
* erase block region : 里面含有1个或多个block, 它们的大小一样
* 一个nor flash含有1个或多个region
* 一个region含有1个或多个block(扇区)
* Erase block region information:
* 前2字节+1 : 表示该region有多少个block
* 后2字节*256 : 表示block的大小
*/
regions = nor_dat(0x2c);
region_info_base = 0x2d;
block_addr = 0;
printf("Block/Sector start Address:\n\r");
cnt = 0;
for (i = 0; i < regions; i++)
{
blocks = 1 + nor_dat(region_info_base) + (nor_dat(region_info_base+1)<<8);
block_size = 256 * (nor_dat(region_info_base+2) + (nor_dat(region_info_base+3)<<8));
region_info_base += 4;
// printf("\n\rregion %d, blocks = %d, block_size = 0x%x, block_addr = 0x%x\n\r", i, blocks, block_size, block_addr);
for (j = 0; j < blocks; j++)
{
/* 打印每个block的起始地址 */
//printf("0x%08x ", block_addr);
printHex(block_addr);
putchar(' ');
cnt++;
block_addr += block_size;
if (cnt % 5 == 0)
printf("\n\r");
}
}
printf("\n\r");
/* 退出CFI模式 */
nor_cmd(0, 0xf0);
}
2.擦除norflash
flash要擦除的话都是以blocks为单位,解锁后输入blocks起始地址(按照上面打印),然后扇区全部变为ff,写的话只能写0。
判断是否擦除完或者写完方法。如果没有擦除完Q6(data bit6)会一直不停的反转,当停止反转后,也就是当前和上一次一样的值,表示擦除完成。(时序怎么考虑?)
一直疑惑要不要考虑时序,继续看下kernel MTD实现代码,也是直接读两次比较。
void wait_ready(unsigned int addr)
{
unsigned int val;
unsigned int pre;
pre = nor_dat(addr>>1);
val = nor_dat(addr>>1);
while ((val & (1<<6)) != (pre & (1<<6)))
{
pre = val;
val = nor_dat(addr>>1);
}
}
void do_erase_nor_flash(void)
{
unsigned int addr;
/* 获得地址 */
printf("Enter the address of sector to erase: ");
addr = get_uint();
printf("erasing ...\n\r");
nor_cmd(0x555, 0xaa); /* 解锁 */
nor_cmd(0x2aa, 0x55);
nor_cmd(0x555, 0x80); /* erase sector */
nor_cmd(0x555, 0xaa); /* 解锁 */
nor_cmd(0x2aa, 0x55);
nor_cmd(addr>>1, 0x30); /* 发出扇区地址 */
wait_ready(addr);
}
3.norflash写操作
norflash写只能把1写成0,也就是写之前必须把扇区擦除才能写,所以这点看来,flash永远是flash,不能顶替内存。
void do_write_nor_flash(void)
{
unsigned int addr;
unsigned char str[100];
int i, j;
unsigned int val;
/* 获得地址 */
printf("Enter the address of sector to write: ");
addr = get_uint();
printf("Enter the string to write: ");
gets(str);
printf("writing ...\n\r");
/* str[0],str[1]==>16bit
* str[2],str[3]==>16bit
*/
i = 0;
j = 1;
while (str[i] && str[j])
{
val = str[i] + (str[j]<<8);
/* 烧写 */
nor_cmd(0x555, 0xaa); /* 解锁 */
nor_cmd(0x2aa, 0x55);
nor_cmd(0x555, 0xa0); /* program */
nor_cmd(addr>>1, val);
/* 等待烧写完成 : 读数据, Q6无变化时表示结束 */
wait_ready(addr);
i += 2;
j += 2;
addr += 2;
}
val = str[i];
/* 烧写 */
nor_cmd(0x555, 0xaa); /* 解锁 */
nor_cmd(0x2aa, 0x55);
nor_cmd(0x555, 0xa0); /* program */
nor_cmd(addr>>1, val);
/* 等待烧写完成 : 读数据, Q6无变化时表示结束 */
wait_ready(addr);
}
部分参考:
http://www.cnblogs.com/jason-lu/archive/2013/03/13/2957399.html