diff options
| author | Wolfgang Denk <wd@pollux.(none)> | 2005-07-30 16:39:27 +0200 | 
|---|---|---|
| committer | Wolfgang Denk <wd@pollux.(none)> | 2005-07-30 16:39:27 +0200 | 
| commit | 010162eb729e9ed8233e902313c5f770b89e9202 (patch) | |
| tree | b03a605aa6833fb96e2dbc7277a01a9518981e8b /board/alaska/flash.c | |
| parent | 15f36a5efd31fe608b43dc197ebbd80d3cecbe44 (diff) | |
| download | olio-uboot-2014.01-010162eb729e9ed8233e902313c5f770b89e9202.tar.xz olio-uboot-2014.01-010162eb729e9ed8233e902313c5f770b89e9202.zip | |
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.
Diffstat (limited to 'board/alaska/flash.c')
| -rw-r--r-- | board/alaska/flash.c | 161 | 
1 files changed, 145 insertions, 16 deletions
| diff --git a/board/alaska/flash.c b/board/alaska/flash.c index 48c94727e..383491f56 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) |