
On Mon, Nov 05, 2007 at 12:21:50PM +0100, Stefan Roese wrote:
I don't like the indentation problem we get from this #ifdef here. Perhaps we should add a __weak__ function flash_detect_legacy() in this file, that can be overridden by board specific functions. Like this:
ulong __flash_detect_legacy(ulong base, int banknum) { return 0; } ulong flash_detect_legacy(ulong base, int banknum) __attribute__((weak, alias("__flash_detect_legacy")));
This way we get rid of the #ifdef too.
Hm - I need some common code at that point, which would otherwise be duplicated in every board code. I have now moved the code to a function flash_detect_legacy.
for (j = 0; j < erase_region_count; j++) {
if (sect_cnt >= CFG_MAX_FLASH_SECT)
break;
Please add an error output here too.
Done.
Okey, next version. The board-specific code may either fill out the complete flash_info struct as in the previous patch, or (preferred), only set info->portwidth, info->chipwidth and info->interface, in which case the code will probe the flash by jedec IDs and look up a table in jedec_flash.c. The table is a near copy of the table in the Linux jedec_flash.c, with the removal of some unused fields.
There is one problem with the AMD_ADDR_* definitions: I believe they are only correct for 16-bit flash ROMS (in 8/16 bit mode), but are wrong for 8-bit flashs. I think the CFI "interface" parameter should be the correct way to distinguish these cases. What is left is the number of bits in the unlock addresses - I have used 0x2AAA/0x5555, since all flash roms I know treat the upper bits as "don't care" (and specify that behaviour in the datasheet), so if the flash datasheet specifies 0xAAA/0x555, using the longer constants should do no harm. However, there are probably lots of flash roms I do *not* know, so I would appreciate feedback on this. If different addresses are really necessary, we would need to add them to the flash_info struct.
This currently works on my IXP425 board (big endian) with one SST39VF020 and one Intel TE28F640J3. I am not sure if the jedec flash code is correct in case of 16-bit JEDEC flashs or multiple 8-bit flash roms on a wider data bus.
Signed-off-by: Michael Schwingen michael@schwingen.org
diff --git a/drivers/cfi_flash.c b/drivers/cfi_flash.c index 5579a1e..399deab 100644 --- a/drivers/cfi_flash.c +++ b/drivers/cfi_flash.c @@ -98,9 +98,9 @@ #define AMD_STATUS_TOGGLE 0x40 #define AMD_STATUS_ERROR 0x20
-#define AMD_ADDR_ERASE_START ((info->portwidth == FLASH_CFI_8BIT) ? 0xAAA : 0x555) -#define AMD_ADDR_START ((info->portwidth == FLASH_CFI_8BIT) ? 0xAAA : 0x555) -#define AMD_ADDR_ACK ((info->portwidth == FLASH_CFI_8BIT) ? 0x555 : 0x2AA) +#define AMD_ADDR_ERASE_START ((info->portwidth == FLASH_CFI_8BIT && info->interface == FLASH_CFI_X8X16) ? 0x2AAA : 0x5555) +#define AMD_ADDR_START ((info->portwidth == FLASH_CFI_8BIT && info->interface == FLASH_CFI_X8X16) ? 0x2AAA : 0x5555) +#define AMD_ADDR_ACK ((info->portwidth == FLASH_CFI_8BIT && info->interface == FLASH_CFI_X8X16) ? 0x5555 : 0x2AAA)
#define FLASH_OFFSET_MANUFACTURER_ID 0x00 #define FLASH_OFFSET_DEVICE_ID 0x01 @@ -331,6 +331,43 @@ ulong flash_read_long (flash_info_t * info, flash_sect_t sect, uint offset) }
+#ifdef CFG_FLASH_CFI_LEGACY +ulong flash_detect_legacy(ulong 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<sizeof(modes)/sizeof(modes[0]); i++) { + info->vendor = modes[i]; + info->start[0] = base; + 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, base)) + break; + } + } + switch(info->vendor) { + case CFI_CMDSET_AMD_LEGACY: + info->cmd_reset = AMD_CMD_RESET; + break; + } + info->flash_id = FLASH_MAN_CFI; + } + return info->size; +} +#else +ulong inline flash_detect_legacy(ulong base, int banknum) +{ + return 0; +} +#endif + + /*----------------------------------------------------------------------- */ unsigned long flash_init (void) @@ -345,7 +382,11 @@ unsigned long flash_init (void) /* Init: no FLASHes known */ for (i = 0; i < CFG_MAX_FLASH_BANKS; ++i) { flash_info[i].flash_id = FLASH_UNKNOWN; - size += flash_info[i].size = flash_get_size (bank_base[i], i); + + flash_detect_legacy (bank_base[i], i); + if (flash_info[i].size == 0) + flash_info[i].size = flash_get_size (bank_base[i], i); + size += flash_info[i].size; if (flash_info[i].flash_id == FLASH_UNKNOWN) { #ifndef CFG_FLASH_QUIET_TEST printf ("## Unknown FLASH on Bank %d - Size = 0x%08lx = %ld MB\n", @@ -488,6 +529,16 @@ int flash_erase (flash_info_t * info, int s_first, int s_last) flash_unlock_seq (info, sect); flash_write_cmd (info, sect, 0, AMD_CMD_ERASE_SECTOR); break; +#ifdef CFG_FLASH_CFI_LEGACY + case CFI_CMDSET_AMD_LEGACY: + flash_write_cmd (info, 0, 0x5555, AMD_CMD_UNLOCK_START); + flash_write_cmd (info, 0, 0x2AAA, AMD_CMD_UNLOCK_ACK); + flash_write_cmd (info, 0, 0x5555, AMD_CMD_ERASE_START); + flash_write_cmd (info, 0, 0x5555, AMD_CMD_UNLOCK_START); + flash_write_cmd (info, 0, 0x2AAA, AMD_CMD_UNLOCK_ACK); + flash_write_cmd (info, sect, 0, AMD_CMD_ERASE_SECTOR); + break; +#endif default: debug ("Unkown flash vendor %d\n", info->vendor); @@ -518,8 +569,12 @@ void flash_print_info (flash_info_t * info)
printf ("CFI conformant FLASH (%d x %d)", (info->portwidth << 3), (info->chipwidth << 3)); - printf (" Size: %ld MB in %d Sectors\n", - info->size >> 20, info->sector_count); + if (info->size < 1024*1024) + printf (" Size: %ld kB in %d Sectors\n", + info->size >> 10, info->sector_count); + else + printf (" Size: %ld MB in %d Sectors\n", + info->size >> 20, info->sector_count); printf (" "); switch (info->vendor) { case CFI_CMDSET_INTEL_STANDARD: @@ -534,6 +589,11 @@ void flash_print_info (flash_info_t * info) case CFI_CMDSET_AMD_EXTENDED: printf ("AMD Extended"); break; +#ifdef CFG_FLASH_CFI_LEGACY + case CFI_CMDSET_AMD_LEGACY: + printf ("AMD (non-CFI)"); + break; +#endif default: printf ("Unknown (%d)", info->vendor); break; @@ -777,6 +837,9 @@ static int flash_is_busy (flash_info_t * info, flash_sect_t sect) break; case CFI_CMDSET_AMD_STANDARD: case CFI_CMDSET_AMD_EXTENDED: +#ifdef CFG_FLASH_CFI_LEGACY + case CFI_CMDSET_AMD_LEGACY: +#endif retval = flash_toggle (info, sect, 0, AMD_STATUS_TOGGLE); break; default: @@ -1282,6 +1345,10 @@ ulong flash_get_size (ulong base, int banknum) 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 (sect_cnt >= CFG_MAX_FLASH_SECT) { + printf("ERROR: too many flash sectors\n"); + break; + } info->start[sect_cnt] = sector; sector += (erase_region_size * size_ratio);
@@ -1387,6 +1454,12 @@ static int flash_write_cfiword (flash_info_t * info, ulong dest, flash_unlock_seq (info, 0); flash_write_cmd (info, 0, AMD_ADDR_START, AMD_CMD_WRITE); break; +#ifdef CFG_FLASH_CFI_LEGACY + case CFI_CMDSET_AMD_LEGACY: + flash_write_cmd (info, 0, 0x5555, AMD_CMD_UNLOCK_START); + flash_write_cmd (info, 0, 0x2AAA, AMD_CMD_UNLOCK_ACK); + flash_write_cmd (info, 0, 0x5555, AMD_CMD_WRITE); +#endif }
switch (info->portwidth) { diff --git a/include/flash.h b/include/flash.h index b0bf733..c4d8ded 100644 --- a/include/flash.h +++ b/include/flash.h @@ -101,6 +101,12 @@ extern void flash_read_user_serial(flash_info_t * info, void * buffer, int offse extern void flash_read_factory_serial(flash_info_t * info, void * buffer, int offset, int len); #endif /* CFG_FLASH_PROTECTION */
+#ifdef CFG_FLASH_CFI_LEGACY +extern ulong board_flash_get_legacy(ulong base, int banknum, flash_info_t *info); +extern int jedec_flash_match(flash_info_t *info, ulong base); +#define CFI_CMDSET_AMD_LEGACY 0xFFF0 +#endif + /*----------------------------------------------------------------------- * return codes from flash_write(): */
new file jedec_flash.c:
/* * (C) Copyright 2007 * Michael Schwingen, michael@schwingen.org * * based in great part on jedec_probe.c from linux kernel: * (C) 2000 Red Hat. GPL'd. * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com * * See file CREDITS for list of people who contributed to this * project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA * */
/* The DEBUG define must be before common to enable debugging */ #define DEBUG
#include <common.h> #include <asm/processor.h> #include <asm/io.h> #include <asm/byteorder.h> #include <environment.h>
#if defined CFG_FLASH_CFI_DRIVER && defined CFG_FLASH_CFI_LEGACY
#define P_ID_AMD_STD CFI_CMDSET_AMD_LEGACY
/* Manufacturers */ #define MANUFACTURER_SST 0x00BF
/* SST */ #define SST39LF800 0x2781 #define SST39LF160 0x2782 #define SST39VF1601 0x234b #define SST39LF512 0x00D4 #define SST39LF010 0x00D5 #define SST39LF020 0x00D6 #define SST39LF040 0x00D7 #define SST39SF010A 0x00B5 #define SST39SF020A 0x00B6
struct amd_flash_info { const __u16 mfr_id; const __u16 dev_id; const char *name; const int NumEraseRegions; const int CmdSet; const ulong regions[6]; };
#define ERASEINFO(size,blocks) (size<<8)|(blocks-1)
/* * Please keep this list ordered by manufacturer! * Fortunately, the list isn't searched often and so a * slow, linear search isn't so bad. */ static const struct amd_flash_info jedec_table[] = { { .mfr_id = MANUFACTURER_SST, .dev_id = SST39LF010, .name = "SST 39LF010", .CmdSet = P_ID_AMD_STD, .NumEraseRegions= 1, .regions = { ERASEINFO(0x01000,32), } }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST39LF020, .name = "SST 39LF020", .CmdSet = P_ID_AMD_STD, .NumEraseRegions= 1, .regions = { ERASEINFO(0x01000,64), } }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST39LF040, .name = "SST 39LF040", .CmdSet = P_ID_AMD_STD, .NumEraseRegions= 1, .regions = { ERASEINFO(0x01000,128), } } };
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
static inline void fill_info(flash_info_t *info, const struct amd_flash_info *jedec_entry, ulong base) { int i,j; int sect_cnt; int size_ratio; int total_size;
size_ratio = info->portwidth / info->chipwidth;
debug("Found JEDEC Flash: %s\n", jedec_entry->name); info->vendor = jedec_entry->CmdSet; /* Todo: do we need device-specific timeouts? */ info->erase_blk_tout = 30000; info->buffer_write_tout = 1000; info->write_tout = 100;
sect_cnt = 0; total_size = 0; for (i = 0; i < jedec_entry->NumEraseRegions; i++) { ulong erase_region_size = jedec_entry->regions[i] >> 8; ulong erase_region_count = (jedec_entry->regions[i] & 0xff) + 1;
total_size += erase_region_size * erase_region_count; 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 (sect_cnt >= CFG_MAX_FLASH_SECT) { printf("ERROR: too many flash sectors\n"); break; } info->start[sect_cnt] = base; base += (erase_region_size * size_ratio); sect_cnt++; } } info->sector_count = sect_cnt; info->size = total_size * size_ratio; }
/*----------------------------------------------------------------------- * 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; }
#endif /* defined CFG_FLASH_CFI_DRIVER && defined CFG_FLASH_CFI_LEGACY */
And finally, the board-specific code:
ulong board_flash_get_legacy(ulong base, int banknum, flash_info_t *info) { if (banknum == 0) { info->portwidth = 1; info->chipwidth = 1; info->interface = FLASH_CFI_X8; return 1; } else return 0; }
cu Michael