Skip to content
Snippets Groups Projects
flash.c 13.4 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * (C) Copyright 2001-2005
     * Wolfgang Denk, DENX Software Engineering, wd@denx.de.
     *
     * Flash Routines for Intel devices
     *
     *--------------------------------------------------------------------
    
     * SPDX-License-Identifier:	GPL-2.0+
    
     */
    
    #include <common.h>
    #include <mpc8xx.h>
    #include "cpu87.h"
    
    
    flash_info_t flash_info[CONFIG_SYS_MAX_FLASH_BANKS];
    
    
    /*-----------------------------------------------------------------------
     */
    ulong flash_int_get_size (volatile unsigned long *baseaddr,
    					  flash_info_t * info)
    {
    	short i;
    	unsigned long flashtest_h, flashtest_l;
    
    	info->sector_count = info->size = 0;
    	info->flash_id = FLASH_UNKNOWN;
    
    	/* Write identify command sequence and test FLASH answer
    	 */
    	baseaddr[0] = 0x00900090;
    	baseaddr[1] = 0x00900090;
    
    	flashtest_h = baseaddr[0];	/* manufacturer ID	*/
    	flashtest_l = baseaddr[1];
    
    	if (flashtest_h != INTEL_MANUFACT || flashtest_l != INTEL_MANUFACT)
    		return (0);		/* no or unknown flash	*/
    
    	flashtest_h = baseaddr[2];	/* device ID	        */
    	flashtest_l = baseaddr[3];
    
    	if (flashtest_h != flashtest_l)
    		return (0);
    
    	switch (flashtest_h) {
    	case INTEL_ID_28F160C3B:
    		info->flash_id = FLASH_28F160C3B;
    		info->sector_count = 39;
    		info->size = 0x00800000;	/* 4 * 2 MB = 8 MB	*/
    		break;
    	case INTEL_ID_28F160F3B:
    		info->flash_id = FLASH_28F160F3B;
    		info->sector_count = 39;
    		info->size = 0x00800000;	/* 4 * 2 MB = 8 MB      */
    		break;
    	case INTEL_ID_28F640C3B:
    		info->flash_id = FLASH_28F640C3B;
    		info->sector_count = 135;
    		info->size = 0x02000000;	/* 16 * 2 MB = 32 MB	*/
    		break;
    	default:
    		return (0);			/* no or unknown flash	*/
    	}
    
    	info->flash_id |= INTEL_MANUFACT << 16; /* set manufacturer offset */
    
    	if (info->flash_id & FLASH_BTYPE) {
    		volatile unsigned long *tmp = baseaddr;
    
    		/* set up sector start adress table (bottom sector type)
    		 * AND unlock the sectors (if our chip is 160C3)
    		 */
    		for (i = 0; i < info->sector_count; i++) {
    			if (((info->flash_id & FLASH_TYPEMASK) == FLASH_28F160C3B) ||
    			    ((info->flash_id & FLASH_TYPEMASK) == FLASH_28F640C3B)) {
    				tmp[0] = 0x00600060;
    				tmp[1] = 0x00600060;
    				tmp[0] = 0x00D000D0;
    				tmp[1] = 0x00D000D0;
    			}
    			info->start[i] = (uint) tmp;
    			tmp += i < 8 ? 0x2000 : 0x10000; /* pointer arith       */
    		}
    	}
    
    	memset (info->protect, 0, info->sector_count);
    
    	baseaddr[0] = 0x00FF00FF;
    	baseaddr[1] = 0x00FF00FF;
    
    	return (info->size);
    }
    
    static ulong flash_amd_get_size (vu_char *addr, flash_info_t *info)
    {
    	short i;
    	uchar vendor, devid;
    	ulong base = (ulong)addr;
    
    	/* Write auto select command: read Manufacturer ID */
    	addr[0x0555] = 0xAA;
    	addr[0x02AA] = 0x55;
    	addr[0x0555] = 0x90;
    
    	udelay(1000);
    
    	vendor = addr[0];
    	devid = addr[1] & 0xff;
    
    	/* only support AMD */
    	if (vendor != 0x01) {
    		return 0;
    	}
    
    	vendor &= 0xf;
    	devid &= 0xff;
    
    	if (devid == AMD_ID_F040B) {
    		info->flash_id     = vendor << 16 | devid;
    		info->sector_count = 8;
    		info->size         = info->sector_count * 0x10000;
    	}
    	else if (devid == AMD_ID_F080B) {
    		info->flash_id     = vendor << 16 | devid;
    		info->sector_count = 16;
    		info->size         = 4 * info->sector_count * 0x10000;
    	}
    	else if (devid == AMD_ID_F016D) {
    		info->flash_id     = vendor << 16 | devid;
    		info->sector_count = 32;
    		info->size         = 4 * info->sector_count * 0x10000;
    	}
    	else {
    		printf ("## Unknown Flash Type: %02x\n", devid);
    		return 0;
    	}
    
    	/* check for protected sectors */
    	for (i = 0; i < info->sector_count; i++) {
    		/* sector base address */
    		info->start[i] = base + i * (info->size / info->sector_count);
    		/* read sector protection at sector address, (A7 .. A0) = 0x02 */
    		/* D0 = 1 if protected */
    		addr = (volatile unsigned char *)(info->start[i]);
    		info->protect[i] = addr[2] & 1;
    	}
    
    	/*
    	 * Prevent writes to uninitialized FLASH.
    	 */
    	if (info->flash_id != FLASH_UNKNOWN) {
    		addr = (vu_char *)info->start[0];
    		addr[0] = 0xF0; /* reset bank */
    	}
    
    	return (info->size);
    }
    
    
    /*-----------------------------------------------------------------------
     */
    unsigned long flash_init (void)
    {
    	unsigned long size_b0 = 0;
    	unsigned long size_b1 = 0;
    	int i;
    
    	/* Init: no FLASHes known
    	 */
    
    	for (i = 0; i < CONFIG_SYS_MAX_FLASH_BANKS; ++i) {
    
    		flash_info[i].flash_id = FLASH_UNKNOWN;
    	}
    
    	/* Disable flash protection */
    	CPU86_BCR |= (CPU86_BCR_FWPT | CPU86_BCR_FWRE);
    
    	/* Static FLASH Bank configuration here (only one bank) */
    
    
    	size_b0 = flash_int_get_size ((ulong *) CONFIG_SYS_FLASH_BASE, &flash_info[0]);
    	size_b1 = flash_amd_get_size ((uchar *) CONFIG_SYS_BOOTROM_BASE, &flash_info[1]);
    
    
    	if (size_b0 > 0 || size_b1 > 0) {
    
    		printf("(");
    
    		if (size_b0 > 0) {
    			puts ("Bank#1 - ");
    			print_size (size_b0, (size_b1 > 0) ? ", " : ") ");
    		}
    
    		if (size_b1 > 0) {
    			puts ("Bank#2 - ");
    			print_size (size_b1, ") ");
    		}
    	}
    	else {
    		printf ("## No FLASH found.\n");
    		return 0;
    	}
    	/* protect monitor and environment sectors
    	 */
    
    
    #if CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_BOOTROM_BASE
    
    	if (size_b1) {
    
    		/* If U-Boot is booted from ROM the CONFIG_SYS_MONITOR_BASE > CONFIG_SYS_FLASH_BASE
    
    		 * but we shouldn't protect it.
    		 */
    
    		flash_protect  (FLAG_PROTECT_SET,
    
    				CONFIG_SYS_MONITOR_BASE,
    				CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1, &flash_info[1]
    
    #if CONFIG_SYS_MONITOR_BASE >= CONFIG_SYS_FLASH_BASE
    
    	flash_protect (FLAG_PROTECT_SET,
    
    		       CONFIG_SYS_MONITOR_BASE,
    		       CONFIG_SYS_MONITOR_BASE + monitor_flash_len - 1, &flash_info[0]
    
    #if defined(CONFIG_ENV_IS_IN_FLASH) && defined(CONFIG_ENV_ADDR)
    # ifndef  CONFIG_ENV_SIZE
    #  define CONFIG_ENV_SIZE	CONFIG_ENV_SECT_SIZE
    
    # if CONFIG_ENV_ADDR >= CONFIG_SYS_BOOTROM_BASE
    
    	if (size_b1) {
    		flash_protect (FLAG_PROTECT_SET,
    
    				CONFIG_ENV_ADDR,
    				CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1, &flash_info[1]);
    
    	}
    # else
    	flash_protect (FLAG_PROTECT_SET,
    
    		       CONFIG_ENV_ADDR,
    		       CONFIG_ENV_ADDR + CONFIG_ENV_SIZE - 1, &flash_info[0]);
    
    # endif
    #endif
    
    	return (size_b0 + size_b1);
    }
    
    /*-----------------------------------------------------------------------
     */
    void flash_print_info (flash_info_t * info)
    {
    	int i;
    
    	if (info->flash_id == FLASH_UNKNOWN) {
    		printf ("missing or unknown FLASH type\n");
    		return;
    	}
    
    	switch ((info->flash_id >> 16) & 0xff) {
    	case 0x89:
    		printf ("INTEL ");
    		break;
    	case 0x1:
    		printf ("AMD ");
    		break;
    	default:
    		printf ("Unknown Vendor ");
    		break;
    	}
    
    	switch (info->flash_id & FLASH_TYPEMASK) {
    	case FLASH_28F160C3B:
    		printf ("28F160C3B (16 Mbit, bottom sector)\n");
    		break;
    	case FLASH_28F160F3B:
    		printf ("28F160F3B (16 Mbit, bottom sector)\n");
    		break;
    	case FLASH_28F640C3B:
    		printf ("28F640C3B (64 M, bottom sector)\n");
    		break;
    	case AMD_ID_F040B:
    		printf ("AM29F040B (4 Mbit)\n");
    		break;
    	default:
    		printf ("Unknown Chip Type\n");
    		break;
    	}
    
    	if (info->size < 0x100000)
    		printf ("  Size: %ld KB in %d Sectors\n",
    				info->size >> 10, info->sector_count);
    	else
    		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");
    }
    
    /*-----------------------------------------------------------------------
     */
    int flash_erase (flash_info_t * info, int s_first, int s_last)
    {
    	vu_char *addr = (vu_char *)(info->start[0]);
    	int flag, prot, sect, l_sect;
    	ulong start, now, last;
    
    	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;
    	}
    
    	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");
    	}
    
    	/* Check the type of erased flash
    	 */
    	if (info->flash_id >> 16 == 0x1) {
    		/* Erase AMD flash
    		 */
    		l_sect = -1;
    
    		/* Disable interrupts which might cause a timeout here */
    		flag = disable_interrupts();
    
    		addr[0x0555] = 0xAA;
    		addr[0x02AA] = 0x55;
    		addr[0x0555] = 0x80;
    		addr[0x0555] = 0xAA;
    		addr[0x02AA] = 0x55;
    
    		/* wait at least 80us - let's wait 1 ms */
    		udelay (1000);
    
    		/* Start erase on unprotected sectors */
    		for (sect = s_first; sect<=s_last; sect++) {
    			if (info->protect[sect] == 0) { /* not protected */
    				addr = (vu_char *)(info->start[sect]);
    				addr[0] = 0x30;
    				l_sect = sect;
    			}
    		}
    
    		/* re-enable interrupts if necessary */
    		if (flag)
    			enable_interrupts();
    
    		/* wait at least 80us - let's wait 1 ms */
    		udelay (1000);
    
    		/*
    		 * We wait for the last triggered sector
    		 */
    		if (l_sect < 0)
    			goto AMD_DONE;
    
    		start = get_timer (0);
    		last  = start;
    		addr = (vu_char *)(info->start[l_sect]);
    		while ((addr[0] & 0x80) != 0x80) {
    
    			if ((now = get_timer(start)) > CONFIG_SYS_FLASH_ERASE_TOUT) {
    
    				printf ("Timeout\n");
    				return 1;
    			}
    			/* show that we're waiting */
    			if ((now - last) > 1000) {      /* every second */
    				serial_putc ('.');
    				last = now;
    			}
    		}
    
    AMD_DONE:
    		/* reset to read mode */
    		addr = (volatile unsigned char *)info->start[0];
    		addr[0] = 0xF0;     /* reset bank */
    
    	} else {
    		/* Erase Intel flash
    		 */
    
    		/* Start erase on unprotected sectors
    		 */
    		for (sect = s_first; sect <= s_last; sect++) {
    			volatile ulong *addr =
    				(volatile unsigned long *) info->start[sect];
    
    			start = get_timer (0);
    			last = start;
    			if (info->protect[sect] == 0) {
    			/* Disable interrupts which might cause a timeout here
    			 */
    				flag = disable_interrupts ();
    
    				/* Erase the block
    				 */
    				addr[0] = 0x00200020;
    				addr[1] = 0x00200020;
    				addr[0] = 0x00D000D0;
    				addr[1] = 0x00D000D0;
    
    				/* re-enable interrupts if necessary
    				 */
    				if (flag)
    					enable_interrupts ();
    
    				/* wait at least 80us - let's wait 1 ms
    				 */
    				udelay (1000);
    
    				last = start;
    				while ((addr[0] & 0x00800080) != 0x00800080 ||
    				   (addr[1] & 0x00800080) != 0x00800080) {
    
    					if ((now = get_timer (start)) > CONFIG_SYS_FLASH_ERASE_TOUT) {
    
    						printf ("Timeout (erase suspended!)\n");
    						/* Suspend erase
    						 */
    						addr[0] = 0x00B000B0;
    						addr[1] = 0x00B000B0;
    						goto DONE;
    					}
    					/* show that we're waiting
    					 */
    					if ((now - last) > 1000) {	/* every second */
    						serial_putc ('.');
    						last = now;
    					}
    				}
    				if (addr[0] & 0x00220022 || addr[1] & 0x00220022) {
    					printf ("*** ERROR: erase failed!\n");
    					goto DONE;
    				}
    			}
    			/* Clear status register and reset to read mode
    			 */
    			addr[0] = 0x00500050;
    			addr[1] = 0x00500050;
    			addr[0] = 0x00FF00FF;
    			addr[1] = 0x00FF00FF;
    		}
    	}
    
    	printf (" done\n");
    
    DONE:
    	return 0;
    }
    
    static int write_word (flash_info_t *, volatile unsigned long *, ulong);
    static int write_byte (flash_info_t *info, ulong dest, uchar data);
    
    /*-----------------------------------------------------------------------
     * 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)
    {
    	ulong v;
    	int i, l, rc, cc = cnt, res = 0;
    
    	if (info->flash_id >> 16 == 0x1) {
    
    		/* Write to AMD 8-bit flash
    		 */
    		while (cnt > 0) {
    			if ((rc = write_byte(info, addr, *src)) != 0) {
    				return (rc);
    			}
    			addr++;
    			src++;
    			cnt--;
    		}
    
    		return (0);
    	} else {
    
    		/* Write to Intel 64-bit flash
    		 */
    		for (v=0; cc > 0; addr += 4, cc -= 4 - l) {
    			l = (addr & 3);
    			addr &= ~3;
    
    			for (i = 0; i < 4; i++) {
    				v = (v << 8) + (i < l || i - l >= cc ?
    					*((unsigned char *) addr + i) : *src++);
    			}
    
    			if ((res = write_word (info, (volatile unsigned long *) addr, v)) != 0)
    				break;
    		}
    	}
    
    	return (res);
    }
    
    /*-----------------------------------------------------------------------
     * Write a word to Flash, returns:
     * 0 - OK
     * 1 - write timeout
     * 2 - Flash not erased
     */
    static int write_word (flash_info_t * info, volatile unsigned long *addr,
    					   ulong data)
    {
    	int flag, res = 0;
    	ulong start;
    
    	/* Check if Flash is (sufficiently) erased
    	 */
    	if ((*addr & data) != data)
    		return (2);
    
    	/* Disable interrupts which might cause a timeout here
    	 */
    	flag = disable_interrupts ();
    
    	*addr = 0x00400040;
    	*addr = data;
    
    	/* re-enable interrupts if necessary
    	 */
    	if (flag)
    		enable_interrupts ();
    
    	start = get_timer (0);
    	while ((*addr & 0x00800080) != 0x00800080) {
    
    		if (get_timer (start) > CONFIG_SYS_FLASH_WRITE_TOUT) {
    
    			/* Suspend program
    			 */
    			*addr = 0x00B000B0;
    			res = 1;
    			goto OUT;
    		}
    	}
    
    	if (*addr & 0x00220022) {
    		printf ("*** ERROR: program failed!\n");
    		res = 1;
    	}
    
    OUT:
    	/* Clear status register and reset to read mode
    	 */
    	*addr = 0x00500050;
    	*addr = 0x00FF00FF;
    
    	return (res);
    }
    
    /*-----------------------------------------------------------------------
     * Write a byte to Flash, returns:
     * 0 - OK
     * 1 - write timeout
     * 2 - Flash not erased
     */
    static int write_byte (flash_info_t *info, ulong dest, uchar data)
    {
    	vu_char *addr = (vu_char *)(info->start[0]);
    	ulong start;
    	int flag;
    
    	/* Check if Flash is (sufficiently) erased */
    	if ((*((vu_char *)dest) & data) != data) {
    		return (2);
    	}
    	/* Disable interrupts which might cause a timeout here */
    	flag = disable_interrupts();
    
    	addr[0x0555] = 0xAA;
    	addr[0x02AA] = 0x55;
    	addr[0x0555] = 0xA0;
    
    	*((vu_char *)dest) = data;
    
    	/* re-enable interrupts if necessary */
    	if (flag)
    		enable_interrupts();
    
    	/* data polling for D7 */
    	start = get_timer (0);
    	while ((*((vu_char *)dest) & 0x80) != (data & 0x80)) {
    
    		if (get_timer(start) > CONFIG_SYS_FLASH_WRITE_TOUT) {
    
    			return (1);
    		}
    	}
    	return (0);
    }
    
    /*-----------------------------------------------------------------------
     */