]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - drivers/mtd/cfi_flash.c
cfi_flash: Use uintptr_t for casts from u32 to void *
[karo-tx-uboot.git] / drivers / mtd / cfi_flash.c
index 5788328ef149c5bb50814ad8101261f11c2a2fb3..cf10b0d4ca4af366ab2576aae41c77bf4a258225 100644 (file)
@@ -40,6 +40,7 @@
 #include <asm/byteorder.h>
 #include <environment.h>
 #include <mtd/cfi_flash.h>
+#include <watchdog.h>
 
 /*
  * This file implements a Common Flash Interface (CFI) driver for
@@ -209,9 +210,11 @@ unsigned long flash_sector_size(flash_info_t *info, flash_sect_t sect)
 static inline void *
 flash_map (flash_info_t * info, flash_sect_t sect, uint offset)
 {
-       unsigned int byte_offset = offset * info->portwidth;
+       unsigned int byte_offset = offset * info->portwidth / info->chipwidth;
+       unsigned int addr = (info->start[sect] + byte_offset);
+       unsigned int mask = 0xffffffff << (info->portwidth - 1);
 
-       return (void *)(info->start[sect] + byte_offset);
+       return (void *)(uintptr_t)(addr & mask);
 }
 
 static inline void flash_unmap(flash_info_t *info, flash_sect_t sect,
@@ -397,6 +400,8 @@ void flash_write_cmd (flash_info_t * info, flash_sect_t sect,
 #endif
                flash_write64(cword.ll, addr);
                break;
+       default:
+               printf("fwc: Unknown port width %d\n", info->portwidth);
        }
 
        /* Ensure all the instructions are fully finished */
@@ -573,8 +578,11 @@ static int flash_status_check (flash_info_t * info, flash_sect_t sector,
 #endif
 
        /* Wait for command completion */
+#ifdef CONFIG_SYS_LOW_RES_TIMER
        reset_timer();
+#endif
        start = get_timer (0);
+       WATCHDOG_RESET();
        while (flash_is_busy (info, sector)) {
                if (get_timer (start) > tout) {
                        printf ("Flash %s timeout at address %lx data %lx\n",
@@ -628,6 +636,7 @@ static int flash_full_status_check (flash_info_t * info, flash_sect_t sector,
                                puts ("Vpp Low Error.\n");
                }
                flash_write_cmd (info, sector, 0, info->cmd_reset);
+               udelay(1);
                break;
        default:
                break;
@@ -660,8 +669,11 @@ static int flash_status_poll(flash_info_t *info, void *src, void *dst,
 #endif
 
        /* Wait for command completion */
+#ifdef CONFIG_SYS_LOW_RES_TIMER
        reset_timer();
+#endif
        start = get_timer(0);
+       WATCHDOG_RESET();
        while (1) {
                switch (info->portwidth) {
                case FLASH_CFI_8BIT:
@@ -743,13 +755,9 @@ static void flash_add_byte (flash_info_t * info, cfiword_t * cword, uchar c)
  */
 static flash_sect_t find_sector (flash_info_t * info, ulong addr)
 {
-       static flash_sect_t saved_sector = 0; /* previously found sector */
-       static flash_info_t *saved_info = 0; /* previously used flash bank */
+       static flash_sect_t saved_sector; /* previously found sector */
        flash_sect_t sector = saved_sector;
 
-       if ((info != saved_info) || (sector >= info->sector_count))
-               sector = 0;
-
        while ((info->start[sector] < addr)
                        && (sector < info->sector_count - 1))
                sector++;
@@ -761,7 +769,6 @@ static flash_sect_t find_sector (flash_info_t * info, ulong addr)
                sector--;
 
        saved_sector = sector;
-       saved_info = info;
        return sector;
 }
 
@@ -778,12 +785,15 @@ static int flash_write_cfiword (flash_info_t * info, ulong dest,
        /* Check if Flash is (sufficiently) erased */
        switch (info->portwidth) {
        case FLASH_CFI_8BIT:
+               debug("%s: 8-bit 0x%02x\n", __func__, cword.c);
                flag = ((flash_read8(dstaddr) & cword.c) == cword.c);
                break;
        case FLASH_CFI_16BIT:
+               debug("%s: 16-bit 0x%04x\n", __func__, cword.w);
                flag = ((flash_read16(dstaddr) & cword.w) == cword.w);
                break;
        case FLASH_CFI_32BIT:
+               debug("%s: 32-bit 0x%08lx\n", __func__, cword.l);
                flag = ((flash_read32(dstaddr) & cword.l) == cword.l);
                break;
        case FLASH_CFI_64BIT:
@@ -864,7 +874,7 @@ static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp,
        void *src = cp;
        void *dst = (void *)dest;
        void *dst2 = dst;
-       int flag = 0;
+       int flag = 1;
        uint offset = 0;
        unsigned int shift;
        uchar write_cmd;
@@ -889,7 +899,7 @@ static int flash_write_cfibuffer (flash_info_t * info, ulong dest, uchar * cp,
 
        cnt = len >> shift;
 
-       while ((cnt-- > 0) && (flag == 0)) {
+       while ((cnt-- > 0) && (flag == 1)) {
                switch (info->portwidth) {
                case FLASH_CFI_8BIT:
                        flag = ((flash_read8(dst2) & flash_read8(src)) ==
@@ -1044,6 +1054,8 @@ int flash_erase (flash_info_t * info, int s_first, int s_last)
        flash_sect_t sect;
        int st;
 
+       debug("%s: erasing sectors %d to %d\n", __func__, s_first, s_last);
+
        if (info->flash_id != FLASH_MAN_CFI) {
                puts ("Can't erase unknown flash type - aborted\n");
                return 1;
@@ -1068,7 +1080,38 @@ int flash_erase (flash_info_t * info, int s_first, int s_last)
 
 
        for (sect = s_first; sect <= s_last; sect++) {
+               if (ctrlc()) {
+                       printf("\n");
+                       return 1;
+               }
+
                if (info->protect[sect] == 0) { /* not protected */
+#ifdef CONFIG_SYS_FLASH_CHECK_BLANK_BEFORE_ERASE
+                       int k;
+                       int size;
+                       int erased;
+                       u32 *flash;
+
+                       /*
+                        * Check if whole sector is erased
+                        */
+                       size = flash_sector_size(info, sect);
+                       erased = 1;
+                       flash = (u32 *)info->start[sect];
+                       /* divide by 4 for longword access */
+                       size = size >> 2;
+                       for (k = 0; k < size; k++) {
+                               if (flash_read32(flash++) != 0xffffffff) {
+                                       erased = 0;
+                                       break;
+                               }
+                       }
+                       if (erased) {
+                               if (flash_verbose)
+                                       putc(',');
+                               continue;
+                       }
+#endif
                        switch (info->vendor) {
                        case CFI_CMDSET_INTEL_PROG_REGIONS:
                        case CFI_CMDSET_INTEL_STANDARD:
@@ -1088,7 +1131,7 @@ int flash_erase (flash_info_t * info, int s_first, int s_last)
                                                AMD_CMD_ERASE_START);
                                flash_unlock_seq (info, sect);
                                flash_write_cmd (info, sect, 0,
-                                                AMD_CMD_ERASE_SECTOR);
+                                                info->cmd_erase_sector);
                                break;
 #ifdef CONFIG_FLASH_CFI_LEGACY
                        case CFI_CMDSET_AMD_LEGACY:
@@ -1107,8 +1150,9 @@ int flash_erase (flash_info_t * info, int s_first, int s_last)
                        }
 
                        if (use_flash_status_poll(info)) {
-                               cfiword_t cword = (cfiword_t)0xffffffffffffffffULL;
+                               cfiword_t cword;
                                void *dest;
+                               cword.ll = 0xffffffffffffffffULL;
                                dest = flash_map(info, sect, 0);
                                st = flash_status_poll(info, &cword, dest,
                                                       info->erase_blk_tout, "erase");
@@ -1121,6 +1165,9 @@ int flash_erase (flash_info_t * info, int s_first, int s_last)
                                rcode = 1;
                        else if (flash_verbose)
                                putc ('.');
+               } else {
+                       debug("\nSector %d is protected.\n",
+                                               info->protect[sect]);
                }
        }
 
@@ -1202,9 +1249,12 @@ void flash_print_info (flash_info_t * info)
                info->manufacturer_id);
        printf (info->chipwidth == FLASH_CFI_16BIT ? "%04X" : "%02X",
                info->device_id);
-       if (info->device_id == 0x7E) {
-               printf("%04X", info->device_id2);
+       if ((info->device_id & 0xff) == 0x7E) {
+               printf(info->chipwidth == FLASH_CFI_16BIT ? "%04X" : "%02X",
+               info->device_id2);
        }
+       if ((info->vendor == CFI_CMDSET_AMD_STANDARD) && (info->legacy_unlock))
+               printf("\n  Advanced Sector Protection (PPB) enabled");
        printf ("\n  Erase timeout: %ld ms, write timeout: %ld ms\n",
                info->erase_blk_tout,
                info->write_tout);
@@ -1343,6 +1393,9 @@ int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
                src += i;
                cnt -= i;
                FLASH_SHOW_PROGRESS(scale, dots, digit, i);
+               /* Only check every once in a while */
+               if ((cnt & 0xFFFF) < buffered_size && ctrlc())
+                       return ERR_ABORTED;
        }
 #else
        while (cnt >= info->portwidth) {
@@ -1355,6 +1408,9 @@ int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
                wp += info->portwidth;
                cnt -= info->portwidth;
                FLASH_SHOW_PROGRESS(scale, dots, digit, info->portwidth);
+               /* Only check every once in a while */
+               if ((cnt & 0xFFFF) < info->portwidth && ctrlc())
+                       return ERR_ABORTED;
        }
 #endif /* CONFIG_SYS_FLASH_USE_BUFFER_WRITE */
 
@@ -1377,10 +1433,49 @@ int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt)
        return flash_write_cfiword (info, wp, cword);
 }
 
+static inline int manufact_match(flash_info_t *info, u32 manu)
+{
+       return info->manufacturer_id == ((manu & FLASH_VENDMASK) >> 16);
+}
+
 /*-----------------------------------------------------------------------
  */
 #ifdef CONFIG_SYS_FLASH_PROTECTION
 
+static int cfi_protect_bugfix(flash_info_t *info, long sector, int prot)
+{
+       if (manufact_match(info, INTEL_MANUFACT)
+           && info->device_id == NUMONYX_256MBIT) {
+               /*
+                * see errata called
+                * "Numonyx Axcell P33/P30 Specification Update" :)
+                */
+               flash_write_cmd(info, sector, 0, FLASH_CMD_READ_ID);
+               if (!flash_isequal(info, sector, FLASH_OFFSET_PROTECT,
+                                  prot)) {
+                       /*
+                        * cmd must come before FLASH_CMD_PROTECT + 20us
+                        * Disable interrupts which might cause a timeout here.
+                        */
+                       int flag = disable_interrupts();
+                       unsigned short cmd;
+
+                       if (prot)
+                               cmd = FLASH_CMD_PROTECT_SET;
+                       else
+                               cmd = FLASH_CMD_PROTECT_CLEAR;
+                               flash_write_cmd(info, sector, 0,
+                                         FLASH_CMD_PROTECT);
+                       flash_write_cmd(info, sector, 0, cmd);
+                       /* re-enable interrupts if necessary */
+                       if (flag)
+                               enable_interrupts();
+               }
+               return 1;
+       }
+       return 0;
+}
+
 int flash_real_protect (flash_info_t * info, long sector, int prot)
 {
        int retcode = 0;
@@ -1389,37 +1484,24 @@ int flash_real_protect (flash_info_t * info, long sector, int prot)
                case CFI_CMDSET_INTEL_PROG_REGIONS:
                case CFI_CMDSET_INTEL_STANDARD:
                case CFI_CMDSET_INTEL_EXTENDED:
-                       /*
-                        * see errata called
-                        * "Numonyx Axcell P33/P30 Specification Update" :)
-                        */
-                       flash_write_cmd (info, sector, 0, FLASH_CMD_READ_ID);
-                       if (!flash_isequal (info, sector, FLASH_OFFSET_PROTECT,
-                                           prot)) {
-                               /*
-                                * cmd must come before FLASH_CMD_PROTECT + 20us
-                                * Disable interrupts which might cause a timeout here.
-                                */
-                               int flag = disable_interrupts ();
-                               unsigned short cmd;
-
+                       if (!cfi_protect_bugfix(info, sector, prot)) {
+                               flash_write_cmd(info, sector, 0,
+                                        FLASH_CMD_CLEAR_STATUS);
+                               flash_write_cmd(info, sector, 0,
+                                       FLASH_CMD_PROTECT);
                                if (prot)
-                                       cmd = FLASH_CMD_PROTECT_SET;
+                                       flash_write_cmd(info, sector, 0,
+                                               FLASH_CMD_PROTECT_SET);
                                else
-                                       cmd = FLASH_CMD_PROTECT_CLEAR;
+                                       flash_write_cmd(info, sector, 0,
+                                               FLASH_CMD_PROTECT_CLEAR);
 
-                               flash_write_cmd (info, sector, 0,
-                                                 FLASH_CMD_PROTECT);
-                               flash_write_cmd (info, sector, 0, cmd);
-                               /* re-enable interrupts if necessary */
-                               if (flag)
-                                       enable_interrupts ();
                        }
                        break;
                case CFI_CMDSET_AMD_EXTENDED:
                case CFI_CMDSET_AMD_STANDARD:
                        /* U-Boot only checks the first byte */
-                       if (info->manufacturer_id == (uchar)ATM_MANUFACT) {
+                       if (manufact_match(info, ATM_MANUFACT)) {
                                if (prot) {
                                        flash_unlock_seq (info, 0);
                                        flash_write_cmd (info, 0,
@@ -1437,6 +1519,47 @@ int flash_real_protect (flash_info_t * info, long sector, int prot)
                                                        0, ATM_CMD_UNLOCK_SECT);
                                }
                        }
+                       if (info->legacy_unlock) {
+                               int flag = disable_interrupts();
+                               int lock_flag;
+
+                               flash_unlock_seq(info, 0);
+                               flash_write_cmd(info, 0, info->addr_unlock1,
+                                               AMD_CMD_SET_PPB_ENTRY);
+                               lock_flag = flash_isset(info, sector, 0, 0x01);
+                               if (prot) {
+                                       if (lock_flag) {
+                                               flash_write_cmd(info, sector, 0,
+                                                       AMD_CMD_PPB_LOCK_BC1);
+                                               flash_write_cmd(info, sector, 0,
+                                                       AMD_CMD_PPB_LOCK_BC2);
+                                       }
+                                       debug("sector %ld %slocked\n", sector,
+                                               lock_flag ? "" : "already ");
+                               } else {
+                                       if (!lock_flag) {
+                                               debug("unlock %ld\n", sector);
+                                               flash_write_cmd(info, 0, 0,
+                                                       AMD_CMD_PPB_UNLOCK_BC1);
+                                               flash_write_cmd(info, 0, 0,
+                                                       AMD_CMD_PPB_UNLOCK_BC2);
+                                       }
+                                       debug("sector %ld %sunlocked\n", sector,
+                                               !lock_flag ? "" : "already ");
+                               }
+                               if (flag)
+                                       enable_interrupts();
+
+                               if (flash_status_check(info, sector,
+                                               info->erase_blk_tout,
+                                               prot ? "protect" : "unprotect"))
+                                       printf("status check error\n");
+
+                               flash_write_cmd(info, 0, 0,
+                                               AMD_CMD_SET_PPB_EXIT_BC1);
+                               flash_write_cmd(info, 0, 0,
+                                               AMD_CMD_SET_PPB_EXIT_BC2);
+                       }
                        break;
 #ifdef CONFIG_FLASH_CFI_LEGACY
                case CFI_CMDSET_AMD_LEGACY:
@@ -1490,6 +1613,7 @@ void flash_read_user_serial (flash_info_t * info, void *buffer, int offset,
        flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID);
        memcpy (dst, src + offset, len);
        flash_write_cmd (info, 0, 0, info->cmd_reset);
+       udelay(1);
        flash_unmap(info, 0, FLASH_OFFSET_USER_PROTECTION, src);
 }
 
@@ -1505,6 +1629,7 @@ void flash_read_factory_serial (flash_info_t * info, void *buffer, int offset,
        flash_write_cmd (info, 0, 0, FLASH_CMD_READ_ID);
        memcpy (buffer, src + offset, len);
        flash_write_cmd (info, 0, 0, info->cmd_reset);
+       udelay(1);
        flash_unmap(info, 0, FLASH_OFFSET_INTEL_PROTECTION, src);
 }
 
@@ -1536,6 +1661,7 @@ static void cfi_reverse_geometry(struct cfi_qry *qry)
 static void cmdset_intel_read_jedec_ids(flash_info_t *info)
 {
        flash_write_cmd(info, 0, 0, FLASH_CMD_RESET);
+       udelay(1);
        flash_write_cmd(info, 0, 0, FLASH_CMD_READ_ID);
        udelay(1000); /* some flash are slow to respond */
        info->manufacturer_id = flash_read_uchar (info,
@@ -1599,20 +1725,38 @@ static void cmdset_amd_read_jedec_ids(flash_info_t *info)
        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);
 }
 
 static int cmdset_amd_init(flash_info_t *info, struct cfi_qry *qry)
 {
        info->cmd_reset = AMD_CMD_RESET;
+       info->cmd_erase_sector = AMD_CMD_ERASE_SECTOR;
 
        cmdset_amd_read_jedec_ids(info);
        flash_write_cmd(info, 0, info->cfi_offset, FLASH_CMD_CFI);
 
+#ifdef CONFIG_SYS_FLASH_PROTECTION
+       if (info->ext_addr) {
+               /* read sector protect/unprotect scheme (at 0x49) */
+               if (flash_read_uchar(info, info->ext_addr + 9) == 0x8)
+                       info->legacy_unlock = 1;
+       }
+#endif
+
        return 0;
 }
 
@@ -1719,10 +1863,10 @@ static void flash_read_cfi (flash_info_t *info, void *buf,
        unsigned int i;
 
        for (i = 0; i < len; i++)
-               p[i] = flash_read_uchar(info, start + i);
+               p[i] = flash_read_uchar(info, start + (i * 2));
 }
 
-void __flash_cmd_reset(flash_info_t *info)
+static void __flash_cmd_reset(flash_info_t *info)
 {
        /*
         * We do not yet know what kind of commandset to use, so we issue
@@ -1730,6 +1874,7 @@ void __flash_cmd_reset(flash_info_t *info)
         * that AMD flash roms ignore the Intel command.
         */
        flash_write_cmd(info, 0, 0, AMD_CMD_RESET);
+       udelay(1);
        flash_write_cmd(info, 0, 0, FLASH_CMD_RESET);
 }
 void flash_cmd_reset(flash_info_t *info)
@@ -1739,21 +1884,40 @@ static int __flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
 {
        int cfi_offset;
 
-       /* Issue FLASH reset command */
-       flash_cmd_reset(info);
-
        for (cfi_offset=0;
             cfi_offset < sizeof(flash_offset_cfi) / sizeof(uint);
             cfi_offset++) {
+               /* Issue FLASH reset command */
+               flash_cmd_reset(info);
                flash_write_cmd (info, 0, flash_offset_cfi[cfi_offset],
                                 FLASH_CMD_CFI);
-               if (flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP, 'Q')
-                   && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 1, 'R')
-                   && flash_isequal (info, 0, FLASH_OFFSET_CFI_RESP + 2, 'Y')) {
+               if (flash_isequal(info, 0, FLASH_OFFSET_CFI_RESP, 'Q') &&
+                       flash_isequal(info, 0,
+                                       FLASH_OFFSET_CFI_RESP + 2, 'R') &&
+                       flash_isequal(info, 0,
+                                       FLASH_OFFSET_CFI_RESP + 4, 'Y')) {
                        flash_read_cfi(info, qry, FLASH_OFFSET_CFI_RESP,
                                        sizeof(struct cfi_qry));
+#ifdef CONFIG_SYS_FLASH_INTERFACE_WIDTH
+                       info->interface = CONFIG_SYS_FLASH_INTERFACE_WIDTH;
+#else
                        info->interface = le16_to_cpu(qry->interface_desc);
-
+                       /* Some flash chips can support multiple bus widths.
+                        * In this case, override the interface width and
+                        * limit it to the port width.
+                        */
+                       if ((info->interface == FLASH_CFI_X8X16) &&
+                               (info->portwidth == FLASH_CFI_8BIT)) {
+                                       debug("Overriding 16-bit interface"
+                                               " width to 8-bit port width.\n");
+                               info->interface = FLASH_CFI_X8;
+                       } else if ((info->interface == FLASH_CFI_X16X32) &&
+                               (info->portwidth == FLASH_CFI_16BIT)) {
+                                       debug("Overriding 16-bit interface"
+                                               " width to 16-bit port width.\n");
+                               info->interface = FLASH_CFI_X16;
+                       }
+#endif
                        info->cfi_offset = flash_offset_cfi[cfi_offset];
                        debug ("device interface is %d\n",
                               info->interface);
@@ -1764,8 +1928,8 @@ static int __flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
                               info->chipwidth << CFI_FLASH_SHIFT_WIDTH);
 
                        /* calculate command offsets as in the Linux driver */
-                       info->addr_unlock1 = 0x555;
-                       info->addr_unlock2 = 0x2aa;
+                       info->addr_unlock1 = 0xaaa;
+                       info->addr_unlock2 = 0x555;
 
                        /*
                         * modify the unlock address if we are
@@ -1799,8 +1963,12 @@ static int flash_detect_cfi (flash_info_t * info, struct cfi_qry *qry)
                for (info->chipwidth = FLASH_CFI_BY8;
                     info->chipwidth <= info->portwidth;
                     info->chipwidth <<= 1)
-                       if (__flash_detect_cfi(info, qry))
+                       if (__flash_detect_cfi(info, qry)) {
+                               debug("Found CFI flash, portwidth %d,"
+                                       " chipwidth %d\n",
+                                       info->portwidth, info->chipwidth);
                                return 1;
+                       }
        }
        debug ("not found\n");
        return 0;
@@ -1819,7 +1987,7 @@ static void flash_fixup_amd(flash_info_t *info, struct cfi_qry *qry)
                        /* CFI < 1.1, try to guess from device id */
                        if ((info->device_id & 0x80) != 0)
                                cfi_reverse_geometry(qry);
-               } else if (flash_read_uchar(info, info->ext_addr + 0xf) == 3) {
+               } else if (flash_read_uchar(info, info->ext_addr + 0x1e) == 3) {
                        /* CFI >= 1.1, deduct from top/bottom flag */
                        /* note: ext_addr is valid since cfi_version > 0 */
                        cfi_reverse_geometry(qry);
@@ -1852,15 +2020,59 @@ static void flash_fixup_stm(flash_info_t *info, struct cfi_qry *qry)
        if (qry->num_erase_regions > 1) {
                /* reverse geometry if top boot part */
                if (info->cfi_version < 0x3131) {
-                       /* CFI < 1.1, guess by device id (M29W320{DT,ET} only) */
-                       if (info->device_id == 0x22CA ||
-                           info->device_id == 0x2256) {
+                       /* CFI < 1.1, guess by device id */
+                       if (info->device_id == 0x22CA || /* M29W320DT */
+                           info->device_id == 0x2256 || /* M29W320ET */
+                           info->device_id == 0x22D7) { /* M29W800DT */
                                cfi_reverse_geometry(qry);
                        }
+               } else if (flash_read_uchar(info, info->ext_addr + 0xf) == 3) {
+                       /* CFI >= 1.1, deduct from top/bottom flag */
+                       /* note: ext_addr is valid since cfi_version > 0 */
+                       cfi_reverse_geometry(qry);
                }
        }
 }
 
+static void flash_fixup_sst(flash_info_t *info, struct cfi_qry *qry)
+{
+       /*
+        * SST, for many recent nor parallel flashes, says they are
+        * CFI-conformant. This is not true, since qry struct.
+        * reports a std. AMD command set (0x0002), while SST allows to
+        * erase two different sector sizes for the same memory.
+        * 64KB sector (SST call it block)  needs 0x30 to be erased.
+        * 4KB  sector (SST call it sector) needs 0x50 to be erased.
+        * Since CFI query detect the 4KB number of sectors, users expects
+        * a sector granularity of 4KB, and it is here set.
+        */
+       if (info->device_id == 0x5D23 || /* SST39VF3201B */
+           info->device_id == 0x5C23) { /* SST39VF3202B */
+               /* set sector granularity to 4KB */
+               info->cmd_erase_sector=0x50;
+       }
+}
+
+static void flash_fixup_num(flash_info_t *info, struct cfi_qry *qry)
+{
+       /*
+        * The M29EW devices seem to report the CFI information wrong
+        * when it's in 8 bit mode.
+        * There's an app note from Numonyx on this issue.
+        * So adjust the buffer size for M29EW while operating in 8-bit mode
+        */
+       if (((qry->max_buf_write_size) > 0x8) &&
+                       (info->device_id == 0x7E) &&
+                       (info->device_id2 == 0x2201 ||
+                       info->device_id2 == 0x2301 ||
+                       info->device_id2 == 0x2801 ||
+                       info->device_id2 == 0x4801)) {
+               debug("Adjusted buffer size on Numonyx flash"
+                       " M29EW family in 8 bit mode\n");
+               qry->max_buf_write_size = 0x8;
+       }
+}
+
 /*
  * The following code cannot be run from FLASH!
  *
@@ -1891,14 +2103,15 @@ ulong flash_get_size (phys_addr_t base, int banknum)
 
        if (flash_detect_cfi (info, &qry)) {
                info->vendor = le16_to_cpu(qry.p_id);
-               info->ext_addr = le16_to_cpu(qry.p_adr);
+               info->ext_addr = le16_to_cpu(qry.p_adr) * 2;
+               debug("extended address is 0x%x\n", info->ext_addr);
                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->ext_addr + 6) << 8;
                        info->cfi_version |= (ushort) flash_read_uchar (info,
-                                               info->ext_addr + 4);
+                                               info->ext_addr + 8);
                }
 
 #ifdef DEBUG
@@ -1939,6 +2152,12 @@ ulong flash_get_size (phys_addr_t base, int banknum)
                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);
@@ -1946,6 +2165,8 @@ ulong flash_get_size (phys_addr_t base, int banknum)
                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);
+               debug("port width: %d, chipwidth: %d, interface: %d\n",
+                       info->portwidth, info->chipwidth, info->interface);
 
                size_ratio = info->portwidth / info->chipwidth;
                /* if the chip is x8/x16 reduce the ratio by half */
@@ -2016,6 +2237,27 @@ ulong flash_get_size (phys_addr_t base, int banknum)
                                                             FLASH_OFFSET_PROTECT,
                                                             FLASH_STATUS_PROTECT);
                                        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;
@@ -2089,6 +2331,14 @@ static void cfi_flash_set_config_reg(u32 base, u16 val)
 
 void flash_protect_default(void)
 {
+#if defined(CONFIG_SYS_FLASH_AUTOPROTECT_LIST)
+       int i;
+       struct apl_s {
+               ulong start;
+               ulong size;
+       } apl[] = CONFIG_SYS_FLASH_AUTOPROTECT_LIST;
+#endif
+
        /* Monitor protection ON by default */
 #if (CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE) && \
        (!defined(CONFIG_MONITOR_IS_IN_RAM))
@@ -2116,7 +2366,7 @@ void flash_protect_default(void)
 
 #if defined(CONFIG_SYS_FLASH_AUTOPROTECT_LIST)
        for (i = 0; i < (sizeof(apl) / sizeof(struct apl_s)); i++) {
-               debug("autoprotecting from %08x to %08x\n",
+               debug("autoprotecting from %08lx to %08lx\n",
                      apl[i].start, apl[i].start + apl[i].size - 1);
                flash_protect(FLAG_PROTECT_SET,
                               apl[i].start,
@@ -2130,12 +2380,6 @@ unsigned long flash_init (void)
 {
        unsigned long size = 0;
        int i;
-#if defined(CONFIG_SYS_FLASH_AUTOPROTECT_LIST)
-       struct apl_s {
-               ulong start;
-               ulong size;
-       } apl[] = CONFIG_SYS_FLASH_AUTOPROTECT_LIST;
-#endif
 
 #ifdef CONFIG_SYS_FLASH_PROTECTION
        /* read environment from EEPROM */