/**************************************************************************
*
* Copyright (c) 2013 Alcatel-Lucent
*
* Alcatel Lucent licenses this file to You under the Apache License,
* Version 2.0 (the "License"); you may not use this file except in
* compliance with the License. A copy of the License is contained the
* file LICENSE at the top level of this repository.
* You may also obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
**************************************************************************
*
* flash.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.
*
* Original author: Ed Sutter (ed.sutter@alcatel-lucent.com)
*
*/
#include "config.h"
#include "genlib.h"
#if INCLUDE_FLASH
#include "cpu.h"
#include "flash.h"
#include <ctype.h>
#include "stddefs.h"
#include "tfs.h"
#include "tfsprivate.h"
#include "cli.h"
extern struct flashdesc FlashNamId[];
int FlashTrace;
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_INTSDECL unsigned long oints
#define FLASH_INTSOFF() oints = intsoff()
#define FLASH_INTSRESTORE(ival) intsrestore(oints)
#else
#define FLASH_INTSDECL
#define FLASH_INTSOFF()
#define FLASH_INTSRESTORE(ival)
#endif
/* showlockop():
* Return a string that verbosely represents the flash lock
* operation...
*/
char *
showlockop(int operation)
{
switch(operation) {
case FLASH_LOCK:
return("lock");
case FLASH_UNLOCK:
return("unlock");
case FLASH_LOCKDWN:
return("lock_down");
case FLASH_LOCKQRY:
return("lock_qry");
case FLASH_LOCKABLE:
return("lockable");
default:
return("???");
}
}
/* 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, int showid)
{
struct flashdesc *fdp;
if(showid) {
printf("Flash ID: 0x%lx\n",id);
}
fdp = FlashNamId;
while(fdp->desc) {
if(id == fdp->id) {
printf("Device = %s\n",fdp->desc);
return(0);
}
fdp++;
}
if(id == FLASHRAM) {
printf("Device = FLASH-RAM\n");
return(0);
}
printf("Flash id 0x%lx not recognized\n",id);
return(-1);
}
void
showflashtotal(void)
{
#if FLASHBANKS > 1
printf("Total of %d banks (0-%d), current default bank: %d\n",
FLASHBANKS,FLASHBANKS-1,FlashCurrentBank);
#else
printf("Current flash bank: 0\n");
#endif
}
/* showflashinfo():
* Dump information about specified flash device.
*/
int
showflashinfo(char *devrange)
{
uchar *base;
struct sectorinfo *sp;
struct flashinfo *fdev;
char *range, varname[32];
int first_sector_of_device, devtot;
int locksupported, sector, lastsector, hdrprinted;
devtot = 0;
hdrprinted = 0;
lastsector = lastflashsector();
/* An incoming NULL range implies "any" sector...
*/
if(devrange == 0) {
showflashtotal();
range = "any";
} else {
range = devrange;
}
/* Loop through all sectors on all devices, picking only those
* sectors that fall in the specified range.
*/
for(sector = 0; sector <= lastsector; sector++) {
if(gotachar()) {
getchar();
printf("<break>\n");
break;
}
if(!inRange(range,sector)) {
continue;
}
if(!(fdev = snumtofdev(sector))) {
return(-1);
}
first_sector_of_device = fdev->sectors[0].snum;
if(flashlock(first_sector_of_device,FLASH_LOCKABLE) > 0) {
locksupported = 1;
} else {
locksupported = 0;
}
/* If sector is first sector of a device, then print
* device info also...
*/
if((sector == first_sector_of_device) && (!strcmp(range,"any"))) {
if(showflashtype(fdev->id,0) < 0) {
return(-1);
}
printf(" Bank width : %d\n",fdev->width);
printf(" Sectors : %d\n",fdev->sectorcnt);
printf(" Base addr : 0x%08lx\n",(ulong)(fdev->base));
hdrprinted = 0;
if(devrange == 0) {
sprintf(varname,"FLASH_BASE_%d",devtot);
shell_sprintf(varname,"0x%lx",(ulong)(fdev->base));
sprintf(varname,"FLASH_SCNT_%d",devtot);
shell_sprintf(varname,"%d",fdev->sectorcnt);
sprintf(varname,"FLASH_END_%d",devtot);
shell_sprintf(varname,"0x%lx",(ulong)(fdev->end));
}
devtot++;
}
if(!hdrprinted) {
printf(" Sctr TFS? Begin End Size %s %s%s",
"SWProt?", "Erased?", locksupported ? " Locked?\n" : "\n");
hdrprinted = 1;
}
sp = &fdev->sectors[sector - first_sector_of_device];
sectortoaddr(sp->snum,0,&base);
if((range == 0) || inRange(range,sp->snum)) {
printf(" %3d %c 0x%08lx 0x%08lx 0x%06lx %s %s ",
sp->snum, tfsspace((char *)base) ? '*' : ' ',
(ulong)(sp->begin), (ulong)(sp->end), sp->size,
sp->protected ? "yes" : " no",
flasherased(sp->begin,sp->end) ? "yes" : " no");
if(locksupported) {
switch(flashlock(sp->snum,FLASH_LOCKQRY)) {
case -1:
printf("???");
break;
case 1:
printf("yes");
break;
case 0:
printf(" no");
break;
}
}
printf("\n");
}
}
if(devrange == 0) {
shell_sprintf("FLASH_DEVTOT","%d",devtot);
}
return(0);
}
#ifdef FLASH_COPY_TO_RAM
/* 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)
{
/* Some CPUs have 16bit opcodes and can only do aligned accesses. */
unsigned short *sBegin = (unsigned short *)begin;
unsigned short *sEnd = (unsigned short *)end;
unsigned short *sCopy = (unsigned short *)copy;
/* 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);
}
/* Initially fill the copy space with 0xff so that the space
* remaining is viewable...
*/
memset((char *)copy,0xff,size);
/* Copy function() to RAM, then verify: */
while(sBegin < sEnd) {
*sCopy = *sBegin;
if(*sCopy++ != *sBegin++) {
printf("flashopload failed\n");
return(-1);
}
}
return(0);
}
#endif
/* flashtype():
* Use the device-specific function pointer to call the routine
* relocated to RAM space.
*/
int
flashtype(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.
* Return...
* 1 if successful
* -1 if failure
* 0 if sector is protected or locked
*/
int
flasherase(int snum)
{
uchar *tmp;
ulong *base, *end;
int size, rc, dev_snum;
struct flashinfo *fdev;
struct sectorinfo *sinfo;
if(FlashTrace) {
printf("flasherase(%d)\n",snum);
}
if(!(fdev = snumtofdev(snum))) {
return(-1);
}
/* If the device type is RAM, the erase is a bit different...
*/
if(fdev->id == FLASHRAM) {
// Use 'tmp' here to eliminate a 3.4 toolset warning.
sectortoaddr(snum,&size,&tmp);
base = (ulong *)tmp;
end = base + (size/sizeof(long));
while(base < end) {
*base = 0xffffffff;
if(*base != 0xffffffff) {
return(-1);
}
base++;
}
return(1);
}
/* If the sector is soft-protected or locked, return negative
* and print failure. If the sector is already erased, then
* there is no need to issue the device-specific erase algorithm.
*/
dev_snum = snum - fdev->sectors[0].snum;
sinfo = &fdev->sectors[dev_snum];
if(!flasherased(sinfo->begin,sinfo->end)) {
if((!FlashProtectWindow) && (sinfo->protected)) {
printf("Sector %d protected\n",snum);
return(0);
}
if(flashlocked(snum,1)) {
return(0);
}
rc = fdev->flerase(fdev,dev_snum);
if(rc < 0) {
return(rc);
}
}
return(1);
}
/* 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, rc, first_sector_of_device;
register uchar *dp, *sp, *edp;
if(FlashTrace) {
printf("flashwrite(0x%lx,0x%lx,%ld)\n",(long)dest,(long)src,bytecnt);
}
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;
first_sector_of_device = fdev->sectors[0].snum;
/* 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; j<fdev->sectorcnt; j++) {
if((dp >= fdev->sectors[j].begin) &&
(dp <= fdev->sectors[j].end)) {
lowsector = j;
}
}
for(j=0; j<fdev->sectorcnt; 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 or locked...
*/
for(j=lowsector; j<=highsector; j++) {
if(fdev->sectors[j].protected) {
printf("flashwrite() failed: sector protected\n");
return(-1);
}
if(flashlocked(j+first_sector_of_device,1)) {
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(0x%lx) failed: bit 0->1 rqst denied.\n",
(long)dp);
return(-1);
}
dp++;
sp++;
WATCHDOG_MACRO;
}
rc = fdev->flwrite(fdev,dest,src,bytecnt);
if(rc < 0) {
return(rc);
}
/* Assuming everything else appears to have passed, make sure the
* source and destination addresses match...
*/
if(memcmp((char *)dest,(char *)src,(int)bytecnt) != 0) {
printf("flashwrite() post-verify failed.\n");
return(-1);
}
return(0);
}
/* flashewrite():
* Use the device-specific function pointer to call the routine
* relocated to RAM space.
*/
int
flashewrite(uchar *dest,uchar *src,long bytecnt)
{
int i;
struct flashinfo *fdev;
if(FlashTrace) {
printf("flashwrite(0x%lx,0x%lx,%ld)\n",(long)dest,(long)src,bytecnt);
}
if((fdev = addrtobank(dest)) == 0) {
return(-1);
}
/* 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; i<fdev->sectorcnt; 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));
}
/* flashlock():
Use a function pointer to call the routine relocated to RAM space.
*/
int
flashlock(int snum,int operation)
{
int dev_snum;
struct flashinfo *fdev;
if(FlashTrace) {
printf("flashlock(%d,%s)\n",snum,showlockop(operation));
}
fdev = snumtofdev(snum);
dev_snum = snum - fdev->sectors[0].snum;
return(fdev->fllock(fdev,dev_snum,operation));
}
int
flashlocked(int snum, int verbose)
{
if((flashlock(snum,FLASH_LOCKABLE) > 0) &&
(flashlock(snum,FLASH_LOCKQRY) == 1)) {
if(verbose) {
printf("Sector %d locked\n",snum);
}
return(1);
}
return(0);
}
/* snumtofdev():
* Return the flash device pointer that corresponds to the incoming
* sector number.
*/
struct flashinfo *
snumtofdev(int snum)
{
int dev;
struct flashinfo *fbnk;
fbnk = FlashBank;
for(dev=0; dev<FLASHBANKS; dev++,fbnk++) {
if((snum >= fbnk->sectors[0].snum) &&
(snum <= fbnk->sectors[fbnk->sectorcnt-1].snum)) {
return(fbnk);
}
}
printf("snumtofdev(%d) failed\n",snum);
return(0);
}
/* 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;
fbnk = FlashBank;
for(dev=0; dev<FLASHBANKS; dev++,fbnk++) {
for(i=0; i<fbnk->sectorcnt; 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;
fbnk = FlashBank;
for(dev=0; dev<FLASHBANKS; dev++,fbnk++) {
if((addr >= 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;
fbnk = FlashBank;
for(dev=0; dev<FLASHBANKS; dev++,fbnk++) {
for(i=0; i<fbnk->sectorcnt; 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);
}
/* InFlashSpace():
* Return 1 if the block of memory is within flash space;
* else return 0.
*/
int
InFlashSpace(uchar *begin, int size)
{
int dev;
uchar *end;
struct flashinfo *fbnk;
end = begin+size;
fbnk = FlashBank;
for(dev=0; dev<FLASHBANKS; dev++,fbnk++) {
if(((begin >= fbnk->base) && (begin <= fbnk->end)) ||
((end >= fbnk->base) && (end <= fbnk->end))) {
return(1);
}
}
return(0);
}
/* 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.
* * from_addr defines the search start address:
* * from_addr = 0 means start from the first sector;
* * from_addr MUST be sector aligned.
* * sectorcnt defines the numbers of contiguous sectors to search across;
* * sectorcnt = 0 means search to the last sector.
* Return 0 if successful; else -1.
*/
int
lastlargesector(int bank, uchar *from_addr,
int sectorcnt, int *sector, int *size, uchar **base)
{
struct flashinfo *fbnk;
struct sectorinfo *sinfo;
uchar *largest_sbase;
int i, from_sector, to_sector, largest_ssize, largest_snum;
if(bank >= FLASHBANKS) {
printf("lastlargesector(%d) failed\n",bank);
return(-1);
}
fbnk = &FlashBank[bank];
largest_ssize = 0;
largest_snum = 0;
largest_sbase = (uchar *)0;
if(from_addr) {
if(addrtosector(from_addr, &from_sector, 0, 0) == -1) {
return(-1);
}
if(fbnk->sectors[from_sector].begin != from_addr) {
printf("lastlargesector failed:\n"
" parameter from_addr (%0x08X) must be sector aligned\n",
from_addr);
return(-1);
}
} else {
from_sector = 0;
}
to_sector = from_sector + sectorcnt - 1;
if(to_sector > fbnk->sectorcnt - 1) {
to_sector = fbnk->sectorcnt - 1;
}
//printf("from_addr = 0x%08X\n", from_addr);
//printf("from_sector = %d\n", from_sector);
//printf("to_sector = %d\n", to_sector);
sinfo = &fbnk->sectors[from_sector];
for(i=from_sector; i<=to_sector; i++, sinfo++) {
if(sinfo->size >= largest_ssize) {
largest_ssize = sinfo->size;
largest_snum = sinfo->snum;
largest_sbase = sinfo->begin;
}
}
//printf("largest_sbase = 0x%08X\n", largest_sbase);
//printf("largest_snum = %d\n", largest_snum);
//printf("largest_ssize = 0x%08X\n", largest_ssize);
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(uchar *dest,uchar *src,long bytecnt)
{
int ret;
FLASH_INTSDECL;
long tmpcnt;
struct flashinfo *fbnk;
ret = 0;
while(bytecnt > 0) {
fbnk = addrtobank((uchar *)dest);
if(!fbnk) {
return(-1);
}
if((dest + bytecnt) <= fbnk->end) {
tmpcnt = bytecnt;
} else {
tmpcnt = (fbnk->end - dest) + 1;
}
FLASH_INTSOFF();
ret = flashwrite(fbnk,dest,src,tmpcnt);
FLASH_INTSRESTORE();
if(ret < 0) {
printf("AppFlashWrite(0x%lx,0x%lx,%ld) failed (%d)\n",
(ulong)dest,(ulong)src,bytecnt,ret);
break;
}
src += tmpcnt;
dest += tmpcnt;
bytecnt -= tmpcnt;
}
return(ret);
}
#if INCLUDE_FLASHREAD
/* flashread():
* Use the device-specific function pointer to call the routine
* relocated to RAM space.
* First make a few checks on the request, then read from flash
* if the checks succeed.
*/
int
flashread(struct flashinfo *fdev,uchar *dest,uchar *src,long bytecnt)
{
int rc, first_sector_of_device;
register uchar *dp, *sp, *edp;
if(FlashTrace) {
printf("flashread(0x%lx,0x%lx,%ld)\n",(long)dest,(long)src,bytecnt);
}
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;
first_sector_of_device = fdev->sectors[0].snum;
/* If outside the devices space, return failed.. */
if((edp < fdev->sectors[0].begin) ||
(dp > fdev->sectors[fdev->sectorcnt-1].end)) {
printf("flashread() failed: dest out of flash range\n");
return(-1);
}
rc = fdev->flread(fdev,dest,src,bytecnt);
if(rc < 0) {
return(rc);
}
/* Assuming everything else appears to have passed, make sure the
* source and destination addresses match...
*/
if(memcmp((char *)dest,(char *)src,(int)bytecnt) != 0) {
printf("flashread() post-verify failed.\n");
return(-1);
}
return(0);
}
/* AppFlashRead():
* 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
AppFlashRead(uchar *dest,uchar *src,long bytecnt)
{
int ret;
FLASH_INTSDECL;
long tmpcnt;
struct flashinfo *fbnk;
ret = 0;
while(bytecnt > 0) {
fbnk = addrtobank((uchar *)dest);
if(!fbnk) {
return(-1);
}
if((dest + bytecnt) <= fbnk->end) {
tmpcnt = bytecnt;
} else {
tmpcnt = (fbnk->end - dest) + 1;
}
FLASH_INTSOFF();
ret = flashread(fbnk,dest,src,tmpcnt);
FLASH_INTSRESTORE();
if(ret < 0) {
printf("AppFlashRead(0x%lx,0x%lx,%ld) failed (%d)\n",
(ulong)dest,(ulong)src,bytecnt,ret);
break;
}
src += tmpcnt;
dest += tmpcnt;
bytecnt -= tmpcnt;
}
return(ret);
}
#endif
int
lastflashsector(void)
{
int lastsnum;
struct flashinfo *lastfbnk;
lastfbnk = &FlashBank[FLASHBANKS-1];
lastsnum = lastfbnk->sectors[lastfbnk->sectorcnt-1].snum;
return(lastsnum);
}
int
AppFlashEraseAll()
{
FLASH_INTSDECL;
int ret, snum, lastsnum;
ret = 0;
FLASH_INTSOFF();
/* Loop through all sectors of all banks...
*/
lastsnum = lastflashsector();
for(snum=0; snum<=lastsnum; snum++) {
WATCHDOG_MACRO;
ret = flasherase(snum);
if(ret <= 0) {
break;
}
}
FLASH_INTSRESTORE();
return(ret);
}
/* Erase the flash sector specified. */
int
AppFlashErase(int snum) /* erase specified sector */
{
int ret;
FLASH_INTSDECL;
FLASH_INTSOFF();
ret = flasherase(snum);
FLASH_INTSRESTORE();
return(ret);
}
/* sectorProtect():
* Set or clear (based on value of protect) the protected flag for the
* specified range of sectors...
* This supports incoming ranges that can be dash and/or comma delimited.
* For example a range can be "0", "0-3", or "0,2-4", etc...
*/
int
sectorProtect(char *range, int protect)
{
struct flashinfo *fbnk;
int i, dev, snum;
snum = 0;
for(dev = 0; dev < FLASHBANKS; dev++) {
fbnk = &FlashBank[dev];
for(i = 0; i < fbnk->sectorcnt; i++,snum++) {
if((range == 0) || (*range == 0) || inRange(range,snum)) {
fbnk->sectors[i].protected = protect;
}
}
}
return(0);
}
#ifdef FLASHRAM_BASE
struct sectorinfo sinfoRAM[FLASHRAM_SECTORCOUNT];
/* FlashRamInit():
* This monitor supports TFS space allocated across multiple flash devices
* that may not be in contiguous memory space. To allow RAM 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 RAM 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. If this pointer is NULL, then
* this function sets all sector sizes to FLASHRAM_SECTORSIZE.
*/
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 = FlashOpNotSupported; /* Flashtype() function. */
fbnk->flerase = FlashOpNotSupported; /* Flasherase() function. */
fbnk->flwrite = FlashOpNotSupported; /* Flashwrite() function. */
fbnk->flewrite = FlashOpNotSupported; /* Flashewrite() function. */
fbnk->fllock = FlashOpNotSupported; /* Flashlock() function. */
fbnk->sectors = sinfo; /* Ptr to sector size table. */
begin = fbnk->base;
for(i=0; i<fbnk->sectorcnt; i++,snum++) {
sinfo[i].snum = snum;
if(ssizes == 0) {
sinfo[i].size = FLASHRAM_SECTORSIZE;
} else {
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]",
#if INCLUDE_VERBOSEHELP
"Ops...",
" opw",
" init",
" type",
" bank [#]",
" prot {rnge}",
" info [rnge]",
" unprot {rnge}",
" lock {rnge}",
" unlock [rnge]",
" lockdwn {rnge}",
" erase {rnge}",
" trace {lvl}",
" write {dest} {src} {byte_cnt}",
" ewrite {dest} {src} {byte_cnt}",
"",
" rnge = range of affected sectors",
" Range syntax examples: <1> <1-5> <1,3,7> <all>",
#endif
0,
};
/* FlashCmd():
* Code that handles the user interface. See FlashHelp[] below for usage.
*/
int
FlashCmd(int argc,char *argv[])
{
int ret;
FLASH_INTSDECL;
ulong dest, src;
long bytecnt, rslt;
struct flashinfo *fbnk;
FLASH_INTSOFF();
fbnk = &FlashBank[FlashCurrentBank];
ret = CMD_SUCCESS;
if(strcmp(argv[1],"init") == 0) {
FlashInit();
} else if(strcmp(argv[1],"info") == 0) {
showflashinfo(argv[2]);
} else if(strcmp(argv[1],"type") == 0) {
showflashtype((ulong)flashtype(fbnk),1);
} else if(strcmp(argv[1],"trace") == 0) {
if(argc == 3) {
FlashTrace = atoi(argv[2]);
} else if(argc == 2) {
printf("Flash trace lvl: %d\n",FlashTrace);
} else {
return(CMD_PARAM_ERROR);
}
} else if(strcmp(argv[1],"bank") == 0) {
int tmpbank;
if(argc == 3) {
tmpbank = atoi(argv[2]);
if(tmpbank < FLASHBANKS) {
FlashCurrentBank = tmpbank;
} else {
printf("Bank %d out of range\n",tmpbank);
return(CMD_PARAM_ERROR);
}
printf("Subsequent flash ops apply to bank %d\n",
FlashCurrentBank);
} else {
showflashtotal();
}
} 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);
rslt = flashewrite((uchar *)dest,(uchar *)src,bytecnt);
if(rslt < 0) {
printf("ewrite failed (%ld)\n",rslt);
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);
rslt = AppFlashWrite((uchar *)dest,(uchar *)src,bytecnt);
if(rslt < 0) {
printf("Write failed (%ld)\n",rslt);
ret = CMD_FAILURE;
}
} else {
ret = CMD_PARAM_ERROR;
}
}
#if INCLUDE_FLASHREAD
else if(!strcmp(argv[1],"read")) {
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);
rslt = AppFlashRead((uchar *)dest,(uchar *)src,bytecnt);
if(rslt < 0) {
printf("Read failed (%ld)\n",rslt);
ret = CMD_FAILURE;
}
} else {
ret = CMD_PARAM_ERROR;
}
}
#endif
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 {
uchar *base;
int rc, snum, size, stot = 0;
if(strncmp(argv[2],"0x",2) == 0) {
ulong begin, end;
char *dash = strchr(argv[2],'-');
begin = end = strtoul(argv[2],0,0);
if(dash) {
end = strtoul(dash+1,0,0);
}
while(begin <= end) {
if(addrtosector((uchar *)begin,&snum,&size,&base) < 0) {
break;
}
begin = (ulong)base;
rc = flasherase(snum);
if(rc != 1) {
printf("Erase failed (%d)\n",rc);
ret = CMD_FAILURE;
break;
}
stot++;
begin += size;
}
} else {
int last;
last = lastflashsector();
for(snum=0; snum<=last; snum++) {
int rc;
if((argv[2] == 0) || inRange(argv[2],snum)) {
ticktock();
rc = flasherase(snum);
if(rc != 1) {
printf("Erase failed (%d)\n",rc);
ret = CMD_FAILURE;
break;
}
stot++;
}
}
}
printf("%d sectors erased\n",stot);
}
} else if((!strcmp(argv[1],"lock")) || (!strcmp(argv[1],"unlock")) ||
(!strcmp(argv[1],"lockdwn"))) {
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 == 2) {
#ifdef FLASH_PROTECT_RANGE
argv[2] = FLASH_PROTECT_RANGE;
argc = 3;
printf("Applying %s to sector(s) %s...\n",argv[1],argv[2]);
#else
printf("Monitor not built with specified protection range\n");
ret = CMD_FAILURE;
#endif
}
if(argc != 3) {
ret = CMD_PARAM_ERROR;
} else {
int last;
struct flashinfo *fdev;
last = lastflashsector();
for(snum=0; snum<=last; snum++) {
if(inRange(argv[2],snum)) {
ticktock();
if((fdev = snumtofdev(snum)) == 0) {
ret = CMD_FAILURE;
break;
}
if(flashlock(fdev->sectors[0].snum,FLASH_LOCKABLE) <= 0) {
printf("Sector %d does not support %s\n",snum,argv[1]);
ret = CMD_FAILURE;
break;
}
rslt = flashlock(snum,operation);
if(rslt < 0) {
printf("%s failed (%ld) at sector %d\n",
argv[1],rslt,snum);
ret = CMD_FAILURE;
break;
}
}
}
}
} else {
ret = CMD_PARAM_ERROR;
}
FLASH_INTSRESTORE();
return(ret);
}
/* FlashOpOverride():
* This function is used by monlib to provide the application with the
* ability to override the underlying flash operations with functions
* provided by the application.
* The finfo parameter is actually a flashinfo pointer; set void here
* to eliminate confusion when used with monlib.h and the application .
*/
int
FlashOpOverride(void *finfo, int get, int bank)
{
char *src, *dst;
struct flashinfo *fdev;
if((!finfo) || (bank >= FLASHBANKS)) {
return(-1);
}
fdev = &FlashBank[bank];
if(get) {
src = (char *)fdev;
dst = (char *)finfo;
} else {
src = (char *)finfo;
dst = (char *)fdev;
}
memcpy(dst,src,sizeof(struct flashinfo));
return(0);
}
int
FlashOpNotSupported(void)
{
if(FlashTrace) {
printf("flash operation not supported\n");
}
return(-1);
}
/* Used as a placeholder for the flash drivers that don't
* support flash lock...
*/
int
FlashLockNotSupported(struct flashinfo *fdev,int snum,int operation)
{
if(operation == FLASH_LOCKABLE) {
return(0);
} else {
return(-1);
}
}
#endif
/* flasherased():
* Return 1 if range of memory is all 0xff; else 0.
* Scan through the range of memory 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(unsigned char *begin, unsigned char *end)
{
unsigned long *lp, *lp1, ltmp;
/* If begin is greater than end, the range is illegal. The only
* exception to this is the case where end may be zero. This is
* considered an exception because in cases where we are dealing
* with the last sector of a device that sits at the end of memory
* space, the end point will wrap.
*/
if((begin > end) && (end != 0)) {
printf("flasherased(): bad range\n");
return(0);
}
/* 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 = (unsigned long *)begin;
ltmp = (unsigned long)end;
ltmp &= ~3;
lp1 = (unsigned long *)ltmp;
while(lp != lp1) {
if(*lp != 0xffffffff) {
return(0);
}
lp++;
#ifdef WATCHDOG_ENABLED
/* For each 64K through this loop, tickle the watchdog.
*/
if((0xffff & (unsigned long)lp) == 0) {
WATCHDOG_MACRO;
}
#endif
}
if(lp > (unsigned long *)end) {
return(1);
}
begin = (unsigned char *)lp;
do {
if(*begin != 0xff) {
return(0);
}
} while(begin++ != end);
return(*end == 0xff);
}