diff options
Diffstat (limited to 'board/o2dnt/flash.c')
| -rw-r--r-- | board/o2dnt/flash.c | 587 | 
1 files changed, 587 insertions, 0 deletions
| diff --git a/board/o2dnt/flash.c b/board/o2dnt/flash.c new file mode 100644 index 000000000..037d28732 --- /dev/null +++ b/board/o2dnt/flash.c @@ -0,0 +1,587 @@ +/* + * (C) Copyright 2005 + * Wolfgang Denk, DENX Software Engineering, wd@denx.de. + * + * flash_real_protect() routine based on boards/alaska/flash.c + * (C) Copyright 2001 + * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> + +/* Intel-compatible flash commands */ +#define INTEL_ERASE	0x20 +#define INTEL_PROGRAM	0x40 +#define INTEL_CLEAR	0x50 +#define INTEL_LOCKBIT	0x60 +#define INTEL_PROTECT	0x01 +#define INTEL_STATUS	0x70 +#define INTEL_READID	0x90 +#define INTEL_READID	0x90 +#define INTEL_SUSPEND	0xB0 +#define INTEL_CONFIRM	0xD0 +#define INTEL_RESET	0xFF + +/* Intel-compatible flash status bits */ +#define INTEL_FINISHED	0x80 +#define INTEL_OK	0x80 + +typedef unsigned char FLASH_PORT_WIDTH; +typedef volatile unsigned char FLASH_PORT_WIDTHV; +#define FPW		FLASH_PORT_WIDTH +#define FPWV		FLASH_PORT_WIDTHV +#define	FLASH_ID_MASK	0xFF + +#define ORMASK(size) ((-size) & OR_AM_MSK) + +#define FLASH_CYCLE1	0x0555 +#define FLASH_CYCLE2	0x02aa + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; /* info for FLASH chips */ + +/*----------------------------------------------------------------------- + * Functions + */ +static ulong flash_get_size(FPWV *addr, flash_info_t *info); +static void flash_reset(flash_info_t *info); +static flash_info_t *flash_get_info(ulong base); +static int write_data (flash_info_t *info, FPWV *dest, FPW data); /* O2D */ +static void flash_sync_real_protect (flash_info_t * info); +static unsigned char intel_sector_protected (flash_info_t *info, ushort sector); + +/*----------------------------------------------------------------------- + * flash_init() + * + * sets up flash_info and returns size of FLASH (bytes) + */ +unsigned long flash_init (void) +{ +	unsigned long size = 0; +	int i; +	extern void flash_preinit(void); +	extern void flash_afterinit(ulong); + +	flash_preinit(); + +	/* Init: no FLASHes known */ +	for (i = 0; i < CFG_MAX_FLASH_BANKS; ++i) { +		memset(&flash_info[i], 0, sizeof(flash_info_t)); +		flash_info[i].flash_id = FLASH_UNKNOWN; +	} + +	/* Query flash chip */ +	flash_info[0].size = +		flash_get_size((FPW *)CFG_FLASH_BASE, &flash_info[0]); +	size += flash_info[0].size; + +	/* get the h/w and s/w protection status in sync */ +	flash_sync_real_protect(&flash_info[0]); + +#if CFG_MONITOR_BASE >= CFG_FLASH_BASE +	/* monitor protection ON by default */ +	flash_protect(FLAG_PROTECT_SET, +			CFG_MONITOR_BASE, +			CFG_MONITOR_BASE+monitor_flash_len-1, +			flash_get_info(CFG_MONITOR_BASE)); +#endif + +#ifdef	CFG_ENV_IS_IN_FLASH +	/* ENV protection ON by default */ +	flash_protect(FLAG_PROTECT_SET, +			CFG_ENV_ADDR, +			CFG_ENV_ADDR+CFG_ENV_SIZE-1, +			flash_get_info(CFG_ENV_ADDR)); +#endif + + +	flash_afterinit(size); +	return (size ? size : 1); +} + +/*----------------------------------------------------------------------- + */ +static void flash_reset(flash_info_t *info) +{ +	FPWV *base = (FPWV *)(info->start[0]); + +	/* Put FLASH back in read mode */ +	if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_INTEL) +		*base = (FPW) INTEL_RESET;	/* Intel Read Mode */ +} + +/*----------------------------------------------------------------------- + */ + +static flash_info_t *flash_get_info(ulong base) +{ +	int i; +	flash_info_t * info; + +	for (i = 0; i < CFG_MAX_FLASH_BANKS; i ++) { +		info = & flash_info[i]; +		if (info->size && +				info->start[0] <= base && +				base <= info->start[0] + info->size - 1) +			break; +	} + +	return (i == CFG_MAX_FLASH_BANKS ? 0 : info); +} + +/*----------------------------------------------------------------------- + */ + +void flash_print_info (flash_info_t *info) +{ +	int i; +	uchar *boottype; +	uchar *bootletter; +	char *fmt; +	uchar botbootletter[] = "B"; +	uchar topbootletter[] = "T"; +	uchar botboottype[] = "bottom boot sector"; +	uchar topboottype[] = "top boot sector"; + +	if (info->flash_id == FLASH_UNKNOWN) { +		printf ("missing or unknown FLASH type\n"); +		return; +	} + +	switch (info->flash_id & FLASH_VENDMASK) { +		case FLASH_MAN_INTEL: +			printf ("INTEL "); +			break; +		default: +			printf ("Unknown Vendor "); +			break; +	} + +	/* check for top or bottom boot, if it applies */ +	if (info->flash_id & FLASH_BTYPE) { +		boottype = botboottype; +		bootletter = botbootletter; +	} else { +		boottype = topboottype; +		bootletter = topbootletter; +	} + +	switch (info->flash_id & FLASH_TYPEMASK) { +		case FLASH_28F128J3A: +			fmt = "28F128J3 (128 Mbit, uniform sectors)\n"; +			break; +		default: +			fmt = "Unknown Chip Type\n"; +			break; +	} + +	printf (fmt, bootletter, boottype); +	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"); +} + +/*----------------------------------------------------------------------- + */ + +/* + * The following code cannot be run from FLASH! + */ +ulong flash_get_size (FPWV *addr, flash_info_t *info) +{ +	int i; + +	/* Write auto select command: read Manufacturer ID */ +	/* Write auto select command sequence and test FLASH answer */ +	addr[FLASH_CYCLE1] = (FPW) INTEL_READID;	/* selects Intel or AMD */ + +	/* The manufacturer codes are only 1 byte, so just use 1 byte. +	 * This works for any bus width and any FLASH device width. +	 */ +	udelay(100); +	switch (addr[0] & 0xff) { +		case (uchar)INTEL_MANUFACT: +			info->flash_id = FLASH_MAN_INTEL; +			break; +		default: +			info->flash_id = FLASH_UNKNOWN; +			info->sector_count = 0; +			info->size = 0; +			break; +	} + +	/* Strataflash is configurable to 8/16bit Bus, +	 * but the Query-Structure is Word-orientated */ +	if (info->flash_id != FLASH_UNKNOWN) { +		switch ((FPW)addr[2]) { +			case (FPW)INTEL_ID_28F128J3: +				info->flash_id += FLASH_28F128J3A; +				info->sector_count = 128; +				info->size = 0x01000000; +				for( i = 0; i < info->sector_count; i++ ) +					info->start[i] = (ulong)addr + (i * 0x20000); +				break;				/* => Intel Strataflash 16MB */ +			default: +				printf("Flash_id != %xd\n", (FPW)addr[2]); +				info->flash_id = FLASH_UNKNOWN; +				info->sector_count = 0; +				info->size = 0; +				return (0);			/* => no or unknown flash */ +		} +	} + +	/* Put FLASH back in read mode */ +	flash_reset(info); + +	return (info->size); +} + +/*----------------------------------------------------------------------- + */ + +int flash_erase (flash_info_t *info, int s_first, int s_last) +{ +	FPWV *addr; +	int flag, prot, sect; +	ulong start, now, last; +	int rcode = 0; + +	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; +	} + +	switch (info->flash_id & FLASH_TYPEMASK) { +		case FLASH_28F128J3A: +			break; +		case FLASH_UNKNOWN: +		default: +			printf ("Can't erase unknown flash type %08lx - aborted\n", +					info->flash_id); +			return 1; +	} + +	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!", +				prot); + +	printf ("\n"); +	last = get_timer(0); + +	/* Start erase on unprotected sectors */ +	for (sect = s_first; sect <= s_last && rcode == 0; sect++) { + +		if (info->protect[sect] != 0)	/* protected, skip it */ +			continue; + +		/* Disable interrupts which might cause a timeout here */ +		flag = disable_interrupts(); + +		addr = (FPWV *)(info->start[sect]); +		*addr = (FPW) INTEL_CLEAR; /* clear status register */ +		*addr = (FPW) INTEL_ERASE; /* erase setup */ +		*addr = (FPW) INTEL_CONFIRM; /* erase confirm */ + +		/* re-enable interrupts if necessary */ +		if (flag) +			enable_interrupts(); + +		start = get_timer(0); + +		/* wait at least 80us for Intel - let's wait 1 ms */ +		udelay (1000); + +		while ((*addr & (FPW) INTEL_FINISHED) != (FPW) INTEL_FINISHED) { +			if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) { +				printf ("Timeout\n"); +				*addr = (FPW) INTEL_SUSPEND;/* suspend erase */ +				flash_reset(info);	/* reset to read mode */ +				rcode = 1;		/* failed */ +				break; +			} + +			/* show that we're waiting */ +			if ((get_timer(last)) > CFG_HZ) { /* every second */ +				putc ('.'); +				last = get_timer(0); +			} +		} + +		flash_reset(info);	/* reset to read mode */ +	} + +	printf (" done\n"); +	return rcode; +} + +/*----------------------------------------------------------------------- + * Copy memory to flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt) +{ +	FPW data = 0;	/* 16 or 32 bit word, matches flash bus width on MPC8XX */ +	int bytes;	/* number of bytes to program in current word		*/ +	int left;	/* number of bytes left to program			*/ +	int i, res; + +	for (left = cnt, res = 0; +			left > 0 && res == 0; +			addr += sizeof(data), left -= sizeof(data) - bytes) { + +		bytes = addr & (sizeof(data) - 1); +		addr &= ~(sizeof(data) - 1); + +		/* combine source and destination data so can program +		 * an entire word of 16 or 32 bits  */ +		for (i = 0; i < sizeof(data); i++) { +			data <<= 8; +			if (i < bytes || i - bytes >= left ) +				data += *((uchar *)addr + i); +			else +				data += *src++; +		} + +		/* write one word to the flash */ +		switch (info->flash_id & FLASH_VENDMASK) { +			case FLASH_MAN_INTEL: +				res = write_data(info, (FPWV *)addr, data); +				break; +			default: +				/* unknown flash type, error! */ +				printf ("missing or unknown FLASH type\n"); +				res = 1;	/* not really a timeout, but gives error */ +				break; +		} +	} + +	return (res); +} + +/*----------------------------------------------------------------------- + * Write a word or halfword to Flash, returns: + * 0 - OK + * 1 - write timeout + * 2 - Flash not erased + */ +static int write_data (flash_info_t *info, FPWV *dest, FPW data) +{ +	FPWV *addr = dest; +	ulong status; +	ulong start; +	int flag; + +	/* Check if Flash is (sufficiently) erased */ +	if ((*addr & data) != data) { +		printf ("not erased at %08lx (%lx)\n", (ulong) addr, *addr); +		return (2); +	} +	/* Disable interrupts which might cause a timeout here */ +	flag = disable_interrupts (); + +	*addr = (FPW) INTEL_PROGRAM;	/* write setup */ +	*addr = data; + +	/* arm simple, non interrupt dependent timer */ +	start = get_timer(0); + +	/* wait while polling the status register */ +	while (((status = *addr) & (FPW) INTEL_FINISHED) != (FPW) INTEL_FINISHED) { +		if (get_timer(start) > CFG_FLASH_WRITE_TOUT) { +			*addr = (FPW) INTEL_RESET;	/* restore read mode */ +			return (1); +		} +	} + +	*addr = (FPW) INTEL_RESET;	/* restore read mode */ +	if (flag) +		enable_interrupts(); + +	return (0); +} + + +/*----------------------------------------------------------------------- + * Set/Clear sector's lock bit, returns: + * 0 - OK + * 1 - Error (timeout, voltage problems, etc.) + */ +int flash_real_protect (flash_info_t * info, long sector, int prot) +{ +	ulong start; +	int i; +	int rc = 0; +	FPWV *addr = (FPWV *) (info->start[sector]); +	int flag = disable_interrupts (); + +	*addr = INTEL_CLEAR;	/* Clear status register    */ +	if (prot) {		/* Set sector lock bit      */ +		*addr = INTEL_LOCKBIT;	/* Sector lock bit          */ +		*addr = INTEL_PROTECT;	/* set                      */ +	} else {		/* Clear sector lock bit    */ +		*addr = INTEL_LOCKBIT;	/* All sectors lock bits    */ +		*addr = INTEL_CONFIRM;	/* clear                    */ +	} + +	start = get_timer (0); + +	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; +		} +	} + +	if (*addr != INTEL_OK) { +		printf ("Flash lock bit operation failed at %08X, CSR=%08X\n", +			(uint) addr, (uint) * addr); +		rc = 1; +	} + +	if (!rc) +		info->protect[sector] = prot; + +	/* +	 * Clear lock bit command clears all sectors lock bits, so +	 * 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; +					} +				} +			} +		} +	} + +	if (flag) +		enable_interrupts (); + +	*addr = INTEL_RESET;	/* Reset to read array mode */ + +	return rc; +} + + +/* + * 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; +	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; +} |