123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876 |
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "io.h"
- #include "../port/error.h"
- /*
- * on the bitsy, all 32 bit accesses to flash are mapped to two 16 bit
- * accesses, one to the low half of the chip and the other to the high
- * half. Therefore for all command accesses, ushort indices in the
- * manuals turn into ulong indices in our code. Also, by copying all
- * 16 bit commands to both halves of a 32 bit command, we erase 2
- * sectors for each request erase request.
- */
- #define mirror(x) (((x)<<16)|(x))
- /* this defines a contiguous set of erase blocks of one size */
- typedef struct FlashRegion FlashRegion;
- struct FlashRegion
- {
- ulong addr; /* start of region */
- ulong end; /* end of region + 1 */
- ulong n; /* number of blocks */
- ulong size; /* size of each block */
- };
- /* this defines a particular access algorithm */
- typedef struct FlashAlg FlashAlg;
- struct FlashAlg
- {
- int id;
- char *name;
- void (*identify)(void); /* identify device */
- void (*erase)(ulong); /* erase a region */
- void (*write)(void*, long, ulong); /* write a region */
- };
- static void ise_id(void);
- static void ise_erase(ulong);
- static void ise_write(void*, long, ulong);
- static void afs_id(void);
- static void afs_erase(ulong);
- static void afs_write(void*, long, ulong);
- static ulong blockstart(ulong);
- static ulong blockend(ulong);
- FlashAlg falg[] =
- {
- { 1, "Intel/Sharp Extended", ise_id, ise_erase, ise_write },
- { 2, "AMD/Fujitsu Standard", afs_id, afs_erase, afs_write },
- };
- struct
- {
- RWlock;
- ulong *p;
- ushort algid; /* access algorithm */
- FlashAlg *alg;
- ushort manid; /* manufacturer id */
- ushort devid; /* device id */
- ulong size; /* size in bytes */
- int wbsize; /* size of write buffer */
- ulong nr; /* number of regions */
- uchar bootprotect;
- FlashRegion r[32];
- } flash;
- enum
- {
- Maxwchunk= 1024, /* maximum chunk written by one call to falg->write */
- };
- /*
- * common flash interface
- */
- static uchar
- cfigetc(int off)
- {
- uchar rv;
- flash.p[0x55] = mirror(0x98);
- rv = flash.p[off];
- flash.p[0x55] = mirror(0xFF);
- return rv;
- }
- static ushort
- cfigets(int off)
- {
- return (cfigetc(off+1)<<8)|cfigetc(off);
- }
- static ulong
- cfigetl(int off)
- {
- return (cfigetc(off+3)<<24)|(cfigetc(off+2)<<16)|
- (cfigetc(off+1)<<8)|cfigetc(off);
- }
- static void
- cfiquery(void)
- {
- uchar q, r, y;
- ulong x, addr;
- q = cfigetc(0x10);
- r = cfigetc(0x11);
- y = cfigetc(0x12);
- if(q != 'Q' || r != 'R' || y != 'Y'){
- print("cfi query failed: %ux %ux %ux\n", q, r, y);
- return;
- }
- flash.algid = cfigetc(0x13);
- flash.size = 1<<(cfigetc(0x27)+1);
- flash.wbsize = 1<<(cfigetc(0x2a)+1);
- flash.nr = cfigetc(0x2c);
- if(flash.nr > nelem(flash.r)){
- print("cfi reports > %d regions\n", nelem(flash.r));
- flash.nr = nelem(flash.r);
- }
- addr = 0;
- for(q = 0; q < flash.nr; q++){
- x = cfigetl(q+0x2d);
- flash.r[q].size = 2*256*(x>>16);
- flash.r[q].n = (x&0xffff)+1;
- flash.r[q].addr = addr;
- addr += flash.r[q].size*flash.r[q].n;
- flash.r[q].end = addr;
- }
- }
- /*
- * flash device interface
- */
- enum
- {
- Qtopdir,
- Q2nddir,
- Qfctl,
- Qfdata,
- Maxpart= 8,
- };
- typedef struct FPart FPart;
- struct FPart
- {
- char *name;
- char *ctlname;
- ulong start;
- ulong end;
- };
- static FPart part[Maxpart];
- #define FQID(p,q) ((p)<<8|(q))
- #define FTYPE(q) ((q) & 0xff)
- #define FPART(q) (&part[(q) >>8])
- static int
- gen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
- {
- Qid q;
- FPart *fp;
- q.vers = 0;
- /* top level directory contains the name of the network */
- if(c->qid.path == Qtopdir){
- switch(i){
- case DEVDOTDOT:
- q.path = Qtopdir;
- q.type = QTDIR;
- devdir(c, q, "#F", 0, eve, DMDIR|0555, dp);
- break;
- case 0:
- q.path = Q2nddir;
- q.type = QTDIR;
- devdir(c, q, "flash", 0, eve, DMDIR|0555, dp);
- break;
- default:
- return -1;
- }
- return 1;
- }
- /* second level contains all partitions and their control files */
- switch(i) {
- case DEVDOTDOT:
- q.path = Qtopdir;
- q.type = QTDIR;
- devdir(c, q, "#F", 0, eve, DMDIR|0555, dp);
- break;
- default:
- if(i >= 2*Maxpart)
- return -1;
- fp = &part[i>>1];
- if(fp->name == nil)
- return 0;
- if(i & 1){
- q.path = FQID(i>>1, Qfdata);
- q.type = QTFILE;
- devdir(c, q, fp->name, fp->end-fp->start, eve, 0660, dp);
- } else {
- q.path = FQID(i>>1, Qfctl);
- q.type = QTFILE;
- devdir(c, q, fp->ctlname, 0, eve, 0660, dp);
- }
- break;
- }
- return 1;
- }
- static FPart*
- findpart(char *name)
- {
- int i;
- for(i = 0; i < Maxpart; i++)
- if(part[i].name != nil && strcmp(name, part[i].name) == 0)
- break;
- if(i >= Maxpart)
- return nil;
- return &part[i];
- }
- static void
- addpart(FPart *fp, char *name, ulong start, ulong end)
- {
- int i;
- char ctlname[64];
- if(fp == nil){
- if(start >= flash.size || end > flash.size)
- error(Ebadarg);
- } else {
- start += fp->start;
- end += fp->start;
- if(start >= fp->end || end > fp->end)
- error(Ebadarg);
- }
- if(blockstart(start) != start)
- error("must start on erase boundary");
- if(blockstart(end) != end && end != flash.size)
- error("must end on erase boundary");
- fp = findpart(name);
- if(fp != nil)
- error(Eexist);
- for(i = 0; i < Maxpart; i++)
- if(part[i].name == nil)
- break;
- if(i == Maxpart)
- error("no more partitions");
- fp = &part[i];
- kstrdup(&fp->name, name);
- snprint(ctlname, sizeof ctlname, "%sctl", name);
- kstrdup(&fp->ctlname, ctlname);
- fp->start = start;
- fp->end = end;
- }
- static void
- rempart(FPart *fp)
- {
- char *p, *cp;
- p = fp->name;
- fp->name = nil;
- cp = fp->ctlname;
- fp->ctlname = nil;
- free(p);
- free(cp);
- }
- void
- flashinit(void)
- {
- int i;
- flash.p = (ulong*)FLASHZERO;
- cfiquery();
- for(i = 0; i < nelem(falg); i++)
- if(flash.algid == falg[i].id){
- flash.alg = &falg[i];
- (*flash.alg->identify)();
- break;
- }
- flash.bootprotect = 1;
- addpart(nil, "flash", 0, flash.size);
- }
- static Chan*
- flashattach(char* spec)
- {
- return devattach('F', spec);
- }
- static Walkqid*
- flashwalk(Chan *c, Chan *nc, char **name, int nname)
- {
- return devwalk(c, nc, name, nname, nil, 0, gen);
- }
- static int
- flashstat(Chan *c, uchar *db, int n)
- {
- return devstat(c, db, n, nil, 0, gen);
- }
- static Chan*
- flashopen(Chan* c, int omode)
- {
- omode = openmode(omode);
- if(strcmp(up->user, eve)!=0)
- error(Eperm);
- return devopen(c, omode, nil, 0, gen);
- }
- static void
- flashclose(Chan*)
- {
- }
- static long
- flashctlread(FPart *fp, void* a, long n, vlong off)
- {
- char *buf, *p, *e;
- int i;
- ulong addr, end;
- buf = smalloc(1024);
- e = buf + 1024;
- p = seprint(buf, e, "0x%-9lux 0x%-9x 0x%-9ux 0x%-9ux\n", fp->end-fp->start,
- flash.wbsize, flash.manid, flash.devid);
- addr = fp->start;
- for(i = 0; i < flash.nr && addr < fp->end; i++)
- if(flash.r[i].addr <= addr && flash.r[i].end > addr){
- if(fp->end <= flash.r[i].end)
- end = fp->end;
- else
- end = flash.r[i].end;
- p = seprint(p, e, "0x%-9lux 0x%-9lux 0x%-9lux\n", addr,
- (end-addr)/flash.r[i].size, flash.r[i].size);
- addr = end;
- }
- n = readstr(off, a, n, buf);
- free(buf);
- return n;
- }
- static long
- flashdataread(FPart *fp, void* a, long n, vlong off)
- {
- rlock(&flash);
- if(waserror()){
- runlock(&flash);
- nexterror();
- }
- if(fp->name == nil)
- error("partition vanished");
- if(!iseve())
- error(Eperm);
- off += fp->start;
- if(off >= fp->end)
- n = 0;
- if(off+n >= fp->end)
- n = fp->end - off;
- if(n > 0)
- memmove(a, ((uchar*)FLASHZERO)+off, n);
- runlock(&flash);
- poperror();
- return n;
- }
- static long
- flashread(Chan* c, void* a, long n, vlong off)
- {
- int t;
- if(c->qid.type == QTDIR)
- return devdirread(c, a, n, nil, 0, gen);
- t = FTYPE(c->qid.path);
- switch(t){
- default:
- error(Eperm);
- case Qfctl:
- n = flashctlread(FPART(c->qid.path), a, n, off);
- break;
- case Qfdata:
- n = flashdataread(FPART(c->qid.path), a, n, off);
- break;
- }
- return n;
- }
- static void
- bootprotect(ulong addr)
- {
- FlashRegion *r;
- if(flash.bootprotect == 0)
- return;
- if(flash.nr == 0)
- error("writing over boot loader disallowed");
- r = flash.r;
- if(addr >= r->addr && addr < r->addr + r->size)
- error("writing over boot loader disallowed");
- }
- static ulong
- blockstart(ulong addr)
- {
- FlashRegion *r, *e;
- ulong x;
- r = flash.r;
- for(e = &flash.r[flash.nr]; r < e; r++)
- if(addr >= r->addr && addr < r->end){
- x = addr - r->addr;
- x /= r->size;
- return r->addr + x*r->size;
- }
-
- return (ulong)-1;
- }
- static ulong
- blockend(ulong addr)
- {
- FlashRegion *r, *e;
- ulong x;
- r = flash.r;
- for(e = &flash.r[flash.nr]; r < e; r++)
- if(addr >= r->addr && addr < r->end){
- x = addr - r->addr;
- x /= r->size;
- return r->addr + (x+1)*r->size;
- }
-
- return (ulong)-1;
- }
- static long
- flashctlwrite(FPart *fp, char *p, long n)
- {
- Cmdbuf *cmd;
- ulong off;
- if(fp == nil)
- panic("flashctlwrite");
- cmd = parsecmd(p, n);
- wlock(&flash);
- if(waserror()){
- wunlock(&flash);
- nexterror();
- }
- if(strcmp(cmd->f[0], "erase") == 0){
- switch(cmd->nf){
- case 2:
- /* erase a single block in the partition */
- off = atoi(cmd->f[1]);
- off += fp->start;
- if(off >= fp->end)
- error("region not in partition");
- if(off != blockstart(off))
- error("erase must be a block boundary");
- bootprotect(off);
- (*flash.alg->erase)(off);
- break;
- case 1:
- /* erase the whole partition */
- bootprotect(fp->start);
- for(off = fp->start; off < fp->end; off = blockend(off))
- (*flash.alg->erase)(off);
- break;
- default:
- error(Ebadarg);
- }
- } else if(strcmp(cmd->f[0], "add") == 0){
- if(cmd->nf != 4)
- error(Ebadarg);
- addpart(fp, cmd->f[1], strtoul(cmd->f[2], nil, 0), strtoul(cmd->f[3], nil, 0));
- } else if(strcmp(cmd->f[0], "remove") == 0){
- rempart(fp);
- } else if(strcmp(cmd->f[0], "protectboot") == 0){
- if(cmd->nf == 0 || strcmp(cmd->f[1], "off") != 0)
- flash.bootprotect = 1;
- else
- flash.bootprotect = 0;
- } else
- error(Ebadarg);
- poperror();
- wunlock(&flash);
- free(cmd);
- return n;
- }
- static long
- flashdatawrite(FPart *fp, uchar *p, long n, long off)
- {
- uchar *end;
- int m;
- int on;
- long ooff;
- uchar *buf;
- if(fp == nil)
- panic("flashctlwrite");
- buf = nil;
- wlock(&flash);
- if(waserror()){
- wunlock(&flash);
- if(buf != nil)
- free(buf);
- nexterror();
- }
- if(fp->name == nil)
- error("partition vanished");
- if(!iseve())
- error(Eperm);
- /* can't cross partition boundaries */
- off += fp->start;
- if(off >= fp->end || off+n > fp->end || n <= 0)
- error(Ebadarg);
- /* make sure we're not writing the boot sector */
- bootprotect(off);
- on = n;
- /*
- * get the data into kernel memory to avoid faults during writing.
- * if write is not on a quad boundary or not a multiple of 4 bytes,
- * extend with data already in flash.
- */
- buf = smalloc(n+8);
- m = off & 3;
- if(m){
- *(ulong*)buf = flash.p[(off)>>2];
- n += m;
- off -= m;
- }
- if(n & 3){
- n -= n & 3;
- *(ulong*)(&buf[n]) = flash.p[(off+n)>>2];
- n += 4;
- }
- memmove(&buf[m], p, on);
- /* (*flash.alg->write) can't cross blocks */
- ooff = off;
- p = buf;
- for(end = p + n; p < end; p += m){
- m = blockend(off) - off;
- if(m > end - p)
- m = end - p;
- if(m > Maxwchunk)
- m = Maxwchunk;
- (*flash.alg->write)(p, m, off);
- off += m;
- }
- /* make sure write succeeded */
- if(memcmp(buf, &flash.p[ooff>>2], n) != 0)
- error("written bytes don't match");
- wunlock(&flash);
- free(buf);
- poperror();
- return on;
- }
- static long
- flashwrite(Chan* c, void* a, long n, vlong off)
- {
- int t;
- if(c->qid.type == QTDIR)
- error(Eperm);
- if(!iseve())
- error(Eperm);
- t = FTYPE(c->qid.path);
- switch(t){
- default:
- panic("flashwrite");
- case Qfctl:
- n = flashctlwrite(FPART(c->qid.path), a, n);
- break;
- case Qfdata:
- n = flashdatawrite(FPART(c->qid.path), a, n, off);
- break;
- }
- return n;
- }
- Dev flashdevtab = {
- 'F',
- "flash",
- devreset,
- flashinit,
- devshutdown,
- flashattach,
- flashwalk,
- flashstat,
- flashopen,
- devcreate,
- flashclose,
- flashread,
- devbread,
- flashwrite,
- devbwrite,
- devremove,
- devwstat,
- };
- enum
- {
- /* status register */
- ISEs_lockerr= 1<<1,
- ISEs_powererr= 1<<3,
- ISEs_progerr= 1<<4,
- ISEs_eraseerr= 1<<5,
- ISEs_ready= 1<<7,
- ISEs_err= (ISEs_lockerr|ISEs_powererr|ISEs_progerr|ISEs_eraseerr),
- /* extended status register */
- ISExs_bufavail= 1<<7,
- };
- /* intel/sharp extended command set */
- static void
- ise_reset(void)
- {
- flash.p[0x55] = mirror(0xff); /* reset */
- }
- static void
- ise_id(void)
- {
- ise_reset();
- flash.p[0x555] = mirror(0x90); /* uncover vendor info */
- flash.manid = flash.p[00];
- flash.devid = flash.p[01];
- ise_reset();
- }
- static void
- ise_clearerror(void)
- {
- flash.p[0x100] = mirror(0x50);
- }
- static void
- ise_error(int bank, ulong status)
- {
- char err[64];
- if(status & (ISEs_lockerr)){
- sprint(err, "flash%d: block locked %lux", bank, status);
- error(err);
- }
- if(status & (ISEs_powererr)){
- sprint(err, "flash%d: low prog voltage %lux", bank, status);
- error(err);
- }
- if(status & (ISEs_progerr|ISEs_eraseerr)){
- sprint(err, "flash%d: i/o error %lux", bank, status);
- error(err);
- }
- }
- static void
- ise_erase(ulong addr)
- {
- ulong start;
- ulong x;
- addr >>= 2; /* convert to ulong offset */
- flashprogpower(1);
- flash.p[addr] = mirror(0x20);
- flash.p[addr] = mirror(0xd0);
- start = m->ticks;
- do {
- x = flash.p[addr];
- if((x & mirror(ISEs_ready)) == mirror(ISEs_ready))
- break;
- } while(TK2MS(m->ticks-start) < 1500);
- flashprogpower(0);
- ise_clearerror();
- ise_error(0, x);
- ise_error(1, x>>16);
- ise_reset();
- }
- /*
- * the flash spec claimes writing goes faster if we use
- * the write buffer. We fill the write buffer and then
- * issue the write request. After the write request,
- * subsequent reads will yield the status register.
- *
- * returns the status, even on timeouts.
- *
- * NOTE: I tried starting back to back buffered writes
- * without reading the status in between, as the
- * flowchart in the intel data sheet suggests.
- * However, it always responded with an illegal
- * command sequence, so I must be missing something.
- * If someone learns better, please email me, though
- * I doubt it will be much faster. - presotto@bell-labs.com
- */
- static ulong
- ise_wbwrite(ulong *p, int n, ulong off, ulong baddr, ulong *status)
- {
- ulong x, start;
- int i;
- int s;
- /* put flash into write buffer mode */
- start = m->ticks;
- for(;;) {
- s = splhi();
- /* request write buffer mode */
- flash.p[baddr] = mirror(0xe8);
- /* look at extended status reg for status */
- if((flash.p[baddr] & mirror(1<<7)) == mirror(1<<7))
- break;
- splx(s);
- /* didn't work, keep trying for 2 secs */
- if(TK2MS(m->ticks-start) > 2000){
- /* set up to read status */
- flash.p[baddr] = mirror(0x70);
- *status = flash.p[baddr];
- pprint("write buffered cmd timed out\n");
- return -1;
- }
- }
- /* fill write buffer */
- flash.p[baddr] = mirror(n-1);
- for(i = 0; i < n; i++)
- flash.p[off+i] = *p++;
- /* program from buffer */
- flash.p[baddr] = mirror(0xd0);
- splx(s);
- /* wait till the programming is done */
- start = m->ticks;
- for(;;) {
- x = *status = flash.p[baddr]; /* read status register */
- if((x & mirror(ISEs_ready)) == mirror(ISEs_ready))
- break;
- if(TK2MS(m->ticks-start) > 2000){
- pprint("read status timed out\n");
- return -1;
- }
- }
- if(x & mirror(ISEs_err))
- return -1;
- return n;
- }
- static void
- ise_write(void *a, long n, ulong off)
- {
- ulong *p, *end;
- int i, wbsize;
- ulong x, baddr;
- /* everything in terms of ulongs */
- wbsize = flash.wbsize>>2;
- baddr = blockstart(off);
- off >>= 2;
- n >>= 2;
- p = a;
- baddr >>= 2;
- /* first see if write will succeed */
- for(i = 0; i < n; i++)
- if((p[i] & flash.p[off+i]) != p[i])
- error("flash needs erase");
- if(waserror()){
- ise_reset();
- flashprogpower(0);
- nexterror();
- }
- flashprogpower(1);
- /*
- * use the first write to reach
- * a write buffer boundary. the intel maunal
- * says writes startng at wb boundaries
- * maximize speed.
- */
- i = wbsize - (off & (wbsize-1));
- for(end = p + n; p < end;){
- if(i > end - p)
- i = end - p;
- if(ise_wbwrite(p, i, off, baddr, &x) < 0)
- break;
- off += i;
- p += i;
- i = wbsize;
- }
- ise_clearerror();
- ise_error(0, x);
- ise_error(1, x>>16);
- ise_reset();
- flashprogpower(0);
- poperror();
- }
- /* amd/fujitsu standard command set
- * I don't have an amd chipset to work with
- * so I'm loathe to write this yet. If someone
- * else does, please send it to me and I'll
- * incorporate it -- presotto@bell-labs.com
- */
- static void
- afs_reset(void)
- {
- flash.p[0x55] = mirror(0xf0); /* reset */
- }
- static void
- afs_id(void)
- {
- afs_reset();
- flash.p[0x55] = mirror(0xf0); /* reset */
- flash.p[0x555] = mirror(0xaa); /* query vendor block */
- flash.p[0x2aa] = mirror(0x55);
- flash.p[0x555] = mirror(0x90);
- flash.manid = flash.p[00];
- afs_reset();
- flash.p[0x555] = mirror(0xaa); /* query vendor block */
- flash.p[0x2aa] = mirror(0x55);
- flash.p[0x555] = mirror(0x90);
- flash.devid = flash.p[01];
- afs_reset();
- }
- static void
- afs_erase(ulong)
- {
- error("amd/fujistsu erase not implemented");
- }
- static void
- afs_write(void*, long, ulong)
- {
- error("amd/fujistsu write not implemented");
- }
|