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