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 }