在上一节中,不知道你有没有注意到,以NOR方式启动u-boot后,运行有一行信息:

Mini440之uboot移植之实践NOR FLASH支持(二)

输出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 FlashInterface],CFI是一个公开的标准的从Flash Memory器件中读取数据的接口。它可以使系统软件查询已安装的Flash Memory器件的各种参数,包括器件阵列结构参数、电气和时间参数以及器件支持的功能等。利用CFI可以不用修改系统软件 就可以用新型的和改进的产品代替旧版本的产品。例如:如果新型的Flash Memory的擦除时间只有旧版本的一半,系统软件只要通过CFI读取新器件的擦除时间等参数,修改一下定时器的时间参数即可。
  • JEDEC是由生产厂商们制定的国际性协议,主要为 计算机 内存制定。JEDEC用来帮助程序读取Flash的制造商ID和设备ID,以确定Flash的大小和算法,如果芯片不支持CFI,就需使用JEDEC了。工业标准的内存通常指的是符合JEDEC标准的一组内存。

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。

Mini440之uboot移植之实践NOR FLASH支持(二)

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来说就被屏蔽掉了。

Mini440之uboot移植之实践NOR FLASH支持(二)

补充说明:

  • 一般来说,ARM处理器内部要设置相应的寄存器,告诉处理器外部扩展的Flash的位宽(8-BIT/16-BIT/32-BIT)。这样,处理器才知道在访问的时候如何从FLASH正确的读取数据;
  • 有些ARM处理器内部可以设置地址的错位。对于支持软件选择地址错位的处理器,在连接16-BIT FLASH的时候,硬件上可以不需要把地址线错位。
  • 如果处理器支持内部设置地址错位,在实际访问的时候,送出的地址实际上是在MCU内部做了错位处理,其作用是等效于硬件连接上的错位的。

1.2 指令集

Mini440之uboot移植之实践NOR FLASH支持(二)

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。

关于如何对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


/*-----------------------------------------------------------------------
 * 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
#define CONFIG_FLASH_SHOW_PROGRESS    45

#define CONFIG_SYS_MAX_FLASH_BANKS    1
#define CONFIG_SYS_FLASH_BANKS_LIST     { CONFIG_SYS_FLASH_BASE }
#define CONFIG_SYS_MAX_FLASH_SECT    (19)

2.2 分析启动信息

在u-boot启动时,我们打印调试信息:

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
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。

我们定位到 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();

    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;
}

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
        else if (strcmp(s, "yes") == 0) {
            /*
             * Only the U-Boot image and it's environment
             * is protected, all other sectors are
             * unprotected (unlocked) if flash hardware
             * protection is used (CONFIG_SYS_FLASH_PROTECTION)
             * and the environment variable "unlock" is
             * set to "yes".
             */
            if (flash_info[i].legacy_unlock) {
                int k;

                /*
                 * Disable legacy_unlock temporarily,
                 * since flash_real_protect would
                 * relock all other sectors again
                 * otherwise.
                 */
                flash_info[i].legacy_unlock = 0;

                /*
                 * Legacy unlocking (e.g. Intel J3) ->
                 * unlock only one sector. This will
                 * unlock all sectors.
                 */
                flash_real_protect (&flash_info[i], 0, 0);

                flash_info[i].legacy_unlock = 1;

                /*
                 * Manually mark other sectors as
                 * unlocked (unprotected)
                 */
                for (k = 1; k < flash_info[i].sector_count; k++)
                    flash_info[i].protect[k] = 0;
            } else {
                /*
                 * No legancy unlocking -> unlock all sectors
                 */
                flash_protect (FLAG_PROTECT_CLEAR,
                           flash_info[i].start[0],
                           flash_info[i].start[0]
                           + flash_info[i].size - 1,
                           &flash_info[i]);
            }
        }
#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 */
/*-----------------------------------------------------------------------
 * 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    */
    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
    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  */
    ulong   addr_unlock2;        /* unlock address 2 for AMD flash roms  */
    const char *name;        /* human-readable name                    */
#endif
#ifdef CONFIG_MTD
    struct mtd_info *mtd;
#endif
} flash_info_t;
View Code

相关文章: