⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 flash.c

📁 LUBBOCK板的BLOB
💻 C
字号:
/*-------------------------------------------------------------------------
 * Filename:      flash.c
 * Version:       $Id: flash.c,v 1.11 2002/01/03 16:07:17 erikm Exp $
 * Copyright:     Copyright (C) 1999, Jan-Derk Bakker
 * Author:        Jan-Derk Bakker <J.D.Bakker@its.tudelft.nl>
 * Description:   Flash I/O functions for blob
 * Created at:    Mon Aug 23 20:00:00 1999
 * Modified by:   Erik Mouw <J.A.K.Mouw@its.tudelft.nl>
 * Modified at:   Sat Jan 15 19:16:34 2000
 *-----------------------------------------------------------------------*/
/*
 * flash.c: Flash I/O functions for blob
 *
 * Copyright (C) 2001  Erik Mouw (J.A.K.Mouw@its.tudelft.nl)
 * Copyright (C) 1999  Jan-Derk Bakker (J.D.Bakker@its.tudelft.nl)
 *
 * 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
 *
 */

#ident "$Id: flash.c,v 1.11 2002/01/03 16:07:17 erikm Exp $"

#ifdef HAVE_CONFIG_H
# include <blob/config.h>
#endif

#include <blob/arch.h>
#include <blob/errno.h>
#include <blob/error.h>
#include <blob/led.h>
#include <blob/util.h>
#include <blob/serial.h>
#include <blob/flash.h>

#include <blob/init.h>
#include <blob/command.h>


/* this is enough for a 32MB flash with 128kB blocks */
#define NUM_FLASH_BLOCKS	(128 * 4)


typedef struct {
	u32 start;
	u32 size;
	int lockable;
} flash_block_t;


static flash_block_t flash_blocks[NUM_FLASH_BLOCKS];
static int num_flash_blocks;

flash_descriptor_t *flash_descriptors;
flash_driver_t *flash_driver;




/* dummy function for enable_vpp and disable_vpp */
int flash_dummy_ok(void)
{
	return 0;
}




/* initialise the flash blocks table */
static void init_flash(void)
{
	int i = 0;
	int j;
	u32 start = 0;
	
#ifdef BLOB_DEBUG
	if(flash_descriptors == NULL) {
		printerrprefix();
		SerialOutputString("undefined flash_descriptors\n");
		return;
	}

	if(flash_driver == NULL) {
		printerrprefix();
		SerialOutputString("undefined flash_driver\n");
		return;
	}
#endif	

	/* fill out missing flash driver functions */
	if(flash_driver->enable_vpp == NULL)
		flash_driver->enable_vpp = flash_dummy_ok;

	if(flash_driver->disable_vpp == NULL)
		flash_driver->disable_vpp = flash_dummy_ok;

	/* initialise flash blocks table */
	num_flash_blocks = 0;

	while(flash_descriptors[i].size != 0) {
#ifdef BLOB_DEBUG
		SerialOutputDec(flash_descriptors[i].num);
		SerialOutputString("x 0x");
		SerialOutputHex(flash_descriptors[i].size);
		SerialOutputString(", ");

		if(!flash_descriptors[i].lockable)
			SerialOutputString("not ");

		SerialOutputString("lockable\n");
#endif

		for(j = 0; j < flash_descriptors[i].num; j++) {
			flash_blocks[num_flash_blocks].start = start;
			flash_blocks[num_flash_blocks].size =
				flash_descriptors[i].size;
			flash_blocks[num_flash_blocks].lockable =
				flash_descriptors[i].lockable;

			start += flash_descriptors[i].size;

			num_flash_blocks++;

			if(num_flash_blocks >= NUM_FLASH_BLOCKS) {
				printerrprefix();
				SerialOutputString("not enough flash_blocks\n");
				break;
			}
		}

		i++;
	}

#ifdef BLOB_DEBUG
	SerialOutputString("Flash map:\n");
	for(i = 0; i < num_flash_blocks; i++) {
		SerialOutputString("  0x");
		SerialOutputHex(flash_blocks[i].size);
		SerialOutputString(" @ 0x");
		SerialOutputHex(flash_blocks[i].start);
		SerialOutputString(" (");
		SerialOutputDec(flash_blocks[i].size / 1024);
		SerialOutputString(" kB), ");

		if(!flash_blocks[i].lockable)
			SerialOutputString("not ");

		SerialOutputString("lockable\n");
	}
#endif
}


__initlist(init_flash, INIT_LEVEL_OTHER_STUFF + 1);


int flash_erase_region(u32 *start, u32 nwords)
{
	u32 *cur;
	u32 *end;
	int rv;

	cur = start;
	end = start + nwords;

#if BLOB_DEBUG
	SerialOutputString(__FUNCTION__ "(): erasing 0x");
	SerialOutputHex(nwords);
	SerialOutputString(" (");
	SerialOutputDec(nwords);
	SerialOutputString(") words at 0x");
	SerialOutputHex((u32)start);
	serial_write('\n');
#endif

	flash_driver->enable_vpp();

	while(cur < end) {
		if(*cur != 0xffffffff) {
			SerialOutputString("erasing dirty block at 0x");
			SerialOutputHex((u32)cur);
			serial_write('\n');

			/* dirty block */
			rv = flash_driver->erase(cur);

			if(rv < 0) {
				printerrprefix();
				SerialOutputString("flash erase error at 0x");
				SerialOutputHex((u32)cur);
				serial_write('\n');
				flash_driver->disable_vpp();
				return rv;
			}

			continue;
		}
		cur ++;
	}
	
	flash_driver->disable_vpp();

	return 0;
}




/* Write a flash region with a minimum number of erase operations.
 *
 * Flash chips wear from erase operations (that's why flash lifetime
 * is specified in erase cycles), so we try to limit the number of
 * erase operations in this function. Luckily the flash helps us a
 * little bit with this, because it only allows you to change a '1'
 * bit into a '0' during a write operation. This means that 0xffff can
 * be changed into 0x1010, and 0x1010 into 0x0000, but 0x0000 can't be
 * changed into anything else anymore because there are no '1' bits
 * left.
 */
int flash_write_region(u32 *dst, const u32 *src, u32 nwords)
{
	int rv;
	u32 nerrors = 0;
	u32 i = 0;

	u32 nerase = 0;
	u32 nwrite = 0;
	u32 nscandown = 0;
	u32 nskip = 0;

#if defined BLOB_DEBUG
	SerialOutputString(__FUNCTION__ "(): flashing 0x");
	SerialOutputHex(nwords);
	SerialOutputString(" (");
	SerialOutputDec(nwords);
	SerialOutputString(") words from 0x");
	SerialOutputHex((u32)src);
	SerialOutputString(" to 0x");
	SerialOutputHex((u32)dst);
	serial_write('\n');
#endif

	flash_driver->enable_vpp();

	while(i < nwords) {
		/* nothing to write */
		if(dst[i] == src[i]) {
			i++;
			nskip++;
			continue;
		}

		/* different, so write to this location */
		rv = flash_driver->write(&dst[i], &src[i]);
		nwrite++;
		
		if(rv == 0) {
			i++;
		} else {
			nerrors++;
			
			SerialOutputString("erasing at 0x");
			SerialOutputHex((u32)&dst[i]);
			SerialOutputString("...");

			/* erase block at current location */
			rv = flash_driver->erase(&dst[i]);
			nerase++;
			if(rv < 0) {
				/* something is obviously wrong */
				SerialOutputString(" error\n");
				flash_driver->disable_vpp();

				return rv;
			}
				
			SerialOutputString(" scanning down...");

			/* scan down until we find the first
                           non-erased location and restart writing
                           again from that location */
			while((i > 0) &&
			      ((dst[i] != src[i]) || (dst[i] == 0xffffffff))) {
				i--;
				nscandown++;
			}

			SerialOutputString(" resume writing at 0x");
			SerialOutputHex((u32)&dst[i]);
			serial_write('\n');
		}
		
		/* there is something seriously wrong if this is true */
		if(nerrors > 2 * nwords) {
			printerrprefix();
			SerialOutputString("too many flash errors, probably hardware error\n");
			flash_driver->disable_vpp();

			return -EFLASHPGM;
		}
	}

#ifdef BLOB_DEBUG
	SerialOutputDec(nwords);
	SerialOutputString(" words source image\n");
	SerialOutputDec(nwrite);
	SerialOutputString(" words written to flash\n");
	SerialOutputDec(nskip);
	SerialOutputString(" words skipped\n");
	SerialOutputDec(nerase);
	SerialOutputString(" erase operations\n");
	SerialOutputDec(nscandown);
	SerialOutputString(" words scanned down\n");
	serial_write('\n');
#endif

	flash_driver->disable_vpp();

	return 0;
}




/* given an address, return the flash block index number (or negative
 * error number otherwise
 */
static int find_block(u32 address)
{
	int i;

	for (i = 0; i < num_flash_blocks; i++) {
		u32 start = flash_blocks[i].start;
		int length = flash_blocks[i].size;
		u32 endIncl = start + length - 1;

		if (address >= start && address <= endIncl)
			return i;
	}

	return -ERANGE;
}




/* convert address range to range of flash blocks. returns 0 on
 * success or negative error number on failure.
 */
static int address_range_to_block_range(u32 startAddress, int length, 
					int *startBlock, int *endInclBlock)
{
	int sb, eib;

#ifdef FLASH_DEBUG
	SerialOutputString(__FUNCTION__ ": startAddress = 0x");
	SerialOutputHex(startAddress);
	SerialOutputString(", length = 0x");
	SerialOutputHex(length);
	SerialOutputString("\n");
#endif

	sb = find_block(startAddress);
	eib = find_block(startAddress + length - 1);

	if(sb < 0)
		return sb;

	if(eib < 0)
		return eib;

#ifdef FLASH_DEBUG
	SerialOutputString("sb: ");
	SerialOutputDec(sb);
	SerialOutputString(", eib: ");
	SerialOutputDec(eib);
	SerialOutputString("\n");
#endif

	// would be nice to warn if sb isn't at the beginning of
	// its block or eib isn't at the very end of its block.

	*startBlock = sb;
	*endInclBlock = eib;

	return 0;
}




static int do_flash_lock(u32 *start, u32 nwords, int lock)
{
	int sb, eib;
	int rv;
	int i;
	u32 *addr;

#ifdef BLOB_DEBUG
	SerialOutputString(__FUNCTION__ "(): ");
	if(lock == 0)
		SerialOutputString("un");
	
	SerialOutputString("lock at 0x");
	SerialOutputHex((u32)start);
	SerialOutputString(", nwords = 0x");
	SerialOutputHex(nwords);
	serial_write('\n');
#endif

	rv = address_range_to_block_range((u32) start, nwords * sizeof(u32),
					  &sb, &eib);

	if(rv < 0)
		return rv;

	/* check if it is lockable at all */
	for(i = sb; i <= eib; i++) {
		if(!flash_blocks[i].lockable) {
#ifdef BLOB_DEBUG
			printerrprefix();
			SerialOutputString("can't (un)lock unlockable blocks\n");
#endif
			return -EFLASHPGM;
		}
	}
	
	flash_driver->enable_vpp();

	for(i = sb; i <= eib; i++) {
		addr = (u32 *)flash_blocks[i].start;

		if(lock)
			rv = flash_driver->lock_block(addr);
		else
			rv = flash_driver->unlock_block(addr);

		if(rv < 0) {
			flash_driver->disable_vpp();
#ifdef BLOB_DEBUG
			printerrprefix();
			SerialOutputString("can't (un)lock block at 0x");
			SerialOutputHex((u32)addr);
			serial_write('\n');
#endif
			return rv;
		}
	}
	
	flash_driver->disable_vpp();

	return 0;
}




int flash_lock_region(u32 *start, u32 nwords)
{
	return do_flash_lock(start, nwords, 1);
}




int flash_unlock_region(u32 *start, u32 nwords)
{
	return do_flash_lock(start, nwords, 0);
}




/* return number of blocks in the region locked, 0 if everything is
 * unlocked, or negative error number otherwise */
int flash_query_region(u32 *start, u32 nwords)
{
	int sb, eib, rv, i;
	int cnt = 0;
	u32 *addr;

	rv = address_range_to_block_range((u32) start, nwords * sizeof(u32),
					  &sb, &eib);

	if(rv < 0)
		return rv;

	for(i = sb; i <= eib; i++) {
		addr = (u32 *)flash_blocks[i].start;
		
		if(flash_blocks[i].lockable) {
			rv = flash_driver->query_block_lock(addr);

			if(rv < 0) 
				return rv;

			if(rv > 0)
				cnt++;
		}
	}

	return cnt;
}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -