📄 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 16MB flash with 128kB blocks */#define NUM_FLASH_BLOCKS (128)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 + -