]> git.karo-electronics.de Git - karo-tx-uboot.git/blobdiff - board/sc520_cdp/flash.c
rename CFG_ENV macros to CONFIG_ENV
[karo-tx-uboot.git] / board / sc520_cdp / flash.c
index 973cde8b99a698514b3a49af2cf4dbb021e56a0e..493d51aca23117a92a15636240acda8daf710a38 100644 (file)
@@ -1,4 +1,7 @@
 /*
+ * (C) Copyright 2002, 2003
+ * Daniel Engström, Omicron Ceti AB, daniel@omicron.se
+ *
  * (C) Copyright 2002
  * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
  * Alex Zuepke <azu@sysgo.de>
  */
 
 #include <common.h>
+#include <asm/io.h>
+#include <pci.h>
+#include <asm/ic/sc520.h>
 
-ulong myflush(void);
-
-
-#define FLASH_BANK_SIZE 0x400000       /* 4 MB */
-#define MAIN_SECT_SIZE  0x20000                /* 128 KB */
-
-flash_info_t    flash_info[CFG_MAX_FLASH_BANKS];
+#define PROBE_BUFFER_SIZE 1024
+static unsigned char buffer[PROBE_BUFFER_SIZE];
 
+#define SC520_MAX_FLASH_BANKS  3
+#define SC520_FLASH_BANK0_BASE 0x38000000  /* BOOTCS */
+#define SC520_FLASH_BANK1_BASE 0x30000000  /* ROMCS0 */
+#define SC520_FLASH_BANK2_BASE 0x28000000  /* ROMCS1 */
+#define SC520_FLASH_BANKSIZE   0x8000000
 
-#define CMD_READ_ARRAY         0x00F000F0
-#define CMD_UNLOCK1            0x00AA00AA
-#define CMD_UNLOCK2            0x00550055
-#define CMD_ERASE_SETUP                0x00800080
-#define CMD_ERASE_CONFIRM      0x00300030
-#define CMD_PROGRAM            0x00A000A0
-#define CMD_UNLOCK_BYPASS      0x00200020
+#define AMD29LV016B_SIZE        0x200000
+#define AMD29LV016B_SECTORS     32
 
-#define MEM_FLASH_ADDR1                (*(volatile u32 *)(CFG_FLASH_BASE + (0x00000555 << 2)))
-#define MEM_FLASH_ADDR2                (*(volatile u32 *)(CFG_FLASH_BASE + (0x000002AA << 2)))
-
-#define BIT_ERASE_DONE         0x00800080
-#define BIT_RDY_MASK           0x00800080
-#define BIT_PROGRAM_ERROR      0x00200020
-#define BIT_TIMEOUT            0x80000000 /* our flag */
+flash_info_t    flash_info[SC520_MAX_FLASH_BANKS];
 
 #define READY 1
 #define ERR   2
@@ -56,394 +51,587 @@ flash_info_t    flash_info[CFG_MAX_FLASH_BANKS];
 /*-----------------------------------------------------------------------
  */
 
+
+static u32 _probe_flash(u32 addr, u32 bw, int il)
+{
+       u32 result=0;
+
+       /* First do an unlock cycle for the benefit of
+        * devices that need it */
+
+       switch (bw) {
+
+       case 1:
+               *(volatile u8*)(addr+0x5555) = 0xaa;
+               *(volatile u8*)(addr+0x2aaa) = 0x55;
+               *(volatile u8*)(addr+0x5555) = 0x90;
+
+               /* Read vendor */
+               result = *(volatile u8*)addr;
+               result <<= 16;
+
+               /* Read device */
+               result |= *(volatile u8*)(addr+2);
+
+               /* Return device to data mode */
+               *(volatile u8*)addr = 0xff;
+               *(volatile u8*)(addr+0x5555), 0xf0;
+               break;
+
+       case 2:
+               *(volatile u16*)(addr+0xaaaa) = 0xaaaa;
+               *(volatile u16*)(addr+0x5554) = 0x5555;
+
+               /* Issue identification command */
+               if (il == 2) {
+                       *(volatile u16*)(addr+0xaaaa) = 0x9090;
+
+                       /* Read vendor */
+                       result = *(volatile u8*)addr;
+                       result <<= 16;
+
+                       /* Read device */
+                       result |= *(volatile u8*)(addr+2);
+
+                       /* Return device to data mode */
+                       *(volatile u16*)addr =  0xffff;
+                       *(volatile u16*)(addr+0xaaaa), 0xf0f0;
+
+               } else {
+                       *(volatile u8*)(addr+0xaaaa) = 0x90;
+                       /* Read vendor */
+                       result = *(volatile u16*)addr;
+                       result <<= 16;
+
+                       /* Read device */
+                       result |= *(volatile u16*)(addr+2);
+
+                       /* Return device to data mode */
+                       *(volatile u8*)addr = 0xff;
+                       *(volatile u8*)(addr+0xaaaa), 0xf0;
+               }
+
+               break;
+
+        case 4:
+               *(volatile u32*)(addr+0x5554) = 0xaaaaaaaa;
+               *(volatile u32*)(addr+0xaaa8) = 0x55555555;
+
+               switch (il) {
+               case 1:
+                       /* Issue identification command */
+                       *(volatile u8*)(addr+0x5554) = 0x90;
+
+                       /* Read vendor */
+                       result = *(volatile u16*)addr;
+                       result <<= 16;
+
+                       /* Read device */
+                       result |= *(volatile u16*)(addr+4);
+
+                       /* Return device to data mode */
+                       *(volatile u8*)addr =  0xff;
+                       *(volatile u8*)(addr+0x5554), 0xf0;
+                       break;
+
+               case 2:
+                       /* Issue identification command */
+                       *(volatile u32*)(addr + 0x5554) = 0x00900090;
+
+                       /* Read vendor */
+                       result = *(volatile u16*)addr;
+                       result <<= 16;
+
+                       /* Read device */
+                       result |= *(volatile u16*)(addr+4);
+
+                       /* Return device to data mode */
+                       *(volatile u32*)addr =  0x00ff00ff;
+                       *(volatile u32*)(addr+0x5554), 0x00f000f0;
+                       break;
+
+               case 4:
+                       /* Issue identification command */
+                       *(volatile u32*)(addr+0x5554) = 0x90909090;
+
+                       /* Read vendor */
+                       result = *(volatile u8*)addr;
+                       result <<= 16;
+
+                       /* Read device */
+                       result |= *(volatile u8*)(addr+4);
+
+                       /* Return device to data mode */
+                       *(volatile u32*)addr =  0xffffffff;
+                       *(volatile u32*)(addr+0x5554), 0xf0f0f0f0;
+                       break;
+               }
+               break;
+       }
+
+
+       return result;
+}
+
+extern int _probe_flash_end;
+asm ("_probe_flash_end:\n"
+     ".long 0\n");
+
+static int identify_flash(unsigned address, int width)
+{
+       int is;
+       int device;
+       int vendor;
+       int size;
+       unsigned res;
+
+       u32 (*_probe_flash_ptr)(u32 a, u32 bw, int il);
+
+       size = (unsigned)&_probe_flash_end - (unsigned)_probe_flash;
+
+       if (size > PROBE_BUFFER_SIZE) {
+               printf("_probe_flash() routine too large (%d) %p - %p\n",
+                      size, &_probe_flash_end, _probe_flash);
+               return 0;
+       }
+
+       memcpy(buffer, _probe_flash, size);
+       _probe_flash_ptr = (void*)buffer;
+
+       is = disable_interrupts();
+       res = _probe_flash_ptr(address, width, 1);
+       if (is) {
+               enable_interrupts();
+       }
+
+
+       vendor = res >> 16;
+       device = res & 0xffff;
+
+
+       return res;
+}
+
 ulong flash_init(void)
 {
-    int i, j;
-    ulong size = 0;
-
-    for (i = 0; i < CFG_MAX_FLASH_BANKS; i++)
-    {
-       ulong flashbase = 0;
-       flash_info[i].flash_id =
-         (AMD_MANUFACT & FLASH_VENDMASK) |
-         (AMD_ID_LV160B & FLASH_TYPEMASK);
-       flash_info[i].size = FLASH_BANK_SIZE;
-       flash_info[i].sector_count = CFG_MAX_FLASH_SECT;
-       memset(flash_info[i].protect, 0, CFG_MAX_FLASH_SECT);
-       if (i == 0)
-         flashbase = PHYS_FLASH_1;
-       else
-         panic("configured to many flash banks!\n");
-       for (j = 0; j < flash_info[i].sector_count; j++)
-       {
-
-           if (j <= 3)
-           {
-               /* 1st one is 32 KB */
-               if (j == 0)
-               {
-                       flash_info[i].start[j] = flashbase + 0;
+       int i, j;
+       ulong size = 0;
+
+       for (i = 0; i < SC520_MAX_FLASH_BANKS; i++) {
+               unsigned id;
+               ulong flashbase = 0;
+               int sectsize = 0;
+
+               memset(flash_info[i].protect, 0, CFG_MAX_FLASH_SECT);
+               switch (i) {
+               case 0:
+                       flashbase = SC520_FLASH_BANK0_BASE;
+                       break;
+               case 1:
+                       flashbase = SC520_FLASH_BANK1_BASE;
+                       break;
+               case 2:
+                       flashbase = SC520_FLASH_BANK2_BASE;
+                       break;
+               default:
+                       panic("configured too many flash banks!\n");
                }
 
-               /* 2nd and 3rd are both 16 KB */
-               if ((j == 1) || (j == 2))
-               {
-                       flash_info[i].start[j] = flashbase + 0x8000 + (j-1)*0x4000;
+               id = identify_flash(flashbase, 4);
+               switch (id & 0x00ff00ff) {
+               case 0x000100c8:
+                       /* 29LV016B/29LV017B */
+                       flash_info[i].flash_id =
+                               (AMD_MANUFACT & FLASH_VENDMASK) |
+                               (AMD_ID_LV016B & FLASH_TYPEMASK);
+
+                       flash_info[i].size = AMD29LV016B_SIZE*4;
+                       flash_info[i].sector_count = AMD29LV016B_SECTORS;
+                       sectsize = (AMD29LV016B_SIZE*4)/AMD29LV016B_SECTORS;
+                       printf("Bank %d: 4 x AMD 29LV017B\n", i);
+                       break;
+
+
+               default:
+                       printf("Bank %d have unknown flash %08x\n", i, id);
+                       flash_info[i].flash_id = FLASH_UNKNOWN;
+                       continue;
                }
 
-               /* 4th 64 KB */
-               if (j == 3)
-               {
-                       flash_info[i].start[j] = flashbase + 0x10000;
+               for (j = 0; j < flash_info[i].sector_count; j++) {
+                       flash_info[i].start[j] = flashbase + j * sectsize;
                }
-           }
-           else
-           {
-               flash_info[i].start[j] = flashbase + (j - 3)*MAIN_SECT_SIZE;
-           }
+               size += flash_info[i].size;
+
+               flash_protect(FLAG_PROTECT_CLEAR,
+                             flash_info[i].start[0],
+                              flash_info[i].start[0] + flash_info[i].size - 1,
+                             &flash_info[i]);
        }
-       size += flash_info[i].size;
-    }
-
-    /*
-     * Protect monitor and environment sectors
-     */
-    flash_protect(FLAG_PROTECT_SET,
-                 i386boot_start-CFG_FLASH_BASE,
-                 i386boot_end-CFG_FLASH_BASE,
-                 &flash_info[0]);
-
-    flash_protect(FLAG_PROTECT_SET,
-                 CFG_ENV_ADDR,
-                 CFG_ENV_ADDR + CFG_ENV_SIZE - 1,
-                 &flash_info[0]);
-    return size;
+
+       /*
+        * Protect monitor and environment sectors
+        */
+       flash_protect(FLAG_PROTECT_SET,
+                     i386boot_start,
+                     i386boot_end,
+                     &flash_info[0]);
+#ifdef CONFIG_ENV_ADDR
+       flash_protect(FLAG_PROTECT_SET,
+                     CONFIG_ENV_ADDR,
+                     CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1,
+                     &flash_info[0]);
+#endif
+       return size;
 }
 
 /*-----------------------------------------------------------------------
  */
-void flash_print_info  (flash_info_t *info)
+void flash_print_info(flash_info_t *info)
 {
-    int i;
-
-    switch (info->flash_id & FLASH_VENDMASK)
-    {
-    case (AMD_MANUFACT & FLASH_VENDMASK):
-       printf("AMD: ");
-       break;
-    default:
-       printf("Unknown Vendor ");
-       break;
-    }
-
-    switch (info->flash_id & FLASH_TYPEMASK)
-    {
-    case (AMD_ID_LV160B & FLASH_TYPEMASK):
-       printf("2x Amd29F160BB (16Mbit)\n");
-       break;
-    default:
-       printf("Unknown Chip Type\n");
-       goto Done;
-       break;
-    }
-
-    printf("  Size: %ld MB in %d Sectors\n",
-          info->size >> 20, info->sector_count);
-
-    printf("  Sector Start Addresses:");
-    for (i = 0; i < info->sector_count; i++)
-    {
-       if ((i % 5) == 0)
-       {
-           printf ("\n   ");
+       int i;
+
+       switch (info->flash_id & FLASH_VENDMASK) {
+
+       case (AMD_MANUFACT & FLASH_VENDMASK):
+               printf("AMD:   ");
+               switch (info->flash_id & FLASH_TYPEMASK) {
+               case (AMD_ID_LV016B & FLASH_TYPEMASK):
+                       printf("4x AMD29LV017B (4x16Mbit)\n");
+                       break;
+               default:
+                       printf("Unknown Chip Type\n");
+                       goto done;
+                       break;
+               }
+
+               break;
+       default:
+               printf("Unknown Vendor ");
+               break;
        }
-       printf (" %08lX%s", info->start[i],
-               info->protect[i] ? " (RO)" : "     ");
-    }
-    printf ("\n");
 
-Done:
+
+       printf("  Size: %ld MB in %d Sectors\n",
+              info->size >> 20, info->sector_count);
+
+       printf("  Sector Start Addresses:");
+       for (i = 0; i < info->sector_count; i++) {
+               if ((i % 5) == 0) {
+                       printf ("\n   ");
+               }
+               printf (" %08lX%s", info->start[i],
+                       info->protect[i] ? " (RO)" : "     ");
+       }
+       printf ("\n");
+
+done:  ;
 }
 
 /*-----------------------------------------------------------------------
  */
 
-int    flash_erase (flash_info_t *info, int s_first, int s_last)
+/* this needs to be inlined, the SWTMRMMILLI register is reset by each read */
+#define __udelay(delay) \
+{      \
+       unsigned micro; \
+       unsigned milli=0; \
+       \
+       micro = *(volatile u16*)(0xfffef000+SC520_SWTMRMILLI); \
+        \
+       for (;;) { \
+               \
+               milli += *(volatile u16*)(0xfffef000+SC520_SWTMRMILLI); \
+               micro = *(volatile u16*)(0xfffef000+SC520_SWTMRMICRO); \
+               \
+               if ((delay) <= (micro + (milli * 1000))) { \
+                       break; \
+               } \
+       } \
+} while (0)
+
+static u32 _amd_erase_flash(u32 addr, u32 sector)
 {
-    ulong result;
-    int iflag, prot, sect;
-    int rc = ERR_OK;
-    int chip1, chip2;
+       unsigned elapsed;
+
+       /* Issue erase */
+       *(volatile u32*)(addr + 0x5554) = 0xAAAAAAAA;
+       *(volatile u32*)(addr + 0xaaa8) = 0x55555555;
+       *(volatile u32*)(addr + 0x5554) = 0x80808080;
+       /* And one unlock */
+       *(volatile u32*)(addr + 0x5554) = 0xAAAAAAAA;
+       *(volatile u32*)(addr + 0xaaa8) = 0x55555555;
+       /* Sector erase command comes last */
+       *(volatile u32*)(addr + sector) = 0x30303030;
+
+       elapsed = *(volatile u16*)(0xfffef000+SC520_SWTMRMILLI); /* dummy read */
+       elapsed = 0;
+       __udelay(50);
+       while (((*(volatile u32*)(addr + sector)) & 0x80808080) != 0x80808080) {
+
+               elapsed += *(volatile u16*)(0xfffef000+SC520_SWTMRMILLI);
+               if (elapsed > ((CFG_FLASH_ERASE_TOUT/CFG_HZ) * 1000)) {
+                       *(volatile u32*)(addr) = 0xf0f0f0f0;
+                       return 1;
+               }
+       }
 
-    /* first look for protection bits */
+       *(volatile u32*)(addr) = 0xf0f0f0f0;
 
-    if (info->flash_id == FLASH_UNKNOWN)
-       return ERR_UNKNOWN_FLASH_TYPE;
+       return 0;
+}
 
-    if ((s_first < 0) || (s_first > s_last)) {
-       return ERR_INVAL;
-    }
+extern int _amd_erase_flash_end;
+asm ("_amd_erase_flash_end:\n"
+     ".long 0\n");
+
+int flash_erase(flash_info_t *info, int s_first, int s_last)
+{
+       u32 (*_erase_flash_ptr)(u32 a, u32 so);
+       int prot;
+       int sect;
+       unsigned size;
+
+       if ((s_first < 0) || (s_first > s_last)) {
+               if (info->flash_id == FLASH_UNKNOWN) {
+                       printf("- missing\n");
+               } else {
+                       printf("- no sectors to erase\n");
+               }
+               return 1;
+       }
+
+       if ((info->flash_id & FLASH_VENDMASK) == (AMD_MANUFACT & FLASH_VENDMASK)) {
+               size = (unsigned)&_amd_erase_flash_end - (unsigned)_amd_erase_flash;
+
+               if (size > PROBE_BUFFER_SIZE) {
+                       printf("_amd_erase_flash() routine too large (%d) %p - %p\n",
+                              size, &_amd_erase_flash_end, _amd_erase_flash);
+                       return 0;
+               }
 
-    if ((info->flash_id & FLASH_VENDMASK) !=
-       (AMD_MANUFACT & FLASH_VENDMASK)) {
-       return ERR_UNKNOWN_FLASH_VENDOR;
-    }
+               memcpy(buffer, _amd_erase_flash, size);
+               _erase_flash_ptr = (void*)buffer;
 
-    prot = 0;
-    for (sect=s_first; sect<=s_last; ++sect) {
-       if (info->protect[sect]) {
-           prot++;
+       }  else {
+               printf ("Can't erase unknown flash type - aborted\n");
+               return 1;
        }
-    }
-    if (prot)
-       return ERR_PROTECTED;
-
-    /*
-     * Disable interrupts which might cause a timeout
-     * here. Remember that our exception vectors are
-     * at address 0 in the flash, and we don't want a
-     * (ticker) exception to happen while the flash
-     * chip is in programming mode.
-     */
-    iflag = disable_interrupts();
-
-    /* Start erase on unprotected sectors */
-    for (sect = s_first; sect<=s_last && !ctrlc(); sect++)
-    {
-       printf("Erasing sector %2d ... ", sect);
-
-       /* arm simple, non interrupt dependent timer */
-       reset_timer();
-
-       if (info->protect[sect] == 0)
-       {       /* not protected */
-           vu_long *addr = (vu_long *)(info->start[sect]);
-
-           MEM_FLASH_ADDR1 = CMD_UNLOCK1;
-           MEM_FLASH_ADDR2 = CMD_UNLOCK2;
-           MEM_FLASH_ADDR1 = CMD_ERASE_SETUP;
-
-           MEM_FLASH_ADDR1 = CMD_UNLOCK1;
-           MEM_FLASH_ADDR2 = CMD_UNLOCK2;
-           *addr = CMD_ERASE_CONFIRM;
-
-           /* wait until flash is ready */
-           chip1 = chip2 = 0;
-
-           do
-           {
-               result = *addr;
-
-               /* check timeout */
-               if (get_timer(0) > CFG_FLASH_ERASE_TOUT)
-               {
-                   MEM_FLASH_ADDR1 = CMD_READ_ARRAY;
-                   chip1 = TMO;
-                   break;
+
+       prot = 0;
+       for (sect=s_first; sect<=s_last; ++sect) {
+               if (info->protect[sect]) {
+                       prot++;
                }
+       }
+
+       if (prot) {
+               printf ("- Warning: %d protected sectors will not be erased!\n", prot);
+       } else {
+               printf ("\n");
+       }
 
-               if (!chip1 && (result & 0xFFFF) & BIT_ERASE_DONE)
-                       chip1 = READY;
 
-               if (!chip1 && (result & 0xFFFF) & BIT_PROGRAM_ERROR)
-                       chip1 = ERR;
+       /* Start erase on unprotected sectors */
+       for (sect = s_first; sect<=s_last; sect++) {
 
-               if (!chip2 && (result >> 16) & BIT_ERASE_DONE)
-                       chip2 = READY;
+               if (info->protect[sect] == 0) { /* not protected */
+                       int res;
+                       int flag;
 
-               if (!chip2 && (result >> 16) & BIT_PROGRAM_ERROR)
-                       chip2 = ERR;
+                       /* Disable interrupts which might cause a timeout here */
+                       flag = disable_interrupts();
 
-           }  while (!chip1 || !chip2);
+                       res = _erase_flash_ptr(info->start[0], info->start[sect]-info->start[0]);
 
-           MEM_FLASH_ADDR1 = CMD_READ_ARRAY;
+                       /* re-enable interrupts if necessary */
+                       if (flag) {
+                               enable_interrupts();
+                       }
 
-           if (chip1 == ERR || chip2 == ERR)
-           {
-               rc = ERR_PROG_ERROR;
-               goto outahere;
-           }
-           if (chip1 == TMO)
-           {
-               rc = ERR_TIMOUT;
-               goto outahere;
-           }
 
-           printf("ok.\n");
+                       if (res) {
+                               printf("Erase timed out, sector %d\n", sect);
+                               return res;
+                       }
+
+                       putc('.');
+               }
        }
-       else /* it was protected */
-       {
-           printf("protected!\n");
+
+
+       return 0;
+}
+
+/*-----------------------------------------------------------------------
+ * Write a word to Flash, returns:
+ * 0 - OK
+ * 1 - write timeout
+ * 2 - Flash not erased
+ */
+static int _amd_write_word(unsigned start, unsigned dest, unsigned data)
+{
+       volatile u32 *addr2 = (u32*)start;
+       volatile u32 *dest2 = (u32*)dest;
+       volatile u32 *data2 = (u32*)&data;
+       unsigned elapsed;
+
+       /* Check if Flash is (sufficiently) erased */
+       if ((*((volatile u32*)dest) & (u32)data) != (u32)data) {
+               return 2;
        }
-    }
 
-    if (ctrlc())
-      printf("User Interrupt!\n");
+       addr2[0x5554] = 0xAAAAAAAA;
+       addr2[0xaaa8] = 0x55555555;
+       addr2[0x5554] = 0xA0A0A0A0;
+
+       dest2[0] = data;
+
+       elapsed = *(volatile u16*)(0xfffef000+SC520_SWTMRMILLI); /* dummy read */
+       elapsed = 0;
 
-outahere:
-    /* allow flash to settle - wait 10 ms */
-    udelay(10000);
+       /* data polling for D7 */
+       while ((dest2[0] & 0x80808080) != (data2[0] & 0x80808080)) {
+               elapsed += *(volatile u16*)(0xfffef000+SC520_SWTMRMILLI);
+               if (elapsed > ((CFG_FLASH_WRITE_TOUT/CFG_HZ) * 1000)) {
+                       addr2[0] = 0xf0f0f0f0;
+                       return 1;
+               }
+       }
 
-    if (iflag)
-      enable_interrupts();
 
+       addr2[0] = 0xf0f0f0f0;
 
-    return rc;
+       return 0;
 }
 
+extern int _amd_write_word_end;
+asm ("_amd_write_word_end:\n"
+     ".long 0\n");
+
+
 /*-----------------------------------------------------------------------
- * Copy memory to flash
+ * Copy memory to flash, returns:
+ * 0 - OK
+ * 1 - write timeout
+ * 2 - Flash not erased
+ * 3 - Unsupported flash type
  */
 
-volatile static int write_word (flash_info_t *info, ulong dest, ulong data)
+int write_buff(flash_info_t *info, uchar *src, ulong addr, ulong cnt)
 {
-    vu_long *addr = (vu_long *)dest;
-    ulong result;
-    int rc = ERR_OK;
-    int iflag;
-    int chip1, chip2;
-
-    /*
-     * Check if Flash is (sufficiently) erased
-     */
-    result = *addr;
-    if ((result & data) != data)
-        return ERR_NOT_ERASED;
-
-
-    /*
-     * Disable interrupts which might cause a timeout
-     * here. Remember that our exception vectors are
-     * at address 0 in the flash, and we don't want a
-     * (ticker) exception to happen while the flash
-     * chip is in programming mode.
-     */
-    iflag = disable_interrupts();
-
-    MEM_FLASH_ADDR1 = CMD_UNLOCK1;
-    MEM_FLASH_ADDR2 = CMD_UNLOCK2;
-    MEM_FLASH_ADDR1 = CMD_UNLOCK_BYPASS;
-    *addr = CMD_PROGRAM;
-    *addr = data;
-
-    /* arm simple, non interrupt dependent timer */
-    reset_timer();
-
-    /* wait until flash is ready */
-    chip1 = chip2 = 0;
-    do
-    {
-       result = *addr;
-
-       /* check timeout */
-       if (get_timer(0) > CFG_FLASH_ERASE_TOUT)
-       {
-           chip1 = ERR | TMO;
-           break;
-       }
-       if (!chip1 && ((result & 0x80) == (data & 0x80)))
-               chip1 = READY;
+       ulong cp, wp, data;
+       int i, l, rc;
+       int flag;
+       u32 (*_write_word_ptr)(unsigned start, unsigned dest, unsigned data);
+       unsigned size;
+
+       if ((info->flash_id & FLASH_VENDMASK) == (AMD_MANUFACT & FLASH_VENDMASK)) {
+               size = (unsigned)&_amd_write_word_end - (unsigned)_amd_write_word;
+
+               if (size > PROBE_BUFFER_SIZE) {
+                       printf("_amd_write_word() routine too large (%d) %p - %p\n",
+                              size, &_amd_write_word_end, _amd_write_word);
+                       return 0;
+               }
 
-       if (!chip1 && ((result & 0xFFFF) & BIT_PROGRAM_ERROR))
-       {
-               result = *addr;
+               memcpy(buffer, _amd_write_word, size);
+               _write_word_ptr = (void*)buffer;
 
-               if ((result & 0x80) == (data & 0x80))
-                       chip1 = READY;
-               else
-                       chip1 = ERR;
+       } else {
+               printf ("Can't program unknown flash type - aborted\n");
+               return 3;
        }
 
-       if (!chip2 && ((result & (0x80 << 16)) == (data & (0x80 << 16))))
-               chip2 = READY;
 
-       if (!chip2 && ((result >> 16) & BIT_PROGRAM_ERROR))
-       {
-               result = *addr;
-
-               if ((result & (0x80 << 16)) == (data & (0x80 << 16)))
-                       chip2 = READY;
-               else
-                       chip2 = ERR;
-       }
+       wp = (addr & ~3);       /* get lower word aligned address */
 
-    }  while (!chip1 || !chip2);
 
-    *addr = CMD_READ_ARRAY;
+       /*
+        * handle unaligned start bytes
+        */
+       if ((l = addr - wp) != 0) {
+               data = 0;
+               for (i=0, cp=wp; i<l; ++i, ++cp) {
+                       data |= (*(uchar *)cp) << (8*i);
+               }
+               for (; i<4 && cnt>0; ++i) {
+                       data |= *src++ << (8*i);
+                       --cnt;
+                       ++cp;
+               }
+               for (; cnt==0 && i<4; ++i, ++cp) {
+                       data |= (*(uchar *)cp)  << (8*i);
+               }
 
-    if (chip1 == ERR || chip2 == ERR || *addr != data)
-        rc = ERR_PROG_ERROR;
+               /* Disable interrupts which might cause a timeout here */
+               flag = disable_interrupts();
 
-    if (iflag)
-      enable_interrupts();
+               rc = _write_word_ptr(info->start[0], wp, data);
 
+               /* re-enable interrupts if necessary */
+               if (flag) {
+                       enable_interrupts();
+               }
+               if (rc != 0) {
+                       return rc;
+               }
+               wp += 4;
+       }
 
-    return rc;
-}
+       /*
+        * handle word aligned part
+        */
+       while (cnt >= 4) {
+               data = 0;
 
-/*-----------------------------------------------------------------------
- * Copy memory to flash.
- */
+               for (i=0; i<4; ++i) {
+                       data |= *src++ << (8*i);
+               }
 
-int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt)
-{
-    ulong cp, wp, data;
-    int l;
-    int i, rc;
+               /* Disable interrupts which might cause a timeout here */
+               flag = disable_interrupts();
 
-    wp = (addr & ~3);  /* get lower word aligned address */
+               rc = _write_word_ptr(info->start[0], wp, data);
 
-    /*
-     * handle unaligned start bytes
-     */
-    if ((l = addr - wp) != 0) {
-       data = 0;
-       for (i=0, cp=wp; i<l; ++i, ++cp) {
-           data = (data >> 8) | (*(uchar *)cp << 24);
+               /* re-enable interrupts if necessary */
+               if (flag) {
+                       enable_interrupts();
+               }
+               if (rc != 0) {
+                       return rc;
+               }
+               wp  += 4;
+               cnt -= 4;
        }
-       for (; i<4 && cnt>0; ++i) {
-           data = (data >> 8) | (*src++ << 24);
-           --cnt;
-           ++cp;
+
+       if (cnt == 0) {
+               return 0;
        }
-       for (; cnt==0 && i<4; ++i, ++cp) {
-           data = (data >> 8) | (*(uchar *)cp << 24);
+
+       /*
+        * handle unaligned tail bytes
+        */
+       data = 0;
+       for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) {
+               data |= *src++ << (8*i);
+               --cnt;
        }
 
-       if ((rc = write_word(info, wp, data)) != 0) {
-           return (rc);
+       for (; i<4; ++i, ++cp) {
+               data |= (*(uchar *)cp) << (8*i);
        }
-       wp += 4;
-    }
-
-    /*
-     * handle word aligned part
-     */
-    while (cnt >= 4) {
-       data = *((vu_long*)src);
-       if ((rc = write_word(info, wp, data)) != 0) {
-           return (rc);
+
+       /* Disable interrupts which might cause a timeout here */
+       flag = disable_interrupts();
+
+       rc = _write_word_ptr(info->start[0], wp, data);
+
+       /* re-enable interrupts if necessary */
+       if (flag) {
+               enable_interrupts();
        }
-       src += 4;
-       wp  += 4;
-       cnt -= 4;
-    }
-
-    if (cnt == 0) {
-       return ERR_OK;
-    }
-
-    /*
-     * handle unaligned tail bytes
-     */
-    data = 0;
-    for (i=0, cp=wp; i<4 && cnt>0; ++i, ++cp) {
-       data = (data >> 8) | (*src++ << 24);
-       --cnt;
-    }
-    for (; i<4; ++i, ++cp) {
-       data = (data >> 8) | (*(uchar *)cp << 24);
-    }
-
-    return write_word(info, wp, data);
+
+       return rc;
+
 }