14.1 norflash 原理
在烧写进去的u-boot 中 Flash 并没有显示实际大小,意味着 norflash 没有识别。
14.1.1 norflash 硬件连接
NOR 的接口与内存的接口是一样的,而 NAND(数据线只有 7 条,发地址又发命令和数据等)。
NOR 可以像内存一样的来读,但不能像内存一样的去写。
NOR 的烧写要发出某些特定的命令,在某地址上写某个值就称为命令。
NOR 存放关键性的代码,如 bootload、内核或文件系统;而 NADN 有位反转的缺点,如存一些海量数据如视频监控文件等。
用 NOR 启动时,CPU 看到的“0”地址是“NOR” FLASH。若为 NAND 启动,则 CPU 看到的“0”地址是 2440 片的 4K 内存。用 NAND 启动时 CPU 完全看不到 NOR,只有用 NOR 启动时, CPU 看到的“0”地址才是 NOR。
- 芯片启动:
- 代码可以直接在 NOR 上运行,在 NAND 上不行。
- CPU 可以直接从 NOR 上取到指令来运行。
- 用 NAND 启动,2440 是有将 NAND 的前 4K 数据自动拷贝到 2440 的 4K 片内内存里,这时 CPU 再从片内 4K 内存中取指令运行。
14.1.2 norflash 命令
norflash 的读写共分6个周期,这里面第一横行的就是命令,比如说 reset mode 命令,意思就是在第一个周期向任意地址写入 0xF0 就可以进入复位。
读厂家ID(manifacture ID),在第一个周期向 0x555 地址写入 0xaa,在第二个周期向 0x2AA 写入 0x55,在第三个周期向 0x555 写入 0x90,第四个周期从 x00 地址读取到厂家 ID C2H
下图是芯片的厂家 ID 和设备 ID:
要注意 word 和 byte,word 表示是16位位宽操作,byte 表示是 8位位宽操作,位宽是寻址方式,根据原理图来确定,我们接了16根地址总线,所以是16位位宽。
14.1.3 norflash 规范
NOR 有两种规范, jedec 和 cfi(common flash interface)
jedec 规范,就包含了“COMMAND DEFINITIONS”中的命令,如发命令可以识别 ID,擦除芯片、擦除“扇区”或“烧写数据” 等。
老式的 NORFLASH 会支持“jedec”规范。要知道容量有多大,就要读出 ID,与数组比较。
以前老式的 NORFLASH 规范就必须读 ID 后,与这个uboot 或 内核中的 "jedec_table[]"结构数组比较,若是此数据中没有你的 NORFLASH 的信息,就得来修改此数组。
新的 NORFLASH 要支持“cfi”通用 flash 接口。就是说 NORFLASH 里面本身就带了这些属性信息(如容量、电压)。往相关地址发相关命令好可以得这些属性信息。
14.2 flash 初始化流程
14.2.1 flash 结构体
在梳理流程之前,先来看看 flash 的结构体定义:
1 typedef struct { 2 ulong size; ///< flash 的总容量(单位是字节(byte)) 3 ushort sector_count; ///< 扇区数量 4 ulong flash_id; ///< flash ID 组合了 device 和 manufacturer 编码 5 ulong start[CONFIG_SYS_MAX_FLASH_SECT]; ///< 虚拟扇区起始地址 6 uchar protect[CONFIG_SYS_MAX_FLASH_SECT]; ///< 扇区保护状态 7 #ifdef CONFIG_SYS_FLASH_CFI ///< 支持 CFI 模式才会定义如下的参数 8 uchar portwidth; ///< 端口位宽 9 uchar chipwidth; ///< 芯片位宽 10 ushort buffer_size; ///< 写入缓冲区字节数 11 ulong erase_blk_tout; ///< 最大块擦除超时 12 ulong write_tout; ///< 最大写入超时 13 ulong buffer_write_tout; ///< 最大缓冲区写入超时 14 ushort vendor; ///< 主要供应商id 15 ushort cmd_reset; ///< 特定于供应商的重置命令 16 uchar cmd_erase_sector; ///< 特定于供应商的扇区擦除命令 17 ushort interface; ///< 用于 x8/x16 适配 18 ushort legacy_unlock; ///< 支持Intel 旧式锁定或解锁 19 ushort manufacturer_id; ///< 厂家 ID 20 ushort device_id; ///< 设备 ID 21 ushort device_id2; ///< 扩展的 设备 ID 22 ushort ext_addr; ///< 扩展的查询表地址 23 ushort cfi_version; ///< cfi 版本 24 ushort cfi_offset; ///< cfi查询的偏移量 25 ulong addr_unlock1; ///< 解锁 AMD 闪存 ROM 的地址 1 26 ulong addr_unlock2; ///< 解锁 AMD 闪存 ROM 的地址 2 27 const char *name; ///< 可读名称 28 #endif 29 #ifdef CONFIG_MTD /** 支持磁盘才会用到 */ 30 struct mtd_info *mtd; 31 #endif 32 } flash_info_t;
14.2.2 总的流程
通过搜索关键字 " flash " 可以知道打印信息在 initr_flash 函数中,此函数位于重定向之后的初始化函数内。看看这个函数主要做了些什么:
1 static int initr_flash(void) 2 { 3 ulong flash_size = 0; ///< 定义存储 flash 大小的变量 4 bd_t *bd = gd->bd; ///< 定义板信息结构体 5
6 puts("Flash: "); ///< 输出字符串 Flash: 7
8 if (board_flash_wp_on()) ///< __weak 开头的函数, 没有相应的复写函数, 直接返回 0 9 printf("Uninitialized - Write Protect On\n");
10 else
11 flash_size = flash_init(); ///< flash 初始化 12
13 print_size(flash_size, ""); ///< 打印 flash 的大小 14
15 /** 进行 flash 校验, 未定义宏 CONFIG_SYS_FLASH_CHECKSUM */
16 #ifdef CONFIG_SYS_FLASH_CHECKSUM 17 /*
18 * Compute and print flash CRC if flashchecksum is set to 'y' 19 * 20 * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX 21 */
22 if (getenv_yesno("flashchecksum") == 1) 23 { 24 printf(" CRC: %08X", crc32(0, (const unsigned char *) CONFIG_SYS_FLASH_BASE, flash_size)); 25 } 26 #endif /* CONFIG_SYS_FLASH_CHECKSUM */
27 putc('\n'); 28
29 /* update start of FLASH memory, 更新 flash 内存的起始地址 */
30 #ifdef CONFIG_SYS_FLASH_BASE 31 bd->bi_flashstart = CONFIG_SYS_FLASH_BASE; ///< bd->bi_flashstart = 0 设定板的 flash 起始地址
32 #endif
33 /* size of FLASH memory (final value) */
34 bd->bi_flashsize = flash_size; ///< 板上的 flash 的大小 35
36 /** 更新 flash 大小, 未定义宏 CONFIG_SYS_UPDATE_FLASH_SIZE */
37 #if defined(CONFIG_SYS_UPDATE_FLASH_SIZE)
38 /* Make a update of the Memctrl. */
39 update_flash_size(flash_size); 40 #endif
41
42
43 #if defined(CONFIG_OXC) || defined(CONFIG_RMU)
44 /* flash mapped at end of memory map */
45 bd->bi_flashoffset = CONFIG_SYS_TEXT_BASE + flash_size; 46 #elif CONFIG_SYS_MONITOR_BASE == CONFIG_SYS_FLASH_BASE
47 /** 执行此处的代码, 监视器预留区 */
48 bd->bi_flashoffset = monitor_flash_len; /* reserved area for monitor */
49 #endif
50 return 0; 51 }
这个函数总体就是初始化 flash,并将 flash 信息记录到全局结构体 gd->bd 中,bd 记录了开发板的所有硬件基本信息。
红色字体部分代码已经很明显的显示了打印过程。
首先是,flash_init 初始化flash ,然后调用 print_size 打印处 flash 的大小。flash_init 函数执行完后,会返回 flash_size 变量,即为 flash 的大小。
14.2.3 flash_init
在来看看这个函数里面做了什么,此函数在 \drivers\mtd\cfi_flash.c 中:
1 unsigned long flash_init(void) 2 { 3 unsigned long size = 0; 4 int i; 5
6 /** S3C2440 未配置,不执行 */
7 #ifdef CONFIG_SYS_FLASH_PROTECTION 8 /* read environment from EEPROM */
9 char s[64]; 10 getenv_f("unlock", s, sizeof(s)); 11 #endif
12
13 /* 用于驱动模型的,未配置 */
14 #ifdef CONFIG_CFI_FLASH 15 cfi_flash_init_dm(); 16 #endif
17
18 /* Init: no FLASHes known, CONFIG_SYS_MAX_FLASH_BANKS = 1, include/configs/jz2440.h中有定义,为 1 */
19 for(i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) 20 { 21 /** flash_id 设置为 未知 */
22 flash_info[i].flash_id = FLASH_UNKNOWN; 23
24 /* Optionally write flash configuration register */
25 /** 这里是 driver model 的初始化,无关代码 */
26 cfi_flash_set_config_reg(cfi_flash_bank_addr(i), cfi_flash_config_reg(i)); 27
28 /** 检测 flash, flash_detect_legacy 是旧的检测策略 */
29 if(!flash_detect_legacy(cfi_flash_bank_addr(i), i)) 30 flash_get_size(cfi_flash_bank_addr(i), i); 31
32 /** flash_info[i].size 这个值在上面那一步中被更改 */
33 size += flash_info[i].size; 34 if(flash_info[i].flash_id == FLASH_UNKNOWN) 35 { 36 #ifndef CONFIG_SYS_FLASH_QUIET_TEST 37 printf ("## Unknown flash on Bank %d - Size = 0x%08lx = %ld MB\n", i + 1, flash_info[i].size, flash_info[i].size >> 20); 38 #endif /* CONFIG_SYS_FLASH_QUIET_TEST */
39 } 40 /** 不执行 */
41 #ifdef CONFIG_SYS_FLASH_PROTECTION 42 else if (strcmp(s, "yes") == 0) 43 { 44 /*
45 * Only the U-Boot image and it's environment is protected, all other sectors are unprotected (unlocked) 46 * if flash hardware protection is used (CONFIG_SYS_FLASH_PROTECTION) and 47 * the environment variable "unlock" is set to "yes". 48 */
49 if (flash_info[i].legacy_unlock) 50 { 51 int k; 52
53 /* Disable legacy_unlock temporarily, since flash_real_protect would relock all other sectors again otherwise. */
54 flash_info[i].legacy_unlock = 0; 55
56 /* Legacy unlocking (e.g. Intel J3) -> unlock only one sector. This will unlock all sectors. */
57 flash_real_protect (&flash_info[i], 0, 0); 58
59 flash_info[i].legacy_unlock = 1; 60
61 /* Manually mark other sectors as unlocked (unprotected) */
62 for (k = 1; k < flash_info[i].sector_count; k++) 63 flash_info[i].protect[k] = 0; 64 } 65 else
66 { 67 /* No legancy unlocking -> unlock all sectors */
68 flash_protect (FLAG_PROTECT_CLEAR, 69 flash_info[i].start[0], 70 flash_info[i].start[0] + flash_info[i].size - 1, 71 &flash_info[i]); 72 } 73 } 74 #endif /* CONFIG_SYS_FLASH_PROTECTION */
75 } 76
77 /** flash 对数据的一些保护操作 */
78 flash_protect_default(); 79 #ifdef CONFIG_FLASH_CFI_MTD 80 cfi_mtd_init(); 81 #endif
82
83 return (size); 84 }
分析到当前,已经可以看见一些端倪了,size 变量是在 检测到 flash 之后,才有正确的值,当前我们无法保证是否检测到了 flash。
14.2.4 flash_detect_legacy
源码位置:Cfi_flash.c (drivers\mtd)
1 static int flash_detect_legacy(phys_addr_t base, int banknum) 2 { 3 /** 全局的flash_info 与 局部 info 地址相同 */ 4 flash_info_t *info = &flash_info[banknum]; 5 6 /** flash 的赢编码设置: 7 portwidth = FLASH_CFI_16BIT 8 chipwidth = FLASH_CFI_BY16 9 interface = FLASH_CFI_X16 */ 10 if (board_flash_get_legacy(base, banknum, info)) 11 { 12 /* board code may have filled info completely. If not, we use JEDEC ID probing. 13 * info->vendor_id = 0 执行 if 语句里面代码 14 */ 15 if (!info->vendor) 16 { 17 int modes[] = {CFI_CMDSET_AMD_STANDARD, CFI_CMDSET_INTEL_STANDARD}; 18 int i; 19 20 for (i = 0; i < ARRAY_SIZE(modes); i++) 21 { 22 info->vendor = modes[i]; 23 /** 这里直接将 base 给返回了 */ 24 info->start[0] = (ulong)map_physmem(base, info->portwidth, MAP_NOCACHE); 25 26 if (info->portwidth == FLASH_CFI_8BIT && info->interface == FLASH_CFI_X8X16) 27 { 28 info->addr_unlock1 = 0x2AAA; 29 info->addr_unlock2 = 0x5555; 30 } 31 else 32 { 33 /** 根据上面的赋值,可知道执行这里,对 flash 进行解锁命令需要发送的地址 */ 34 info->addr_unlock1 = 0x5555; 35 info->addr_unlock2 = 0x2AAA; 36 } 37 38 /** 读取 jedec 规范的 flash 表,这里就是发送命令到 flash 中获取 flash 的信息进行匹配 */ 39 flash_read_jedec_ids(info); 40 debug("JEDEC PROBE: ID %x %x %x\n", info->manufacturer_id, info->device_id, info->device_id2); 41 /** 匹配 jedec_table 表,查找相应型号的 flash*/ 42 if (jedec_flash_match(info, info->start[0])) 43 break; 44 else 45 unmap_physmem((void *)info->start[0], info->portwidth); 46 } 47 } 48 49 /** 根据 vendor 设置复位要发送的命令数据 */ 50 switch(info->vendor) 51 { 52 case CFI_CMDSET_INTEL_PROG_REGIONS: 53 case CFI_CMDSET_INTEL_STANDARD: 54 case CFI_CMDSET_INTEL_EXTENDED: 55 info->cmd_reset = FLASH_CMD_RESET; 56 break; 57 case CFI_CMDSET_AMD_STANDARD: 58 case CFI_CMDSET_AMD_EXTENDED: 59 case CFI_CMDSET_AMD_LEGACY: 60 info->cmd_reset = AMD_CMD_RESET; 61 break; 62 } 63 info->flash_id = FLASH_MAN_CFI; 64 return 1; 65 } 66 return 0; /* use CFI */ 67 }