From 010162eb729e9ed8233e902313c5f770b89e9202 Mon Sep 17 00:00:00 2001 From: Wolfgang Denk Date: Sat, 30 Jul 2005 16:39:27 +0200 Subject: [PATCH] Implement h/w sector protection status synchronization at boot. 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 | 161 ++++++++++++++++++++++++++++++++++++++++++++++----- board/pm520/flash.c | 97 ++++++++++++++++++++++++++++++- 2 files changed, 240 insertions(+), 18 deletions(-) diff --git a/board/alaska/flash.c b/board/alaska/flash.c index 48c9472..383491f 100644 --- a/board/alaska/flash.c +++ b/board/alaska/flash.c @@ -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) diff --git a/board/pm520/flash.c b/board/pm520/flash.c index 572cc9b..62700f9 100644 --- a/board/pm520/flash.c +++ b/board/pm520/flash.c @@ -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) -- 2.7.4