Mini2440之uboot移植之实践NOR FLASH支持(二)
在上一节中,不知道你有没有注意到,以NOR方式启动u-boot后,运行有一行信息:
输出Flash信息这一部分代码是位于board_init_r阶段,执行initr_flash()函数的输出结果。
我们开发板上搭载了型号为S29AL016D70TF102的2MB大小的NOR FLASH,这里输出NOR FALSH大小为0字节,很明显没有能正确识别 2M的NOR FLASH了。
这一节我们将会介绍u-boot如何支持我们的NOR FLASH,这样我们后续就可以通过命令行对NOR FALSH进行读写操作。
一、Mini2440 NOR FLASH介绍
在之前的裸机开发中,我们没有介绍NOR FLASH的裸机程序,如果直接动手修改u-boot程序使其支持NOR FALSH,你可能会很懵逼,这里我们先补充一下NOR FLASH的基础知识。
NOR FLASH存储器接口标准包含CFI和JEDEC:
- CFI为公共Flash接口(Common Flash Interface),CFI是一个公开的标准的从Flash Memory器件中读取数据的接口。它可以使系统软件查询已安装的Flash Memory器件的各种参数,包括器件阵列结构参数、电气和时间参数以及器件支持的功能等。利用CFI可以不用修改系统软件 就可以用新型的和改进的产品代替旧版本的产品。例如:如果新型的Flash Memory的擦除时间只有旧版本的一半,系统软件只要通过CFI读取新器件的擦除时间等参数,修改一下定时器的时间参数即可。
- JEDEC是由生产厂商们制定的国际性协议,主要为 计算机 内存制定。JEDEC用来帮助程序读取Flash的制造商ID和设备ID,以确定Flash的大小和算法,如果芯片不支持CFI,就需使用JEDEC了。工业标准的内存通常指的是符合JEDEC标准的一组内存。
老式的NOR FLASH一般是JEDEC规范,其一般只包含识别 ID、擦除芯片、烧写数据的命令。要想知道其容量大小等信息,就需要先读出其芯片id,根据芯片id匹配uboot中drivers/mtd/jedec_flash.c里的的jedec_table数组,来确定NOR FLASH的哥哥参数(名称、容量、位宽)等,比较麻烦。另外如果内核jedec_table数组中事先没有对应芯片id的信息,还需要先在该数组中添加。
目前的NOR FLASH一般都支持CFI规范,其除了提供识别 ID、擦除芯片、烧写数据的命令之后,还提供了进入CFI模式的命令,进入CFI模式后就可以通过读取相应地址的数据获取芯片属性信息,如容量、电压等信息。 进入CFI模式(使用CFI Query命令)。
1.1 S29AL016D70TF102
Mini2440开发板就是将2M的NOR FLASH(型号S29AL016D70TF102)焊接在了Bank0上,S29AL016D70TF102这款芯片具有以下性质:
- 灵活的扇区架构:
- 一个 16 KB、两个 8 KB、一个 32 KB 和三十一个64 KB 扇区(字节模式);
- — 1 个 8 Kword、2 个 4 Kword、1 个 16 Kword 和 31 Kword 扇区(字模式);
- Top or bottom boot block configurations available;
- 扇区保护特性:
- A hardware method of locking a sector to prevent any program or erase operations within that sector.
- Sectors can be locked in-system(通过NOR FLASH命令寄存器控制) or via programming equipment(编程器,需要高电压8.5V~12.5V).
- Temporary Sector Unprotect feature allows code changes in previously locked sectors.
- 与 JEDEC 标准的兼容性;
- CFI(通用闪存接口)兼容;
- 擦除挂起/擦除恢复;
- 容量2MB;
Spansion 标准产品有多种封装和操作范围。 芯片型号(有效组合)由下面的元素组成,可以看出S29AL016D70TF102型号的参数MODEL NUMBER为02,bottom boot sector device。
S29AL016D70TF102支持8位和16位两种模式,具体选择哪种模式取决于芯片BYTE引脚信号;这里我们Mini2440采用的是16位模式,S3C2440的A1~A22连接NOR FLASH的A0~A21。需要错位连接的原因是:S3C2440处理器的每个地址对应的是一个BYTE的数据单元,而16位模式下,NOR FLASH的每个地址对应的是一个HALF-WORD(16-BIT)的数据单元。为了保持匹配,所以必须错位连接。这样,从S3C2440处理器发送出来的地址信号的最低位A0对16位Flash来说就被屏蔽掉了。
补充说明:
- 一般来说,ARM处理器内部要设置相应的寄存器,告诉处理器外部扩展的Flash的位宽(8-BIT/16-BIT/32-BIT)。这样,处理器才知道在访问的时候如何从FLASH正确的读取数据;
- 有些ARM处理器内部可以设置地址的错位。对于支持软件选择地址错位的处理器,在连接16-BIT FLASH的时候,硬件上可以不需要把地址线错位。
- 如果处理器支持内部设置地址错位,在实际访问的时候,送出的地址实际上是在MCU内部做了错位处理,其作用是等效于硬件连接上的错位的。
1.2 指令集
Legend:
- X = Don’t care
- RA = Address of the memory location to be read.
- RD = Data read from location RA during read operation.
- PA = Address of the memory location to be programmed. Addresses latch on the falling edge of the WE# or CE# pulse, whichever happens later.
- PD = Data to be programmed at location PA. Data latches on the rising edge of WE# or CE# pulse, whichever happens first.
- SA = Address of the sector to be verified (in autoselect mode) or erased. Address bits A19–A12 uniquely select any sector.
Note:
- 1. See Table 1 for description of bus operations.
- 2. All values are in hexadecimal.
- 3. Except for the read cycle and the fourth cycle of the autoselect command sequence, all bus cycles are write cycles.
- 4. Data bits DQ15–DQ8 are don’t cares for unlock and command cycles.
- 5. Address bits A19–A11 are don’t cares for unlock and command cycles, unless SA or PA required.
- 6. No unlock or command cycles required when reading array data.
- 7. The Reset command is required to return to reading array data when device is in the autoselect mode, or if DQ5 goes high (while the device is providing status data).
- 8. The fourth cycle of the autoselect command sequence is a read cycle.
- 9. The data is 00h for an unprotected sector and 01h for a protected sector. See “Autoselect Command Sequence” for more information.
- 10. Command is valid when device is ready to read array data or when device is in autoselect mode.
- 11. The Unlock Bypass command is required prior to the Unlock Bypass Program command.
- 12. The Unlock Bypass Reset command is required to return to reading array data when the device is in the unlock bypass mode. F0 is also acceptable.
- 13. The system may read and program in non-erasing sectors, or enter the autoselect mode, when in the Erase Suspend mode. The Erase Suspend command is valid only during a sector erase operation.
- 14. The Erase Resume command is valid only during the Erase Suspend mode.
以Autoselect Command Sequence为例,当采用 16 位位宽时,先向地址“555”处写入“AA”,再向地址“2AA”处写 入“55”,接着向地址“555”处写入“90” ,最后从 0 地址“X00”处可以读到“01”。 周期“First”和“Second”是“解锁”,周期“Third”是发出命令。 需要注意的是,以地址"555"为例,每个地址对应的数据长度为2个字节,对应8位位宽地址555<<1=AAA。
以读取厂家ID为例,编写代码时序如下:
- 解锁:向地址AAAH(555<<1)写入AAH,向地址554(2AA<<1)写入55H;
- 命令:向地址AAAH(555<<1)写入90H;
- 读地址0x00得到厂家ID;
由于Mini2440的A1接到NOR FLASH的A0,所以2440发出(555<<1),NOR FLASH才能收到“555”这个地址;同理,只要是2440发出的地址都需要在NOR FLASH的地址基础上<<1。
注意:
- 这里有一点需要注意的是,在解锁和命令总线周期地址A19~A11是被忽略的,所以地址555与5555等价,2AA与2AAA是等价的,因为A0~A10位是一致的。
关于如何对NOR FLASH进行读写、擦除,我就简单说一下:
- 读:NOR FLASH上电后处于数据读取状态(Reading Array Data)。此状态可以进行正常的读,这和读取SDRAM/SRAM/ROM一样。(要是不一样的话,芯片上电后如何从NOR FLASH中读取启动代码)。需要注意的是进入自动选择(Autoselect Command)模式后需要发送复位命令才能回到数据读取状态(Reading Array Data)。
- 擦除:在完成信息获取后一般就要擦除数据。NOR FLASH支持扇区擦除(Sector Erase)和整片擦除(Chip Erase),这2种模式都有对应的命令序列,在完成擦除命令后会自动返回到数据读取(Reading Array Data)状态,在返回前可查询编程的状态。
- 编程(写):完成擦除后就需要对芯片进行写入操作也就是编程,这就需要进入编程(Program)状态。在完成编程命令后会自动返回到数据读取(Reading Array Data)状态,在返回前可查询编程的状态,注意:编程前一定要先擦除。因为编程只能将'1'改写为'0',通过擦写可以将数据全部擦写为'1'。如果绕过解锁模式Unlock Bypass,编程只需要两个周期,而不是4个周期。
1.3 裸机程序
这里就不展示裸机程序了,有兴趣的可以参考mini2440硬件篇之Nor Flash。
二、NOR FLASH支持
2.1 宏定义
在include/configs/smdk2440.h文件有NOR FLASH相关的定义,如下:
#define PHYS_FLASH_1 0x00000000 /* Flash Bank #0 */ #define CONFIG_SYS_FLASH_BASE PHYS_FLASH_1 // NOR FLASH物理基地址 /*----------------------------------------------------------------------- * FLASH and environment organization */ #define CONFIG_SYS_FLASH_CFI #define CONFIG_FLASH_CFI_DRIVER #define CONFIG_FLASH_CFI_LEGACY #define CONFIG_SYS_FLASH_LEGACY_512Kx16 // jedec_table数组支持哪些容量大小的芯片 这里表示芯片大小为512kb,位宽为16 我们可以忽略这个 #define CONFIG_FLASH_SHOW_PROGRESS 45 #define CONFIG_SYS_MAX_FLASH_BANKS 1 // NOR FLASH芯片数量 #define CONFIG_SYS_FLASH_BANKS_LIST { CONFIG_SYS_FLASH_BASE } #define CONFIG_SYS_MAX_FLASH_SECT (19) // NOR FLASH最大扇区数量
2.2 分析启动信息
在u-boot以NOR方式启动时,如果我们想打印更加详细的信息,可以在include/configs/smdk2440.h中加入如下宏,重新编译,下载运行,则可以打印更多调试相关信息:
#define DEBUG
这里信息比较多,我就不截图了:
Flash: fwc addr 00000000 cmd f0 00f0 16bit x 16 bit # 复位命令 地址0x00写入0xFO fwc addr 0000aaaa cmd aa 00aa 16bit x 16 bit # 地址0xaaaa写入0xaa fwc addr 00005554 cmd 55 0055 16bit x 16 bit # 地址0x5554写入55 fwc addr 0000aaaa cmd 90 0090 16bit x 16 bit # 地址0xaaaa写入0x90 fwc addr 00000000 cmd f0 00f0 16bit x 16 bit # 复位命令 JEDEC PROBE: ID 1 2249 0 fwc addr 00000000 cmd ff 00ff 16bit x 16 bit fwc addr 00000000 cmd 90 0090 16bit x 16 bit fwc addr 00000000 cmd ff 00ff 16bit x 16 bit JEDEC PROBE: ID be ea00 0 0 Bytes
打印出NOR FLASH的厂家ID=0x01(AMD生产),,设备ID=0x2249,我们查看S29AL016D70TF102芯片的datasheet,发现这两个ID是正确的,但是输出信息中却没有识别到我们NOR FLASH的型号,说明程序底层驱动是对的,但是u-boot没支持我们这个型号的NOR FLASH。
2.2.1 initr_flash
我们定位到 initr_flash(common/board_r.c):
static int initr_flash(void) { ulong flash_size = 0; bd_t *bd = gd->bd; puts("Flash: "); if (board_flash_wp_on()) printf("Uninitialized - Write Protect On\n"); else flash_size = flash_init(); // 获取NOR FLASH大小 print_size(flash_size, ""); #ifdef CONFIG_SYS_FLASH_CHECKSUM /* * Compute and print flash CRC if flashchecksum is set to 'y' * * NOTE: Maybe we should add some WATCHDOG_RESET()? XXX */ if (getenv_yesno("flashchecksum") == 1) { printf(" CRC: %08X", crc32(0, (const unsigned char *) CONFIG_SYS_FLASH_BASE, flash_size)); } #endif /* CONFIG_SYS_FLASH_CHECKSUM */ putc('\n'); /* update start of FLASH memory */ #ifdef CONFIG_SYS_FLASH_BASE bd->bi_flashstart = CONFIG_SYS_FLASH_BASE; #endif /* size of FLASH memory (final value) */ bd->bi_flashsize = flash_size; #if defined(CONFIG_SYS_UPDATE_FLASH_SIZE) /* Make a update of the Memctrl. */ update_flash_size(flash_size); #endif #if defined(CONFIG_OXC) || defined(CONFIG_RMU) /* flash mapped at end of memory map */ bd->bi_flashoffset = CONFIG_SYS_TEXT_BASE + flash_size; #elif CONFIG_SYS_MONITOR_BASE == CONFIG_SYS_FLASH_BASE bd->bi_flashoffset = monitor_flash_len; /* reserved area for monitor */ #endif return 0; }
2.2.2 flash_init
flash_init函数在drivers\mtd\cfi_flash.c中定义:
unsigned long flash_init (void) { unsigned long size = 0; int i; #ifdef CONFIG_SYS_FLASH_PROTECTION /* read environment from EEPROM */ char s[64]; getenv_f("unlock", s, sizeof(s)); #endif #ifdef CONFIG_CFI_FLASH /* for driver model */ cfi_flash_init_dm(); #endif /* Init: no FLASHes known */ for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) { flash_info[i].flash_id = FLASH_UNKNOWN; //0xFFFF /* Optionally write flash configuration register */ cfi_flash_set_config_reg(cfi_flash_bank_addr(i), // 什么也没做 里面是空 cfi_flash_config_reg(i)); if (!flash_detect_legacy(cfi_flash_bank_addr(i), i)) flash_get_size(cfi_flash_bank_addr(i), i); size += flash_info[i].size; if (flash_info[i].flash_id == FLASH_UNKNOWN) { #ifndef CONFIG_SYS_FLASH_QUIET_TEST printf ("## Unknown flash on Bank %d " "- Size = 0x%08lx = %ld MB\n", i+1, flash_info[i].size, flash_info[i].size >> 20); #endif /* CONFIG_SYS_FLASH_QUIET_TEST */ } #ifdef CONFIG_SYS_FLASH_PROTECTION ...... #endif /* CONFIG_SYS_FLASH_PROTECTION */ } flash_protect_default(); #ifdef CONFIG_FLASH_CFI_MTD cfi_mtd_init(); #endif return (size); }
其中CONFIG_SYS_MAX_FLASH_BANKS定义为1,FLASH_UNKNOWN 定义为0xFFFF:
#define CONFIG_SYS_MAX_FLASH_BANKS 1 #define FLASH_UNKNOWN 0xFFFF /* unknown flash type */
flash_info定义如下:
#define CFI_MAX_FLASH_BANKS CONFIG_SYS_MAX_FLASH_BANKS flash_info_t flash_info[CFI_MAX_FLASH_BANKS]; /* FLASH chips info */
2.2.3 flash_info_t结构体
其中flash结构体定义如下:
/*----------------------------------------------------------------------- * FLASH Info: contains chip specific data, per FLASH bank */ typedef struct { ulong size; /* total bank size in bytes 容量、单位字节 */ ushort sector_count; /* number of erase units 扇区数量 */ ulong flash_id; /* combined device & manufacturer code 设备id&厂商id */ 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 /* 支持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 */ uchar cmd_erase_sector; /* vendor specific erase sect. 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 解锁地址1 */ ulong addr_unlock2; /* unlock address 2 for AMD flash roms 解锁地址2 */ const char *name; /* human-readable name */ #endif #ifdef CONFIG_MTD struct mtd_info *mtd; #endif } flash_info_t;
这里我们重点分析flash_detect_legacy和flash_get_size函数。
2.3 flash_detect_legacy
flash_detect_legacy函数在drivers\mtd\cfi_flash.c中定义:
/*----------------------------------------------------------------------- * Call board code to request info about non-CFI flash. * board_flash_get_legacy needs to fill in at least: * info->portwidth, info->chipwidth and info->interface for Jedec probing. */ static int flash_detect_legacy(phys_addr_t base, int banknum) { flash_info_t *info = &flash_info[banknum]; if (board_flash_get_legacy(base, banknum, info)) { // 初始化portwidth、chipwidth等参数 /* board code may have filled info completely. If not, we use JEDEC ID probing. */ if (!info->vendor) { int modes[] = { // 定义两种生产厂商,AMD与INTEL CFI_CMDSET_AMD_STANDARD, CFI_CMDSET_INTEL_STANDARD }; int i; for (i = 0; i < ARRAY_SIZE(modes); i++) { // 循环两次 info->vendor = modes[i]; info->start[0] = (ulong)map_physmem(base, info->portwidth, MAP_NOCACHE); if (info->portwidth == FLASH_CFI_8BIT && info->interface == FLASH_CFI_X8X16) { info->addr_unlock1 = 0x2AAA; info->addr_unlock2 = 0x5555; } else { info->addr_unlock1 = 0x5555; info->addr_unlock2 = 0x2AAA; } flash_read_jedec_ids(info); // 读取芯片id信息 debug("JEDEC PROBE: ID %x %x %x\n", info->manufacturer_id, info->device_id, info->device_id2); if (jedec_flash_match(info, info->start[0])) // 使用jedec规范,通过id比较是否支持该NOR FLASH break; else unmap_physmem((void *)info->start[0], info->portwidth); } } switch(info->vendor) { case CFI_CMDSET_INTEL_PROG_REGIONS: case CFI_CMDSET_INTEL_STANDARD: case CFI_CMDSET_INTEL_EXTENDED: info->cmd_reset = FLASH_CMD_RESET; break; case CFI_CMDSET_AMD_STANDARD: case CFI_CMDSET_AMD_EXTENDED: case CFI_CMDSET_AMD_LEGACY: info->cmd_reset = AMD_CMD_RESET; break; } info->flash_id = FLASH_MAN_CFI; return 1; } return 0; /* use CFI */ }
这里首先初始化芯片位宽、以及端口位宽等参数,然后通过函数flash_read_jedec_ids去读取NOR FLASH的厂家ID和设备ID信息,并写入到info成员。
2.3.1 board_flash_get_legacy
board_flash_get_legacy在board/samsung/smdk2440/smdk2440.c中定义:
/* * Hardcoded flash setup: * Flash 0 is a non-CFI AMD AM29LV800BB flash. */ ulong board_flash_get_legacy(ulong base, int banknum, flash_info_t *info) { info->portwidth = FLASH_CFI_16BIT; // 2 info->chipwidth = FLASH_CFI_BY16; // 2 info->interface = FLASH_CFI_X16; // 2 return 1; }
2.3.2 flash_read_jedec_ids
flash_read_jedec_ids函数在drivers\mtd\cfi_flash.c中定义:
static void flash_read_jedec_ids (flash_info_t * info) { info->manufacturer_id = 0; info->device_id = 0; info->device_id2 = 0; switch (info->vendor) { case CFI_CMDSET_INTEL_PROG_REGIONS: case CFI_CMDSET_INTEL_STANDARD: case CFI_CMDSET_INTEL_EXTENDED: cmdset_intel_read_jedec_ids(info); break; case CFI_CMDSET_AMD_STANDARD: case CFI_CMDSET_AMD_EXTENDED: cmdset_amd_read_jedec_ids(info); // AMD芯片 走这里 break; default: break; } }
其中cmdset_amd_read_jedec_ids函数采用JEDEC协议,读取设备id信息:
static void cmdset_amd_read_jedec_ids(flash_info_t *info) { ushort bankId = 0; uchar manuId; flash_write_cmd(info, 0, 0, AMD_CMD_RESET); // 复位命令 flash_unlock_seq(info, 0); // 解锁命令 flash_write_cmd(info, 0, info->addr_unlock1, FLASH_CMD_READ_ID); // 读取id命令 udelay(1000); /* some flash are slow to respond */ manuId = flash_read_uchar (info, FLASH_OFFSET_MANUFACTURER_ID); /* JEDEC JEP106Z specifies ID codes up to bank 7 */ while (manuId == FLASH_CONTINUATION_CODE && bankId < 0x800) { bankId += 0x100; manuId = flash_read_uchar (info, bankId | FLASH_OFFSET_MANUFACTURER_ID); } info->manufacturer_id = manuId; switch (info->chipwidth){ case FLASH_CFI_8BIT: info->device_id = flash_read_uchar (info, FLASH_OFFSET_DEVICE_ID); if (info->device_id == 0x7E) { /* AMD 3-byte (expanded) device ids */ info->device_id2 = flash_read_uchar (info, FLASH_OFFSET_DEVICE_ID2); info->device_id2 <<= 8; info->device_id2 |= flash_read_uchar (info, FLASH_OFFSET_DEVICE_ID3); } break; case FLASH_CFI_16BIT: // 这里 info->device_id = flash_read_word (info, FLASH_OFFSET_DEVICE_ID); if ((info->device_id & 0xff) == 0x7E) { /* AMD 3-byte (expanded) device ids */ info->device_id2 = flash_read_uchar (info, FLASH_OFFSET_DEVICE_ID2); info->device_id2 <<= 8; info->device_id2 |= flash_read_uchar (info, FLASH_OFFSET_DEVICE_ID3); } break; default: break; } flash_write_cmd(info, 0, 0, AMD_CMD_RESET); udelay(1); }
需要注意的是这里解锁地址为0x5555、0x2AAAA。当然实际向NOR FALSH芯片发送解锁或命令时,会根据选择的位宽重新计算地址。比如:
/* * Write a proper sized command to the correct address */ void flash_write_cmd (flash_info_t * info, flash_sect_t sect, uint offset, u32 cmd) { void *addr; cfiword_t cword; addr = flash_map (info, sect, offset); // 重新计算地址 flash_make_cmd (info, cmd, &cword); switch (info->portwidth) { // 1 case FLASH_CFI_8BIT: debug ("fwc addr %p cmd %x %x 8bit x %d bit\n", addr, cmd, cword.w8, info->chipwidth << CFI_FLASH_SHIFT_WIDTH); flash_write8(cword.w8, addr); break; case FLASH_CFI_16BIT: // 2 debug ("fwc addr %p cmd %x %4.4x 16bit x %d bit\n", addr, cmd, cword.w16, info->chipwidth << CFI_FLASH_SHIFT_WIDTH); flash_write16(cword.w16, addr); break; case FLASH_CFI_32BIT: // 4 debug ("fwc addr %p cmd %x %8.8x 32bit x %d bit\n", addr, cmd, cword.w32, info->chipwidth << CFI_FLASH_SHIFT_WIDTH); flash_write32(cword.w32, addr); break; case FLASH_CFI_64BIT: #ifdef DEBUG { char str[20]; print_longlong (str, cword.w64); debug ("fwrite addr %p cmd %x %s 64 bit x %d bit\n", addr, cmd, str, info->chipwidth << CFI_FLASH_SHIFT_WIDTH); } #endif flash_write64(cword.w64, addr); break; } /* Ensure all the instructions are fully finished */ sync(); flash_unmap(info, sect, offset, addr); } static void flash_unlock_seq (flash_info_t * info, flash_sect_t sect) { flash_write_cmd (info, sect, info->addr_unlock1, AMD_CMD_UNLOCK_START); flash_write_cmd (info, sect, info->addr_unlock2, AMD_CMD_UNLOCK_ACK); }
2.3.3 jedec_flash_match
然后进入jedec_flash_match函数,该函数位于drivers/mtd/jedec_flash.c,这个函数通过遍历jedec_table找到厂家ID、设备ID匹配的NOR FALSH型号信息,然后使用这些信息初始化info成员:
/*----------------------------------------------------------------------- * match jedec ids against table. If a match is found, fill flash_info entry */ int jedec_flash_match(flash_info_t *info, ulong base) { int ret = 0; int i; ulong mask = 0xFFFF; if (info->chipwidth == 1) mask = 0xFF; for (i = 0; i < ARRAY_SIZE(jedec_table); i++) { if ((jedec_table[i].mfr_id & mask) == (info->manufacturer_id & mask) && (jedec_table[i].dev_id & mask) == (info->device_id & mask)) { fill_info(info, &jedec_table[i], base); // 其中包含了info->size的填充 ret = 1; break; } } return ret; }
其中jedec_table数组查看:
static const struct amd_flash_info jedec_table[] = { #ifdef CONFIG_SYS_FLASH_LEGACY_256Kx8 { .mfr_id = (u16)SST_MANUFACT, .dev_id = SST39LF020, .name = "SST 39LF020", .uaddr = { [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ }, .DevSize = SIZE_256KiB, .CmdSet = P_ID_AMD_STD, .NumEraseRegions= 1, .regions = { ERASEINFO(0x01000,64), } }, #endif #ifdef CONFIG_SYS_FLASH_LEGACY_512Kx8 { .mfr_id = (u16)AMD_MANUFACT, .dev_id = AM29LV040B, .name = "AMD AM29LV040B", .uaddr = { [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ }, .DevSize = SIZE_512KiB, .CmdSet = P_ID_AMD_STD, .NumEraseRegions= 1, .regions = { ERASEINFO(0x10000,8), } }, { .mfr_id = (u16)SST_MANUFACT, .dev_id = SST39LF040, .name = "SST 39LF040", .uaddr = { [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ }, .DevSize = SIZE_512KiB, .CmdSet = P_ID_AMD_STD, .NumEraseRegions= 1, .regions = { ERASEINFO(0x01000,128), } }, { .mfr_id = (u16)STM_MANUFACT, .dev_id = STM_ID_M29W040B, .name = "ST Micro M29W040B", .uaddr = { [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ }, .DevSize = SIZE_512KiB, .CmdSet = P_ID_AMD_STD, .NumEraseRegions= 1, .regions = { ERASEINFO(0x10000,8), } }, { .mfr_id = (u16)MX_MANUFACT, .dev_id = MX29LV040, .name = "MXIC MX29LV040", .uaddr = { [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ }, .DevSize = SIZE_512KiB, .CmdSet = P_ID_AMD_STD, .NumEraseRegions= 1, .regions = { ERASEINFO(0x10000, 8), } }, { .mfr_id = (u16)WINB_MANUFACT, .dev_id = W39L040A, .name = "WINBOND W39L040A", .uaddr = { [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ }, .DevSize = SIZE_512KiB, .CmdSet = P_ID_AMD_STD, .NumEraseRegions= 1, .regions = { ERASEINFO(0x10000, 8), } }, { .mfr_id = (u16)AMIC_MANUFACT, .dev_id = A29L040, .name = "AMIC A29L040", .uaddr = { [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ }, .DevSize = SIZE_512KiB, .CmdSet = P_ID_AMD_STD, .NumEraseRegions= 1, .regions = { ERASEINFO(0x10000, 8), } }, { .mfr_id = (u16)EON_MANUFACT, .dev_id = EN29LV040A, .name = "EON EN29LV040A", .uaddr = { [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ }, .DevSize = SIZE_512KiB, .CmdSet = P_ID_AMD_STD, .NumEraseRegions= 1, .regions = { ERASEINFO(0x10000, 8), } }, #endif #ifdef CONFIG_SYS_FLASH_LEGACY_512Kx16 { .mfr_id = (u16)AMD_MANUFACT, .dev_id = AM29F400BB, .name = "AMD AM29F400BB", .uaddr = { [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ }, .DevSize = SIZE_512KiB, .CmdSet = CFI_CMDSET_AMD_LEGACY, .NumEraseRegions= 4, .regions = { ERASEINFO(0x04000, 1), ERASEINFO(0x02000, 2), ERASEINFO(0x08000, 1), ERASEINFO(0x10000, 7), } }, { .mfr_id = (u16)AMD_MANUFACT, .dev_id = AM29LV400BB, .name = "AMD AM29LV400BB", .uaddr = { [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ }, .DevSize = SIZE_512KiB, .CmdSet = CFI_CMDSET_AMD_LEGACY, .NumEraseRegions= 4, .regions = { ERASEINFO(0x04000,1), ERASEINFO(0x02000,2), ERASEINFO(0x08000,1), ERASEINFO(0x10000,7), } }, { .mfr_id = (u16)AMD_MANUFACT, .dev_id = AM29LV800BB, .name = "AMD AM29LV800BB", .uaddr = { [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ }, .DevSize = SIZE_1MiB, .CmdSet = CFI_CMDSET_AMD_LEGACY, .NumEraseRegions= 4, .regions = { ERASEINFO(0x04000, 1), ERASEINFO(0x02000, 2), ERASEINFO(0x08000, 1), ERASEINFO(0x10000, 15), } }, { .mfr_id = (u16)AMD_MANUFACT, .dev_id = AM29LV800BT, .name = "AMD AM29LV800BT", .uaddr = { [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ }, .DevSize = SIZE_1MiB, .CmdSet = CFI_CMDSET_AMD_LEGACY, .NumEraseRegions= 4, .regions = { ERASEINFO(0x10000, 15), ERASEINFO(0x08000, 1), ERASEINFO(0x02000, 2), ERASEINFO(0x04000, 1), } }, { .mfr_id = (u16)MX_MANUFACT, .dev_id = AM29LV800BT, .name = "MXIC MX29LV800BT", .uaddr = { [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ }, .DevSize = SIZE_1MiB, .CmdSet = CFI_CMDSET_AMD_LEGACY, .NumEraseRegions= 4, .regions = { ERASEINFO(0x10000, 15), ERASEINFO(0x08000, 1), ERASEINFO(0x02000, 2), ERASEINFO(0x04000, 1), } }, { .mfr_id = (u16)EON_ALT_MANU, .dev_id = AM29LV800BT, .name = "EON EN29LV800BT", .uaddr = { [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ }, .DevSize = SIZE_1MiB, .CmdSet = CFI_CMDSET_AMD_LEGACY, .NumEraseRegions= 4, .regions = { ERASEINFO(0x10000, 15), ERASEINFO(0x08000, 1), ERASEINFO(0x02000, 2), ERASEINFO(0x04000, 1), } }, { .mfr_id = (u16)STM_MANUFACT, .dev_id = STM29F400BB, .name = "ST Micro M29F400BB", .uaddr = { [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ }, .DevSize = SIZE_512KiB, .CmdSet = CFI_CMDSET_AMD_LEGACY, .NumEraseRegions = 4, .regions = { ERASEINFO(0x04000, 1), ERASEINFO(0x02000, 2), ERASEINFO(0x08000, 1), ERASEINFO(0x10000, 7), } }, #endif };
查询以后发现,确实是这里面没有我们所使用的NOR FLASH型号S29AL016D70TF102。
进入机器ID的宏定义代码,里面找到了这个(比较后四位):
#define AMD_MANUFACT 0x00010001 /* AMD manuf. ID in D23..D16, D7..D0 */
01就是我们使用的NOR FLASH厂家ID,说明u-boot是能够支持的,只是数组中没有将其写入。更改数组,加入我们自己的NOR FLASH,使其能够支持。
2.3.4 修改jedec_table数组(drivers/mtd/jedec_flash.c)
因为数组中有宏定义,我也不打算破坏它的结构了,随便复制了一个将其粘贴到数组的最后,然后修改为如下内容:
亲爱的读者和支持者们,自动博客加入了打赏功能,陆陆续续收到了各位老铁的打赏。在此,我想由衷地感谢每一位对我们博客的支持和打赏。你们的慷慨与支持,是我们前行的动力与源泉。
日期 | 姓名 | 金额 |
---|---|---|
2023-09-06 | *源 | 19 |
2023-09-11 | *朝科 | 88 |
2023-09-21 | *号 | 5 |
2023-09-16 | *真 | 60 |
2023-10-26 | *通 | 9.9 |
2023-11-04 | *慎 | 0.66 |
2023-11-24 | *恩 | 0.01 |
2023-12-30 | I*B | 1 |
2024-01-28 | *兴 | 20 |
2024-02-01 | QYing | 20 |
2024-02-11 | *督 | 6 |
2024-02-18 | 一*x | 1 |
2024-02-20 | c*l | 18.88 |
2024-01-01 | *I | 5 |
2024-04-08 | *程 | 150 |
2024-04-18 | *超 | 20 |
2024-04-26 | .*V | 30 |
2024-05-08 | D*W | 5 |
2024-05-29 | *辉 | 20 |
2024-05-30 | *雄 | 10 |
2024-06-08 | *: | 10 |
2024-06-23 | 小狮子 | 666 |
2024-06-28 | *s | 6.66 |
2024-06-29 | *炼 | 1 |
2024-06-30 | *! | 1 |
2024-07-08 | *方 | 20 |
2024-07-18 | A*1 | 6.66 |
2024-07-31 | *北 | 12 |
2024-08-13 | *基 | 1 |
2024-08-23 | n*s | 2 |
2024-09-02 | *源 | 50 |
2024-09-04 | *J | 2 |
2024-09-06 | *强 | 8.8 |
2024-09-09 | *波 | 1 |
2024-09-10 | *口 | 1 |
2024-09-10 | *波 | 1 |
2024-09-12 | *波 | 10 |
2024-09-18 | *明 | 1.68 |
2024-09-26 | B*h | 10 |
2024-09-30 | 岁 | 10 |
2024-10-02 | M*i | 1 |
2024-10-14 | *朋 | 10 |
2024-10-22 | *海 | 10 |
2024-10-23 | *南 | 10 |
2024-10-26 | *节 | 6.66 |
2024-10-27 | *o | 5 |
2024-10-28 | W*F | 6.66 |
2024-10-29 | R*n | 6.66 |
2024-11-02 | *球 | 6 |
2024-11-021 | *鑫 | 6.66 |
2024-11-25 | *沙 | 5 |
2024-11-29 | C*n | 2.88 |

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了