diff options
Diffstat (limited to 'board/evb4510/flash.c')
| -rw-r--r-- | board/evb4510/flash.c | 540 | 
1 files changed, 540 insertions, 0 deletions
| diff --git a/board/evb4510/flash.c b/board/evb4510/flash.c new file mode 100644 index 000000000..5491827e0 --- /dev/null +++ b/board/evb4510/flash.c @@ -0,0 +1,540 @@ +/* + * + * Copyright (c) 2004	Cucy Systems (http://www.cucy.com) + * Curt Brune <curt@cucy.com> + * + * 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> +#include <asm/hardware.h> +#include <flash.h> + +flash_info_t flash_info[CFG_MAX_FLASH_BANKS]; + +typedef enum { +	FLASH_DEV_U9_512KB = 0, +	FLASH_DEV_U7_2MB = 1 +} FLASH_DEV; + +#define FLASH_DQ7		(0x80) +#define FLASH_DQ5		(0x20) + +#define PROG_ADDR		(0xAAA) +#define SETUP_ADDR		(0xAAA) +#define ID_ADDR			(0xAAA) +#define UNLOCK_ADDR1		(0xAAA) +#define UNLOCK_ADDR2		(0x555) + +#define UNLOCK_CMD1		(0xAA) +#define UNLOCK_CMD2		(0x55) +#define ERASE_SUSPEND_CMD	(0xB0) +#define ERASE_RESUME_CMD	(0x30) +#define RESET_CMD		(0xF0) +#define ID_CMD			(0x90) +#define SELECT_CMD		(0x90) +#define CHIPERASE_CMD		(0x10) +#define BYPASS_CMD		(0x20) +#define SECERASE_CMD		(0x30) +#define PROG_CMD		(0xa0) +#define SETUP_CMD		(0x80) + +#if 0 +#define WRITE_UNLOCK(addr) { \ +	PUT__U8( addr + UNLOCK_ADDR1, UNLOCK_CMD1); \ +	PUT__U8( addr + UNLOCK_ADDR2, UNLOCK_CMD2); \ +} + +/* auto select command */ +#define CMD_ID(addr) WRITE_UNLOCK(addr); { \ +	PUT__U8( addr + ID_ADDR, ID_CMD); \ +} + +#define CMD_RESET(addr) WRITE_UNLOCK(addr); { \ +	PUT__U8( addr + ID_ADDR, RESET_CMD); \ +} + +#define CMD_ERASE_SEC(base, addr) WRITE_UNLOCK(base); \ +	PUT__U8( base + SETUP_ADDR, SETUP_CMD); \ +	WRITE_UNLOCK(base); \ +	PUT__U8( addr, SECERASE_CMD); + +#define CMD_ERASE_CHIP(base) WRITE_UNLOCK(base); \ +	PUT__U8( base + SETUP_ADDR, SETUP_CMD); \ +	WRITE_UNLOCK(base); \ +	PUT__U8( base + SETUP_ADDR, CHIPERASE_CMD); + +/* prepare for bypass programming */ +#define CMD_UNLOCK_BYPASS(addr) WRITE_UNLOCK(addr); { \ +	PUT__U8( addr + ID_ADDR, 0x20); \ +} + +/* terminate bypass programming */ +#define CMD_BYPASS_RESET(addr) { \ +	PUT__U8(addr, 0x90); \ +	PUT__U8(addr, 0x00); \ +} +#endif + +inline static void FLASH_CMD_UNLOCK (FLASH_DEV dev, u32 base) +{ +	switch (dev) { +	case FLASH_DEV_U7_2MB: +		PUT__U8 (base + 0xAAA, 0xAA); +		PUT__U8 (base + 0x555, 0x55); +		break; +	case FLASH_DEV_U9_512KB: +		PUT__U8 (base + 0x555, 0xAA); +		PUT__U8 (base + 0x2AA, 0x55); +		break; +	} +} + +inline static void FLASH_CMD_SELECT (FLASH_DEV dev, u32 base) +{ +	switch (dev) { +	case FLASH_DEV_U7_2MB: +		FLASH_CMD_UNLOCK (dev, base); +		PUT__U8 (base + 0xAAA, SELECT_CMD); +		break; +	case FLASH_DEV_U9_512KB: +		FLASH_CMD_UNLOCK (dev, base); +		PUT__U8 (base + 0x555, SELECT_CMD); +		break; +	} +} + +inline static void FLASH_CMD_RESET (FLASH_DEV dev, u32 base) +{ +	switch (dev) { +	case FLASH_DEV_U7_2MB: +		FLASH_CMD_UNLOCK (dev, base); +		PUT__U8 (base + 0xAAA, RESET_CMD); +		break; +	case FLASH_DEV_U9_512KB: +		FLASH_CMD_UNLOCK (dev, base); +		PUT__U8 (base + 0x555, RESET_CMD); +		break; +	} +} + +inline static void FLASH_CMD_ERASE_SEC (FLASH_DEV dev, u32 base, u32 addr) +{ +	switch (dev) { +	case FLASH_DEV_U7_2MB: +		FLASH_CMD_UNLOCK (dev, base); +		PUT__U8 (base + 0xAAA, SETUP_CMD); +		FLASH_CMD_UNLOCK (dev, base); +		PUT__U8 (addr, SECERASE_CMD); +		break; +	case FLASH_DEV_U9_512KB: +		FLASH_CMD_UNLOCK (dev, base); +		PUT__U8 (base + 0x555, SETUP_CMD); +		FLASH_CMD_UNLOCK (dev, base); +		PUT__U8 (addr, SECERASE_CMD); +		break; +	} +} + +inline static void FLASH_CMD_ERASE_CHIP (FLASH_DEV dev, u32 base) +{ +	switch (dev) { +	case FLASH_DEV_U7_2MB: +		FLASH_CMD_UNLOCK (dev, base); +		PUT__U8 (base + 0xAAA, SETUP_CMD); +		FLASH_CMD_UNLOCK (dev, base); +		PUT__U8 (base, CHIPERASE_CMD); +		break; +	case FLASH_DEV_U9_512KB: +		FLASH_CMD_UNLOCK (dev, base); +		PUT__U8 (base + 0x555, SETUP_CMD); +		FLASH_CMD_UNLOCK (dev, base); +		PUT__U8 (base, CHIPERASE_CMD); +		break; +	} +} + +inline static void FLASH_CMD_UNLOCK_BYPASS (FLASH_DEV dev, u32 base) +{ +	switch (dev) { +	case FLASH_DEV_U7_2MB: +		FLASH_CMD_UNLOCK (dev, base); +		PUT__U8 (base + 0xAAA, BYPASS_CMD); +		break; +	case FLASH_DEV_U9_512KB: +		FLASH_CMD_UNLOCK (dev, base); +		PUT__U8 (base + 0x555, BYPASS_CMD); +		break; +	} +} + +inline static void FLASH_CMD_BYPASS_RESET (FLASH_DEV dev, u32 base) +{ +	PUT__U8 (base, SELECT_CMD); +	PUT__U8 (base, 0x0); +} + +/* poll for flash command completion */ +static u16 _flash_poll (FLASH_DEV dev, u32 addr, u16 data, ulong timeOut) +{ +	u32 done = 0; +	ulong t0; + +	u16 error = 0; +	volatile u16 flashData; + +	data = data & 0xFF; +	t0 = get_timer (0); +	while (get_timer (t0) < timeOut) { +		/*	for( i = 0; i < POLL_LOOPS; i++) { */ +		/*  Read the Data */ +		flashData = GET__U8 (addr); + +		/*  FLASH_DQ7 = Data? */ +		if ((flashData & FLASH_DQ7) == (data & FLASH_DQ7)) { +			done = 1; +			break; +		} + +		/*  Check Timeout (FLASH_DQ5==1) */ +		if (flashData & FLASH_DQ5) { +			/*  Read the Data */ +			flashData = GET__U8 (addr); + +			/*  FLASH_DQ7 = Data? */ +			if (!((flashData & FLASH_DQ7) == (data & FLASH_DQ7))) { +				printf ("_flash_poll(): FLASH_DQ7 & flashData not equal to write value\n"); +				error = ERR_PROG_ERROR; +			} +			FLASH_CMD_RESET (dev, addr); +			done = 1; +			break; +		} +		/*  spin delay */ +		udelay (10); +	} + + +	/*  error update */ +	if (!done) { +		printf ("_flash_poll(): Timeout\n"); +		error = ERR_TIMOUT; +	} + +	/*  Check the data */ +	if (!error) { +		/*  Read the Data */ +		flashData = GET__U8 (addr); +		if (flashData != data) { +			error = ERR_PROG_ERROR; +			printf ("_flash_poll(): flashData(0x%04x) not equal to data(0x%04x)\n", +				flashData, data); +		} +	} + +	return error; +} + +/*----------------------------------------------------------------------- + */ +static int _flash_check_protection (flash_info_t * info, int s_first, int s_last) +{ +	int sect, prot = 0; + +	for (sect = s_first; sect <= s_last; sect++) +		if (info->protect[sect]) { +			printf ("  Flash sector %d protected.\n", sect); +			prot++; +		} +	return prot; +} + +static int _detectFlash (FLASH_DEV dev, u32 base, u8 venId, u8 devId) +{ + +	u32 baseAddr = base | CACHE_DISABLE_MASK; +	u8 vendorId, deviceId; + +	/*	printf(__FUNCTION__"(): detecting flash @ 0x%08x\n", base); */ + +	/* Send auto select command and read manufacturer info */ +	FLASH_CMD_SELECT (dev, baseAddr); +	vendorId = GET__U8 (baseAddr); +	FLASH_CMD_RESET (dev, baseAddr); + +	/* Send auto select command and read device info */ +	FLASH_CMD_SELECT (dev, baseAddr); + +	if (dev == FLASH_DEV_U7_2MB) { +		deviceId = GET__U8 (baseAddr + 2); +	} else if (dev == FLASH_DEV_U9_512KB) { +		deviceId = GET__U8 (baseAddr + 1); +	} else { +		return 0; +	} + +	FLASH_CMD_RESET (dev, baseAddr); + +	/* printf (__FUNCTION__"(): found vendorId 0x%04x, deviceId 0x%04x\n", +		vendorId, deviceId); +	 */ + +	return (vendorId == venId) && (deviceId == devId); + +} + +/****************************************************************************** + * + * Public u-boot interface functions below + * + *****************************************************************************/ + +/*************************************************************************** + * + * Flash initialization + * + * This board has two banks of flash, but the base addresses depend on + * how the board is jumpered. + * + * The two flash types are: + * + *   AMD Am29LV160DB (2MB) sectors layout 16KB, 2x8KB, 32KB, 31x64KB + * + *   AMD Am29LV040B  (512KB)  sectors: 8x64KB + *****************************************************************************/ + +unsigned long flash_init (void) +{ +	flash_info_t *info; +	u16 i; +	u32 flashtest; +	s16 amd160 = -1; +	u32 amd160base = 0; + +#if CFG_MAX_FLASH_BANKS == 2 +	s16 amd040 = -1; +	u32 amd040base = 0; +#endif + +	/* configure PHYS_FLASH_1 */ +	if (_detectFlash (FLASH_DEV_U7_2MB, PHYS_FLASH_1, 0x1, 0x49)) { +		amd160 = 0; +		amd160base = PHYS_FLASH_1; +#if CFG_MAX_FLASH_BANKS == 1 +	} +#else +		if (_detectFlash +		    (FLASH_DEV_U9_512KB, PHYS_FLASH_2, 0x1, 0x4F)) { +			amd040 = 1; +			amd040base = PHYS_FLASH_2; +		} else { +			printf (__FUNCTION__ +				"(): Unable to detect PHYS_FLASH_2: 0x%08x\n", +				PHYS_FLASH_2); +		} +	} else if (_detectFlash (FLASH_DEV_U9_512KB, PHYS_FLASH_1, 0x1, 0x4F)) { +		amd040 = 0; +		amd040base = PHYS_FLASH_1; +		if (_detectFlash (FLASH_DEV_U7_2MB, PHYS_FLASH_2, 0x1, 0x49)) { +			amd160 = 1; +			amd160base = PHYS_FLASH_2; +		} else { +			printf (__FUNCTION__ +				"(): Unable to detect PHYS_FLASH_2: 0x%08x\n", +				PHYS_FLASH_2); +		} +	} +#endif +	else { +		printf (__FUNCTION__ +			"(): Unable to detect PHYS_FLASH_1: 0x%08x\n", +			PHYS_FLASH_1); +	} + +	/* Configure AMD Am29LV160DB (2MB) */ +	info = &flash_info[amd160]; +	info->flash_id = FLASH_DEV_U7_2MB; +	info->sector_count = 35; +	info->size = 2 * 1024 * 1024;	/* 2MB */ +	/* 1*16K Boot Block +	   2*8K Parameter Block +	   1*32K Small Main Block */ +	info->start[0] = amd160base; +	info->start[1] = amd160base + 0x4000; +	info->start[2] = amd160base + 0x6000; +	info->start[3] = amd160base + 0x8000; +	for (i = 1; i < info->sector_count; i++) +		info->start[3 + i] = amd160base + i * (64 * 1024); + +	for (i = 0; i < info->sector_count; i++) { +		/* Write auto select command sequence and query sector protection */ +		FLASH_CMD_SELECT (info->flash_id, +				  info->start[i] | CACHE_DISABLE_MASK); +		flashtest = +			GET__U8 (((info->start[i] + 4) | CACHE_DISABLE_MASK)); +		FLASH_CMD_RESET (info->flash_id, +				 amd160base | CACHE_DISABLE_MASK); +		info->protect[i] = (flashtest & 0x0001); +	} + +	/* +	 * protect monitor and environment sectors in 2MB flash +	 */ +	flash_protect (FLAG_PROTECT_SET, +		       amd160base, amd160base + monitor_flash_len - 1, info); + +	flash_protect (FLAG_PROTECT_SET, +		       CFG_ENV_ADDR, CFG_ENV_ADDR + CFG_ENV_SIZE - 1, info); + +#if CFG_MAX_FLASH_BANKS == 2 +	/* Configure AMD Am29LV040B (512KB) */ +	info = &flash_info[amd040]; +	info->flash_id = FLASH_DEV_U9_512KB; +	info->sector_count = 8; +	info->size = 512 * 1024;	/* 512KB, 8 x 64KB */ +	for (i = 0; i < info->sector_count; i++) { +		info->start[i] = amd040base + i * (64 * 1024); +		/* Write auto select command sequence and query sector protection */ +		FLASH_CMD_SELECT (info->flash_id, +				  info->start[i] | CACHE_DISABLE_MASK); +		flashtest = +			GET__U8 (((info->start[i] + 2) | CACHE_DISABLE_MASK)); +		FLASH_CMD_RESET (info->flash_id, +				 amd040base | CACHE_DISABLE_MASK); +		info->protect[i] = (flashtest & 0x0001); +	} +#endif + +	return flash_info[0].size +#if CFG_MAX_FLASH_BANKS == 2 +		+ flash_info[1].size +#endif +		; +} + +void flash_print_info (flash_info_t * info) +{ +	int i; + +	if (info->flash_id == FLASH_DEV_U7_2MB) { +		printf ("AMD Am29LV160DB (2MB) 16KB,2x8KB,32KB,31x64KB\n"); +	} else if (info->flash_id == FLASH_DEV_U9_512KB) { +		printf ("AMD Am29LV040B	 (512KB) 8x64KB\n"); +	} else { +		printf ("Unknown flash_id ...\n"); +		return; +	} + +	printf ("  Size: %ld KB in %d Sectors\n", +		info->size >> 10, info->sector_count); +	printf ("  Sector Start Addresses:"); +	for (i = 0; i < info->sector_count; i++) { +		if ((i % 4) == 0) +			printf ("\n   "); +		printf (" S%02d @ 0x%08lX%s", i, +			info->start[i], info->protect[i] ? " !" : "  "); +	} +	printf ("\n"); +} + +int flash_erase (flash_info_t * info, int s_first, int s_last) +{ +	u16 i, error = 0; + +	printf ("\n"); + +	/* check flash protection bits */ +	if (_flash_check_protection (info, s_first, s_last)) { +		printf ("  Flash erase aborted due to protected sectors\n"); +		return ERR_PROTECTED; +	} + +	if ((s_first < info->sector_count) && (s_first <= s_last)) { +		for (i = s_first; i <= s_last && !error; i++) { +			printf ("  Erasing Sector %d @ 0x%08lx ... ", i, +				info->start[i]); +			/* bypass the cache to access the flash memory */ +			FLASH_CMD_ERASE_SEC (info->flash_id, +					     (info-> +					      start[0] | CACHE_DISABLE_MASK), +					     (info-> +					      start[i] | CACHE_DISABLE_MASK)); +			/* look for sector to become 0xFF after erase */ +			error = _flash_poll (info->flash_id, +					     info-> +					     start[i] | CACHE_DISABLE_MASK, +					     0xFF, CFG_FLASH_ERASE_TOUT); +			FLASH_CMD_RESET (info->flash_id, +					 (info-> +					  start[0] | CACHE_DISABLE_MASK)); +			printf ("done\n"); +			if (error) { +				break; +			} +		} +	} else +		error = ERR_INVAL; + +	return error; +} + +int write_buff (flash_info_t * info, uchar * src, ulong addr, ulong cnt) +{ +	u16 error = 0, i; +	u32 n; +	u8 *bp, *bps; + +	/*  Write Setup */ +	/* bypass the cache to access the flash memory */ +	FLASH_CMD_UNLOCK_BYPASS (info->flash_id, +				 (info->start[0] | CACHE_DISABLE_MASK)); + +	/*  Write the Data to Flash */ + +	bp = (u8 *) (addr | CACHE_DISABLE_MASK); +	bps = (u8 *) src; + +	for (n = 0; n < cnt && !error; n++, bp++, bps++) { + +		if (!(n % (cnt / 15))) { +			printf ("."); +		} + +		/*  write the flash command for flash memory */ +		*bp = 0xA0; + +		/*  Write the data */ +		*bp = *bps; + +		/*  Check if the write is done */ +		for (i = 0; i < 0xff; i++); +		error = _flash_poll (info->flash_id, (u32) bp, *bps, +				     CFG_FLASH_WRITE_TOUT); +		if (error) { +			return error; +		} +	} + +	/*  Reset the Flash Mode to read */ +	FLASH_CMD_BYPASS_RESET (info->flash_id, info->start[0]); + +	printf (" "); + +	return error; +} |