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)
因为数组中有宏定义,我也不打算破坏它的结构了,随便复制了一个将其粘贴到数组的最后,然后修改为如下内容:
{ .mfr_id = (u16)AMD_MANUFACT, .dev_id = AM29LV160DB, .name = "AMD AM29LV160DB", .uaddr = { [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ }, .DevSize = SIZE_2MiB, .CmdSet = P_ID_AMD_STD, .NumEraseRegions = 4, .regions = { ERASEINFO(16*1024, 1), ERASEINFO(8*1024, 2), ERASEINFO(32*1024, 1), ERASEINFO(64*1024,31), } },
- 厂家ID:mfr_id修改为AMD_MANUFACT;
- 设备ID:dev_id修改为0x2249,即AM29LV160DB;
- 名字:name修改为AMD AM29LV160DB;
- uaddr:解锁地址改为[1] = MTD_UADDR_0x0555_0x02AA 。[0]表示NOR是8位的,我使用的是16位的,因此改为1。NOR FALSH的指令集中有一些命令在写入命令时序之前,需要先进行解锁,即向一些地址写入固定值,解锁地址可以查阅手册。
- 大小:DevSize修改为SIZE_2MiB;
- NumEraseRegions:NOR的结构不同,擦除块是不同的。可以查阅手册,有多少种块,就写几。
- regions:说明擦除块的分布情况,从上往下分别是NOR中地址从低到高排列;
我使用的NOR有4种,在字模式下,分别8Kword 1块,4Kword 2 块,16Kword 1块,32Kword 31块。
所以regions配置如下:
.regions = { ERASEINFO(16*1024, 1), ERASEINFO(8*1024, 2), ERASEINFO(32*1024, 1), ERASEINFO(64*1024,31), }
修改include/configs/smdk2440.h文件,这里使用CONFIG_SYS_MAX_FLASH_SECT定义了我们NOR FLASH最大扇区数量,我们修改为128。 其实这个值多少都可以,只要大于使用的擦除块就行了。
#define CONFIG_SYS_MAX_FLASH_SECT (128)
2.4 flash_get_size
/* * The following code cannot be run from FLASH! * */ 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, 0, 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); if (flash_detect_cfi (info, &qry)) { info->vendor = le16_to_cpu(get_unaligned(&(qry.p_id))); info->ext_addr = le16_to_cpu(get_unaligned(&(qry.p_adr))); num_erase_regions = qry.num_erase_regions; if (info->ext_addr) { info->cfi_version = (ushort) flash_read_uchar (info, info->ext_addr + 3) << 8; info->cfi_version |= (ushort) flash_read_uchar (info, info->ext_addr + 4); } #ifdef DEBUG flash_printqry (&qry); #endif switch (info->vendor) { case CFI_CMDSET_INTEL_PROG_REGIONS: case CFI_CMDSET_INTEL_STANDARD: case CFI_CMDSET_INTEL_EXTENDED: cmdset_intel_init(info, &qry); break; case CFI_CMDSET_AMD_STANDARD: case CFI_CMDSET_AMD_EXTENDED: cmdset_amd_init(info, &qry); break; default: printf("CFI: Unknown command set 0x%x\n", info->vendor); /* * Unfortunately, this means we don't know how * to get the chip back to Read mode. Might * as well try an Intel-style reset... */ flash_write_cmd(info, 0, 0, FLASH_CMD_RESET); return 0; } /* Do manufacturer-specific fixups */ switch (info->manufacturer_id) { case 0x0001: /* AMD */ case 0x0037: /* AMIC */ flash_fixup_amd(info, &qry); break; case 0x001f: flash_fixup_atmel(info, &qry); break; case 0x0020: flash_fixup_stm(info, &qry); break; case 0x00bf: /* SST */ flash_fixup_sst(info, &qry); break; case 0x0089: /* Numonyx */ flash_fixup_num(info, &qry); break; } debug ("manufacturer is %d\n", info->vendor); debug ("manufacturer id is 0x%x\n", info->manufacturer_id); debug ("device id is 0x%x\n", info->device_id); debug ("device id2 is 0x%x\n", info->device_id2); debug ("cfi version is 0x%04x\n", info->cfi_version); 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; } 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); info->size = 1 << qry.dev_size; /* multiply the size by the number of chips */ info->size *= size_ratio; max_size = cfi_flash_bank_size(banknum); if (max_size && (info->size > max_size)) { debug("[truncated from %ldMiB]", info->size >> 20); info->size = max_size; } debug ("found %d erase regions\n", num_erase_regions); sect_cnt = 0; sector = base; for (i = 0; i < num_erase_regions; i++) { if (i > NUM_ERASE_REGIONS) { printf ("%d erase regions found, only %d used\n", num_erase_regions, NUM_ERASE_REGIONS); break; } tmp = le32_to_cpu(get_unaligned( &(qry.erase_region_info[i]))); debug("erase region %u: 0x%08lx\n", i, tmp); erase_region_count = (tmp & 0xffff) + 1; tmp >>= 16; erase_region_size = (tmp & 0xffff) ? ((tmp & 0xffff) * 256) : 128; debug ("erase_region_count = %d erase_region_size = %d\n", erase_region_count, erase_region_size); for (j = 0; j < erase_region_count; j++) { if (sector - base >= info->size) break; if (sect_cnt >= CONFIG_SYS_MAX_FLASH_SECT) { printf("ERROR: too many flash sectors\n"); break; } info->start[sect_cnt] = (ulong)map_physmem(sector, info->portwidth, MAP_NOCACHE); sector += (erase_region_size * size_ratio); /* * Only read protection status from * supported devices (intel...) */ switch (info->vendor) { case CFI_CMDSET_INTEL_PROG_REGIONS: case CFI_CMDSET_INTEL_EXTENDED: case CFI_CMDSET_INTEL_STANDARD: /* * Set flash to read-id mode. Otherwise * reading protected status is not * guaranteed. */ flash_write_cmd(info, sect_cnt, 0, FLASH_CMD_READ_ID); info->protect[sect_cnt] = flash_isset (info, sect_cnt, FLASH_OFFSET_PROTECT, FLASH_STATUS_PROTECT); flash_write_cmd(info, sect_cnt, 0, FLASH_CMD_RESET); break; case CFI_CMDSET_AMD_EXTENDED: case CFI_CMDSET_AMD_STANDARD: if (!info->legacy_unlock) { /* default: not protected */ info->protect[sect_cnt] = 0; break; } /* Read protection (PPB) from sector */ flash_write_cmd(info, 0, 0, info->cmd_reset); flash_unlock_seq(info, 0); flash_write_cmd(info, 0, info->addr_unlock1, FLASH_CMD_READ_ID); info->protect[sect_cnt] = flash_isset( info, sect_cnt, FLASH_OFFSET_PROTECT, FLASH_STATUS_PROTECT); break; default: /* default: not protected */ info->protect[sect_cnt] = 0; } sect_cnt++; } } info->sector_count = sect_cnt; info->buffer_size = 1 << le16_to_cpu(qry.max_buf_write_size); tmp = 1 << qry.block_erase_timeout_typ; info->erase_blk_tout = tmp * (1 << qry.block_erase_timeout_max); tmp = (1 << qry.buf_write_timeout_typ) * (1 << qry.buf_write_timeout_max); /* round up when converting to ms */ info->buffer_write_tout = (tmp + 999) / 1000; tmp = (1 << qry.word_write_timeout_typ) * (1 << qry.word_write_timeout_max); /* round up when converting to ms */ info->write_tout = (tmp + 999) / 1000; info->flash_id = FLASH_MAN_CFI; if ((info->interface == FLASH_CFI_X8X16) && (info->chipwidth == FLASH_CFI_BY8)) { /* XXX - Need to test on x8/x16 in parallel. */ info->portwidth >>= 1; } flash_write_cmd (info, 0, 0, info->cmd_reset); } return (info->size); }
三、 编译下载运行
3.1 编译
make distclean
make smdk2440_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux- V=1
3.2 下载到NOR FALSH
我们再次下载程序,并以NOR方式启动运行,其中串口打印NOR FLASH信息如下:
Flash: fwc addr 00000000 cmd f0 00f0 16bit x 16 bit fwc addr 0000aaaa cmd aa 00aa 16bit x 16 bit fwc addr 00005554 cmd 55 0055 16bit x 16 bit fwc addr 0000aaaa cmd 90 0090 16bit x 16 bit fwc addr 00000000 cmd f0 00f0 16bit x 16 bit JEDEC PROBE: ID 1 2249 0 Found JEDEC Flash: AMD AM29LV160DB unlock address index 1 unlock addresses are 0x555/0x2aa erase_region_count = 1 erase_region_size = 16384 erase_region_count = 2 erase_region_size = 8192 erase_region_count = 1 erase_region_size = 32768 erase_region_count = 31 erase_region_size = 65536 flash_protect ON: from 0x00000000 to 0x0008931B protect on 0 protect on 1 protect on 2 protect on 3 protect on 4 protect on 5 protect on 6 protect on 7 protect on 8 protect on 9 protect on 10 protect on 11 flash_protect ON: from 0x00070000 to 0x0007FFFF protect on 10 2 MiB
3.3 CMD命令
在控制台输入flinfo命令(flash info),就能查看flash的信息了:
SMDK2440 # flinfo Bank # 1: AMD AM29LV160DB flash (16 x 16) Size: 2 MB in 35 Sectors AMD Legacy command set, Manufacturer ID: 0x01, Device ID: 0x2249 Erase timeout: 30000 ms, write timeout: 100 ms Sector Start Addresses: 00000000 RO 00004000 RO 00006000 RO 00008000 RO 00010000 RO 00020000 RO 00030000 RO 00040000 RO 00050000 RO 00060000 RO 00070000 RO 00080000 RO 00090000 000A0000 000B0000 000C0000 000D0000 000E0000 000F0000 00100000 00110000 00120000 00130000 00140000 00150000 00160000 00170000 00180000 00190000 001A0000 001B0000 001C0000 001D0000 001E0000 001F0000
然后通过u-boot命令,检测NOR FLASH的读写是否正确,首先去除写保护:
SMDK2440 # protect off all Un-Protect Flash Bank # 1
查看Flash内容,这里我们查看NOR FLASH起始地址的数据:
SMDK2440 # md.b 0000 00000000: be 00 00 ea 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 ................ 00000010: 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 14 f0 9f e5 ................ 00000020: 60 00 00 00 c0 00 00 00 20 01 00 00 80 01 00 00 `....... ....... 00000030: e0 01 00 00 40 02 00 00 a0 02 00 00 ef be ad de ....@...........
由于我们将u-boot下载到了NOR FLASH中,因此我们直接查看u-boot.dis文件,做一个对比即可:
可以看到数据是一样的,低地址数据在低位,高地址数据在高位,采用的小端存储模式。
四、代码下载
Young / s3c2440_project[u-boot-2016.05-nor-flash】
参考文章