/*----------------------------------------------------------------------- * 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 这里对uboot所在的扇区加锁*/ flash_protect (FLAG_PROTECT_SET, 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地址是什么的过程是一个反推过程。
|