Skip to content
Snippets Groups Projects
flash.c 8.13 KiB
Newer Older
  • Learn to ignore specific revisions
  • Wolfgang Denk's avatar
    Wolfgang Denk committed
    /*
     * (C) Copyright 2002
     * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net
     *
     * (C) Copyright 2002
     * Sysgo Real-Time Solutions, GmbH <www.elinos.com>
     * Marius Groeger <mgroeger@sysgo.de>
     *
    
     * (C) Copyright 2002
     * Robert Schwebel, Pengutronix, <r.schwebel@pengutronix.de> 
     *
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     * 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/arch/pxa-regs.h>
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    #define FLASH_BANK_SIZE 0x02000000
    #define MAIN_SECT_SIZE  0x40000         /* 2x16 = 256k per sector */
    
    flash_info_t    flash_info[CFG_MAX_FLASH_BANKS];
    
    
    
    /**
     * flash_init: - initialize data structures for flash chips
     *
     * @return: size of the flash
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     */
    
    ulong flash_init(void)
    {
    
    	int i, j;
    	ulong size = 0;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	for (i = 0; i < CFG_MAX_FLASH_BANKS; i++) {
    
    		ulong flashbase = 0;
    		flash_info[i].flash_id =
    	  		(INTEL_MANUFACT & FLASH_VENDMASK) |
    	  		(INTEL_ID_28F128J3 & FLASH_TYPEMASK);
    		flash_info[i].size = FLASH_BANK_SIZE;
    		flash_info[i].sector_count = CFG_MAX_FLASH_SECT;
    		memset(flash_info[i].protect, 0, CFG_MAX_FLASH_SECT);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    			case 0:
    				flashbase = PHYS_FLASH_1;
    				break;
    			default:
    				panic("configured to many flash banks!\n");
    				break;
    		}
    
    		for (j = 0; j < flash_info[i].sector_count; j++) {
    
    			flash_info[i].start[j] = flashbase + j*MAIN_SECT_SIZE;
    		}
    		size += flash_info[i].size;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    
    	/* Protect monitor and environment sectors */
    
    	flash_protect(FLAG_PROTECT_SET,
    			CFG_FLASH_BASE,
    			CFG_FLASH_BASE + _armboot_end_data - _armboot_start,
    			&flash_info[0]);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	flash_protect(FLAG_PROTECT_SET,
    			CFG_ENV_ADDR,
    			CFG_ENV_ADDR + CFG_ENV_SIZE - 1,
    			&flash_info[0]);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }
    
    
    
    /**
     * flash_print_info: - print information about the flash situation
     *
     * @param info: 
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    void flash_print_info  (flash_info_t *info)
    {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	for (j=0; j<CFG_MAX_FLASH_BANKS; j++) {
    
    		switch (info->flash_id & FLASH_VENDMASK) {
    
    
    			case (INTEL_MANUFACT & FLASH_VENDMASK):
    				printf("Intel: ");
    				break;
    			default:
    				printf("Unknown Vendor ");
    				break;
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		switch (info->flash_id & FLASH_TYPEMASK) {
    
    
    			case (INTEL_ID_28F128J3 & FLASH_TYPEMASK):
    				printf("28F128J3 (128Mbit)\n");
    				break;
    			default:
    				printf("Unknown Chip Type\n");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		printf("  Size: %ld MB in %d Sectors\n", 
    			info->size >> 20, info->sector_count);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		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");
    		info++;
    	}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }
    
    
    
    /**
     * flash_erase: - erase flash sectors
     *
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     */
    
    
    int flash_erase(flash_info_t *info, int s_first, int s_last)
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    {
    
    	int flag, prot, sect;
    	int rc = ERR_OK;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	if (info->flash_id == FLASH_UNKNOWN)
    		return ERR_UNKNOWN_FLASH_TYPE;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	if ((s_first < 0) || (s_first > s_last)) {
    		return ERR_INVAL;
    	}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	if ((info->flash_id & FLASH_VENDMASK) != (INTEL_MANUFACT & FLASH_VENDMASK))
    
    		return ERR_UNKNOWN_FLASH_VENDOR;
    	
    	prot = 0;
    	for (sect=s_first; sect<=s_last; ++sect) {
    
    		if (info->protect[sect]) prot++;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    
    	if (prot) return ERR_PROTECTED;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	/*
    	 * Disable interrupts which might cause a timeout
    	 * here. Remember that our exception vectors are
    	 * at address 0 in the flash, and we don't want a
    	 * (ticker) exception to happen while the flash
    	 * chip is in programming mode.
    	 */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	flag = disable_interrupts();
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	/* Start erase on unprotected sectors */
    	for (sect = s_first; sect<=s_last && !ctrlc(); sect++) {
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		printf("Erasing sector %2d ... ", sect);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		/* arm simple, non interrupt dependent timer */
    		reset_timer_masked();
    
    		if (info->protect[sect] == 0) {	/* not protected */
    
    			u32 * volatile addr = (u32 * volatile)(info->start[sect]);
    
    			/* erase sector:                                    */
    			/* The strata flashs are aligned side by side on    */
    			/* the data bus, so we have to write the commands   */
    			/* to both chips here:                              */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    			*addr = 0x00200020;	/* erase setup */
    			*addr = 0x00D000D0;	/* erase confirm */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    			while ((*addr & 0x00800080) != 0x00800080) {
    
    				if (get_timer_masked() > CFG_FLASH_ERASE_TOUT) {
    
    					*addr = 0x00B000B0; /* suspend erase*/
    					*addr = 0x00FF00FF; /* read mode    */
    
    					rc = ERR_TIMOUT;
    					goto outahere;
    				}
    			}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    			*addr = 0x00500050; /* clear status register cmd.   */
    			*addr = 0x00FF00FF; /* resest to read mode          */
    
    
    
    	if (ctrlc()) printf("User Interrupt!\n");
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	/* allow flash to settle - wait 10 ms */
    	udelay_masked(10000);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	if (flag) enable_interrupts();
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }
    
    
    
    /**
     * write_word: - copy memory to flash
     * 
     * @param info:
     * @param dest:
     * @param data: 
     * @return:
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     */
    
    static int write_word (flash_info_t *info, ulong dest, ushort data)
    {
    
    	u32 * volatile addr = (u32 * volatile)dest, val;
    	int rc = ERR_OK;
    	int flag;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	/* Check if Flash is (sufficiently) erased */
    	if ((*addr & data) != data) return ERR_NOT_ERASED;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	/*
    	 * Disable interrupts which might cause a timeout
    	 * here. Remember that our exception vectors are
    	 * at address 0 in the flash, and we don't want a
    	 * (ticker) exception to happen while the flash
    	 * chip is in programming mode.
    	 */
    	flag = disable_interrupts();
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	/* clear status register command */
    	*addr = 0x50;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	/* program set-up command */
    	*addr = 0x40;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	/* latch address/data */
    	*addr = data;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	/* arm simple, non interrupt dependent timer */
    	reset_timer_masked();
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	/* wait while polling the status register */
    
    	while(((val = *addr) & 0x80) != 0x80) {
    
    		if (get_timer_masked() > CFG_FLASH_WRITE_TOUT) {
    			rc = ERR_TIMOUT;
    
    			*addr = 0xB0; /* suspend program command */
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    
    	if(val & 0x1A) {	/* check for error */
    		printf("\nFlash write error %02x at address %08lx\n",
    			(int)val, (unsigned long)dest);
    		if(val & (1<<3)) {
    			printf("Voltage range error.\n");
    			rc = ERR_PROG_ERROR;
    			goto outahere;
    		}
    		if(val & (1<<1)) {
    			printf("Device protect error.\n");
    			rc = ERR_PROTECTED;
    			goto outahere;
    		}
    		if(val & (1<<4)) {
    			printf("Programming error.\n");
    			rc = ERR_PROG_ERROR;
    			goto outahere;
    		}
    		rc = ERR_PROG_ERROR;
    		goto outahere;
    	}
    
    	outahere:
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	*addr = 0xFF; /* read array command */
    	if (flag) enable_interrupts();
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }
    
    
    
    /**
     * write_buf: - Copy memory to flash.
     * 
     * @param info:	 
     * @param src:	source of copy transaction
     * @param addr:	where to copy to
     * @param cnt: 	number of bytes to copy
     *
     * @return	error code
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
     */
    
    int write_buff (flash_info_t *info, uchar *src, ulong addr, ulong cnt)
    {
    
    	ulong cp, wp;
    	ushort data;
    	int l;
    	int i, rc;
    
    	wp = (addr & ~1);	/* get lower word aligned address */
    
    	/*
    	 * handle unaligned start bytes
    	 */
    	if ((l = addr - wp) != 0) {
    		data = 0;
    		for (i=0, cp=wp; i<l; ++i, ++cp) {
    			data = (data >> 8) | (*(uchar *)cp << 8);
    		}
    		for (; i<2 && cnt>0; ++i) {
    			data = (data >> 8) | (*src++ << 8);
    			--cnt;
    			++cp;
    		}
    		for (; cnt==0 && i<2; ++i, ++cp) {
    			data = (data >> 8) | (*(uchar *)cp << 8);
    		}
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    		if ((rc = write_word(info, wp, data)) != 0) {
    			return (rc);
    		}
    		wp += 2;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    
    	/*
    	 * handle word aligned part
    	 */
    	while (cnt >= 2) {
    		/* data = *((vushort*)src); */
    		data = *((ushort*)src);
    		if ((rc = write_word(info, wp, data)) != 0) {
    			return (rc);
    		}
    		src += 2;
    		wp  += 2;
    		cnt -= 2;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    	}
    
    
    	if (cnt == 0) return ERR_OK;
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    
    
    	/*
    	 * handle unaligned tail bytes
    	 */
    	data = 0;
    	for (i=0, cp=wp; i<2 && cnt>0; ++i, ++cp) {
    		data = (data >> 8) | (*src++ << 8);
    		--cnt;
    	}
    	for (; i<2; ++i, ++cp) {
    		data = (data >> 8) | (*(uchar *)cp << 8);
    	}
    
    	return write_word(info, wp, data);
    
    Wolfgang Denk's avatar
    Wolfgang Denk committed
    }