/* Trivial Flash Programmer */ /* Author: Till Straumann , 2006 */ /* To keep things simple, this driver makes a few assumptions about the * hardware: * * - no CFI * - devices operate with 16-bit data width * - two devices are used in parallel (stride 4) to * provide 32-bit data. I.e., the devices are * organized like this: * unsigned short flash[FLASH_SIZE][2]; * - no endianness issues (i.e., flash endianness == CPU endianness) * - fixed block size * - fixed buffer size * - all devices in a bank are identical * - NOT THREAD SAFE; no locking scheme is implemented. * - cannot copy within same flash bank. * - timeout uses polling/busy-wait. * * NOTE: some attempts have been made to remove the restrictions * on stride and 16-bit width with the goal to support widths 1/2 (bytes) * and strides 1/2/4 and all (legal) combinations thereof. * However, the intel chip driver only implements stride 4 / width 2 * and other combinations are untested. */ /* * Authorship * ---------- * This software was created by * Till Straumann , 2005-2007, * Stanford Linear Accelerator Center, Stanford University. * * Acknowledgement of sponsorship * ------------------------------ * The software was produced by * the Stanford Linear Accelerator Center, Stanford University, * under Contract DE-AC03-76SFO0515 with the Department of Energy. * * Government disclaimer of liability * ---------------------------------- * Neither the United States nor the United States Department of Energy, * nor any of their employees, makes any warranty, express or implied, or * assumes any legal liability or responsibility for the accuracy, * completeness, or usefulness of any data, apparatus, product, or process * disclosed, or represents that its use would not infringe privately owned * rights. * * Stanford disclaimer of liability * -------------------------------- * Stanford University makes no representations or warranties, express or * implied, nor assumes any liability for the use of this software. * * Stanford disclaimer of copyright * -------------------------------- * Stanford University, owner of the copyright, hereby disclaims its * copyright and all other rights in this software. Hence, anyone may * freely use it for any purpose without restriction. * * Maintenance of notices * ---------------------- * In the interest of clarity regarding the origin and status of this * SLAC software, this and all the preceding Stanford University notices * are to remain affixed to any copy or derivative of this software made * or distributed by the recipient and are to be affixed to any copy of * software made or distributed by the recipient that contains a copy or * derivative of this software. * * ------------------ SLAC Software Notices, Set 4 OTT.002a, 2004 FEB 03 */ #ifndef TESTING #include #include #include #else #include "flashPgm.h" #include "flashPgmPvt.h" #endif #include #include #include #include #include #include #define DEBUG 0 #undef DEBUG #ifdef DEBUG #define STATIC #else #define STATIC static #endif /* Forward decls. */ STATIC uint32_t BSP_flashProbeSize(struct bankdesc *b); STATIC struct bankdesc * bankValidate(int bank, int quiet); static struct bankdesc * argcheck(int bank, uint32_t offset, const char *src, uint32_t size); /* Type definitions */ union bconv { uint32_t u; uint16_t s[2]; char c[4]; }; /* Little helpers... */ /* Read parallel devices */ static void rd_par(struct bankdesc *b, union bconv *pv, uint32_t a) { if ( 4 == FLASH_STRIDE(b) ) { pv->u = *(_u32_a_t*)a; } else if ( 2 == FLASH_STRIDE(b) ) { pv->s[0] = *(_u16_a_t*)a; } else { pv->c[0] = *(_u8_a_t*)a; } } /* 'flush' input buffer and get an upper-case char from stdin */ STATIC int getUc(void) { fseek(stdin, 0, SEEK_END); return toupper(getchar()); } /* Advance rotating progress indicator on stdout. * The idea is that the caller updates a state variable * using 'flip': * * unsigned f = 0; * * // advance indicator * f = flip(f); * * ... * * // erase indicator * wipe(f); */ static unsigned flip(unsigned x) { static char bar[]= { '/', '-', '\\', '|' }; putchar('\b'); putchar(bar[ x & 3]); fflush(stdout); return x+1; } /* If f!=0 then erase one char on stdout. * ( If 'flip' was never called then f should still * be zero and no action is taken). */ static void wipe(unsigned f) { if ( f ) { putchar('\b'); putchar(' '); putchar('\b'); fflush(stdout); } } /* lookup vendor ID in table of known vendors using ops * associated with the vendor. */ STATIC struct vendesc * knownVendor(struct bankdesc *b, uint32_t addr, uint32_t *pd, unsigned quiet) { uint32_t v; struct vendesc *rval; for ( rval=b->knownVendors; rval->name; rval++ ) { if ( rval->ops->get_id(b, addr, &v, pd) ) { if ( quiet < 2 ) fprintf(stderr,"Unable to read vendor/device info at 0x%08"PRIx32"\n", addr); return 0; } if ( rval->id == v ) { return rval; } } if ( quiet < 2 ) fprintf(stderr,"Unknown vendor id (0x%04"PRIx32") at 0x%08"PRIx32"\n",v, addr); return 0; } /* lookup device ID in table of known devices */ STATIC struct devdesc * knownDevice(struct vendesc *v, uint32_t d) { struct devdesc *rval; for ( rval=v->known_devs; rval->name; rval++ ) { if ( rval->id == d ) { return rval; } } return 0; } /* Write 'n_words' (32-bit) from 'src' to 'addr'ess on flash. * (src is a char* to emphasize that no src-alignment is required) * * RETURNS: 0 on success, (destination) address of failure on error. * * NOTES: - device switched back to array mode on exit. * - 'addr' must be 32-bit aligned. */ STATIC uint32_t BSP_flashWriteDataRaw(struct bankdesc *b, uint32_t addr, const char *src, uint32_t n_words, int quiet) { uint32_t sta; uint32_t N; uint32_t nxt, a, i, bufsz; uint32_t then, now; unsigned f; const char *s; #ifdef DEBUG printf("\nflashWriteDataRaw(0x%08"PRIx32", %p, 0x%"PRIx32")\n", addr, src, n_words); #endif if ( 0 == n_words ) { return 0; } if ( !b ) { fprintf(stderr,"Missing bank descriptor argument\n"); return -1; } if ( !b->dd ) { fprintf(stderr,"Bank descriptor not initialized\n"); return -1; } if ( addr & (FLASH_STRIDE(b)-1) ) { fprintf(stderr,"Misaligned address (not on word boundary) 0x%08"PRIx32"\n", addr); return -1; } if ( (sta = b->ops->check_ready(b, addr)) ) { /* Error msgs have already been printed */ return addr; } bufsz = FLASH_NDEVS(b) * b->dd->bufsz; then = BSP_flashBspOps.read_us_timer(); for ( f = 0, a = addr, s=src, i = n_words; i ; s+=N ) { /* start of next buffer */ nxt = (a + bufsz) & ~(bufsz-1); /* number of bytes */ N = (nxt - a); if ( N > i * FLASH_STRIDE(b) ) N = i * FLASH_STRIDE(b); i -= N/FLASH_STRIDE(b); if ( (sta = b->ops->write_line(b, a, s, N)) ) goto bail; if ( ! quiet && (now = BSP_flashBspOps.read_us_timer()) - then > 500000 ) { f = flip(f); then = now; } a = nxt; } sta = 0; /* verify */ for ( i=0, a=addr; i < n_words * FLASH_STRIDE(b); i++, a++ ) { if ( *(char*)a != src[i] ) { sta = -2; goto bail; } } bail: if ( ! quiet ) { wipe(f); } if ( sta ) { switch ( sta ) { default: fprintf(stderr,"Error (flashWriteDataRaw): write error\n"); b->ops->print_stat(b, sta,0); break; case -1: fprintf(stderr,"Error (flashWriteDataRaw): Timeout\n"); break; case -2: fprintf(stderr,"Error (flashWriteDataRaw): write verification failed at 0x%08"PRIx32"\n", a); break; } b->ops->array_mode(b, a); } else { /* no errors */ a = 0; } return a; } /* Query device for basic information verifying that we talk * to a 'known'/'supported' device. * * This is not really clean since (until we implement CFI) * we already need to know what kind of device it is to * be able to read its ID... * * NOTES: - device switched back to array mode on exit. * - 'addr' must be 32-bit aligned. */ STATIC struct devdesc * BSP_flashCheckId(struct bankdesc *b, uint32_t addr, unsigned quiet) { uint8_t x; uint32_t d; struct vendesc *vd; struct devdesc *dd; /* check if it's flash at all: */ x = *(A8)addr; *(A8)addr = ~x; if ( x != *(A8)addr ) { /* restore */ *(A8)addr = x; if ( quiet < 3 ) fprintf(stderr,"Addr 0x%08"PRIx32" seems to be RAM!\n", addr); return 0; } if ( !(vd = knownVendor(b, addr, &d, quiet)) ) { return 0; } /* Use the vendor ops for this bank */ b->ops = vd->ops; if ( !quiet ) printf("Flash device, vendor: %s", vd->name); if ( !(dd = knownDevice(vd, d)) ) { if ( !quiet ) printf("\n"); if ( quiet < 2 ) fprintf(stderr,"Unknown device id (0x%04"PRIx32") at 0x%08"PRIx32"\n",d, addr); return 0; } /* logical sector size is device sector size * multiplied by # of devices in parallel */ b->fblksz = dd->fblksz * FLASH_NDEVS(b); if ( !quiet ) printf(", device: %s -- size 0x%08"PRIx32" bytes\n", dd->name, dd->size); return dd; } /* We don't have device info yet so just * use 64k alignment */ #define SCAN_BACK_OFFSET 0x10000 /* Scan address range for flash devices and compute total size * of bank. * * RETURNS: size in bytes. */ STATIC uint32_t BSP_flashProbeSize(struct bankdesc *b) { int max = b->max_size; uint32_t rval; struct devdesc *dd; unsigned q; if ( max > 0 ) { for ( rval = 0, q=1; rval < max && (dd = BSP_flashCheckId(b, b->start + rval, q)); q=3 ) { rval += dd->size * FLASH_NDEVS(b); } } else { /* bank is populated from the top; scan backwards */ max = -max; for ( rval = 0, q=1; rval < max && (dd = BSP_flashCheckId(b, b->start + max - SCAN_BACK_OFFSET - rval, q)); q=3 ) { rval += dd->size * FLASH_NDEVS(b); } b->start += max - rval; } return rval; } uint32_t BSP_flashStart(int bank) { struct bankdesc *b; if ( ! ( b = argcheck(bank, 0, 0, 0) ) ) return -1; return b->start; } uint32_t BSP_flashSize(int bank) { struct bankdesc *b; if ( ! ( b = argcheck(bank, 0, 0, 0) ) ) return -1; return b->size; } uint32_t BSP_flashBlockSize(int bank) { struct bankdesc *b; if ( ! ( b = argcheck(bank, 0, 0, 0) ) ) return -1; return b->fblksz; } #ifndef TESTING /* Obtain bank description making sure it is initialized and not write protected */ STATIC struct bankdesc * bankValidate(int bank, int quiet) { struct bankdesc *b = BSP_flashBspOps.bankcheck(bank, quiet); /* If flash is write-protected then we can't even talk to it... */ if ( BSP_flashBspOps.flash_wp(bank, -1) ) { fprintf(stderr,"Flash bank #%i is write-protected; use 'BSP_flashWriteEnable(int bank)' and/or jumper\n", bank); return 0; } if ( !b->dd && !(b->dd = BSP_flashCheckId(b, b->start,1)) ) { fprintf(stderr,"Error: unable to detect flash device in bank #%i\n", bank); return 0; } return b; } /* Validate arguments and write-protection status of 'bank'. * * 'bank': 0..max bank supported by board. * 'offset': 0..bank size - 1 * 'src': src .. src + size - 1 must not overlap bank; 'src' may be NULL * (check is skipped in this case) * * RETURNS: pointer to bank description on success, NULL on error (invalid args; * error message is printed to stderr). * * SIDE EFFECTS: probes for bank size and stores result in bank description table. */ static struct bankdesc * argcheck(int bank, uint32_t offset, const char *src, uint32_t size) { struct bankdesc *b; if ( !(b=bankValidate(bank, 0)) ) { return 0; } if ( !b->size && !(b->size = BSP_flashProbeSize(b)) ) { fprintf(stderr,"Configuration Error - unable to determine flash size\n"); return 0; } if ( offset + size > b->size ) { fprintf(stderr,"Error: requested size exceeds available flash (0x%08"PRIx32" bytes)\n", b->size); return 0; } if ( src && ( src + size > (char*)b->start && src < (char*)(b->start + b->size) ) ) { fprintf(stderr,"Error: cannot copy data within flash bank\n"); return 0; } return b; } /* Calculate region that needs to be erased from 'offset' and 'n_bytes' * handling alignment and checking for blank areas that need not be * erased. * Ask for user confirmation and erase calculated region. * * RETURNS: 0 on success, -1 or destination address on error. * * NOTES: - device switched back to array mode on exit. * - prints progress/messages. */ STATIC int regionCheckAndErase(int bank, uint32_t offset, const char *src, uint32_t n_bytes, int quiet) { struct bankdesc *b; uint32_t i; char *p; uint32_t a,e; if ( ! (b = argcheck(bank, offset, src, n_bytes)) ) return -1; a = offset & ~(b->fblksz - 1); e = (offset + n_bytes + b->fblksz - 1) & ~ (b->fblksz - 1); /* If 'offset' is not block-aligned then rest of the block must * be free. */ if ( a != offset ) { a += b->fblksz; i = ( a > offset + n_bytes ) ? offset + n_bytes : a; for ( p = (char*)(b->start + offset); p < (char*)(b->start + i); p++ ) { if ( (char)0xff != *p ) { if ( ! quiet ) { fprintf(stderr,"Starting offset not block-aligned and destination area not empty.\n"); fprintf(stderr,"I'll need to erase data below destination start\n"); } a -= b->fblksz; break; } } } if ( e != offset + n_bytes ) { e -= b->fblksz; i = ( e < offset ) ? offset : e; for ( p = (char*)(b->start + i); p < (char*)(b->start + offset + n_bytes); p++ ) { if ( (char)0xff != *p ) { if ( ! quiet ) { fprintf(stderr,"Ending offset not block-aligned and destination area not empty.\n"); fprintf(stderr,"I'll need to erase data beyond destination end\n"); } e += b->fblksz; break; } } } if ( ! quiet ) { if ( e > a ) printf("ERASING 0x%08"PRIx32" .. 0x%08"PRIx32"\n", (b->start+a), (b->start + e - 1)); printf("WRITING 0x%08"PRIx32" .. 0x%08"PRIx32"\n", (b->start+offset), (b->start + offset + n_bytes - 1)); printf("OK to proceed y/[n]?"); fflush(stdout); if ( 'Y' != getUc() ) { printf("ABORTED\n"); return -1; } } if ( e > a ) { if ( quiet < 2 ) { printf("ERASING "); fflush(stdout); } if ( (i = BSP_flashErase(bank, a, e-a, quiet ? quiet : 1)) ) return i; if ( quiet < 2 ) { printf("DONE\n"); } } return 0; } /* Write to a flash region ('offset'..'offset'+'n_bytes'-1 on 'bank'). * * RETURNS: 0 on success, -1 or destination address on error. * * NOTES: - device switched back to array mode on exit. * - no erase operation is performed. * - written data is verified against source. */ STATIC int BSP_flashWriteRegion(int bank, uint32_t offset, const char *src, uint32_t n_bytes, int quiet) { struct bankdesc *b = BSP_flashBspOps.bankcheck(bank, 0); /* caller did bankValidate() */ uint32_t ab = offset & ~(b->fblksz - 1); uint32_t eb = (offset + n_bytes + b->fblksz - 1) & ~(b->fblksz - 1); uint32_t o,i,a,e; int err; const char *p; union bconv buf; /* unlock */ for ( i=ab; ifblksz ) { b->ops->unlock_block(b, b->start + i ); } err = 0; p = src; /* handle misaligned offset merging old contents */ o = b->start + offset; a = o & ~(FLASH_STRIDE(b)-1); e = (o + n_bytes) & ~(FLASH_STRIDE(b)-1); if ( o > a ) { i = o - a; rd_par(b, &buf, a); while ( i < FLASH_STRIDE(b) && p < src + n_bytes ) { buf.c[i++] = *p++; } if ( (err = BSP_flashWriteDataRaw(b, a, buf.c, 1, quiet)) ) goto bail; a += FLASH_STRIDE(b); } /* caution if misaligned data covering only one or two words */ if ( e > a ) { i = (e-a); if ( (err = BSP_flashWriteDataRaw(b, a, p, i/FLASH_STRIDE(b), quiet)) ) goto bail; p += i; } /* handle misaligned end */ if ( o + n_bytes > e) { rd_par(b, &buf, e); for ( i=0; p < src + n_bytes; ) { buf.c[i++] = *p++; } if ( (err = BSP_flashWriteDataRaw(b, e, buf.c, 1, quiet)) ) goto bail; } bail: /* lock area */ for ( i=ab; ifblksz ) { b->ops->lock_block(b, b->start + i ); } /* final verification */ if ( !err ) { for ( i=0; istart + offset))[i] != src[i] ) { fprintf(stderr,"Final verification failed at offset 0x%08"PRIx32"\n", (offset + i)); return b->start + offset + i; } } } return err; } int BSP_flashErase(int bank, uint32_t offset, uint32_t size, int quiet) { struct bankdesc *b; uint32_t a,i; int f; if ( ! (b = argcheck(bank, offset, 0, size)) ) return -1; if ( offset & (b->fblksz - 1) ) { fprintf(stderr,"Offset misaligned (needs to be multiple of 0x%08"PRIx32")\n", b->fblksz); return -1; } if ( size & (b->fblksz - 1) ) { fprintf(stderr,"Size misaligned (needs to be multiple of 0x%08"PRIx32")\n", b->fblksz); return -1; } a = b->start + offset; if ( !quiet ) { printf("ERASING Flash (Bank #%i)\n from 0x%08"PRIx32" .. 0x%08"PRIx32"\nproceed y/[n]?", bank, a, (a+size-1)); fflush(stdout); if ( 'Y' != getUc() ) { printf("ABORTED\n"); return -1; } } f = 0; while ( size ) { /* work to do ? */ for ( i = 0; ifblksz; i++ ) { if ( (char)0xff != ((char*)a)[i] ) { b->ops->unlock_block(b, a); i = b->ops->erase_block(b, a); b->ops->lock_block(b, a); if (i) { wipe(f); return a; } break; } } if ( quiet < 2 ) { f = flip(f); } a += b->fblksz; size -= b->fblksz; } b->ops->array_mode(b, a); if ( quiet < 2 ) { wipe(f); } return 0; } int BSP_flashWrite(int bank, uint32_t offset, const char *src, uint32_t n_bytes, int quiet) { int rval; if ( !src ) { fprintf(stderr,"Error: Data source pointer is NULL\n"); return -1; } if ( (rval = regionCheckAndErase(bank, offset, src, n_bytes, quiet)) ) return rval; if ( ! quiet ) { printf("WRITING "); fflush(stdout); } rval = BSP_flashWriteRegion(bank, offset, src, n_bytes, quiet); if ( !quiet && !rval ) { printf("DONE"); } if ( !quiet ) printf("\n"); return rval; } static int bfill(int fd, char *buf, int size) { int got, avail; for (avail = size; (got = read(fd, buf, avail)) > 0; avail-=got ) { buf += got; } return size - avail; } int BSP_flashWriteFile(int bank, uint32_t offset, const char *fname, int quiet) { int fd = -1; struct stat sb; uint32_t sz; int rval = -1; char *buf = 0; uint32_t got; struct bankdesc *b; unsigned f = 0; if ( ! (b = bankValidate(bank, 0)) ) return -1; for ( sz = 0; -1 == fd ; ) { if ( (fd = open(fname,O_RDONLY)) < 0 ) { perror("Opening file"); return -1; } if ( sz ) break; if ( fstat(fd, &sb) ) { fprintf(stderr,"Warning: fstat doesn't work; need to slurp file to determine size; please be patient.\n"); FILE *f; close(fd); fd = -1; f = fopen(fname,"r"); if ( !f ) { perror("fdopen"); return -1; } while ( EOF != fgetc(f) ) sz++; fclose(f); /* reopen */ } else { sz = sb.st_size; } if ( 0 == sz ) { fprintf(stderr,"Error: zero file size (?)\n"); goto bail; } } if ( !(buf = malloc(b->fblksz)) ) { perror("buffer allocation"); goto bail; } /* See if we can erase the entire region */ if ( (rval = regionCheckAndErase(bank, offset, buf, sz, quiet)) ) goto bail; /* Proceed copying chunks */ if ( quiet < 2 ) { printf("WRITING "); fflush(stdout); } while ( (got = bfill(fd, buf, b->fblksz)) > 0 && sz ) { if ( (rval = BSP_flashWriteRegion(bank, offset, buf, got, 1)) ) { wipe(f); goto bail; } offset += got; sz -= got; if ( quiet < 2 ) { f = flip(f); } } if ( got < 0 ) { perror("reading file"); rval = offset; goto bail; } if ( quiet < 2 ) { wipe(f); printf("DONE"); } bail: if ( quiet < 2 ) { printf("\n"); } if ( fd > -1 ) close(fd); free(buf); return rval; } int BSP_flashWriteEnable(int bank) { return BSP_flashBspOps.flash_wp(bank,0); } int BSP_flashWriteDisable(int bank) { return BSP_flashBspOps.flash_wp(bank,1); } int BSP_flashDumpInfo(FILE *f) { struct bankdesc *b; int bank; if ( !f ) f = stdout; /* use 'bankValidate()' with 'quiet' flag to suppress error message when * we reach the end of the table. */ for ( bank = 0; BSP_flashBspOps.bankcheck(bank,1); bank++ ) { if ( (b=argcheck(bank,0,0,0)) ) { fprintf(f,"Flash Bank #%i; 0x%08"PRIx32" .. 0x%08"PRIx32" (%"PRId32" bytes)\n", bank, b->start, (b->start + b->size - 1), b->size); fprintf(f,"%i * %i-bit devices in parallel; block size 0x%"PRIx32"\n", FLASH_NDEVS(b), FLASH_WIDTH(b)*8, b->fblksz); BSP_flashCheckId(b, b->start, 0); } } return 0; } #else int main(int argc, char **argv) { uint32_t fla[1000]; uint32_t qqq[1000]; int i; for ( i=0; i