📄 flash.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 + -