]> git.kernelconcepts.de Git - karo-tx-uboot.git/commitdiff
Implement h/w sector protection status synchronization at boot.
authorWolfgang Denk <wd@pollux.(none)>
Sat, 30 Jul 2005 14:39:27 +0000 (16:39 +0200)
committerWolfgang Denk <wd@pollux.(none)>
Sat, 30 Jul 2005 14:39:27 +0000 (16:39 +0200)
The code is provided for, and was tested on, the Yukon/Alaska
and PM520 boards only.

A bug in flash_real_protect() for the Yukon board was fixed by adding
a function that tells if two banks are on one flash chip.

board/alaska/flash.c
board/pm520/flash.c

index 48c94727e07961cc41b3ac7d8ee79eb03b8ba4e1..383491f566b1de2249d114d9133a34d86657fe9f 100644 (file)
@@ -64,7 +64,6 @@ typedef volatile unsigned char FLASH_PORT_WIDTHV;
 #define FLASH_CYCLE2    0x02aa
 
 #define WR_BLOCK        0x20
-
 /*-----------------------------------------------------------------------
  * Functions
  */
@@ -74,6 +73,9 @@ static int write_data_block (flash_info_t * info, ulong src, ulong dest);
 static int write_word_amd (flash_info_t * info, FPWV * dest, FPW data);
 static void flash_get_offsets (ulong base, flash_info_t * info);
 void inline spin_wheel (void);
+static void flash_sync_real_protect (flash_info_t * info);
+static unsigned char intel_sector_protected (flash_info_t *info, ushort sector);
+static unsigned char same_chip_banks (int bank1, int bank2);
 
 /*-----------------------------------------------------------------------
  */
@@ -115,6 +117,9 @@ unsigned long flash_init (void)
                        break;
                }
                size += flash_info[i].size;
+
+               /* get the h/w and s/w protection status in sync */
+               flash_sync_real_protect(&flash_info[i]);
        }
 
        /* Protect monitor and environment sectors
@@ -167,7 +172,6 @@ static void flash_get_offsets (ulong base, flash_info_t * info)
        if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL) {
                for (i = 0; i < info->sector_count; i++) {
                        info->start[i] = base + (i * PHYS_INTEL_SECT_SIZE);
-                       info->protect[i] = 0;
                }
        }
 }
@@ -305,6 +309,98 @@ static ulong flash_get_size (FPW * addr, flash_info_t * info)
 }
 
 
+/*
+ * This function gets the u-boot flash sector protection status
+ * (flash_info_t.protect[]) in sync with the sector protection
+ * status stored in hardware.
+ */
+static void flash_sync_real_protect (flash_info_t * info)
+{
+       int i;
+
+       switch (info->flash_id & FLASH_TYPEMASK) {
+       case FLASH_28F128J3A:
+               for (i = 0; i < info->sector_count; ++i) {
+                       info->protect[i] = intel_sector_protected(info, i);
+               }
+               break;
+       case FLASH_AM040:
+       default:
+               /* no h/w protect support */
+               break;
+       }
+}
+
+
+/*
+ * checks if "sector" in bank "info" is protected. Should work on intel
+ * strata flash chips 28FxxxJ3x in 8-bit mode.
+ * Returns 1 if sector is protected (or timed-out while trying to read
+ * protection status), 0 if it is not.
+ */
+static unsigned char intel_sector_protected (flash_info_t *info, ushort sector)
+{
+       FPWV *addr;
+       FPWV *lock_conf_addr;
+       ulong start;
+       unsigned char ret;
+
+       /*
+        * first, wait for the WSM to be finished. The rationale for
+        * waiting for the WSM to become idle for at most
+        * CFG_FLASH_ERASE_TOUT is as follows. The WSM can be busy
+        * because of: (1) erase, (2) program or (3) lock bit
+        * configuration. So we just wait for the longest timeout of
+        * the (1)-(3), i.e. the erase timeout.
+        */
+
+       /* wait at least 35ns (W12) before issuing Read Status Register */
+       udelay(1);
+       addr = (FPWV *) info->start[sector];
+       *addr = (FPW) INTEL_STATUS;
+
+       start = get_timer (0);
+       while ((*addr & (FPW) INTEL_FINISHED) != (FPW) INTEL_FINISHED) {
+               if (get_timer (start) > CFG_FLASH_ERASE_TOUT) {
+                       *addr = (FPW) INTEL_RESET; /* restore read mode */
+                       printf("WSM busy too long, can't get prot status\n");
+                       return 1;
+               }
+       }
+
+       /* issue the Read Identifier Codes command */
+       *addr = (FPW) INTEL_READID;
+
+       /* wait at least 35ns (W12) before reading */
+       udelay(1);
+
+       /* Intel example code uses offset of 4 for 8-bit flash */
+       lock_conf_addr = (FPWV *) info->start[sector] + 4;
+       ret = (*lock_conf_addr & (FPW) INTEL_PROTECT) ? 1 : 0;
+
+       /* put flash back in read mode */
+       *addr = (FPW) INTEL_RESET;
+
+       return ret;
+}
+
+
+/*
+ * Checks if "bank1" and "bank2" are on the same chip.  Returns 1 if they
+ * are and 0 otherwise.
+ */
+static unsigned char same_chip_banks (int bank1, int bank2)
+{
+       unsigned char same_chip[CFG_MAX_FLASH_BANKS][CFG_MAX_FLASH_BANKS] = {
+               {1, 1, 0, 0},
+               {1, 1, 0, 0},
+               {0, 0, 1, 1},
+               {0, 0, 1, 1}
+       };
+       return same_chip[bank1][bank2];
+}
+
+
 /*-----------------------------------------------------------------------
  */
 int flash_erase (flash_info_t * info, int s_first, int s_last)
@@ -729,7 +825,9 @@ void inline spin_wheel (void)
 int flash_real_protect (flash_info_t * info, long sector, int prot)
 {
        ulong start;
-       int i;
+       int i, j;
+       int curr_bank;
+       int bank;
        int rc = 0;
        FPWV *addr = (FPWV *) (info->start[sector]);
        int flag = disable_interrupts ();
@@ -779,23 +877,54 @@ int flash_real_protect (flash_info_t * info, long sector, int prot)
         * we have to restore lock bits of protected sectors.
         */
        if (!prot) {
-               for (i = 0; i < info->sector_count; i++) {
-                       if (info->protect[i]) {
-                               start = get_timer (0);
-                               addr = (FPWV *) (info->start[i]);
-                               *addr = INTEL_LOCKBIT;  /* Sector lock bit  */
-                               *addr = INTEL_PROTECT;  /* set              */
-                               while ((*addr & INTEL_FINISHED) !=
-                                      INTEL_FINISHED) {
-                                       if (get_timer (start) >
-                                           CFG_FLASH_UNLOCK_TOUT) {
-                                               printf ("Flash lock bit operation timed out\n");
-                                               rc = 1;
-                                               break;
+               /*
+                * re-locking must be done for all banks that belong on one
+                * FLASH chip, as all the sectors on the chip were unlocked
+                * by INTEL_LOCKBIT/INTEL_CONFIRM commands. (let's hope
+                * that banks never span chips, in particular chips which
+                * support h/w protection differently).
+                */
+
+               /* find the current bank number */
+               curr_bank = CFG_MAX_FLASH_BANKS + 1;
+               for (j = 0; j < CFG_MAX_FLASH_BANKS; ++j) {
+                       if (&flash_info[j] == info) {
+                               curr_bank = j;
+                       }
+               }
+               if (curr_bank == CFG_MAX_FLASH_BANKS + 1) {
+                       printf("Error: can't determine bank number!\n");
+               }
+
+               for (bank = 0; bank < CFG_MAX_FLASH_BANKS; ++bank) {
+                       if (!same_chip_banks(curr_bank, bank)) {
+                               continue;
+                       }
+                       info = &flash_info[bank];
+                       for (i = 0; i < info->sector_count; i++) {
+                               if (info->protect[i]) {
+                                       start = get_timer (0);
+                                       addr = (FPWV *) (info->start[i]);
+                                       *addr = INTEL_LOCKBIT;  /* Sector lock bit  */
+                                       *addr = INTEL_PROTECT;  /* set              */
+                                       while ((*addr & INTEL_FINISHED) !=
+                                              INTEL_FINISHED) {
+                                               if (get_timer (start) >
+                                                   CFG_FLASH_UNLOCK_TOUT) {
+                                                       printf ("Flash lock bit operation timed out\n");
+                                                       rc = 1;
+                                                       break;
+                                               }
                                        }
                                }
                        }
                }
+
+               /*
+                * get the s/w sector protection status in sync with the h/w,
+                * in case something went wrong during the re-locking.
+                */
+               flash_sync_real_protect(info); /* resets flash to read  mode */
        }
 
        if (flag)
index 572cc9bbeb0b1acb1f3c8e1bad6f34e93a880b33..62700f9f9f903c82b5ed3be5888f6fe12985c006 100644 (file)
@@ -75,6 +75,8 @@ static ulong flash_get_size (FPW *addr, flash_info_t *info);
 static int write_data (flash_info_t *info, ulong dest, FPW data);
 static void flash_get_offsets (ulong base, flash_info_t *info);
 void inline spin_wheel (void);
+static void flash_sync_real_protect (flash_info_t * info);
+static unsigned char intel_sector_protected (flash_info_t *info, ushort sector);
 
 /*-----------------------------------------------------------------------
  */
@@ -101,6 +103,9 @@ unsigned long flash_init (void)
                        break;
                }
                size += flash_info[i].size;
+
+               /* get the h/w and s/w protection status in sync */
+               flash_sync_real_protect(&flash_info[i]);
        }
 
        /* Protect monitor and environment sectors
@@ -138,7 +143,6 @@ static void flash_get_offsets (ulong base, flash_info_t *info)
        if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL) {
                for (i = 0; i < info->sector_count; i++) {
                        info->start[i] = base + (i * PHYS_FLASH_SECT_SIZE);
-                       info->protect[i] = 0;
                }
        }
 }
@@ -270,6 +274,85 @@ static ulong flash_get_size (FPW *addr, flash_info_t *info)
 }
 
 
+/*
+ * This function gets the u-boot flash sector protection status
+ * (flash_info_t.protect[]) in sync with the sector protection
+ * status stored in hardware.
+ */
+static void flash_sync_real_protect (flash_info_t * info)
+{
+       int i;
+
+       switch (info->flash_id & FLASH_TYPEMASK) {
+
+       case FLASH_28F128J3A:
+       case FLASH_28F640J3A:
+       case FLASH_28F320J3A:
+               for (i = 0; i < info->sector_count; ++i) {
+                       info->protect[i] = intel_sector_protected(info, i);
+               }
+               break;
+       default:
+               /* no h/w protect support */
+               break;
+       }
+}
+
+
+/*
+ * checks if "sector" in bank "info" is protected. Should work on intel
+ * strata flash chips 28FxxxJ3x in 8-bit mode.
+ * Returns 1 if sector is protected (or timed-out while trying to read
+ * protection status), 0 if it is not.
+ */
+static unsigned char intel_sector_protected (flash_info_t *info, ushort sector)
+{
+       FPWV *addr;
+       FPWV *lock_conf_addr;
+       ulong start;
+       unsigned char ret;
+
+       /*
+        * first, wait for the WSM to be finished. The rationale for
+        * waiting for the WSM to become idle for at most
+        * CFG_FLASH_ERASE_TOUT is as follows. The WSM can be busy
+        * because of: (1) erase, (2) program or (3) lock bit
+        * configuration. So we just wait for the longest timeout of
+        * the (1)-(3), i.e. the erase timeout.
+        */
+
+       /* wait at least 35ns (W12) before issuing Read Status Register */
+       udelay(1);
+       addr = (FPWV *) info->start[sector];
+       *addr = (FPW) INTEL_STATUS;
+
+       start = get_timer (0);
+       while ((*addr & (FPW) INTEL_FINISHED) != (FPW) INTEL_FINISHED) {
+               if (get_timer (start) > CFG_FLASH_ERASE_TOUT) {
+                       *addr = (FPW) INTEL_RESET; /* restore read mode */
+                       printf("WSM busy too long, can't get prot status\n");
+                       return 1;
+               }
+       }
+       
+       /* issue the Read Identifier Codes command */
+       *addr = (FPW) INTEL_READID;
+
+       /* wait at least 35ns (W12) before reading */
+       udelay(1);
+
+       /* Intel example code uses offset of 2 for 16 bit flash */
+       lock_conf_addr = (FPWV *) info->start[sector] + 2;
+       ret = (*lock_conf_addr & (FPW) INTEL_PROTECT) ? 1 : 0;
+
+       /* put flash back in read mode */
+       *addr = (FPW) INTEL_RESET;
+
+       return ret;
+}
+
+
+
 /*-----------------------------------------------------------------------
  */
 
@@ -491,7 +574,7 @@ void inline spin_wheel (void)
  * 0 - OK
  * 1 - Error (timeout, voltage problems, etc.)
  */
-int flash_real_protect(flash_info_t *info, long sector, int prot)
+int flash_real_protect (flash_info_t *info, long sector, int prot)
 {
        ulong start;
        int i;
@@ -531,6 +614,11 @@ int flash_real_protect(flash_info_t *info, long sector, int prot)
        /*
         * Clear lock bit command clears all sectors lock bits, so
         * we have to restore lock bits of protected sectors.
+        * WARNING: code below re-locks sectors only for one bank (info).
+        * This causes problems on boards where several banks share
+        * the same chip, as sectors in othere banks will be unlocked
+        * but not re-locked. It works fine on pm520 though, as there
+        * is only one chip and one bank.
         */
        if (!prot)
        {
@@ -553,6 +641,11 @@ int flash_real_protect(flash_info_t *info, long sector, int prot)
                                }
                        }
                }
+               /*
+                * get the s/w sector protection status in sync with the h/w,
+                * in case something went wrong during the re-locking.
+                */
+               flash_sync_real_protect(info); /* resets flash to read  mode */
        }
 
        if (flag)