www.pudn.com > tfs.rar > flash.c
/* flashcom.c:
* This file contains the portions of the flash code that are device
* independent. Refer to the appropriate device sub-directory for the
* code that is specific to the flash device on the target.
*
* General notice:
* This code is part of a boot-monitor package developed as a generic base
* platform for embedded system designs. As such, it is likely to be
* distributed to various projects beyond the control of the original
* author. Please notify the author of any enhancements made or bugs found
* so that all may benefit from the changes. In addition, notification back
* to the author will allow the new user to pick up changes that may have
* been made by other users after this version of the code was distributed.
*
* Note1: the majority of this code was edited with 4-space tabs.
* Note2: as more and more contributions are accepted, the term "author"
* is becoming a mis-representation of credit.
*
* Original author: Ed Sutter
* Email: esutter@lucent.com
* Phone: 908-582-2351
*/
#include "config.h"
#if INCLUDE_FLASH
//#include "cpu.h"
#include "flashdev.h"
#include "flash.h"
#include "genlib.h"
#include "ctype.h"
#include "stddefs.h"
#include "tfs.h"
//#include "cli.h"
extern struct flashdesc FlashNamId[];
int FlashCurrentBank;
int sectortoaddr(int,int *,uchar **);
#define SRANGE_ERROR -1
#define SRANGE_SINGLE 1
#define SRANGE_RANGE 2
#define SRANGE_ALL 3
/* FlashProtectWindow:
* Must be set to allow any flash operation to be done on space assumed
* to be software protected.
*/
int FlashProtectWindow;
/* FlashBank[]:
* This table contains all of the information that is needed to keep the
* flash code somewhat generic across multiple flash devices.
*/
struct flashinfo FlashBank[FLASHBANKS];
#ifdef DISABLE_INTERRUPTS_DURING_FLASHOPS
#define FLASH_INTSOFF() intsoff()
#define FLASH_INTSRESTORE(ival) intsrestore(ival)
#else
#define FLASH_INTSOFF() 0
#define FLASH_INTSRESTORE(ival)
#endif
/* showflashtype():
* Find a match between the incoming id and an entry in the FlashNamId[]
* table. The FlashNamId[] table is part of the device-specific code.
*/
int
showflashtype(ulong id)
{
struct flashdesc *fdp;
fdp = FlashNamId;
while(fdp->desc) {
if (id == fdp->id) {
printf("Device = %s\n",fdp->desc);
return(0);
}
fdp++;
}
printf("Flash id 0x%lx not recognized\n",id);
return(-1);
}
/* flasherased():
* Return 1 if range of memory is all 0xff; else 0.
* Scan through the range of memor specified by begin-end (inclusive)
* looking for anything that is not 0xff. Do this in three sections so
* that the pointers can be 4-byte aligned for the bulk of the comparison
* range...
* The beginning steps through as a char pointer until aligned on a 4-byte
* boundary. Then do ulong * comparisons until the just before the end
* where we once again use char pointers to align on the last few
* non-aligned bytes (if any).
*/
int
flasherased(uchar *begin, uchar *end)
{
ulong *lp, *lp1;
/* Get pointers aligned so that we can do the bulk of the comparison
* with long pointers...
*/
while(((long)begin & 3) && (begin != end)) {
if (*begin != 0xff) {
return(0);
}
begin++;
}
if (begin >= end)
return(1);
lp = (ulong *)begin;
lp1 = (ulong *)end;
(long)lp1 &= ~3;
while(lp < lp1) {
if (*lp != 0xffffffff) {
return(0);
}
lp++;
}
if (lp >= (ulong *)end)
return(1);
begin = (uchar *)lp;
do {
if (*begin++ != 0xff)
return(0);
} while(begin != end);
return(1);
}
/* showflashinfo():
* Dump information about specified flash device.
*/
int
showflashinfo(struct flashinfo *fdev, char *range)
{
int i;
struct sectorinfo *sp;
if (showflashtype(fdev->id) < 0)
return(-1);
printf(" Base addr : 0x%08lx\n",(ulong)(fdev->base));
printf(" Sectors : %d\n",fdev->sectorcnt);
printf(" Bank width : %d\n",fdev->width);
printf(" Sector Begin End Size SWProt? Erased?\n");
for(i=0;isectorcnt;i++) {
sp = &fdev->sectors[i];
if (inRange(range,sp->snum)) {
printf(" %2d 0x%08lx 0x%08lx 0x%06lx %s %s\n",
sp->snum, (ulong)(sp->begin), (ulong)(sp->end), sp->size,
sp->protected ? "yes" : " no",
flasherased(sp->begin,sp->end) ? "yes" : "no");
}
}
return(0);
}
/* flashopload():
* Copy flash operation to ram space.
* Note that this function assumes that cache is disabled at this point.
* This is important because we are copying text into bss space and if
* cache was on, there could be a coherency problem.
*/
int
flashopload(ulong *begin,ulong *end,ulong *copy,int size)
{
volatile ulong *bp;
int ret;
/* Verify space availability: */
if (((int)end - (int)begin) >= size) {
printf("flashopload overflow ((0x%lx-0x%lx) > 0x%x)\n",
(ulong)end,(ulong)begin,size);
return(-1);
}
ret = 0;
/* Copy function() to RAM, then verify: */
bp = begin;
while(bp <= end) {
*copy = *bp;
if (*copy++ != *bp++) {
printf("flashopload failed\n");
ret = -1;
break;
}
}
return(ret);
}
/* flashtype():
* Use the device-specific function pointer to call the routine
* relocated to RAM space.
*/
int
flashtype(fdev)
struct flashinfo *fdev;
{
return(fdev->fltype(fdev));
}
/* flasherase():
* Use the device-specific function pointer to call the routine
* relocated to RAM space.
* Note that flasherase() is called with a sector number. The sector
* number is relative to the entire system, not just the particular device.
* This means that if there is more than one flash device in the system that
* the actual sector number (relative to the device) may not be the same
* value. This adjustment is made here so that the underlying code that is
* pumped into ram for execution does not have to be aware of this.
*/
int
flasherase(fdev,snum)
struct flashinfo *fdev;
int snum;
{
int size;
unsigned char *base, *end;
if (fdev->id == FLASHRAM) {
if (snum == ALL_SECTORS) {
size = fdev->end - fdev->base;
base = fdev->base;
}
else {
sectortoaddr(snum,&size,&base);
}
end = base+size;
while(base < end) {
*base = 0xff;
if (*base++ != 0xff)
return(-1);
}
return(0);
}
if ((snum != ALL_SECTORS) && (fdev->sectors[0].snum != 0)) {
/* printf("Adjusting snum from %d to",snum); */
snum -= fdev->sectors[0].snum;
/* printf(" %d.\n",snum); */
}
return(fdev->flerase(fdev,snum));
}
/* flashwrite():
* Use the device-specific function pointer to call the routine
* relocated to RAM space.
* First make a few checks on the request, then write to flash if all
* checks succeed.
*/
int
flashwrite(struct flashinfo *fdev,uchar *dest,uchar *src,long bytecnt)
{
int j, lowsector, highsector;
register uchar *dp, *sp, *edp;
if (fdev->id == FLASHRAM) {
uchar *sp, *dp, *end;
sp = src;
dp = dest;
end = dp+bytecnt;
while(dp < end) {
*dp = *sp;
if (*dp != *sp)
return(-1);
dp++; sp++;
}
return(0);
}
dp = dest;
sp = src;
edp = (dest + bytecnt) - 1;
/* If outside the devices space, return failed.. */
if ((edp < fdev->sectors[0].begin) ||
(dp > fdev->sectors[fdev->sectorcnt-1].end)) {
printf("flashwrite() failed: dest out of flash range\n");
return(-1);
}
/* Make sure the destination is not within a protected sector */
if (FlashProtectWindow == FLASH_PROTECT_WINDOW_CLOSED) {
/* First determine the sectors that overlap with the
* flash space to be written...
*/
lowsector = highsector = -1;
for(j=0;jsectorcnt;j++) {
if ((dp >= fdev->sectors[j].begin) &&
(dp <= fdev->sectors[j].end))
lowsector = j;
}
for(j=0;jsectorcnt;j++) {
if ((edp >= fdev->sectors[j].begin) &&
(edp <= fdev->sectors[j].end))
highsector = j;
}
if ((lowsector == -1) || (highsector == -1)) {
printf("flashwrite() failed: can't find sector\n");
return(-1);
}
/* Now that the range of affected sectors is known,
* verify that those sectors are not protected...
*/
for(j=lowsector;j<=highsector;j++) {
if (fdev->sectors[j].protected) {
printf("flashwrite() failed: sector protected\n");
return(-1);
}
}
}
/* Now make sure that there is no attempt to transition a bit
* in the affected range from 0 to 1... A flash write can only
* bring bits low (erase brings them high).
*/
while(dp < edp) {
if ((*dp & *sp) != *sp) {
printf("flashwrite() failed: bit 0->1 rqst denied.\n");
return(-1);
}
dp++;
sp++;
}
return(fdev->flwrite(fdev,dest,src,bytecnt));
}
/* flashewrite():
* Use the device-specific function pointer to call the routine
* relocated to RAM space.
*/
int
flashewrite(struct flashinfo *fdev,uchar *dest,uchar *src,long bytecnt)
{
int i;
/* Source and destination addresses must be long-aligned. */
if (((int)src & 3) || ((int)dest & 3))
return(-1);
/* If the protection window is closed, then verify that no protected
* sectors will be written over...
*/
if (FlashProtectWindow == FLASH_PROTECT_WINDOW_CLOSED) {
for (i=0;isectorcnt;i++) {
if((((uchar *)dest) > (fdev->sectors[i].end)) ||
(((uchar *)dest+bytecnt) < (fdev->sectors[i].begin)))
continue;
else
if (fdev->sectors[i].protected)
return(-1);
}
}
return(fdev->flewrite(fdev,dest,src,bytecnt));
}
/* addrtosector():
* Incoming address is translated to sector number, size of sector
* and base of sector.
* Return 0 if successful; else -1.
*/
int
addrtosector(uchar *addr,int *sector,int *size,uchar **base)
{
struct flashinfo *fbnk;
struct sectorinfo *sinfo;
int dev, sec, i;
sec = 0;
for(dev=0;devsectorcnt;i++,sec++) {
sinfo = &fbnk->sectors[i];
if ((addr >= sinfo->begin) && (addr <= sinfo->end)) {
if (sector) {
*sector = sec;
}
if (base) {
*base = sinfo->begin;
}
if (size) {
*size = sinfo->size;
}
return(0);
}
}
}
printf("addrtosector(0x%lx) failed\n",(ulong)addr);
return(-1);
}
/* addrtobank():
* From the incoming address, return a pointer to the flash bank that
* this address is within.
*/
struct flashinfo *
addrtobank(uchar *addr)
{
struct flashinfo *fbnk;
int dev;
for(dev=0;dev= fbnk->base) && (addr <= fbnk->end))
return(fbnk);
}
printf("addrtobank(0x%lx) failed\n",(ulong)addr);
return(0);
}
int
sectortoaddr(int sector,int *size,uchar **base)
{
struct flashinfo *fbnk;
struct sectorinfo *sinfo;
int dev, sec, i;
sec = 0;
for(dev=0;devsectorcnt;i++,sec++) {
if (sec == sector) {
sinfo = &fbnk->sectors[i];
if (base) *base = sinfo->begin;
if (size) *size = sinfo->size;
return(0);
}
}
}
printf("sectortoaddr(%d) failed\n",sector);
return(-1);
}
/* flashbankinfo():
* Based on the incoming bank number, return the beginning, end and
* number of sectors within that bank.
*/
int
flashbankinfo(int bank,uchar **begin,uchar **end,int *sectorcnt)
{
struct flashinfo *fbnk;
if (bank >= FLASHBANKS)
return(-1);
fbnk = &FlashBank[bank];
if (begin)
*begin = fbnk->base;
if (end)
*end = fbnk->end;
if (sectorcnt)
*sectorcnt = fbnk->sectorcnt;
return(0);
}
/* lastlargesector():
* Incoming bank number is used to populate the sector information
* (sector number, sector size and address) of the last large sector
* in the specified bank.
* Return 0 if successful; else -1.
*/
int
lastlargesector(int bank,int *sector,int *size,uchar **base)
{
struct flashinfo *fbnk;
struct sectorinfo *sinfo;
uchar *largest_sbase;
int i, largest_ssize, largest_snum;
if (bank >= FLASHBANKS) {
printf("lastlargesector(%d) failed\n",bank);
return(-1);
}
fbnk = &FlashBank[bank];
sinfo = fbnk->sectors;
largest_ssize = 0;
largest_snum = 0;
largest_sbase = (uchar *)0;
for(i=0;isectorcnt;i++,sinfo++) {
if (sinfo->size >= largest_ssize) {
largest_ssize = sinfo->size;
largest_snum = sinfo->snum;
largest_sbase = sinfo->begin;
}
}
if (sector)
*sector = largest_snum;
if (size)
*size = largest_ssize;
if (base)
*base = largest_sbase;
return(0);
}
void
LowerFlashProtectWindow()
{
if (FlashProtectWindow)
FlashProtectWindow--;
}
/* AppFlashWrite():
* Takes in a source, destination and byte count and converts that to
* the appropriate flashwrite() call. This function supports the possibility
* of having one write request span across multiple devices in contiguous
* memory space.
*/
int
AppFlashWrite(dest,src,bytecnt)
ulong *src, *dest;
long bytecnt;
{
struct flashinfo *fbnk;
ulong oints;
int ret, tmpint;
long tmpcnt;
ret = 0;
while(bytecnt > 0) {
fbnk = addrtobank((uchar *)dest);
if (!fbnk)
return(-1);
if (((int)dest + bytecnt) <= (int)(fbnk->end))
tmpcnt = bytecnt;
else
tmpcnt = ((int)(fbnk->end) - (int)dest) + 1;
oints = FLASH_INTSOFF();
ret = flashwrite(fbnk,(uchar *)dest,(uchar *)src,tmpcnt);
FLASH_INTSRESTORE(oints);
if (ret < 0) {
printf("AppFlashWrite(0x%lx,0x%lx,%ld) failed\n",
(ulong)dest,(ulong)src,bytecnt);
break;
}
tmpint = (int)dest;
tmpint += tmpcnt;
dest = (ulong *)tmpint;
tmpint = (int)src;
tmpint += tmpcnt;
src = (ulong *)tmpint;
bytecnt -= tmpcnt;
}
return(ret);
}
int
AppFlashEraseAll()
{
int ret, i;
ulong oints;
struct flashinfo *fbnk;
ret = 0;
oints = FLASH_INTSOFF();
fbnk = FlashBank;
for(i=0;isectorcnt;i++,snum++) {
if (inRange(range,snum))
fbnk->sectors[i].protected = protect;
}
}
return(0);
}
#ifdef FLASHRAM_BASE
/* FlashRamInit():
* This monitor supports TFS space allocated across multiple flash devices
* that may not be in contiguous memory space. To allow BBRAM to be seen
* as a "flash-like" device to TFS, we set it up in sectors similar to
* those in a real flash device.
* Input...
* snum: All the "flash" space is broken up into individual sectors.
* This is the starting sector number that is to be used for
* the block of sectors within this BBRAM space.
* fbnk: Pointer to the structure that must be populated with the
* flash bank information. Usually this contains pointers to the
* functions that operate on the flash; but for RAM they aren't
* necessary.
* sinfo: Table populated with the characteristics (size, start, etc...)
* of each sector.
* ssizes: A table containing the size of each of the sectors. This is
* copied to the sinfo space.
*/
int
FlashRamInit(int snum, int scnt, struct flashinfo *fbnk,
struct sectorinfo *sinfo, int *ssizes)
{
int i;
uchar *begin;
/* FLASHRAM_SECTORCOUNT (in config.h) must match the number of sectors
* allocated to the flash ram device in flashdev.c...
*/
if (scnt != FLASHRAM_SECTORCOUNT)
printf("Warning: flashram sector count inconsistency\n");
fbnk->id = FLASHRAM; /* Device id. */
fbnk->base = (uchar *)FLASHRAM_BASE; /* Base address of bank. */
fbnk->end = (uchar *)FLASHRAM_END; /* End address of bank. */
fbnk->sectorcnt = scnt; /* Number of sectors. */
fbnk->width = 1; /* Width (in bytes). */
fbnk->fltype = NotUsed; /* Flashtype() function. */
fbnk->flerase = NotUsed; /* Flasherase() function. */
fbnk->flwrite = NotUsed; /* Flashwrite() function. */
fbnk->flewrite = NotUsed; /* Flashewrite() function. */
fbnk->sectors = sinfo; /* Ptr to sector size table. */
begin = fbnk->base;
for(i=0;isectorcnt;i++,snum++) {
sinfo[i].snum = snum;
sinfo[i].size = ssizes[i];
sinfo[i].begin = begin;
sinfo[i].end = sinfo[i].begin + sinfo[i].size - 1;
sinfo[i].protected = 0;
begin += sinfo[i].size;
}
return(snum);
}
#endif
char *FlashHelp[] = {
"Flash memory operations",
"{op} [args]",
"Ops...",
" opw",
" info [rnge]",
" init",
" bank [#]",
" prot {rnge}",
" unprot {rnge}",
#if FLASH_LOCK_SUPPORTED
" lock {rnge}",
" unlock {rnge}",
" lockdwn {rnge}",
#endif
" erase {rnge}",
" write {dest} {src} {byte_cnt}",
" ewrite {dest} {src} {byte_cnt}",
"",
" rnge = range of affected sectors",
0,
};
/* FlashCmd():
* Code that handles the user interface. See FlashHelp[] below for usage.
*/
int
FlashCmd(int argc,char *argv[])
{
int snum, ret;
ulong dest, src, oints;
long bytecnt, rslt;
struct flashinfo *fbnk;
oints = FLASH_INTSOFF();
fbnk = &FlashBank[FlashCurrentBank];
ret = CMD_SUCCESS;
if (strcmp(argv[1],"init") == 0)
FlashInit();
else if (strcmp(argv[1],"info") == 0) {
showflashinfo(fbnk,argv[2]);
}
else if (strcmp(argv[1],"bank") == 0) {
int tmpbank;
if (argc == 3) {
tmpbank = atoi(argv[2]);
if (tmpbank < FLASHBANKS)
FlashCurrentBank = tmpbank;
printf("Subsequent flash ops apply to bank %d\n",FlashCurrentBank);
}
else
printf("Current flash bank: %d\n",FlashCurrentBank);
}
else if (strcmp(argv[1],"ewrite") == 0) {
if (argc == 5) {
dest = strtoul(argv[2],(char **)0,0);
src = strtoul(argv[3],(char **)0,0);
bytecnt = (long)strtoul(argv[4],(char **)0,0);
if (flashewrite(fbnk,(uchar *)dest,(uchar *)src,bytecnt) == -1) {
printf("ewrite failed\n");
ret = CMD_FAILURE;
}
}
else
ret = CMD_PARAM_ERROR;
}
else if (!strcmp(argv[1],"write")) {
if (argc == 5) {
dest = strtoul(argv[2],(char **)0,0);
src = strtoul(argv[3],(char **)0,0);
bytecnt = (long)strtoul(argv[4],(char **)0,0);
#if 0
rslt = flashwrite(fbnk,(uchar *)dest,(uchar *)src,bytecnt);
#else
rslt = AppFlashWrite((ulong *)dest,(ulong *)src,bytecnt);
#endif
if (rslt == -1) {
printf("Write failed\n");
ret = CMD_FAILURE;
}
}
else
ret = CMD_PARAM_ERROR;
}
else if (!strcmp(argv[1],"opw")) {
if (getUsrLvl() != MAXUSRLEVEL)
printf("Must be user level %d\n",MAXUSRLEVEL);
else
FlashProtectWindow = 2;
}
else if (!strcmp(argv[1],"unprot")) {
if (argc != 3)
ret = CMD_PARAM_ERROR;
else
sectorProtect(argv[2],0);
}
else if (!strcmp(argv[1],"prot")) {
if (argc != 3)
ret = CMD_PARAM_ERROR;
else
sectorProtect(argv[2],1);
}
else if (!strcmp(argv[1],"erase")) {
if (argc != 3) {
ret = CMD_PARAM_ERROR;
}
else {
for(snum=fbnk->sectors[0].snum;snumsectorcnt;snum++) {
if (inRange(argv[2],snum)) {
if (flasherase(fbnk,snum) == -1) {
printf("Erase failed\n");
ret = CMD_FAILURE;
break;
}
}
}
}
}
#if FLASH_LOCK_SUPPORTED
else if ((!strcmp(argv[1],"lock")) || (!strcmp(argv[1],"unlock")) ||
(!strcmp(argv[1],"lockdwn"))) {
extern int flashlock(struct flashinfo *fbnk,int snum,int operation);
int operation, snum;
if (!strcmp(argv[1],"lock"))
operation = FLASH_LOCK;
else if (!strcmp(argv[1],"unlock"))
operation = FLASH_UNLOCK;
else
operation = FLASH_LOCKDWN;
if (argc != 3)
ret = CMD_PARAM_ERROR;
else {
for(snum=fbnk->sectors[0].snum;snumsectorcnt;snum++) {
if (inRange(argv[2],snum)) {
if (flashlock(fbnk,snum,operation) == -1) {
printf("Erase failed\n");
ret = CMD_FAILURE;
break;
}
}
}
}
}
#endif
else {
ret = CMD_PARAM_ERROR;
}
FLASH_INTSRESTORE(oints);
return(ret);
}
int
NotUsed()
{
printf("ERROR: flash operation not supported\n");
return(0);
}
#endif