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

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

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

输出Flash信息这一部分代码是位于board_init_r阶段,执行initr_flash()函数的输出结果。

我们开发板上搭载了型号为S29AL016D70TF102的2MB(1M*16bit)大小的NOR FLASH,这里输出NOR FALSH大小为0字节,很明显没有能正确识别 2M的NOR FLASH了。

这一节我们将会介绍u-boot如何支持我们的NOR FLASH,这样我们后续就可以通过命令行对NOR FALSH进行读写操作。

一、NOR FLASH支持

1.1 分析启动信息

在NOR启动时,我们打印调试信息:

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:

Mini440之uboot移植之实践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 */
Mini440之uboot移植之实践NOR 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    */
    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

这里我们重点分析flash_detect_legacy和flash_get_size函数。

1.2 flash_detect_legacy

/*-----------------------------------------------------------------------
 * 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)) {
        /* board code may have filled info completely. If not, we
           use JEDEC ID probing. */
        if (!info->vendor) {
            int modes[] = {
                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);
                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]))
                    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 */
}

 进入jedec_flash_match函数,位于drivers/mtd/jedec_flash.c:

/*-----------------------------------------------------------------------
 * 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);
            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,使其能够支持。

1.3 修改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(64*1024, 31),
            ERASEINFO(32*1024, 1),
            ERASEINFO(8*1024, 2),
            ERASEINFO(16*1024, 1),
        }
    },
  • 厂家ID:mfr_id修改为AMD_MANUFACT;
  • 设备ID:dev_id修改为0x2249,即AM29LV160DB;
  • 名字:name修改为AMD AM29LV160DB;
  • uaddr:解锁地址改为[1] = MTD_UADDR_0x0555_0x02AA /* x16 */ /*NOR flash 解锁地址 */。[0]表示NOR是8位的,我使用的是16位的,因此改为1。NORFlash使用的时候会锁定,因此写的时候要先解锁,在一些地址中写入值才能解锁。解锁地址可以查阅手册。
  • 大小:DevSize修改为SIZE_2MiB;
  • NumEraseRegions:NOR的结构不同,擦除块是不同的。可以查阅手册,有多少种块,就写几。
  • regions:说明擦除块的分布情况,从上往下分别是NOR中地址从低到高排布;

我使用的NOR有4种,分别8K字1块,4K字节1块,16K字1块,32K字31块。1K字大小为2048字节。

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

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

所以regions配置如下:

        .regions        = {
            ERASEINFO(64*1024, 31),
            ERASEINFO(32*1024, 1),
            ERASEINFO(8*1024, 2),
            ERASEINFO(16*1024, 1),
        }

修改include/configs/smdk2440.h文件,这里使用CONFIG_SYS_MAX_FLASH_SECT定义了我们NOR FLASH最大扇区数量,我们修改为128。 其实这个值多少都可以,只要大于使用的擦除块就行了。

#define CONFIG_SYS_MAX_FLASH_SECT (128)

1.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);
}

二、 编译下载运行

2.1 编译

make distclean
make smdk2440_config
make ARCH=arm CROSS_COMPILE=arm-linux- V=1

2.2 下载到NOR FALSH

我们再次下载运行,其中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 = 31 erase_region_size = 65536
erase_region_count = 1 erase_region_size = 32768
erase_region_count = 2 erase_region_size = 8192
erase_region_count = 1 erase_region_size = 16384
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
flash_protect ON: from 0x00070000 to 0x0007FFFF
protect on 7
2 MiB

在控制台输入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   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        001F8000        001FA000        001FC000 

然后通过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文件,做一个对比即可:

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

可以看到数据是一样的,低地址数据在低位,高地址数据在高位,采用的小端存储模式。

参考文章

[1]S3C2440移植uboot之支持NORFLASH

[2]uboot支持NORFlash

[3]移植uboot第五步:支持NORFlash

[4]S29AL016D70TFI020

[5]mini2440硬件篇之Nor Flash

 

上一篇:浅析嵌入式系统之uboot详解(4.3)—异常和异常向量


下一篇:迅为2K1000龙芯开发板pmon BSP移植之配置CAN总线