mini2440 支持 sst39vf1601,实现saveenv。
http://blogimg.chinaunix.net/blog/upfile2/090927210004.tar
把board/100askxxx/makefile里的flash.o去掉
用上面的文件分别去替换你的config文件和cfi_flash.c文件,就可以了,本来想做个patch,但是我的diff不好用。有的时候saveenv不成功,索性直接把写超时设置为 (0),就ok了。就是红色字体部分。
韦东山 大神的 详解里对没有支持sst39vf1601 nor flash,所以总是bad crc,更不用说saveenv命令了。
下面使用cfi(common flash interface),实现对sst39vf1601的支持。
#define CFG_ENV_ADDR (CFG_FLASH_BASE + 0X0F0000)
#define CFG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */
配置是上面这样的,我用的是sst39vf1601,共32个block,每个block 32k word = 64k = 0x10000
也就是说env放在了 第15块上(逻辑上是第16),前三块是uboot的代码,第15块是存放环境参数的块。
这里的block = sector,下面说到的扇区等于这里的block。即 扇区 = block = 64k = 0x10000
00000000 (RO) 00010000 (RO) 00020000 (RO) 00030000 00040000
00050000 00060000 00070000 00080000 00090000
000A0000 000B0000 000C0000 000D0000 000E0000
000F0000 (RO) 00100000 00110000 00120000 00130000
00140000 00150000 00160000 00170000 00180000
00190000 001A0000 001B0000 001C0000 001D0000
001E0000 001F0000
用kscope搜索saveenv字符串,找到这里来了。
#if defined(CFG_ENV_IS_IN_NVRAM) || defined(CFG_ENV_IS_IN_EEPROM) || /
((CONFIG_COMMANDS & (CFG_CMD_ENV|CFG_CMD_FLASH)) == /
(CFG_CMD_ENV|CFG_CMD_FLASH)) || /
((CONFIG_COMMANDS & (CFG_CMD_ENV|CFG_CMD_NAND)) == /
(CFG_CMD_ENV|CFG_CMD_NAND))
U_BOOT_CMD(
saveenv, 1, 0, do_saveenv,
"saveenv - save environment variables to persistent storage/n",
NULL
);
#endif /* CFG_CMD_ENV */
上面就是这个函数,如下
int do_saveenv (cmd_tbl_t *cmdtp, int flag, int argc, char *argv[])
{
extern char * env_name_spec;
printf ("Saving Environment to %s.../n", env_name_spec);
return (saveenv() ? 1 : 0);
}
在env_flash.c中,有这样的字符。
#if defined(CFG_ENV_IS_IN_FLASH) /* Environment is in Flash */
char * env_name_spec = "Flash";
#endif /* CFG_ENV_IS_IN_FLASH */
#define PHYS_FLASH_1 0x00000000 /* Flash Bank #1 */
#define CFG_FLASH_BASE 0x00000000
#define CFG_MONITOR_BASE 0x00000000
#define CFG_ENV_ADDR 0x0F0000
#define CFG_ENV_SIZE 0x10000
#define CFG_ENV_OFFSET 0x0F0000
#define CFG_ENV_SECT_SIZE 0x10000
#define CFG_ENV_IS_IN_FLASH 1
#define CFG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */
给 saveenv(),去掉宏之后
env_t *env_ptr = (env_t *)CFG_ENV_ADDR;
static env_t *flash_addr = (env_t *)CFG_ENV_ADDR;
#define PHYS_FLASH_1 0x00000000 /* Flash Bank #1 */
#define CFG_FLASH_BASE PHYS_FLASH_1
#define CFG_ENV_ADDR (CFG_FLASH_BASE + 0X0F0000)
#define CFG_ENV_SIZE 0x10000 /* Total Size of Environment Sector */
int saveenv(void)
{
int len, rc;
ulong end_addr;
ulong flash_sect_addr;
uchar *env_buffer = (uchar *)env_ptr;
int rcode = 0;
flash_sect_addr = (ulong)flash_addr;
len = CFG_ENV_SIZE;
end_addr = flash_sect_addr + len - 1;
if (flash_sect_protect (0, flash_sect_addr, end_addr)) 首先取消保护
return 1;
puts ("Erasing Flash...");
if (flash_sect_erase (flash_sect_addr, end_addr)) 然后开始擦除
return 1;
puts ("Writing to Flash... ");
rc = flash_write((char *)env_buffer, flash_sect_addr, len); 开始写进去
if (rc != 0) {
flash_perror (rc);
rcode = 1;
} else {
puts ("done/n");
}
/* try to re-protect */
(void) flash_sect_protect (1, flash_sect_addr, end_addr); 最后在保护起来
return rcode;
}
逻辑很清晰
逐个分析下面的实现:
1.首先取消保护
2.然后开始擦除
3.开始写进去
1.首先取消保护,有个疑问,什么时候加的保护,都那些块加了保护呢?先不管。
int flash_sect_protect (0, flash_sect_addr, end_addr);
int flash_sect_protect (0, 0xf0000, 0x10000);
可以从函数原型分析出,0代表解锁,地址是从0xf0000-0x100000,如果成功将返回0。
int flash_sect_protect (int p, ulong addr_first, ulong addr_last)
{
flash_info_t *info;
ulong bank;
int s_first[CFG_MAX_FLASH_BANKS], s_last[CFG_MAX_FLASH_BANKS]; 这里CFG_MAX_FLASH_BANKS=1
int protected, i;
int planned;
int rcode;
/*下面计算出要撤销保护的扇区范围,和 扇区数量,成功返回0*/
rcode = flash_fill_sect_ranges( addr_first, addr_last, s_first, s_last, &planned );
protected = 0;
if (planned && (rcode == 0)) { 这里说明flash_fill_sect_ranges成功的完成了任务
for (bank=0,info=&flash_info[0]; bank < CFG_MAX_FLASH_BANKS; ++bank, ++info) { 遍历所有的块,显然是一次
if (info->flash_id == FLASH_UNKNOWN) {
continue;
}
if (s_first[bank]>=0 && s_first[bank]<=s_last[bank]) {
protected += s_last[bank] - s_first[bank] + 1;
for (i=s_first[bank]; i<=s_last[bank]; ++i) {
info->protect[i] = p; 撤销保护的具体操作就是这里了,可见这是uboot在自己软件层次的保护方法
} 到底是取消保护,还是加上保护,取决于p的取值,这里假设0,是解锁。
}
}
printf ("%sProtected %d sectors/n",
p ? "" : "Un-", protected);
} else if (rcode == 0) { 到了这里,说明给的撤销保护的地址范围不正确
puts ("Error: start and/or end address"
" not on sector boundary/n");
rcode = 1;
}
return rcode;
}
static int
flash_fill_sect_ranges (ulong addr_first, ulong addr_last,
int *s_first, int *s_last,
int *s_count )
{
flash_info_t *info;
ulong bank;
int rcode = 0;
*s_count = 0;
for (bank=0; bank < CFG_MAX_FLASH_BANKS; ++bank) {
s_first[bank] = -1; /* first sector to erase */
s_last [bank] = -1; /* last sector to erase */
}
for (bank=0,info=&flash_info[0]; 只遍历一次
(bank < CFG_MAX_FLASH_BANKS) && (addr_first <= addr_last);
++bank, ++info) {
ulong b_end;
int sect;
short s_end;
if (info->flash_id == FLASH_UNKNOWN) {
continue;
}
b_end = info->start[0] + info->size - 1; /* bank end addr */ 就是结束地址
s_end = info->sector_count - 1; /* last sector */ 最后一个扇区数,这些内容在初始化是添好了。
for (sect=0; sect < info->sector_count; ++sect) { 遍历这块nor的所有扇区
ulong end; /* last address in current sect */
end = (sect == s_end) ? b_end : info->start[sect + 1] - 1;
if (addr_first > end)
continue;
if (addr_last < info->start[sect])
continue;
if (addr_first == info->start[sect]) {
s_first[bank] = sect; 找到起始的那个扇区
}
if (addr_last == end) {
s_last[bank] = sect; 和最后的那个扇区,可知对 addr_first和addr_last的要求比较苛刻。
}
}
if (s_first[bank] >= 0) {
if (s_last[bank] < 0) {
if (addr_last > b_end) {
s_last[bank] = s_end;
} else {
puts ("Error: end address"
" not on sector boundary/n");
rcode = 1;
break;
}
}
if (s_last[bank] < s_first[bank]) {
puts ("Error: end sector"
" precedes start sector/n");
rcode = 1;
break;
}
sect = s_last[bank]; 改到最后
addr_first = (sect == s_end) ? b_end + 1: info->start[sect + 1];
(*s_count) += s_last[bank] - s_first[bank] + 1; 计算出共几个扇区,指针传参。
} else if (addr_first >= info->start[0] && addr_first < b_end) {
puts ("Error: start address not on sector boundary/n");
rcode = 1;
break;
} else if (s_last[bank] >= 0) {
puts ("Error: cannot span across banks when they are"
" mapped in reverse order/n");
rcode = 1;
break;
}
}
return rcode; 返回0
}
现回顾下 flash_info_t 结构,然后在看上面的代码。
typedef struct {
ulong size; /* total bank size in bytes */
ushort sector_count; /* number of erase units */
ulong flash_id; /* combined device & manufacturer code */
ulong start[CFG_MAX_FLASH_SECT]; /* physical sector start addresses */
uchar protect[CFG_MAX_FLASH_SECT]; /* sector protection status */ 这就保护的初始数值是多少,那里赋值的呢?
#ifdef CFG_FLASH_CFI
uchar portwidth; /* the width of the port */
uchar chipwidth; /* the width of the chip */
ushort buffer_size; /* # of bytes in write buffer */
ulong erase_blk_tout; /* maximum block erase timeout */
ulong write_tout; /* maximum write timeout */
ulong buffer_write_tout; /* maximum buffer write timeout */
ushort vendor; /* the primary vendor id */
ushort cmd_reset; /* Vendor specific reset command */
ushort interface; /* used for x8/x16 adjustments */
ushort legacy_unlock; /* support Intel legacy (un)locking */
#endif
} flash_info_t;
2.然后开始擦除,成功返回0,猜想,有保护的,应该不能执行擦出操作。
这个可就不简单了,因为要深入到cfi借口的具体操作上。
int flash_sect_erase (0, flash_sect_addr, end_addr);
int flash_sect_erase (0, 0xf0000, 0x10000);
int flash_sect_erase (ulong addr_first, ulong addr_last)
{
flash_info_t *info;
ulong bank;
int s_first[CFG_MAX_FLASH_BANKS], s_last[CFG_MAX_FLASH_BANKS];
int erased = 0;
int planned;
int rcode = 0;
rcode = flash_fill_sect_ranges (addr_first, addr_last,
s_first, s_last, &planned ); 代码复用了呵呵。
if (planned && (rcode == 0)) { 成功到了这里
for (bank=0,info=&flash_info[0];
(bank < CFG_MAX_FLASH_BANKS) && (rcode == 0);
++bank, ++info) {
if (s_first[bank]>=0) {
erased += s_last[bank] - s_first[bank] + 1; 计算出要擦出的总扇区数量
rcode = flash_erase (info, s_first[bank], s_last[bank]); 执行擦出操作
}
}
printf ("Erased %d sectors/n", erased);
} else if (rcode == 0) {
puts ("Error: start and/or end address"
" not on sector boundary/n");
rcode = 1;
}
return rcode;
}
主要的操作函数是这个,他的参数是 擦出nor的 flash_info结构 和 起始 结束 扇区 数
成功返回0。我们跟进去。
int flash_erase (info, 15, 15);
int flash_erase (flash_info_t * info, int s_first, int s_last)
{
int rcode = 0;
int prot;
flash_sect_t sect;
if (info->flash_id != FLASH_MAN_CFI) {
puts ("Can't erase unknown flash type - aborted/n");
return 1;
}
if ((s_first < 0) || (s_first > s_last)) {
puts ("- no sectors to erase/n");
return 1;
}
prot = 0;
for (sect = s_first; sect <= s_last; ++sect) { 这里计算出即将要擦出的扇区中有保护的扇区数
if (info->protect[sect]) {
prot++;
}
}
if (prot) { 可见,这个函数不会理会flash_info结构中的保护,他只管擦出。
printf ("- Warning: %d protected sectors will not be erased!/n", prot);
} else {
putc ('/n');
}
for (sect = s_first; sect <= s_last; sect++) {
if (info->protect[sect] == 0) { /* not protected */
switch (info->vendor) {
case CFI_CMDSET_INTEL_STANDARD:
case CFI_CMDSET_INTEL_EXTENDED:
flash_write_cmd (info, sect, 0, FLASH_CMD_CLEAR_STATUS);
flash_write_cmd (info, sect, 0, FLASH_CMD_BLOCK_ERASE);
flash_write_cmd (info, sect, 0, FLASH_CMD_ERASE_CONFIRM);
break;
case CFI_CMDSET_AMD_STANDARD:
case CFI_CMDSET_AMD_EXTENDED:
flash_unlock_seq (info, sect);
flash_write_cmd (info, sect, AMD_ADDR_ERASE_START,
AMD_CMD_ERASE_START);
flash_unlock_seq (info, sect);
flash_write_cmd (info, sect, 0, AMD_CMD_ERASE_SECTOR);
break;
case CFI_SST_1601_LZD: 上面两个是intel和amd的nor flash擦出方法,这里是我加的sst39vf1601的擦出方法
flash_write_cmd (info, 0, 0x5555, 0xaa);
flash_write_cmd (info, 0, 0x2aaa, 0x55);
flash_write_cmd (info, 0, 0x5555,0x80);
flash_write_cmd (info, 0, 0x5555,0xaa);
flash_write_cmd (info, 0, 0x2aaa, 0x55);
flash_write_cmd (info, sect, 0, 0x50); 确定要擦除的扇区
break;
default:
debug ("Unkown flash vendor %d/n",
info->vendor);
break;
}
if (flash_full_status_check 擦出要花费一段时间,这里进行检查擦除是否ok。在下面
(info, sect, info->erase_blk_tout, "erase")) {
rcode = 1;
} else
putc ('.');
}
}
puts (" done/n");
return rcode;
}
先看看下面这个函数的行为。
typedef unsigned long flash_sect_t;
它是把cmd这个命令写到一个地址处,这个地址是经过精心安排的,让我们看看这个地址是怎么来的 ^_^
static void flash_write_cmd (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd)
上面函数用到的数据结构
typedef union {
volatile unsigned char *cp;
volatile unsigned short *wp;
volatile unsigned long *lp;
volatile unsigned long long *llp;
} cfiptr_t;
typedef union {
unsigned char c;
unsigned short w;
unsigned long l;
unsigned long long ll;
} cfiword_t;
#define FLASH_CFI_8BIT 0x01
#define FLASH_CFI_16BIT 0x02 我的是16bit数据宽度的,就是每个地址产生16bit数据
#define FLASH_CFI_32BIT 0x04
#define FLASH_CFI_64BIT 0x08
/*
* Write a proper sized command to the correct address
*/
static void flash_write_cmd (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd)
{
volatile cfiptr_t addr;
cfiword_t cword;
addr.cp = flash_make_addr (info, sect, offset); 这个函数产生一个地址,根据sect和offset计算出,在下面
flash_make_cmd (info, cmd, &cword);
switch (info->portwidth) {
case FLASH_CFI_8BIT:
debug ("fwc addr %p cmd %x %x 8bit x %d bit/n", addr.cp, cmd,
cword.c, info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
*addr.cp = cword.c;
break;
case FLASH_CFI_16BIT:
debug ("fwc addr %p cmd %x %4.4x 16bit x %d bit/n", addr.wp,
cmd, cword.w,
info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
*addr.wp = cword.w; 这里就是写命令的过程。
break;
case FLASH_CFI_32BIT:
debug ("fwc addr %p cmd %x %8.8lx 32bit x %d bit/n", addr.lp,
cmd, cword.l,
info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
*addr.lp = cword.l;
break;
case FLASH_CFI_64BIT:
*addr.llp = cword.ll;
break;
}
}
inline uchar *flash_make_addr (flash_info_t * info, flash_sect_t sect, uint offset)
{
return ((uchar *) (info->start[sect] + (offset * info->portwidth)));
}
上面就是计算地址的过程,这个地址根据sect和offset计算出确切的地址,比如我们要访问0x5555这个地址,可以是sect= 0,offset = 0x5555
然后把offset左移一位,这个地址就是nor flash想要的地址,从nor的角度看到的是0x5555这个地址,从cpu来说,它输出的地址是 0x5555<<1。
如果要访问第n块的第0x5555这个地址,那么从cpu的角度输出的地址是 n*0x10000+0x5555<<1
下面是产生命令的过程
static void flash_make_cmd (flash_info_t * info, uchar cmd, void *cmdbuf)
{
int i;
uchar *cp = (uchar *) cmdbuf;
for (i = info->portwidth; i > 0; i--)
*cp++ = (i & (info->chipwidth - 1)) ? '/0' : cmd; 小端模式,在最低的那个byte放上命令字节,很妙
}
地址和命令字节就是这样产生的。回去看erase。
下面的是检查nor的状态的函数,也就相当于检查擦除操作成功与否的函数,擦除成功要返回0
成功后还要复位一下,使nor进入到read mode
他的第三个参数是最大的等待时间,毫秒为单位,最大为32毫秒
static int flash_full_status_check(info, 15, info->erase_blk_tout, "erase")
static int flash_full_status_check (flash_info_t * info, flash_sect_t sector,
ulong tout, char *prompt)
{
int retcode;
retcode = flash_status_check (info, sector, tout, prompt);
switch (info->vendor) {
case CFI_CMDSET_INTEL_EXTENDED:
case CFI_CMDSET_INTEL_STANDARD:
if ((retcode == ERR_OK)
&& !flash_isequal (info, sector, 0, FLASH_STATUS_DONE)) {
retcode = ERR_INVAL;
printf ("Flash %s error at address %lx/n", prompt,
info->start[sector]);
if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS | FLASH_STATUS_PSLBS)) {
puts ("Command Sequence Error./n");
} else if (flash_isset (info, sector, 0, FLASH_STATUS_ECLBS)) {
puts ("Block Erase Error./n");
retcode = ERR_NOT_ERASED;
} else if (flash_isset (info, sector, 0, FLASH_STATUS_PSLBS)) {
puts ("Locking Error/n");
}
if (flash_isset (info, sector, 0, FLASH_STATUS_DPS)) {
puts ("Block locked./n");
retcode = ERR_PROTECTED;
}
if (flash_isset (info, sector, 0, FLASH_STATUS_VPENS))
puts ("Vpp Low Error./n");
}
flash_write_cmd (info, sector, 0, info->cmd_reset);
break;
default: 我添加的复位命令,为sst39vf1601
flash_write_cmd (info, 0, 0, info->cmd_reset);
break;
}
return retcode;
}
它把参数全部传递给了 flash_status_check 函数,寄希望于他能够返回0。
static int flash_status_check (flash_info_t * info, flash_sect_t sector,
ulong tout, char *prompt)
{
ulong start;
tout *= CFG_HZ/1000;
/* Wait for command completion */
start = get_timer (0);
while (flash_is_busy (info, sector)) {
if (get_timer (start) > tout) {
printf ("Flash %s timeout at address %lx data %lx/n",
prompt, info->start[sector],
flash_read_long (info, sector, 0));
flash_write_cmd (info, sector, 0, info->cmd_reset);
return ERR_TIMOUT;
}
udelay (1); /* also triggers watchdog */
}
return ERR_OK;
}
/* the PWM TImer 4 uses a counter of 15625 for 10 ms, so we need */
/* it to wrap 100 times (total 1562500) to get 1 sec. */
#define CFG_HZ 1562500
#define ERR_OK 0
显然希望flash_is_busy (info, sector) 在时间到期之前返回0
这个函数完成的任务是查询flash忙不忙,不忙返回0
flash_is_busy (info, sector)
static int flash_is_busy (flash_info_t * info, flash_sect_t sect)
{
int retval;
switch (info->vendor) {
case CFI_CMDSET_INTEL_STANDARD:
case CFI_CMDSET_INTEL_EXTENDED:
retval = !flash_isset (info, sect, 0, FLASH_STATUS_DONE);
break;
case CFI_CMDSET_AMD_STANDARD:
case CFI_CMDSET_AMD_EXTENDED:
retval = flash_toggle (info, sect, 0, AMD_STATUS_TOGGLE);
break;
default:
我添加的 for sst39vf1601,这个函数的功能见下面
retval = !flash_isset (info, sect, 0, FLASH_STATUS_DONE); 经过这里的取反后,返回0
retval = 0;
}
debug ("flash_is_busy: %d/n", retval);
return retval;
}
下面的这个函数测试FLASH_STATUS_DONE(flash 状态完成标志),返回这个标志的数值
flash_isset (info, sect, 0, FLASH_STATUS_DONE)
static int flash_isset (flash_info_t * info, flash_sect_t sect, uint offset, uchar cmd)
{
cfiptr_t cptr;
cfiword_t cword;
int retval;
cptr.cp = flash_make_addr (info, sect, offset); 这里无所谓
flash_make_cmd (info, cmd, &cword);
switch (info->portwidth) {
case FLASH_CFI_8BIT:
retval = ((cptr.cp[0] & cword.c) == cword.c);
break;
case FLASH_CFI_16BIT:
retval = ((cptr.wp[0] & cword.w) == cword.w); 从地址处取得一个16bit数,与要求的poll date相比较,不忙返回真
break;
case FLASH_CFI_32BIT:
retval = ((cptr.lp[0] & cword.l) == cword.l);
break;
case FLASH_CFI_64BIT:
retval = ((cptr.llp[0] & cword.ll) == cword.ll);
break;
default:
retval = 0;
break;
}
return retval;
}
到了这里,我们的程序,已经擦除了要求的扇区了。^_^
返回int saveenv(void)
3.开始写进去
写成功,返回0,这个(char *)0xxxxx 是什么呢?下面告诉你,反正是个内存地址而不是0xf0000
rc = flash_write((char *)0xxxxx, 0xf0000, 0x10000); 开始写进去
env_t *env_ptr = (env_t *)CFG_ENV_ADDR = 0xf0000;
static env_t *flash_addr = (env_t *)CFG_ENV_ADDR = 0xf0000;
/*-----------------------------------------------------------------------
* Copy memory to flash.
* Make sure all target addresses are within Flash bounds,
* and no protected sectors are hit.
* Returns:
* ERR_OK 0 - OK
* ERR_TIMOUT 1 - write timeout
* ERR_NOT_ERASED 2 - Flash not erased
* ERR_PROTECTED 4 - target range includes protected sectors
* ERR_INVAL 8 - target address not in Flash memory
* ERR_ALIGN 16 - target address not aligned on boundary
* (only some targets require alignment)
*/
int
flash_write (char *src, ulong addr, ulong cnt)
{
int i;
ulong end = addr + cnt - 1; 环境参数的结束地址?在flash里的结束地址
flash_info_t *info_first = addr2info (addr); 这是在做什么呢?下面有解释,他返回了这个地址对应的flash_info结构
flash_info_t *info_last = addr2info (end ); 同样,返回了对应flash_info结构,这两个flash_info可能不同,但是这里肯定相同。
flash_info_t *info;
if (cnt == 0) { 不写也可以认为是成功的写操作
return (ERR_OK);
}
if (!info_first || !info_last) { 出错判断
return (ERR_INVAL);
}
for (info = info_first; info <= info_last; ++info) { 这个多余了,只有一个flash_info结构
ulong b_end = info->start[0] + info->size; /* bank end addr */ 结束地址2M
short s_end = info->sector_count - 1; 扇区数量
for (i=0; i<info->sector_count; ++i) { 扫描所有扇区
ulong e_addr = (i == s_end) ? b_end : info->start[i + 1];
if ((end >= info->start[i]) && (addr < e_addr) &&
(info->protect[i] != 0) ) {
return (ERR_PROTECTED);
}
}
}
/* finally write data to flash */
for (info = info_first; info <= info_last && cnt>0; ++info) { 只有一遍
ulong len;
len = info->start[0] + info->size - addr;
if (len > cnt)
len = cnt;
if ((i = write_buff(info, (uchar *)src, addr, len)) != 0) { 这里是真正的写操作
return (i);
}
cnt -= len;
addr += len;
src += len;
}
return (ERR_OK);
}
addr2info 他根据给定的地址,确认自己所在那个bank,然后返回这个bank的flash_info结构。
flash_info_t *
addr2info (ulong addr)
{
flash_info_t *info;
int i;
for (i=0, info=&flash_info[0]; i<CFG_MAX_FLASH_BANKS; ++i, ++info) { 扫描所有bank
if (info->flash_id != FLASH_UNKNOWN &&
addr >= info->start[0] &&
/* WARNING - The '- 1' is needed if the flash
* is at the end of the address space, since
* info->start[0] + info->size wraps back to 0.
* Please don't change this unless you understand this.
*/
addr <= info->start[0] + info->size - 1) {
return (info);
}
}
return (NULL);
}
希望这个函数返回0
write_buff(info, (uchar *)src, addr, len)
write_buff(info, (uchar *)0x0xxxxx, 0xf0000, 0x10000)
/*-----------------------------------------------------------------------
* Copy memory to flash, returns:
* 0 - OK
* 1 - write timeout
* 2 - Flash not erased
*/
int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
{
ulong wp;
ulong cp;
int aln;
cfiword_t cword;
int i, rc;
/* get lower aligned address */
/* get lower aligned address */
wp = (addr & ~(info->portwidth - 1)); 不要地址的最x(0,1,2)地位,分别对应8,16,32bit数据的nor
/* handle unaligned start */ 处理没有对齐的部分
if ((aln = addr - wp) != 0) {
cword.l = 0;
cp = wp;
for (i = 0; i < aln; ++i, ++cp)
flash_add_byte (info, &cword, (*(uchar *) cp));
for (; (i < info->portwidth) && (cnt > 0); i++) {
flash_add_byte (info, &cword, *src++);
cnt--;
cp++;
}
for (; (cnt == 0) && (i < info->portwidth); ++i, ++cp)
flash_add_byte (info, &cword, (*(uchar *) cp));
if ((rc = flash_write_cfiword (info, wp, cword)) != 0)
return rc;
wp = cp;
}
/* handle the aligned part */
while (cnt >= info->portwidth) { 处理对齐的部分,从这里分析 呵呵。
cword.l = 0;
for (i = 0; i < info->portwidth; i++) { 我的是16位的,所以循环了两次
flash_add_byte (info, &cword, *src++); 下边有解释
}
if ((rc = flash_write_cfiword (info, wp, cword)) != 0)
return rc;
wp += info->portwidth;
cnt -= info->portwidth;
}
if (cnt == 0) {
return (0);
}
/*
* handle unaligned tail bytes
*/
cword.l = 0;
for (i = 0, cp = wp; (i < info->portwidth) && (cnt > 0); ++i, ++cp) {
flash_add_byte (info, &cword, *src++);
--cnt;
}
for (; i < info->portwidth; ++i, ++cp) {
flash_add_byte (info, &cword, (*(uchar *) cp));
}
return flash_write_cfiword (info, wp, cword);
}
将内存中2字节的数据组合到一起。小端模式。
static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c)
{
unsigned short w;
unsigned int l;
unsigned long long ll;
switch (info->portwidth) {
case FLASH_CFI_8BIT:
cword->c = c;
break;
case FLASH_CFI_16BIT: 这里,
w = c;
w <<= 8;
cword->w = (cword->w >> 8) | w; 这就是小端的方式,后来的那个8bit 放到了这个字的高 byte,把这个16BIT 的数据组合到一起
break;
case FLASH_CFI_32BIT:
l = c;
l <<= 24;
cword->l = (cword->l >> 8) | l;
break;
case FLASH_CFI_64BIT:
ll = c;
ll <<= 56;
cword->ll = (cword->ll >> 8) | ll;
break;
}
}
wp是待写入的地址,cword是要写入的数据
flash_write_cfiword (info, wp, cword)
static int flash_write_cfiword (flash_info_t * info, ulong dest,
cfiword_t cword)
{
cfiptr_t ctladdr;
cfiptr_t cptr;
int flag;
ctladdr.cp = flash_make_addr (info, 0, 0);
cptr.cp = (uchar *) dest;
/* Check if Flash is (sufficiently) erased */
switch (info->portwidth) {
case FLASH_CFI_8BIT:
flag = ((cptr.cp[0] & cword.c) == cword.c);
break;
case FLASH_CFI_16BIT:
flag = ((cptr.wp[0] & cword.w) == cword.w); 这里,如果刚才的擦除操作成功的话,给定地址处应该为0xffff,这里flag应该为1
break;
case FLASH_CFI_32BIT:
flag = ((cptr.lp[0] & cword.l) == cword.l);
break;
case FLASH_CFI_64BIT:
flag = ((cptr.llp[0] & cword.ll) == cword.ll);
break;
default:
return 2;
}
if (!flag)
return 2;
/* Disable interrupts which might cause a timeout here */
flag = disable_interrupts ();
switch (info->vendor) {
case CFI_CMDSET_INTEL_EXTENDED:
case CFI_CMDSET_INTEL_STANDARD:
flash_write_cmd (info, 0, 0, FLASH_CMD_CLEAR_STATUS);
flash_write_cmd (info, 0, 0, FLASH_CMD_WRITE);
break;
case CFI_CMDSET_AMD_EXTENDED:
case CFI_CMDSET_AMD_STANDARD:
flash_unlock_seq (info, 0);
flash_write_cmd (info, 0, AMD_ADDR_START, AMD_CMD_WRITE);
break;
default: 我为sst39vf1601加的写命令序列
flash_write_cmd (info, 0, 0x5555, 0xaa);
flash_write_cmd (info, 0, 0x2aaa, 0x55);
flash_write_cmd (info, 0, 0x5555, 0xa0);
}
switch (info->portwidth) {
case FLASH_CFI_8BIT:
cptr.cp[0] = cword.c;
break;
case FLASH_CFI_16BIT:
cptr.wp[0] = cword.w; 到了这里才真正把数据写到数据线上
break;
case FLASH_CFI_32BIT:
cptr.lp[0] = cword.l;
break;
case FLASH_CFI_64BIT:
cptr.llp[0] = cword.ll;
break;
}
/* re-enable interrupts if necessary */
if (flag)
enable_interrupts ();
return flash_full_status_check (info, find_sector (info, dest), 这一步检查写入操作的成功吗?这个函数上面已经作了修改了
info->write_tout, "write"); 注意这里的info->write_tout初始化的时候正确不正确,不然可有你好看的
}
到了这里,写入操作完毕。
上面的所有操作都依赖正确的初始化配置,下面分析下flash的初始化过程
这个函数填写flash_info[i].flash_id,获取flash的大小,然后把需要保护的区间,软件保护起来。而flash_get_size完成了大部分初始化工作
static ulong bank_base[CFG_MAX_FLASH_BANKS] = CFG_FLASH_BANKS_LIST;
flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* FLASH chips info */
unsigned long flash_init (void)
{
unsigned long size = 0;
int i;
/* Init: no FLASHes known */
for (i = 0; i < CFG_MAX_FLASH_BANKS; ++i) {
flash_info[i].flash_id = SST_ID_xF1601; 填写flash_id
size += flash_info[i].size = flash_get_size (bank_base[i], i); 获取nor容量
if (flash_info[i].flash_id == FLASH_UNKNOWN) {
printf ("## Unknown FLASH on Bank %d - Size = 0x%08lx = %ld MB/n",
i, flash_info[i].size, flash_info[i].size << 20);
}
}
/* Monitor protection ON by default */
flash_protect (FLAG_PROTECT_SET, 这里对uboot所在的扇区加锁
CFG_MONITOR_BASE,
CFG_MONITOR_BASE + monitor_flash_len - 1,
flash_get_info(CFG_MONITOR_BASE));
/* Environment protection ON by default */
flash_protect (FLAG_PROTECT_SET, 这里对存储 环境参数 的扇区加锁
CFG_ENV_ADDR,
CFG_ENV_ADDR + CFG_ENV_SECT_SIZE - 1,
flash_get_info(CFG_ENV_ADDR));
return (size);
}
主要分析下flash_get_size这个函数
现看看bank_base[0]是什么
static ulong bank_base[CFG_MAX_FLASH_BANKS] = CFG_FLASH_BANKS_LIST;
#define CFG_FLASH_BANKS_LIST { CFG_FLASH_BASE }=0x00000000
就是bank_base[1] = {0x00000000};所以
flash_get_size (0x00000000,0)
/*
* The following code cannot be run from FLASH!
*
*/
ulong flash_get_size (ulong base, int banknum)
{
flash_info_t *info = &flash_info[banknum];
int i, j;
flash_sect_t sect_cnt;
unsigned long sector;
unsigned long tmp;
int size_ratio;
uchar num_erase_regions;
int erase_region_size;
int erase_region_count;
info->start[0] = base;
if (flash_detect_cfi (info)) { 首先探测这个nor是不是cfi接口兼容的。
debug ("pass flash_detect_cfi()/n");
info->vendor = flash_read_ushort (info, 0, 0); 填充flash_info.vendor 这个vendor是0x00bf,但是在这里读错了,
因为他返回的是0x4bbf,不是0xbf
info->vendor = CFI_SST_1601_LZD; 所以我人为的加了下面的语句,呵呵。可能是读的太快了吧,不管了。
switch (info->vendor) {
case CFI_SST_1601_LZD: 这个是我加的,CFI_SST_1601_LZD = 0x00bf
default:
info->cmd_reset = SST_FLASH_CMD_RESET; 填充flash_info.cmd_reset,设置复位命令
break;
}
debug ("manufacturer is %x/n", info->vendor);
size_ratio = info->portwidth / info->chipwidth;
/* if the chip is x8/x16 reduce the ratio by half */
if ((info->interface == FLASH_CFI_X8X16) 这里不满足条件
&& (info->chipwidth == FLASH_CFI_BY8)) {
size_ratio >>= 1;
}
num_erase_regions = flash_read_uchar (info, FLASH_OFFSET_NUM_ERASE_REGIONS);
debug ("size_ratio %d port %d bits chip %d bits/n",
size_ratio, info->portwidth << CFI_FLASH_SHIFT_WIDTH,
info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
debug ("found %d erase regions/n", num_erase_regions);
sect_cnt = 0;
sector = base;
erase_region_size = 0xffff+1;
erase_region_count = 32;
debug ("erase_region_count = %d erase_region_size = %d/n",
erase_region_count, erase_region_size);
for (j = 0; j < erase_region_count; j++) { 这里完成了各个扇区地址的确定
info->start[sect_cnt] = sector;
sector += (erase_region_size * size_ratio);
info->protect[sect_cnt] = 0; 起初没有任何保护
sect_cnt++;
}
info->sector_count = sect_cnt; 确定扇区的数量
/* multiply the size by the number of chips */
info->size = (1 << flash_read_uchar (info, FLASH_OFFSET_SIZE)) * size_ratio; 确定大小,2^15=2M byte
info->buffer_size = (1 << flash_read_ushort (info, 0, FLASH_OFFSET_BUFFER_SIZE)); 不支持
tmp = 1 << flash_read_uchar (info, FLASH_OFFSET_ETOUT);
info->erase_blk_tout = (tmp * (1 << flash_read_uchar (info, FLASH_OFFSET_EMAX_TOUT)));
debug ("info->erase_blk_tout:%d/n", info->erase_blk_tout);
tmp = (1 << flash_read_uchar (info, FLASH_OFFSET_WBTOUT)) *
(1 << flash_read_uchar (info, FLASH_OFFSET_WBMAX_TOUT));
info->buffer_write_tout = tmp / 1000 + (tmp % 1000 ? 1 : 0); /* round up when converting to ms */
debug ("info->buffer_write_tout:%d/n", info->buffer_write_tout);
tmp = (1 << flash_read_uchar (info, FLASH_OFFSET_WTOUT)) *
(1 << flash_read_uchar (info, FLASH_OFFSET_WMAX_TOUT));
info->write_tout = ~0; 这里要把得到的us变成ms,应该是1ms了,因为datasheet上说最大25us。
debug ("info->write_tout:%d/n", info->write_tout); 但是我改成了~0,因为这个参数让我写不成功,所以我增加了timeout
info->flash_id = FLASH_MAN_CFI;
if ((info->interface == FLASH_CFI_X8X16) && (info->chipwidth == FLASH_CFI_BY8)) {
info->portwidth >>= 1; /* XXX - Need to test on x8/x16 in parallel. */
}
}
flash_write_cmd (info, 0, 0, info->cmd_reset); 这里必须要复位一下,回到read 模式,否则就处在cfi模式里,出不来了。150+70ns就ok。
debug ("info->size : %d/n", info->size);
return (info->size);
}
到了这里就ok了。
这个函数将进入cfi的QRY模式,并完成chipwidth和portwidth的设置,都是2,返回1
static int flash_detect_cfi (flash_info_t * info)
{
debug ("flash detect cfi/n");
for (info->portwidth = CFG_FLASH_CFI_WIDTH;
info->portwidth <= FLASH_CFI_64BIT; info->portwidth <<= 1) {
for (info->chipwidth = FLASH_CFI_BY8;
info->chipwidth <= info->portwidth;
info->chipwidth <<= 1) {
flash_write_cmd (info, 0, 0x5555, 0xaa); 进入cfi的QRY模式
flash_write_cmd (info, 0, 0x2aaa, 0x55);
flash_write_cmd (info, 0, 0x5555, 0x98);
if (flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP, 'Q')
&& flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 1, 'R')
&& flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 2, 'Y')) {
info->interface = flash_read_ushort (info, 0, FLASH_OFFSET_INTERFACE);
debug ("device interface is %d/n",
info->interface);
debug ("found port %d chip %d ",
info->portwidth, info->chipwidth);
debug ("port %d bits chip %d bits/n",
info->portwidth << CFI_FLASH_SHIFT_WIDTH,
info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
return 1;
}
}
}
debug ("not found/n");
return 0;
}
下面我们来看看这个神秘的0xxxxx地址是怎么确定的,可以猜测到,他是个内存地址,而且,这个地址里有环境变量的值,
而且这个环境变量的值是从0xf0000这块flash里拷贝到内存的。:-)
在board.c里有下面的初始化指针数组
init_fnc_t *init_sequence[] = {
cpu_init, /* basic cpu dependent setup */
board_init, /* basic board dependent setup */
interrupt_init, /* set up exceptions */
env_init, 就是这个函数负责环境参数的初始化
......
在common/env_flash.c中
不要忘了这个 env_t *env_ptr = (env_t *)CFG_ENV_ADDR 哦!
CFG_ENV_ADDR 这个预定义 确定了我们的环境变量在nor flash的什么位置。
int env_init(void)
{
if (crc32(0, env_ptr->data, ENV_SIZE) == env_ptr->crc) { 如果这段数据通过了crc校验,就设置下面两个变量。
gd->env_addr = (ulong)&(env_ptr->data); 这个全局量确定了环境数据的地址
gd->env_valid = 1; 这个量指示,用norflash 里的环境变量数据,程序当然会运行到这里。
return(0);
}
gd->env_addr = (ulong)&default_environment[0]; 否则就用默认的环境参数
gd->env_valid = 0; 相反
return (0);
}
typedef struct environment_s {
unsigned long crc; /* CRC32 over data bytes */ 前四个字节是crc校验的值
unsigned char data[ENV_SIZE]; /* Environment data */ 这里是环境变量字符串
} env_t;
# define ENV_HEADER_SIZE (sizeof(unsigned long))
#define ENV_SIZE (CFG_ENV_SIZE - ENV_HEADER_SIZE)
出于好奇,看看 default_environment是什么。
uchar default_environment[] = {
#ifdef CONFIG_BOOTARGS
"bootargs=" CONFIG_BOOTARGS "/0"
#endif
#ifdef CONFIG_BOOTCOMMAND
"bootcmd=" CONFIG_BOOTCOMMAND "/0"
#endif
#ifdef CONFIG_RAMBOOTCOMMAND
"ramboot=" CONFIG_RAMBOOTCOMMAND "/0"
#endif
#ifdef CONFIG_NFSBOOTCOMMAND
"nfsboot=" CONFIG_NFSBOOTCOMMAND "/0"
#endif
#if defined(CONFIG_BOOTDELAY) && (CONFIG_BOOTDELAY >= 0)
"bootdelay=" MK_STR(CONFIG_BOOTDELAY) "/0"
#endif
#if defined(CONFIG_BAUDRATE) && (CONFIG_BAUDRATE >= 0)
"baudrate=" MK_STR(CONFIG_BAUDRATE) "/0"
#endif
#ifdef CONFIG_LOADS_ECHO
"loads_echo=" MK_STR(CONFIG_LOADS_ECHO) "/0"
#endif
#ifdef CONFIG_ETHADDR
"ethaddr=" MK_STR(CONFIG_ETHADDR) "/0"
#endif
#ifdef CONFIG_ETH1ADDR
"eth1addr=" MK_STR(CONFIG_ETH1ADDR) "/0"
#endif
#ifdef CONFIG_ETH2ADDR
"eth2addr=" MK_STR(CONFIG_ETH2ADDR) "/0"
#endif
#ifdef CONFIG_ETH3ADDR
"eth3addr=" MK_STR(CONFIG_ETH3ADDR) "/0"
#endif
#ifdef CONFIG_IPADDR
"ipaddr=" MK_STR(CONFIG_IPADDR) "/0"
#endif
#ifdef CONFIG_SERVERIP
"serverip=" MK_STR(CONFIG_SERVERIP) "/0"
#endif
#ifdef CFG_AUTOLOAD
"autoload=" CFG_AUTOLOAD "/0"
#endif
#ifdef CONFIG_PREBOOT
"preboot=" CONFIG_PREBOOT "/0"
#endif
#ifdef CONFIG_ROOTPATH
"rootpath=" MK_STR(CONFIG_ROOTPATH) "/0"
#endif
#ifdef CONFIG_GATEWAYIP
"gatewayip=" MK_STR(CONFIG_GATEWAYIP) "/0"
#endif
#ifdef CONFIG_NETMASK
"netmask=" MK_STR(CONFIG_NETMASK) "/0"
#endif
#ifdef CONFIG_HOSTNAME
"hostname=" MK_STR(CONFIG_HOSTNAME) "/0"
#endif
#ifdef CONFIG_BOOTFILE
"bootfile=" MK_STR(CONFIG_BOOTFILE) "/0"
#endif
#ifdef CONFIG_LOADADDR
"loadaddr=" MK_STR(CONFIG_LOADADDR) "/0"
#endif
#ifdef CONFIG_CLOCKS_IN_MHZ
"clocks_in_mhz=1/0"
#endif
#if defined(CONFIG_PCI_BOOTDELAY) && (CONFIG_PCI_BOOTDELAY > 0)
"pcidelay=" MK_STR(CONFIG_PCI_BOOTDELAY) "/0"
#endif
#ifdef CONFIG_EXTRA_ENV_SETTINGS
CONFIG_EXTRA_ENV_SETTINGS
#endif
"/0"
};
符合道理,呵呵。现在
gd->env_addr = (ulong)&(env_ptr->data) = 0xf0000 + 4
gd->env_valid = 1;
显然,环境变量如果只在flash中,setenv这样的命令,将不能工作,因为setenv只是在内存中改变环境参数的值,
printenv可以查看到改变的值。
在下面的函数中
void env_relocate (void)
{
/*
* We must allocate a buffer for the environment
*/
env_ptr = (env_t *)malloc (CFG_ENV_SIZE); 首先分配内存,看到希望了,^_^
/*
* After relocation to RAM, we can always use the "memory" functions
*/
env_get_char = env_get_char_memory;
if (gd->env_valid == 0) { 经过刚才env_init的初始化,gd->env_valid 这个值是1
if (sizeof(default_environment) > ENV_SIZE)
{
puts ("*** Error - default environment is too large/n/n");
return;
}
memset (env_ptr, 0, sizeof(env_t));
memcpy (env_ptr->data,
default_environment,
sizeof(default_environment));
env_crc_update ();
gd->env_valid = 1;
}
else { 所以到了这里
env_relocate_spec ();
}
gd->env_addr = (ulong)&(env_ptr->data);
}
void env_relocate_spec (void)
{
memcpy (env_ptr, (void*)flash_addr, CFG_ENV_SIZE);
}
memcpy (env_ptr, 0xf0000, 0x10000);
上面分析0xxxxx地址是什么的过程是一个反推过程。
(完)
mini2440 支持 sst39vf1601,实现saveenv。
最新推荐文章于 2021-05-14 08:13:07 发布