]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - drivers/mtd/cfi_flash.c
sf: Use spi_flash_alloc() in each SPI flash driver
[karo-tx-uboot.git] / drivers / mtd / cfi_flash.c
index aa76f6ce4812092350e924e2a7d1bbbac0f53e72..60dbb7864f2b40476e7dab9a03930aa89dc50158 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
@@ -573,14 +574,18 @@ 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",
                                prompt, info->start[sector],
                                flash_read_long (info, sector, 0));
                        flash_write_cmd (info, sector, 0, info->cmd_reset);
+                       udelay(1);
                        return ERR_TIMOUT;
                }
                udelay (1);             /* also triggers watchdog */
@@ -628,6 +633,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 +666,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,8 +752,8 @@ 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 */
+       static flash_info_t *saved_info; /* previously used flash bank */
        flash_sect_t sector = saved_sector;
 
        if ((info != saved_info) || (sector >= info->sector_count))
@@ -864,7 +873,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 +898,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)) ==
@@ -1068,7 +1077,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 +1128,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 +1147,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");
@@ -1206,6 +1247,8 @@ void flash_print_info (flash_info_t * info)
                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);
@@ -1344,6 +1387,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) {
@@ -1356,6 +1402,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 */
 
@@ -1378,10 +1427,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;
@@ -1390,37 +1478,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,
@@ -1438,6 +1513,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:
@@ -1491,6 +1607,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);
 }
 
@@ -1506,6 +1623,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);
 }
 
@@ -1537,6 +1655,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,
@@ -1613,15 +1732,25 @@ static void cmdset_amd_read_jedec_ids(flash_info_t *info)
                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;
 }
 
@@ -1731,7 +1860,7 @@ static void flash_read_cfi (flash_info_t *info, void *buf,
                p[i] = flash_read_uchar(info, start + i);
 }
 
-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
@@ -1739,6 +1868,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)
@@ -1867,10 +1997,33 @@ static void flash_fixup_stm(flash_info_t *info, struct cfi_qry *qry)
                            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;
+       }
+}
+
 /*
  * The following code cannot be run from FLASH!
  *
@@ -1949,6 +2102,9 @@ 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;
                }
 
                debug ("manufacturer is %d\n", info->vendor);
@@ -2026,6 +2182,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;
@@ -2134,7 +2311,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,