#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 */
//下面是portwidth、chipwidth的定义 /* * Values for the width of the port*/ #define FLASH_CFI_8BIT 0x01 #define FLASH_CFI_16BIT 0x02 #define FLASH_CFI_32BIT 0x04 #define FLASH_CFI_64BIT 0x08 /* * Values for the width of the chip */ #define FLASH_CFI_BY8 0x01 #define FLASH_CFI_BY16 0x02 #define FLASH_CFI_BY32 0x04 #define FLASH_CFI_BY64 0x08
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) { /*保护的不擦除*/ 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; } //擦除要花费一段时间,这里进行检查擦除是否ok。在下面 if (flash_full_status_check(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数据,就是portwidth的值 #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); //----addr.wp、cword.w就和这里有关,仔细研究 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);//2<<3 = 16 *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; //直接修改了cp指向的地址的值,其实就是联合体cfiword_t cword的值,因此不需要返回 //小端模式,在最低的那个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相比较,不忙返回真???
//目前就定位到这里,retval返回0,读出的值与写入的值不相等。 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;
|