程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)

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-30I*B1
2024-01-28*兴20
2024-02-01QYing20
2024-02-11*督6
2024-02-18一*x1
2024-02-20c*l18.88
2024-01-01*I5
2024-04-08*程150
2024-04-18*超20
2024-04-26.*V30
2024-05-08D*W5
2024-05-29*辉20
2024-05-30*雄10
2024-06-08*:10
2024-06-23小狮子666
2024-06-28*s6.66
2024-06-29*炼1
2024-06-30*!1
2024-07-08*方20
2024-07-18A*16.66
2024-07-31*北12
2024-08-13*基1
2024-08-23n*s2
2024-09-02*源50
2024-09-04*J2
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-26B*h10
2024-09-3010
2024-10-02M*i1
2024-10-14*朋10
2024-10-22*海10
2024-10-23*南10
2024-10-26*节6.66
2024-10-27*o5
2024-10-28W*F6.66
2024-10-29R*n6.66
2024-11-02*球6
2024-11-021*鑫6.66
2024-11-25*沙5
2024-11-29C*n2.88
posted @   大奥特曼打小怪兽  阅读(1467)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
如果有任何技术小问题,欢迎大家交流沟通,共同进步

公告 & 打赏

>>

欢迎打赏支持我 ^_^

最新公告

程序项目代做,有需求私信(小程序、网站、爬虫、电路板设计、驱动、应用程序开发、毕设疑难问题处理等)。

了解更多

点击右上角即可分享
微信分享提示