452 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			452 lines
		
	
	
		
			9.3 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  * (C) Copyright 2000-2003
 | |
|  * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
 | |
|  *
 | |
|  * Copyright (C) 2004-2007 Freescale Semiconductor, Inc.
 | |
|  * TsiChung Liew (Tsi-Chung.Liew@freescale.com)
 | |
|  *
 | |
|  * SPDX-License-Identifier:	GPL-2.0+
 | |
|  */
 | |
| 
 | |
| #include <common.h>
 | |
| 
 | |
| #include <asm/immap.h>
 | |
| 
 | |
| #ifndef CONFIG_SYS_FLASH_CFI
 | |
| typedef unsigned short FLASH_PORT_WIDTH;
 | |
| typedef volatile unsigned short FLASH_PORT_WIDTHV;
 | |
| 
 | |
| #define FPW             FLASH_PORT_WIDTH
 | |
| #define FPWV            FLASH_PORT_WIDTHV
 | |
| 
 | |
| #define FLASH_CYCLE1    0x5555
 | |
| #define FLASH_CYCLE2    0x2aaa
 | |
| 
 | |
| #define SYNC			__asm__("nop")
 | |
| 
 | |
| /*-----------------------------------------------------------------------
 | |
|  * Functions
 | |
|  */
 | |
| 
 | |
| ulong flash_get_size(FPWV * addr, flash_info_t * info);
 | |
| int flash_get_offsets(ulong base, flash_info_t * info);
 | |
| int write_word(flash_info_t * info, FPWV * dest, u16 data);
 | |
| void inline spin_wheel(void);
 | |
| 
 | |
| flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS];
 | |
| 
 | |
| ulong flash_init(void)
 | |
| {
 | |
| 	ulong size = 0;
 | |
| 	ulong fbase = 0;
 | |
| 
 | |
| 	fbase = (ulong) CONFIG_SYS_FLASH_BASE;
 | |
| 	flash_get_size((FPWV *) fbase, &flash_info[0]);
 | |
| 	flash_get_offsets((ulong) fbase, &flash_info[0]);
 | |
| 	fbase += flash_info[0].size;
 | |
| 	size += flash_info[0].size;
 | |
| 
 | |
| 	/* Protect monitor and environment sectors */
 | |
| 	flash_protect(FLAG_PROTECT_SET,
 | |
| 		      CONFIG_SYS_MONITOR_BASE,
 | |
| 		      CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1, &flash_info[0]);
 | |
| 
 | |
| 	return size;
 | |
| }
 | |
| 
 | |
| int flash_get_offsets(ulong base, flash_info_t * info)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST) {
 | |
| 
 | |
| 		info->start[0] = base;
 | |
| 		info->protect[0] = 0;
 | |
| 		for (i = 1; i < CONFIG_SYS_SST_SECT; i++) {
 | |
| 			info->start[i] = info->start[i - 1]
 | |
| 						+ CONFIG_SYS_SST_SECTSZ;
 | |
| 			info->protect[i] = 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return ERR_OK;
 | |
| }
 | |
| 
 | |
| void flash_print_info(flash_info_t * info)
 | |
| {
 | |
| 	int i;
 | |
| 
 | |
| 	switch (info->flash_id & FLASH_VENDMASK) {
 | |
| 	case FLASH_MAN_SST:
 | |
| 		printf("SST ");
 | |
| 		break;
 | |
| 	default:
 | |
| 		printf("Unknown Vendor ");
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	switch (info->flash_id & FLASH_TYPEMASK) {
 | |
| 	case FLASH_SST6401B:
 | |
| 		printf("SST39VF6401B\n");
 | |
| 		break;
 | |
| 	default:
 | |
| 		printf("Unknown Chip Type\n");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (info->size > 0x100000) {
 | |
| 		int remainder;
 | |
| 
 | |
| 		printf("  Size: %ld", info->size >> 20);
 | |
| 
 | |
| 		remainder = (info->size % 0x100000);
 | |
| 		if (remainder) {
 | |
| 			remainder >>= 10;
 | |
| 			remainder = (int)((float)
 | |
| 					  (((float)remainder / (float)1024) *
 | |
| 					   10000));
 | |
| 			printf(".%d ", remainder);
 | |
| 		}
 | |
| 
 | |
| 		printf("MB in %d Sectors\n", info->sector_count);
 | |
| 	} else
 | |
| 		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 % 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)
 | |
| {
 | |
| 	u16 value;
 | |
| 
 | |
| 	addr[FLASH_CYCLE1] = (FPWV) 0x00AA00AA;	/* for Atmel, Intel ignores this */
 | |
| 	addr[FLASH_CYCLE2] = (FPWV) 0x00550055;	/* for Atmel, Intel ignores this */
 | |
| 	addr[FLASH_CYCLE1] = (FPWV) 0x00900090;	/* selects Intel or Atmel */
 | |
| 
 | |
| 	switch (addr[0] & 0xffff) {
 | |
| 	case (u8) SST_MANUFACT:
 | |
| 		info->flash_id = FLASH_MAN_SST;
 | |
| 		value = addr[1];
 | |
| 		break;
 | |
| 	default:
 | |
| 		printf("Unknown Flash\n");
 | |
| 		info->flash_id = FLASH_UNKNOWN;
 | |
| 		info->sector_count = 0;
 | |
| 		info->size = 0;
 | |
| 
 | |
| 		*addr = (FPW) 0x00F000F0;
 | |
| 		return (0);	/* no or unknown flash  */
 | |
| 	}
 | |
| 
 | |
| 	switch (value) {
 | |
| 	case (u16) SST_ID_xF6401B:
 | |
| 		info->flash_id += FLASH_SST6401B;
 | |
| 		break;
 | |
| 	default:
 | |
| 		info->flash_id = FLASH_UNKNOWN;
 | |
| 		break;
 | |
| 	}
 | |
| 
 | |
| 	info->sector_count = 0;
 | |
| 	info->size = 0;
 | |
| 	info->sector_count = CONFIG_SYS_SST_SECT;
 | |
| 	info->size = CONFIG_SYS_SST_SECT * CONFIG_SYS_SST_SECTSZ;
 | |
| 
 | |
| 	/* reset ID mode */
 | |
| 	*addr = (FPWV) 0x00F000F0;
 | |
| 
 | |
| 	if (info->sector_count > CONFIG_SYS_MAX_FLASH_SECT) {
 | |
| 		printf("** ERROR: sector count %d > max (%d) **\n",
 | |
| 		       info->sector_count, CONFIG_SYS_MAX_FLASH_SECT);
 | |
| 		info->sector_count = CONFIG_SYS_MAX_FLASH_SECT;
 | |
| 	}
 | |
| 
 | |
| 	return (info->size);
 | |
| }
 | |
| 
 | |
| int flash_erase(flash_info_t * info, int s_first, int s_last)
 | |
| {
 | |
| 	FPWV *addr;
 | |
| 	int flag, prot, sect, count;
 | |
| 	ulong type, start;
 | |
| 	int rcode = 0, flashtype = 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;
 | |
| 	}
 | |
| 
 | |
| 	type = (info->flash_id & FLASH_VENDMASK);
 | |
| 
 | |
| 	switch (type) {
 | |
| 	case FLASH_MAN_SST:
 | |
| 		flashtype = 1;
 | |
| 		break;
 | |
| 	default:
 | |
| 		type = (info->flash_id & FLASH_VENDMASK);
 | |
| 		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!\n",
 | |
| 		       prot);
 | |
| 	else
 | |
| 		printf("\n");
 | |
| 
 | |
| 	flag = disable_interrupts();
 | |
| 
 | |
| 	start = get_timer(0);
 | |
| 
 | |
| 	if ((s_last - s_first) == (CONFIG_SYS_SST_SECT - 1)) {
 | |
| 		if (prot == 0) {
 | |
| 			addr = (FPWV *) info->start[0];
 | |
| 
 | |
| 			addr[FLASH_CYCLE1] = 0x00AA;	/* unlock */
 | |
| 			addr[FLASH_CYCLE2] = 0x0055;	/* unlock */
 | |
| 			addr[FLASH_CYCLE1] = 0x0080;	/* erase mode */
 | |
| 			addr[FLASH_CYCLE1] = 0x00AA;	/* unlock */
 | |
| 			addr[FLASH_CYCLE2] = 0x0055;	/* unlock */
 | |
| 			*addr = 0x0030;	/* erase chip */
 | |
| 
 | |
| 			count = 0;
 | |
| 			start = get_timer(0);
 | |
| 
 | |
| 			while ((*addr & 0x0080) != 0x0080) {
 | |
| 				if (count++ > 0x10000) {
 | |
| 					spin_wheel();
 | |
| 					count = 0;
 | |
| 				}
 | |
| 
 | |
| 				if (get_timer(start) > CONFIG_SYS_FLASH_ERASE_TOUT) {
 | |
| 					printf("Timeout\n");
 | |
| 					*addr = 0x00F0;	/* reset to read mode */
 | |
| 
 | |
| 					return 1;
 | |
| 				}
 | |
| 			}
 | |
| 
 | |
| 			*addr = 0x00F0;	/* reset to read mode */
 | |
| 
 | |
| 			printf("\b. done\n");
 | |
| 
 | |
| 			if (flag)
 | |
| 				enable_interrupts();
 | |
| 
 | |
| 			return 0;
 | |
| 		} else if (prot == CONFIG_SYS_SST_SECT) {
 | |
| 			return 1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Start erase on unprotected sectors */
 | |
| 	for (sect = s_first; sect <= s_last; sect++) {
 | |
| 		if (info->protect[sect] == 0) {	/* not protected */
 | |
| 
 | |
| 			addr = (FPWV *) (info->start[sect]);
 | |
| 
 | |
| 			printf(".");
 | |
| 
 | |
| 			/* arm simple, non interrupt dependent timer */
 | |
| 			start = get_timer(0);
 | |
| 
 | |
| 			switch (flashtype) {
 | |
| 			case 1:
 | |
| 				{
 | |
| 					FPWV *base;	/* first address in bank */
 | |
| 
 | |
| 					flag = disable_interrupts();
 | |
| 
 | |
| 					base = (FPWV *) (CONFIG_SYS_FLASH_BASE);	/* First sector */
 | |
| 
 | |
| 					base[FLASH_CYCLE1] = 0x00AA;	/* unlock */
 | |
| 					base[FLASH_CYCLE2] = 0x0055;	/* unlock */
 | |
| 					base[FLASH_CYCLE1] = 0x0080;	/* erase mode */
 | |
| 					base[FLASH_CYCLE1] = 0x00AA;	/* unlock */
 | |
| 					base[FLASH_CYCLE2] = 0x0055;	/* unlock */
 | |
| 					*addr = 0x0050;	/* erase sector */
 | |
| 
 | |
| 					if (flag)
 | |
| 						enable_interrupts();
 | |
| 
 | |
| 					while ((*addr & 0x0080) != 0x0080) {
 | |
| 						if (get_timer(start) >
 | |
| 						    CONFIG_SYS_FLASH_ERASE_TOUT) {
 | |
| 							printf("Timeout\n");
 | |
| 							*addr = 0x00F0;	/* reset to read mode */
 | |
| 
 | |
| 							rcode = 1;
 | |
| 							break;
 | |
| 						}
 | |
| 					}
 | |
| 
 | |
| 					*addr = 0x00F0;	/* reset to read mode */
 | |
| 					break;
 | |
| 				}
 | |
| 			}	/* switch (flashtype) */
 | |
| 		}
 | |
| 	}
 | |
| 	printf(" done\n");
 | |
| 
 | |
| 	if (flag)
 | |
| 		enable_interrupts();
 | |
| 
 | |
| 	return rcode;
 | |
| }
 | |
| 
 | |
| int write_buff(flash_info_t * info, uchar * src, ulong addr, ulong cnt)
 | |
| {
 | |
| 	ulong wp, count;
 | |
| 	u16 data;
 | |
| 	int rc;
 | |
| 
 | |
| 	if (info->flash_id == FLASH_UNKNOWN)
 | |
| 		return 4;
 | |
| 
 | |
| 	/* get lower word aligned address */
 | |
| 	wp = addr;
 | |
| 
 | |
| 	/* handle unaligned start bytes */
 | |
| 	if (wp & 1) {
 | |
| 		data = *((FPWV *) wp);
 | |
| 		data = (data << 8) | *src;
 | |
| 
 | |
| 		if ((rc = write_word(info, (FPWV *) wp, data)) != 0)
 | |
| 			return (rc);
 | |
| 
 | |
| 		wp++;
 | |
| 		cnt -= 1;
 | |
| 		src++;
 | |
| 	}
 | |
| 
 | |
| 	while (cnt >= 2) {
 | |
| 		/*
 | |
| 		 * handle word aligned part
 | |
| 		 */
 | |
| 		count = 0;
 | |
| 		data = *((FPWV *) src);
 | |
| 
 | |
| 		if ((rc = write_word(info, (FPWV *) wp, data)) != 0)
 | |
| 			return (rc);
 | |
| 
 | |
| 		wp += 2;
 | |
| 		src += 2;
 | |
| 		cnt -= 2;
 | |
| 
 | |
| 		if (count++ > 0x800) {
 | |
| 			spin_wheel();
 | |
| 			count = 0;
 | |
| 		}
 | |
| 	}
 | |
| 	/* handle word aligned part */
 | |
| 	if (cnt) {
 | |
| 		/* handle word aligned part */
 | |
| 		count = 0;
 | |
| 		data = *((FPWV *) wp);
 | |
| 
 | |
| 		data = (data & 0x00FF) | (*src << 8);
 | |
| 
 | |
| 		if ((rc = write_word(info, (FPWV *) wp, data)) != 0)
 | |
| 			return (rc);
 | |
| 
 | |
| 		wp++;
 | |
| 		src++;
 | |
| 		cnt -= 1;
 | |
| 		if (count++ > 0x800) {
 | |
| 			spin_wheel();
 | |
| 			count = 0;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (cnt == 0)
 | |
| 		return ERR_OK;
 | |
| 
 | |
| 	return ERR_OK;
 | |
| }
 | |
| 
 | |
| /*-----------------------------------------------------------------------
 | |
|  * Write a word to Flash
 | |
|  * A word is 16 bits, whichever the bus width of the flash bank
 | |
|  * (not an individual chip) is.
 | |
|  *
 | |
|  * returns:
 | |
|  * 0 - OK
 | |
|  * 1 - write timeout
 | |
|  * 2 - Flash not erased
 | |
|  */
 | |
| int write_word(flash_info_t * info, FPWV * dest, u16 data)
 | |
| {
 | |
| 	ulong start;
 | |
| 	int flag;
 | |
| 	int res = 0;		/* result, assume success */
 | |
| 	FPWV *base;		/* first address in flash bank */
 | |
| 
 | |
| 	/* Check if Flash is (sufficiently) erased */
 | |
| 	if ((*dest & (u8) data) != (u8) data) {
 | |
| 		return (2);
 | |
| 	}
 | |
| 
 | |
| 	base = (FPWV *) (CONFIG_SYS_FLASH_BASE);
 | |
| 
 | |
| 	/* Disable interrupts which might cause a timeout here */
 | |
| 	flag = disable_interrupts();
 | |
| 
 | |
| 	base[FLASH_CYCLE1] = (u8) 0x00AA00AA;	/* unlock */
 | |
| 	base[FLASH_CYCLE2] = (u8) 0x00550055;	/* unlock */
 | |
| 	base[FLASH_CYCLE1] = (u8) 0x00A000A0;	/* selects program mode */
 | |
| 
 | |
| 	*dest = data;		/* start programming the data */
 | |
| 
 | |
| 	/* re-enable interrupts if necessary */
 | |
| 	if (flag)
 | |
| 		enable_interrupts();
 | |
| 
 | |
| 	start = get_timer(0);
 | |
| 
 | |
| 	/* data polling for D7 */
 | |
| 	while (res == 0
 | |
| 	       && (*dest & (u8) 0x00800080) != (data & (u8) 0x00800080)) {
 | |
| 		if (get_timer(start) > CONFIG_SYS_FLASH_WRITE_TOUT) {
 | |
| 			*dest = (u8) 0x00F000F0;	/* reset bank */
 | |
| 			res = 1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	*dest++ = (u8) 0x00F000F0;	/* reset bank */
 | |
| 
 | |
| 	return (res);
 | |
| }
 | |
| 
 | |
| void inline spin_wheel(void)
 | |
| {
 | |
| 	static int p = 0;
 | |
| 	static char w[] = "\\/-";
 | |
| 
 | |
| 	printf("\010%c", w[p]);
 | |
| 	(++p == 3) ? (p = 0) : 0;
 | |
| }
 | |
| 
 | |
| #endif
 |