Uboot下的Nor Flash的驱动以及使用
Uboot 下 CFI Nor Flash 的使用 韩大卫@吉林师范大学 2015.1.23 Flash : Micron Technology. 32MB. Uboot: 2_3_0 CPU平台: Cavium Inc 交叉编译器: mips64-octeon-linux-gnu-gcc (Cavium Inc. Version: 2_3_0 build 128) 4.3.3 nor flash 的使用特点是 :? 读操作可以按地址读,写之前必须进行擦除,一旦擦除必须擦除整个扇区.? 新型的flash 使用3V 的电压便可以进行整个扇区的擦除和写入操作 任何芯片的使用,都离不开驱动的支持. uboot下的nor flash的驱动逻辑非常简单. 而且,对于符合 CFI ( Common Flash Interface )规范的flash芯片,驱动有很大的通用性.? uboot 提供了很好的 flash 驱动逻辑 和 flash的使用范例,这些基本的使用方法在linux里也是同样的逻辑,只不过linux下需要加上一层分区信息. 结合flash 芯片手册,可以对nor flash的使用逻辑有较为清晰的理解.? nor flash的驱动初始化部分: arch/mips/cpu/octeon/start.S board_init_r? -> flash_init() drivers/mtd/cfi_flash.c unsigned long flash_init (void){? ? ? for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) { ? ? ? ? flash_info[i].flash_id = FLASH_UNKNOWN; ?? ? ? ? ? ? ? ? ? … ? ? ? //由于使用的flash 是新型的CFI 规范的flash,没有使用 CONFIG_FLASH_CFI_LEGACY 这个宏,所以flash_detect_legacy直接返回0 ? ? ? ? if (!flash_detect_legacy(cfi_flash_bank_addr(i),i)) ? ? ? ? ? ? flash_get_size(cfi_flash_bank_addr(i),i); ? ? ? ? size += flash_info[i].size; ulong flash_get_size (phys_addr_t base,int banknum) {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? flash_info_t *info = &flash_info[banknum]; ? ? int i,j;? ? ? ? ? ? ? ? ? ? ? flash_sect_t sect_cnt; ? ? phys_addr_t sector;? ? ? ? unsigned long tmp; ? ? ? ? int size_ratio;? ? ? ? ? ? ? ? uchar num_erase_regions; ? ? int erase_region_size; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int erase_region_count; ? ? struct cfi_qry qry;? ? ? ? unsigned long max_size; ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? memset(&qry,sizeof(qry)); ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->ext_addr = 0;? ? ? ? info->cfi_version = 0;? #ifdef CONFIG_SYS_FLASH_PROTECTION ? ? info->legacy_unlock = 0;? #endif ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->start[0] = (ulong)map_physmem(base,info->portwidth,MAP_NOCACHE); ? ? ? ? ? ? ? ? ? ? //如果是CFI 接口,那么有统一的查询规范,将查询到的信息保存到 qry中? ? ? ? ? ? ? ? ? ? if (flash_detect_cfi (info,&qry)) { ? ? ? ? info->vendor = le16_to_cpu(qry.p_id); ? ? ? ? info->ext_addr = le16_to_cpu(qry.p_adr) * 2;? ? ? ? ? debug("extended address is 0x%xn",info->ext_addr); ? ? ? ? num_erase_regions = qry.num_erase_regions; ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (info->ext_addr) { ? ? #define FLASH_OFFSET_CFI_RESP ? ? ? 0x20? flash_detect_cfi -> static int __flash_detect_cfi (flash_info_t * info,struct cfi_qry *qry) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int cfi_offset; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? for (cfi_offset=0;? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? cfi_offset < sizeof(flash_offset_cfi) / sizeof(uint); ?? ? ? ? cfi_offset++) {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /* Issue FLASH reset command */ ? ? ? ? ? ? ? ? flash_cmd_reset(info);? ? ? ? ? ? ? ? ? ? ? ? ? flash_write_cmd (info,flash_offset_cfi[cfi_offset], ?? ? ? ? ? ? ? ? FLASH_CMD_CFI); ? ? ? ? ? ? ?? ? //向0x20 地址进行查询,? CFI 规定,前三个字符应该是 Q,R,Y? ? ? ? ? ? if (flash_isequal (info,FLASH_OFFSET_CFI_RESP,'Q') ? ? ? ? ? ? && flash_isequal (info,FLASH_OFFSET_CFI_RESP + 2,'R') ? ? ? ? ? ? && flash_isequal (info,FLASH_OFFSET_CFI_RESP + 4,'Y')) { ?? ? ? ? ? //如果确认为CFI 规范,那么就按照 struct cfi_qry数据结构进行查询? ? ? ? ? ? ? flash_read_cfi(info,qry,FLASH_OFFSET_CFI_RESP,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sizeof(struct cfi_qry)); ?? … ? ? //在进行CFI 规范查询之后,还要将addr_unlock1,addr_unlock2 进行赋值,? 这两个地址分别表示8位宽的地址和16位宽的地址,可以实现byte和word的操作.? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //一般地,我们只使用addr_unlock1 ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //在一些代码里,这两个数值就通过宏定义来实现的 ? ? ? ? ? ? info->addr_unlock1 = 0xaaa; ? ? ? ? ? ? info->addr_unlock2 = 0x555;? ? ? ? ? … } 下面是flash 芯片手册里CFI 规范查询的信息: cfi_qry 定义: struct cfi_qry { ? ? u8? qry[3]; //保存 Q,Y ? ? u16 p_id; ? ? //Primary algorithm? ? ? u16 p_adr; ? //Address for primary algorithm? ? ? u16 a_id;? ? //Alternate? ? ? u16 a_adr;? ? //Address for alternate? ? ? u8? vcc_min;? // 最小Vcc ? ? u8? vcc_max;? //最大Vcc ? ? u8? vpp_min; ? //最小Vpp ? ? u8? vpp_max;? ? //最大Vpp ? ? u8? word_write_timeout_typ; ? //字节写典型超时 ? ? u8? buf_write_timeout_typ;? ? //缓存写典型超时 ? ? u8? block_erase_timeout_typ;? //块擦除典型超时 ? ? u8? chip_erase_timeout_typ;? ? //整片擦除典型超时 ? ? u8? word_write_timeout_max;? ? //字节写最大超时 ? ? u8? buf_write_timeout_max;? ? ? //缓存写最大超时 ? ? u8? block_erase_timeout_max;? ? //块写最大超时 ? ? u8? chip_erase_timeout_max;? ? ? //整片擦除最大超时 ? ? u8? dev_size; ? ? ? ? //芯片大小 ? ? u16 interface_desc; //接口描述 ? ? u16 max_buf_write_size; //最大缓存写长度 ? ? u8? num_erase_regions; //擦除块扇区数量 ? ? u32 erase_region_info[NUM_ERASE_REGIONS];? ? ? ? //4个块区的信息 } __attribute__((packed));
从上图可以看到,? 是获取了CFI query identification string?,System interface information,Device geometry definition? 信息,对照手册,? 就可以知道成员的数值 其中,最为重要的是擦写扇区信息?erase_region_info,对应手册的如下信息: 手册给出了扇区的信息,第一部分说明了扇区(block)的个数 : 0xff + 1 = 256 个,第二部分说明了一个扇区(block)大小: 0x200 * 256 =131072,即128K字节? ? 我们的flash,为00ff,和0200 .那么uint32_t的tmp 的数值应该为:? 0x020000ff ?? ? ? ? ? tmp = le32_to_cpu(qry.erase_region_info[i]); ? ? ? ? ? ? ? debug("erase region %u: 0x%08lxn",i,tmp); ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? erase_region_count = (tmp & 0xffff) + 1;? ? ? ? ? ? ? ? ? tmp >>= 16;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? erase_region_size =? (tmp & 0xffff) ? ((tmp & 0xffff) * 256) : 128; ? tmp =? qry.erase_region_info[i] = 0x20000ff tmp >>=16 后,tmp = 0x200 擦写扇区的大小 erase_region_size =? (tmp & 0xffff) * 256 = 0x20000,即一个扇区的大小为0x2000字节. 擦写扇区的个数 erase_region_count为0x201,即256个扇区 那么,可以知道,整个nor flash 总的容量为: 0x2000 * 256 = 33554432? 字节,? 验证一下: ? 33554432 / 1024 / 1024 = 32 M ? ? ? ? sect_cnt = 0; ? ? ? ? sector = base;//基地址为 0x1dc00000 … 那么会循环256次. for (j = 0; j < erase_region_count; j++) { .. ? ? ? ? ? ? ? //在256次循环中,256个start成员保存各个扇区的地址 ? ? ? ? ? ? ? ? info->start[sect_cnt] = ? ? ? ? ? ? ? ? ? ? (ulong)map_physmem(sector,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->portwidth, ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? MAP_NOCACHE); ? ? ? ? ? ? ? ? //计算各个扇区的地址,地址计算方法为,扇区的大小 * size_ratio(? 为 size_ratio = info->portwidth / info->chipwidth;,比值为1)? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //可以看出,? 各个扇区的地址相隔一个扇区的大小 ? ? ? ? ? ? ? ? sector += (erase_region_size * size_ratio);? ? ? ? ? ? ? ? ? ? ? ? ? ? ? … ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sect_cnt++; ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->sector_count = sect_cnt; ? ? ? ? //buffer_size 为 1 << 8,256 info->buffer_size = 1 << (8 * info->portwidth); … } 循环结束后,? sect_cnt 的数值为 256 现在,所有扇区的地址都保存到了init->start数组里. 那么现在如果要向flash里烧写一个文件,? 在知道文件的大小的情况下,就可以计算出要使用几个扇区.? include/flash.h: #define CONFIG_SYS_MAX_FLASH_SECT ? (256) ? ? ? ? ? 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[CONFIG_SYS_MAX_FLASH_SECT]; ? /* virtual sector start address */ ? ? uchar ? protect[CONFIG_SYS_MAX_FLASH_SECT]; /* sector protection status */ #ifdef CONFIG_SYS_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 */ ? ? ushort? manufacturer_id;? ? /* manufacturer id? ? ? ? ? */ ? ? ushort? device_id;? ? ? /* device id? ? ? ? ? ? ? ? */ ? ? ushort? device_id2; ? ? /* extended device id ? ? ? ? ? */ ? ? ushort? ext_addr; ? ? ? /* extended query table address ? ? */ ? ? ushort? cfi_version;? ? ? ? /* cfi version? ? ? ? ? ? ? */ ? ? ushort? cfi_offset; ? ? /* offset for cfi query ? ? ? ? */ ? ? ulong ? addr_unlock1; ? ? ? /* unlock address 1 for AMD flash roms? */ ? ? ulong ? addr_unlock2; ? ? ? /* unlock address 2 for AMD flash roms? */ ? ? const char *name; ? ? ? /* human-readable name? ? ? ? ? ? ? ? ? */ #endif? ? ? ? ? } flash_info_t; uboot 就是按照如上的思路来实现uboot的更新,common/cmd_flash.c 有很好的flash使用范例: int do_upgrade (cmd_tbl_t *cmdtp,int flag,int argc,char * const argv[]) {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int rcode = 0; ? ? ? ? ? ? ? ulong addr,addr_first,addr_last; ? ? const bootloader_header_t *header; ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (argc != 4) { ? ? ? ? ? ? ? ? if (argc == 2 || argc == 3) { ? ? ? ? ? ? if (strcmp(argv[1],"uboot") != 0) ? ? ? ? ? ? ? ? return cmd_usage(cmdtp); ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //获取环境变量loadaddr的数值,这是要更新的文件在内存里的起始地址? ? ? ? ? ? ? if (getenv("loadaddr") != NULL) ? ? ? ? ? ? ? ? addr = simple_strtoul(getenv("loadaddr"),NULL,16); ? ? ? ? ? ? else ? ? ? ? ? ? ? ? ? ? ? ? ? ? return cmd_usage(cmdtp); ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //(0x1fc00000 - CONFIG_SYS_FLASH_SIZE)? = 0x1dc00000 ?? ? ? ? ? //计算出uboot的起始地址 ? ? ? ? ? ? addr_first = CONFIG_SYS_FLASH_BASE; ? ? ? ? ? ? if (argc == 3 && strcmp(argv[2],"all") == 0) { ? ? ? ? ? ? ? ? addr_last = addr_first + CONFIG_BOOT_SIZE - 1; ? ? ? ? ? ? }else? ? ? ? ? ? ? ? ? ? ? ? //CONFIG_ENV_ADDR = 0x1fbe0000 ? ? ? ? ? ? //addr_last = 0x1fbdffff ?? ? ? ? ? ? //计算出uboot的结束地址 ? ? ? ? ? ? ? ? addr_last = CONFIG_ENV_ADDR - 1; ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? // 验证下载的uboot 释放符合bootload 的格式. ? ? ? ? ? ? header = (void *)addr; ? ? ? ? ? ? if (validate_header(header)) { ? ? ? ? ? ? ? ? printf("Image does not have valid header form addr:0x%lxn",addr); ? ? ? ? ? ? ? ? return 1; ? ? ? ? ? ? }? ? ? ? ? ? ? ? ... ?? //知道了uboot的起始,结束地址,就可以知道uboot在flash 里要使用几个扇区.? ? ? /* ? ? 一,先取消要使用的扇区保护,参数0 表示取消保护 ? ? */ ? ? ? ? ? if ((rcode = flash_sect_protect(0,addr_last)) != 0) ? ? ? ? return rcode; ? ? ? ? ? ? ? ? ? ? //擦除要使用到的扇区 ? ? ? ? ? ? if ((rcode = flash_sect_erase(addr_first,addr_last)) != 0) ? ? ? ? return rcode; ? ? ? ? ? ? ?? ? ? //向要使用到的扇区写入数据 ? ? ? ? ? ? ? ? puts ("Copy to Flash... "); ? ? if ((rcode = flash_write((char *)addr,addr_last - addr_first)) != 0) { ? ? ? ? flash_perror(rcode); ? ? ? ? return 1;? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? ? ? puts ("donen"); ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? return 0;? ? ? ? ? ? } int flash_sect_protect (int p,ulong addr_first,ulong addr_last) {? ? ? ? flash_info_t *info; ? ? ulong bank; ? ? int s_first[CONFIG_SYS_MAX_FLASH_BANKS],s_last[CONFIG_SYS_MAX_FLASH_BANKS]; ? ? int protected,i; ? ? int planned; ? ? int rcode; ?? ? ? ? /* ? ? 通过flash的起始地址和结束地址,计算出起始扇区和结束扇区,以及要使用到的扇区个数,分别保存到s_first,s_last,planned 中. ? ? */ ? ? rcode = flash_fill_sect_ranges( addr_first,addr_last,s_first,&planned ); … 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 < CONFIG_SYS_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 < CONFIG_SYS_MAX_FLASH_BANKS) && (addr_first <= addr_last); ?? ? ? ? ++bank,++info) { ? ? ? ? ulong b_end; ? ? ? ? int sect; ? ? ? ? short s_end; ?? ? ? ? ? ? ? ? ? ? ? if (info->flash_id == FLASH_UNKNOWN) { ? ? ? ? ? ? continue; ? ? ? ? }? ? ? ?? ? ? ? ? ? ? ? //start[0]保存的是flash的起始地址,size是整个芯片的大小,那么info->start[0] + info->size - 1的 含义就是 整个芯片的结束地址 ? ? ? ? b_end = info->start[0] + info->size - 1;? ? /* bank end addr */ ?? ? ? //最后一个扇区的标号 ? ? ? ? ? ? ? s_end = info->sector_count - 1; ? ? ? ? /* last sector ? */ ?? ? ? ? ? ? ? ? ? ? ? ? ? ? //遍历所有扇区,即256个扇区 ? ? ? ? for (sect=0; sect < info->sector_count; ++sect) { ? ? ? ? ? ? ulong end;? /* last address in current sect */ ?? ? ? ? ? ? ? ?? ? ? ? ? //当前扇区的最后地址 ? ? ? ? ? ? end = (sect == s_end) ? b_end : info->start[sect + 1] - 1; ?? ? ? ? ? ? ? ? ? ? ? ? ? if (addr_first > end) ? ? ? ? ? ? ? ? continue; ? ? ? ? ? //当uboot的结束地址小于当前扇区的地址时,直接判断下个扇区. 目的是快速找到uboot的结束地址所在flash的扇区.? ? ? ? ? ? ? if (addr_last < info->start[sect]) ? ? ? ? ? ? ? ? continue; ?? ? ? ? ? ? ? //当文件起始地址等于扇区起始地址,将当前扇区地址保存到s_first[0] 中. ? ? ? ? ? ? if (addr_first == info->start[sect]) { ? ? ? ? ? ? ? ? s_first[bank] = sect; ? ? ? ? ? ? } ? ?? ? ? ? ? ? //当文件结束地址等于当前扇区结束地址时,将当前扇区标号保存到s_last[0]中.. 这个部分uboot的代码需要优化,正常的逻辑下,这个时候可以直接break了. 无须再进入循环. 本人已经验证通过 ? ? ? ? ? ? if (addr_last? == end) { ? ? ? ? ? ? ? ? s_last[bank]? = sect; ? ? ? ? ? ? } ? ? ? ? ? }? ? ? ? ? ? ? //如果s_first[0]有数值,即查找成功的话,计算出占有了几个扇区. ? ? ? ? ? if (s_first[bank] >= 0) { ?? ? ? ? ? //如果没有找到s_last,有两种情况,如果目标文件大于flash的大小,那么设定s_last 为最后一个扇区. 否则是逻辑错误.? ? ? ? ? ? ? if (s_last[bank] < 0) { ? ? ? ? ? ? ? ? if (addr_last > b_end) { ? ? ? ? ? ? ? ? ? ? s_last[bank] = s_end; ? ? ? ? ? ? ? ? } else { ? ? ? ? ? ? ? ? ? ? puts ("Error: end address" ? ? ? ? ? ? ? ? ? ? ? ? " not on sector boundaryn"); ? ? ? ? ? ? ? ? ? ? rcode = 1; ? ? ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? ? ? } ? ? ? ? ? ? }? //如果得到的结果是结束的扇区标号小于起始扇区标号,也是逻辑错误 ? ? ? ? ? ? if (s_last[bank] < s_first[bank]) { ? ? ? ? ? ? ? ? puts ("Error: end sector" ? ? ? ? ? ? ? ? ? ? " precedes start sectorn"); ? ? ? ? ? ? ? ? rcode = 1; ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? }? ?? ? ? ? ? //记录结束扇区的编号. ? ? ? ? ? ? sect = s_last[bank]; ? ? ? ? ? ? addr_first = (sect == s_end) ? b_end + 1: info->start[sect + 1]; ?? ? ? ? ? ? //s_last[bank] - s_first[bank]? + 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 boundaryn"); ? ? ? ? ? ? rcode = 1; ? ? ? ? ? ? break; ? ? ? ? } else if (s_last[bank] >= 0) { ? ? ? ? ? ? puts ("Error: cannot span across banks when they are" ?? ? ? ? ? ? ? ? ? " mapped in reverse ordern"); ? ? ? ? ? ? rcode = 1; ? ? ? ? ? ? break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? }? ? ? ? ? ?? ? ? ? ? ? ? ? ? return rcode; } 回到: #ifndef CONFIG_SYS_NO_FLASH int flash_sect_protect (int p,ulong addr_last) {? ? ? ? ? ? ? ? ? ? ? ? flash_info_t *info; ? ? ulong bank; ? ? ? int s_first[CONFIG_SYS_MAX_FLASH_BANKS],s_last[CONFIG_SYS_MAX_FLASH_BANKS]; ? ? int protected,i; ? ? int planned;? ? ? int rcode; ??? ? ? ? ? ? ? ? ?? ? ? rcode = flash_fill_sect_ranges( addr_first,&planned ); ?? ? ? protected = 0; ?? ? ? ? if (planned && (rcode == 0)) { ? ? ? ? for (bank=0,info = &flash_info[0]; bank < CONFIG_SYS_MAX_FLASH_BANKS; ++bank,++info) { ? ? ? ? ? ? if (info->flash_id == FLASH_UNKNOWN) { ? ? ? ? ? ? ? ? continue; ? ? ? ? ? ? } ?? ? ? ? ? ? ? ? if (s_first[bank]>=0 && s_first[bank]<=s_last[bank]) { ? ? ? ? ? ? ? ? debug ("%sProtecting sectors %d..%d in bank %ldn", ? ? ? ? ? ? ? ? ? ? p ? "" : "Un-", ? ? ? ? ? ? ? ? ? ? s_first[bank],s_last[bank],bank+1); ? ? ? ? ? ? ? ? protected += s_last[bank] - s_first[bank] + 1; ?? ? ? ? ? ? ? //为获取到的扇区取消保护 ? ? ? ? ? ? ? ? for (i=s_first[bank]; i<=s_last[bank]; ++i) { #if defined(CONFIG_SYS_FLASH_PROTECTION) ?? ? ? ? ? ? ? ? ? //就是? 改变 info->addr_unlock1 的标识和将info->protect 的对应成员置0,否则后面不能 erase 和write ? ? ? ? ? ? ? ? ? ? if (flash_real_protect(info,p)) ? ? ? ? ? ? ? ? ? ? ? ? rcode = 1; ? ? ? ? ? ? ? ? ? ? putc ('.'); #else ? ? ? ? ? ? ? ? ? ? info->protect[i] = p; #endif? /* CONFIG_SYS_FLASH_PROTECTION */ ? ? ? ? ? ? ? ? } ? ? ? ? ? ? } ? ? ? ? } #if defined(CONFIG_SYS_FLASH_PROTECTION) ? ? ? ? puts (" donen"); #endif? /* CONFIG_SYS_FLASH_PROTECTION */ ?? ? ? ? ? ? printf ("%sProtected %d sectorsn", ? ? ? ? ? ? p ? "" : "Un-",protected); ? ? } else if (rcode == 0) { ? ? ? ? puts ("Error: start and/or end address" ? ? ? ? ? ? " not on sector boundaryn"); ? ? ? ? rcode = 1; ? ? } ? ? return rcode; } ?? ? ? ? ? ? ? ? ? ? #ifndef CONFIG_SYS_NO_FLASH int flash_sect_erase (ulong addr_first,ulong addr_last) {? ? ? ? ? ? ? ? ? ? ? ? flash_info_t *info; ? ? ulong bank;? ? ? ? ? int s_first[CONFIG_SYS_MAX_FLASH_BANKS],s_last[CONFIG_SYS_MAX_FLASH_BANKS]; ? ? int erased = 0;? ? ? int planned; ? ? ? ? int rcode = 0; ? ?? ? ? ? ? ? ? ? ? ? //跟之前取消保护一样,也需要通过给定地址计算出要操作的扇区. 这个地方实在多余,完全可以使用之前已经获取到的数据作为参数传下来.? ?? ? ? ? //总之 flash_sect_erase 和 flash_sect_protect 的重复度太高? ? ? ? rcode = flash_fill_sect_ranges (addr_first,? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? s_first,&planned ); ?? ? ? ? ? ? ? ? ? ? ? ? if (planned && (rcode == 0)) { ? ? ? ? for (bank=0,info = &flash_info[0]; ?? ? ? ? ? ? (bank < CONFIG_SYS_MAX_FLASH_BANKS) && (rcode == 0); ?? ? ? ? ? ? ++bank,++info) { ? ? ? ? ? ? if (s_first[bank]>=0) { ? ? ? ? ? ? ? ? erased += s_last[bank] - s_first[bank] + 1; ? ? ? ? ? ? ? ? debug ("Erase Flash from 0x%08lx to 0x%08lx " ? ? ? ? ? ? ? ? ? ? "in Bank # %ld ", ? ? ? ? ? ? ? ? ? ? info->start[s_first[bank]], ? ? ? ? ? ? ? ? ? ? (s_last[bank] == info->sector_count) ? ? ? ? ? ? ? ? ? ? ? ? ? info->start[0] + info->size - 1: ? ? ? ? ? ? ? ? ? ? ? ? info->start[s_last[bank]+1] - 1, ? ? ? ? ? ? ? ? ? ? bank+1); ?? ? ? ? ? ? ? //flash_erase 是drivers/mtd/cfi_flash.c 提供的flash 擦除接口. ? ? ? ? ? ? ? ? rcode = flash_erase (info,s_first[bank],s_last[bank]); ? ? ? ? ? ? }? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? printf ("Erased %d sectorsn",erased); ? ? } else if (rcode == 0) { ? ? ? ? puts ("Error: start and/or end address" ? ? ? ? ? ? " not on sector boundaryn"); ? ? ? ? rcode = 1; ? ? ? }? ? ? ? ? ? ? ? ? ? return rcode;? ? }? ? ? ? ? ? ? ? ? ? #endi int flash_erase (flash_info_t * info,int s_first,int s_last) {? ? ?? ? … ?? ? ? ? for (sect = s_first; sect <= s_last; sect++) { ? ? ? ? ////如果扇区处于保护状态,将无法擦除 ? ? ? ? if (info->protect[sect] == 0) { /* not protected */ ? ? ? ? ? ? switch (info->vendor) { … ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? case CFI_CMDSET_AMD_STANDARD: ? ? ? ? ? ? case CFI_CMDSET_AMD_EXTENDED: ? ? ? ? ? ? ? ? flash_write_cmd (info,AMD_CMD_RESET);? ? // (1) ? ? ? ? ? ? ? ? flash_unlock_seq (info,sect); //(2) ? ? ? ? ? ? ? ? flash_write_cmd (info,sect,? info->addr_unlock1,AMD_CMD_ERASE_START); //(3) ? ? ? ? ? ? ? ? flash_unlock_seq (info,sect);//(4) ? ? ? ? ? ? ? ? flash_write_cmd (info,AMD_CMD_ERASE_SECTOR);//(5) ? ? ? ? ? ? ? ? break; … ? ? ? ? ? ? } /* 根据手册,扇区的擦写动作指令为: #define AMD_CMD_UNLOCK_START? ? ? ? 0xAA #define AMD_CMD_UNLOCK_ACK? ? ? 0x55 static void flash_unlock_seq (flash_info_t * info,flash_sect_t sect){ ? ? flash_write_cmd (info,info->addr_unlock1,AMD_CMD_UNLOCK_START); ? ? flash_write_cmd (info,info->addr_unlock2,AMD_CMD_UNLOCK_ACK); } ? 全部擦写的操作是,? __RESET? 1,? 向 0xaaa? 写入 aa ? 2,? 向 0x555 写入 55 3,? 向 0xaaa 写入80 4,向 0xaaa 写入aa 5,向0x555? 写入55 6,向扇区地址 写入30 __RESET 由(1) 完成 1,2 由 (2) 完成 3 由 (3)完成 4,5由(4)完成 6 由 (5)完成 */ /* 指令的下发后,还要使用状态查询函数,等待指令的完成,即硬件的执行完成. 这个过程是最耗时的.? */ ? ? ? ? ? ? if (use_flash_status_poll(info)) { ? ? ? ? ? ? ? ? cfiword_t cword = (cfiword_t)0xffffffffffffffffULL; ? ? ? ? ? ? ? ? void *dest; ?? ? ? //获取扇区的内存地址 ? ? ? ? ? ? ? ? dest = flash_map(info,0); ?? ? ? ? ? ? ? //传入的超时时间为 info->erase_blk_tout,这个数值为:? (1 << qry.block_erase_timeout_typ) * (1 << qry.block_erase_timeout_max) //根据手册,计算出扇区最大超时时间为: 4096s,? 意味着,如果4096s内扇区还没有擦写完成,那么就超时退出 ? ? ? ? ? ? ? ? st = flash_status_poll(info,&cword,dest,info->erase_blk_tout,"erase"); ? ? ? ? ? ? ? ? flash_unmap(info,dest); ? ? ? ? ? ? } else ? ? ? ? ? ? ? ? st = flash_full_status_check(info, ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? info->erase_blk_tout, ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "erase"); ? ? ? ? ? ? if (st) ? ? ? ? ? ? ? ? rcode = 1; ? ? ? ? ? ? else if (flash_verbose) ? ? ? ? ? ? ? ? putc ('.'); ? ? ? ? ? ? if (ctrlc()) { ? ? ? ? ? ? ? ? puts(" Interruptedn"); ? ? ? ? ? ? ? ? return 1; ? ? ? ? ? ? } ? ? ? ? } ? ? } ?? ? ? ? if (flash_verbose) ? ? ? ? puts (" donen"); ?? ? ? ? return rcode; } ??? ? static int flash_status_poll(flash_info_t *info,void *src,void *dst, ?? ? ? ? ? ? ? ? ulong tout,char *prompt) {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #ifdef CONFIG_SYS_CFI_FLASH_STATUS_POLL? ? ? ? ulong start; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int ready; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? … ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? start = get_timer(0);? ? ? ? ? ? ? ? ? ? ? WATCHDOG_RESET();? ? ? ? ? ? ? ? ? ? ? ? ? while (1) {? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? switch (info->portwidth) { ? ? ? ? ? ? ? ? case FLASH_CFI_8BIT:? ? ?? ? ? ? ? /*根据flash 的位宽(portwidth),判断目的地址的数值是否等于src地址的数值. 上面传下来src的数值为全f,dst地址是当前扇区的0地址,? ?? ? ? ? ? ? ? ? ? ? ? ? ? 那么flash_erase 的擦写指令完成的判断条件是:? 当前扇区的0地址的数值为0xff? ?? ? ? ? ? 如果判断条件成立后跳出循环,? 否则udelay后,再次进入循环 */ ? ? ? ? ? ? ? ? ? ? ready = flash_read8(dst) == flash_read8(src); ? ? ? ? ? ? break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? case FLASH_CFI_16BIT:? ? ? ? ? ? ? ? ? ? ? ? ? ready = flash_read16(dst) == flash_read16(src); ? ? ? ? ? ? break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? case FLASH_CFI_32BIT:? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ready = flash_read32(dst) == flash_read32(src); ? ? ? ? ? ? break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? case FLASH_CFI_64BIT:? ? ? ? ? ? ? ? ? ? ? ? ? ready = flash_read64(dst) == flash_read64(src); ? ? ? ? ? ? break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? default: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ready = 0; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (ready) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (get_timer(start) > tout) { ? ? ? ? ? ? ? ? printf("Flash %s timeout at address %lx data %lxn", ?? ? ? ? ? ? ? ? ? prompt,(ulong)dst,(ulong)flash_read8(dst)); ? ? ? ? ? ? return ERR_TIMOUT; ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? udelay(1);? ? ? /* also triggers watchdog */ ? ? }? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? #endif /* CONFIG_SYS_CFI_FLASH_STATUS_POLL */ ? ? return ERR_OK; ? ? ? ? ? ? ? ? ? ? ? ? } 回到do_upgrade,? 扇区擦写完成后,调用flash_write 进行写入操作 code = flash_write((char *)addr,addr_last - addr_first)) != 0) { src? 是要烧些的文件的起始,addr 是要烧写到flash的目的地址,cnt 是要烧写的长度 int flash_write (char *src,ulong addr,ulong cnt){ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? int i; ? ? ? ? ? ? ? ? ? ? ? ? ? ulong ? ? ? ? end? ? ? ? = addr + cnt - 1; ? ? //在单个bank的flash里,只有一个info,info_first等于info_last ? ? flash_info_t *info_first = addr2info (addr); ? ? flash_info_t *info_last? = addr2info (end ); ? ? flash_info_t *info; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? … ? ? //在单个bank的flash里,只有一次循环 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? for (info = info_first; info <= info_last; ++info) { ? ? ? ? ulong b_end = info->start[0] + info->size;? /* bank end addr */ ? ? ? ? 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;? ?? ? ? ? //单个bank的flash调用 write_buf后返回操作结果? ? ? ? ? ? ? ? ? if ((i = write_buff(info,(uchar *)src,addr,len)) != 0) { ? ? ? ? ? ? return (i);? ? ? ? ? ? ? ? ? }? ? ? ? ? //多个bank的情况 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cnt? -= len; ? ? ? ? ? ? ? ? ? ? addr += len; ? ? ? ? ? ? ? ? ? ? src? += len; ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return (ERR_OK);? ? ? ? } ? //info 为flash的数据结构,src为源文件的内存地址,addr 为目的flash 地址,cnt 为文件要写的长度 ? int write_buff (flash_info_t * info,uchar * src,ulong cnt) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ulong wp; ? ? ? ? ? ? ? ? ? ? ? ? uchar *p; ? ? ? ? ? ? ? ? ? ? ? ? int aln;? ? ? ? ? ? ? ? ? ? ? ? ? cfiword_t cword;? ? ? ? ? ? ? ? ? int i,rc;? ? ? ? ? ? ? ? ? ? #ifdef CONFIG_SYS_FLASH_USE_BUFFER_WRITE ? ? int buffered_size;? ? ? ? ? ? #endif? ? ? ? ? ? ? ? ? ? ? ? ? ? #ifdef CONFIG_FLASH_SHOW_PROGRESS ? ? int digit = CONFIG_FLASH_SHOW_PROGRESS; ? ? int scale = 0;? ? ? ? ? ? ? ? ? ? int dots? = 0;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /*? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? * Suppress if there are fewer than CONFIG_FLASH_SHOW_PROGRESS writes. ?? ? */ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (cnt >= CONFIG_FLASH_SHOW_PROGRESS) { ? ? ? ? scale = (int)((cnt + CONFIG_FLASH_SHOW_PROGRESS - 1) / ? ? ? ? ? ? CONFIG_FLASH_SHOW_PROGRESS); ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? #endif ? ? ? ? ? ? ?? ? ? //wp的数值为addr? ? ? ? ? ? ? ? ? ? ? ? wp = (addr & ~(info->portwidth - 1));? ? ? ? ? ? ? ? ? ? ? ? ? ? …? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? buffered_size = (info->portwidth / info->chipwidth); ? ? buffered_size *= info->buffer_size; ? ? //buffered_size 为256 ? ? while (cnt >= info->portwidth) { ? ? ? ? //buffer_size 长度为1的情况,就是按字节写的情况 ? ? ? ? if (info->buffer_size == 1) { ? ? ? ? ? ? cword.l = 0;? ? ? ? ? ? ? ? ? ? ? for (i = 0; i < info->portwidth; i++) ? ? ? ? ? ? ? ? flash_add_byte (info,*src++); ? ? ? ? ? ? if ((rc = flash_write_cfiword (info,wp,cword)) != 0) ? ? ? ? ? ? ? ? return rc;? ? ? ? ? ? ? ? ? ? wp += info->portwidth; ? ? ? ? ? ? cnt -= info->portwidth; ? ? ? ? ? ? continue; ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //buffer_size 不为1,按buffer 写的情况 ? ? ? ? ? ? ? ? ? ? //如果地址为buffer_size 的整数倍,那么i 就等于 buffer_size.256 字节. ? ? ? ? ? ? ? ? ? ? //可以看到,按缓存写的话,总共会执行 ? (文件长度? / 256 + 1 次) . 如果要写入的长度为 0xdffff,那么要执行的次数为 0xdffff / 256 + 1? = 3584 次. ? ? ? ? i = buffered_size - (wp % buffered_size); ? ? ? ? if (i > cnt)? ? ? ? ? ? ? ? ? ? ? ? ? i = cnt;? ? //如果缓存写长度大于剩余的要写入的文件长度,那么长度截为cnt? ? ? ? ? ? ? ? ? if ((rc = flash_write_cfibuffer (info,src,i)) != ERR_OK) ? ? ? ? ? ? return rc;? ? ? ? ? ? ? ? ? ? i -= i & (info->portwidth - 1); ? ? ? ? wp += i; ? //要写入的内容的地址移动 i 长度 ? ? ? ? ? ? ? ? ? ? ? src += i; //要写入的文件的地址向后移动 i 长度 ? ? ? ? cnt -= i; ? //文件的剩余长度减去 i 长度? ? ? ? ? ? ? ? ? ? ? FLASH_SHOW_PROGRESS(scale,dots,digit,i); ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? … ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (cnt == 0) { ? ? ? ? ? ? ? ? ? ? ? return (0); ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? /*? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? * handle unaligned tail bytes ?? ? */ ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? cword.l = 0;? ? ? ? ? ? ? ? ? ? ? p = (uchar *)wp;? ? ? ? ? ? ? ? ? for (i = 0; (i < info->portwidth) && (cnt > 0); ++i) { ? ? ? ? flash_add_byte (info,*src++); ? ? ? ? --cnt; ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? for (; i < info->portwidth; ++i) ? ? ? ? flash_add_byte (info,flash_read8(p + i)); ?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return flash_write_cfiword (info,cword); }? 对于字节写和缓存写,分别 有flash_write_cfiword 和flash_write_cfibuffer 实现
static int flash_write_cfiword (flash_info_t * info,ulong dest, ? ? ? ? ? ? ? ? cfiword_t cword) { ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? void *dstaddr = (void *)dest; ? ? int flag; ? ? ? ? ? ? ? ? ? ? ? flash_sect_t sect = 0;? ? ? ? ? char sect_found = 0;? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //根据端口宽度,判断要操作的地址上的数值是否为cword的数值.? ? ? ? ? //上面传的cword 为0,那么要判断要写的地址的数值是否为0,如果判断结果为假,那么退出,返回ERR_NOT_ERASE错误数值.提示没有经过擦写. ? ? switch (info->portwidth) {? ? ? case FLASH_CFI_8BIT:? ? ? ? ? ? ? ? flag = ((flash_read8(dstaddr) & cword.c) == cword.c); ? ? ? ? break;? ? ? ? ? ? ? ? ? ? ? case FLASH_CFI_16BIT: ? ? ? ? ? ? ? flag = ((flash_read16(dstaddr) & cword.w) == cword.w); ? ? ? ? break;? ? ? ? ? ? ? ? ? ? ? case FLASH_CFI_32BIT: ? ? ? ? ? ? ? flag = ((flash_read32(dstaddr) & cword.l) == cword.l); ? ? ? ? break;? ? ? ? ? ? ? ? ? ? ? case FLASH_CFI_64BIT: ? ? ? ? ? ? ? flag = ((flash_read64(dstaddr) & cword.ll) == cword.ll); ? ? ? ? break;? ? ? ? ? ? ? ? ? ? ? default:? ? ? ? ? ? ? ? ? ? ? ? ? ? flag = 0; ? ? ? ? ? ? ? ? ? ? ? break;? ? ? ? ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (!flag)? ? ? ? ? ? ? ? ? ? ? ? ? return ERR_NOT_ERASED;? ?? //上面看到,flash在执行烧些前,要先取消保护,再进行擦除,当两者都成功后,才可以进行write? ? ? //在执行烧些过程中,关闭全部中断,所有的中断新号会被忽略 ? ? flag = disable_interrupts (); ?? ? ? ? ? ? ? ? ? ? ? ? ? ? //根据不同厂商,执行对应的指令. ? ? ? ? ? ? switch (info->vendor) { ? ? ? ? case CFI_CMDSET_INTEL_PROG_REGIONS: ?? case CFI_CMDSET_INTEL_EXTENDED: ? ? case CFI_CMDSET_INTEL_STANDARD://intel 的规范 ? ? ? ? flash_write_cmd (info,FLASH_CMD_CLEAR_STATUS); ? ? ? ? flash_write_cmd (info,FLASH_CMD_WRITE); ? ? ? ? break; ? ? ? ? ? ? ? ? ? ? ? case CFI_CMDSET_AMD_EXTENDED: ? ? case CFI_CMDSET_AMD_STANDARD: //AMD 的规范 ? ? ? ? ? ? ? ? ? //根据目的地址找到要操作的扇区 ? ? ? ? sect = find_sector(info,dest); ? ? ? ? //解锁扇区 ? ? ? ? flash_unlock_seq (info,sect); //输入write 指令 ? ? ? ? flash_write_cmd (info,AMD_CMD_WRITE); ? ? ? ? sect_found = 1;? ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? ? ? ? …? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? ? ? ? ? ?? ? ? ? ? ? ? //等待指令完成 ? ? ? ? ? ? ? ? ? ? ? ? ? switch (info->portwidth) { ? ? ? case FLASH_CFI_8BIT: ? ? ? ? ? ? ? ? flash_write8(cword.c,dstaddr); ? ? ? ? if (info->vendor != 1) { ? ? ? ? ? ? while (flash_read8(dstaddr) != cword.c) ? ? ? ? ? ? ? ? ;? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? ? ? ? ? ? case FLASH_CFI_16BIT:? ? ? ? ? ? ? ? flash_write16(cword.w,dstaddr); ? ? ? ? if (info->vendor != 1) { ? ? ? ? ? ? while (flash_read16(dstaddr) != cword.w) ? ? ? ? ? ? ? ? ;? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? break; ? ? ? ? ? ? ? ? ? ? ? case FLASH_CFI_32BIT:? ? ? ? ? ? ? ? flash_write32(cword.l,dstaddr); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? case FLASH_CFI_64BIT:? ? ? ? ? ? flash_write64(cword.ll,dstaddr); ? ? ? ? if (info->vendor != 1) { ? ? ? ? ? ? while (flash_read64(dstaddr) != cword.ll) ? ? ? ? ? ? ? ? ; ? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? break;? ? ? ? ? ? ? } ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? //恢复中断 ? ? if (flag) ? ? ? ? ? ? ? ? ? enable_interrupts (); ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (!sect_found)? ? ? ? ? ? sect = find_sector (info,dest); ? ? ? ? ? ? ? ? ? ? ? ? ? ? if (use_flash_status_poll(info)) ? ? ? ? return flash_status_poll(info,dstaddr, ?? ? ? ? ? ? ? ? ? ? info->write_tout,"write"); ? ? else? ? ? ? ? ? ? ? ? ? ? ? return flash_full_status_check(info, ?? ? ? ? ? ? ? ? ? ? ? ? ? info->write_tout,"write"); }? flash_write_cfibuffer 使用了同样的逻辑,不同的指令 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |