diff options
author | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
---|---|---|
committer | Taru Karttunen <taruti@taruti.net> | 2011-03-30 15:46:40 +0300 |
commit | e5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch) | |
tree | d8d51eac403f07814b9e936eed0c9a79195e2450 /sys/src/9/port/devflash.c |
Import sources from 2011-03-30 iso image
Diffstat (limited to 'sys/src/9/port/devflash.c')
-rwxr-xr-x | sys/src/9/port/devflash.c | 654 |
1 files changed, 654 insertions, 0 deletions
diff --git a/sys/src/9/port/devflash.c b/sys/src/9/port/devflash.c new file mode 100755 index 000000000..87331ac53 --- /dev/null +++ b/sys/src/9/port/devflash.c @@ -0,0 +1,654 @@ +/* + * flash memory + */ + +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" + +#include "../port/flashif.h" + +typedef struct Flashtype Flashtype; +struct Flashtype { + char* name; + int (*reset)(Flash*); + Flashtype* next; +}; + +enum { + Nbanks = 2, +}; + +static struct +{ + Flash* card[Nbanks]; /* actual card type, reset for access */ + Flashtype* types; /* possible card types */ +}flash; + +enum{ + Qtopdir, + Qflashdir, + Qdata, + Qctl, +}; + +#define TYPE(q) ((ulong)(q) & 0xFF) +#define PART(q) ((ulong)(q)>>8) +#define QID(p,t) (((p)<<8) | (t)) + +static Flashregion* flashregion(Flash*, ulong); +static char* flashnewpart(Flash*, char*, ulong, ulong); +static ulong flashaddr(Flash*, Flashpart*, char*); +static void protect(Flash*, ulong); +static void eraseflash(Flash*, Flashregion*, ulong); +static long readflash(Flash*, void*, long, int); +static long writeflash(Flash*, long, void*,int); + +static char Eprotect[] = "flash region protected"; + +static int +flash2gen(Chan *c, ulong p, Dir *dp) +{ + Flashpart *fp; + Flash *f; + Qid q; + int mode; + + f = flash.card[c->dev]; + fp = &f->part[PART(p)]; + if(fp->name == nil) + return 0; + mkqid(&q, p, 0, QTFILE); + switch(TYPE(p)){ + case Qdata: + mode = 0660; + if(f->write == nil) + mode = 0440; + devdir(c, q, fp->name, fp->end-fp->start, eve, mode, dp); + return 1; + case Qctl: + snprint(up->genbuf, sizeof(up->genbuf), "%sctl", fp->name); + devdir(c, q, up->genbuf, 0, eve, 0660, dp); + return 1; + default: + return -1; + } +} + +static int +flashgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp) +{ + Qid q; + char *n; + + if(s == DEVDOTDOT){ + mkqid(&q, QID(0, Qtopdir), 0, QTDIR); + n = "#F"; + if(c->dev != 0){ + sprint(up->genbuf, "#F%ld", c->dev); + n = up->genbuf; + } + devdir(c, q, n, 0, eve, 0555, dp); + return 1; + } + switch(TYPE(c->qid.path)){ + case Qtopdir: + if(s != 0) + break; + mkqid(&q, QID(0, Qflashdir), 0, QTDIR); + n = "flash"; + if(c->dev != 0){ + sprint(up->genbuf, "flash%ld", c->dev); + n = up->genbuf; + } + devdir(c, q, n, 0, eve, 0555, dp); + return 1; + case Qflashdir: + if(s >= 2*nelem(flash.card[c->dev]->part)) + return -1; + return flash2gen(c, QID(s>>1, s&1?Qctl:Qdata), dp); + case Qctl: + case Qdata: + return flash2gen(c, (ulong)c->qid.path, dp); + } + return -1; +} + +static void +flashreset(void) +{ + Flash *f; + Flashtype *t; + char *e; + int bank; + + for(bank = 0; bank < Nbanks; bank++){ + f = malloc(sizeof(*f)); + if(f == nil){ + print("#F%d: can't allocate Flash data\n", bank); + return; + } + f->cmask = ~(ulong)0; + if(archflashreset(bank, f) < 0 || f->type == nil || + f->addr == nil){ + free(f); + return; + } + for(t = flash.types; t != nil; t = t->next) + if(strcmp(f->type, t->name) == 0) + break; + if(t == nil){ + iprint("#F%d: no flash driver for type %s (addr %p)\n", + bank, f->type, f->addr); + free(f); + return; + } + f->reset = t->reset; + f->protect = 1; + if(f->reset(f) == 0){ + flash.card[bank] = f; + iprint("#F%d: %s addr %#p len %lud width %d interleave %d\n", +// bank, f->type, PADDR(f->addr), f->size, + bank, f->type, f->addr, f->size, + f->width, f->interleave); + e = flashnewpart(f, "flash", 0, f->size); + if(e != nil) + panic("#F%d: couldn't init table: %s", bank, e); + }else + iprint("#F%d: %#p: reset failed (%s)\n", + bank, f->addr, f->type); + } +} + +static Chan* +flashattach(char *spec) +{ + Flash *f; + int bank; + Chan *c; + + bank = strtol(spec, nil, 0); + if(bank < 0 || bank >= Nbanks || + (f = flash.card[bank]) == nil || + f->attach != nil && f->attach(f) < 0) + error(Enodev); + c = devattach('F', spec); + c->dev = bank; + return c; +} + +static Walkqid* +flashwalk(Chan *c, Chan *nc, char **name, int nname) +{ + return devwalk(c, nc, name, nname, nil, 0, flashgen); +} + +static int +flashstat(Chan *c, uchar *dp, int n) +{ + return devstat(c, dp, n, nil, 0, flashgen); +} + +static Chan* +flashopen(Chan *c, int omode) +{ + omode = openmode(omode); + switch(TYPE(c->qid.path)){ + case Qdata: + case Qctl: + if(flash.card[c->dev] == nil) + error(Enodev); + break; + } + return devopen(c, omode, nil, 0, flashgen); +} + +static void +flashclose(Chan*) +{ +} + +static long +flashread(Chan *c, void *buf, long n, vlong offset) +{ + Flash *f; + Flashpart *fp; + Flashregion *r; + int i; + ulong start, end; + char *s, *o; + + if(c->qid.type & QTDIR) + return devdirread(c, buf, n, nil, 0, flashgen); + + f = flash.card[c->dev]; + fp = &f->part[PART(c->qid.path)]; + if(fp->name == nil) + error(Egreg); + switch(TYPE(c->qid.path)){ + case Qdata: + offset += fp->start; + if(offset >= fp->end) + return 0; + if(offset+n > fp->end) + n = fp->end - offset; + n = readflash(f, buf, offset, n); + if(n < 0) + error(Eio); + return n; + case Qctl: + s = malloc(READSTR); + if(waserror()){ + free(s); + nexterror(); + } + o = seprint(s, s+READSTR, "%#2.2ux %#4.4ux %d %q\n", + f->id, f->devid, f->width, f->sort!=nil? f->sort: "nor"); + for(i=0; i<f->nr; i++){ + r = &f->regions[i]; + if(r->start < fp->end && fp->start < r->end){ + start = r->start; + if(fp->start > start) + start = fp->start; + end = r->end; + if(fp->end < end) + end = fp->end; + o = seprint(o, s+READSTR, "%#8.8lux %#8.8lux %#8.8lux", + start, end, r->erasesize); + if(r->pagesize) + o = seprint(o, s+READSTR, " %#8.8lux", + r->pagesize); + o = seprint(o, s+READSTR, "\n"); + } + } + n = readstr(offset, buf, n, s); + poperror(); + free(s); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +enum { + CMerase, + CMadd, + CMremove, + CMsync, + CMprotectboot, +}; + +static Cmdtab flashcmds[] = { + {CMerase, "erase", 2}, + {CMadd, "add", 0}, + {CMremove, "remove", 2}, + {CMsync, "sync", 0}, + {CMprotectboot, "protectboot", 0}, +}; + +static long +flashwrite(Chan *c, void *buf, long n, vlong offset) +{ + Cmdbuf *cb; + Cmdtab *ct; + ulong addr, start, end; + char *e; + Flashpart *fp; + Flashregion *r; + Flash *f; + + f = flash.card[c->dev]; + fp = &f->part[PART(c->qid.path)]; + if(fp->name == nil) + error(Egreg); + switch(TYPE(c->qid.path)){ + case Qdata: + if(f->write == nil) + error(Eperm); + offset += fp->start; + if(offset >= fp->end) + return 0; + if(offset+n > fp->end) + n = fp->end - offset; + n = writeflash(f, offset, buf, n); + if(n < 0) + error(Eio); + return n; + case Qctl: + cb = parsecmd(buf, n); + if(waserror()){ + free(cb); + nexterror(); + } + ct = lookupcmd(cb, flashcmds, nelem(flashcmds)); + switch(ct->index){ + case CMerase: + if(strcmp(cb->f[1], "all") != 0){ + addr = flashaddr(f, fp, cb->f[1]); + r = flashregion(f, addr); + if(r == nil) + error("nonexistent flash region"); + if(addr%r->erasesize != 0) + error("invalid erase block address"); + eraseflash(f, r, addr); + }else if(fp->start == 0 && fp->end == f->size && + f->eraseall != nil){ + eraseflash(f, nil, 0); + }else{ + for(addr = fp->start; addr < fp->end; + addr += r->erasesize){ + r = flashregion(f, addr); + if(r == nil) + error("nonexistent flash region"); + if(addr%r->erasesize != 0) + error("invalid erase block address"); + eraseflash(f, r, addr); + } + } + break; + case CMadd: + if(cb->nf < 3) + error(Ebadarg); + start = flashaddr(f, fp, cb->f[2]); + if(cb->nf > 3 && strcmp(cb->f[3], "end") != 0) + end = flashaddr(f, fp, cb->f[3]); + else + end = fp->end; + if(start > end || start >= fp->end || end > fp->end) + error(Ebadarg); + e = flashnewpart(f, cb->f[1], start, end); + if(e != nil) + error(e); + break; + case CMremove: + /* TO DO */ + break; + case CMprotectboot: + if(cb->nf > 1 && strcmp(cb->f[1], "off") == 0) + f->protect = 0; + else + f->protect = 1; + break; + case CMsync: + /* TO DO? */ + break; + default: + error(Ebadarg); + } + poperror(); + free(cb); + return n; + } + error(Egreg); + return 0; /* not reached */ +} + +static char* +flashnewpart(Flash *f, char *name, ulong start, ulong end) +{ + Flashpart *fp, *empty; + int i; + + empty = nil; + for(i = 0; i < nelem(f->part); i++){ + fp = &f->part[i]; + if(fp->name == nil){ + if(empty == nil) + empty = fp; + }else if(strcmp(fp->name, name) == 0) + return Eexist; + } + if((fp = empty) == nil) + return "partition table full"; +// fp->name = nil; + kstrdup(&fp->name, name); + if(fp->name == nil) + return Enomem; + fp->start = start; + fp->end = end; + return nil; +} + +static ulong +flashaddr(Flash *f, Flashpart *fp, char *s) +{ + Flashregion *r; + ulong addr; + + addr = strtoul(s, &s, 0); + if(*s) + error(Ebadarg); + if(fp->name == nil) + error("partition removed"); + addr += fp->start; + r = flashregion(f, addr); + if(r != nil && addr%r->erasesize != 0) + error("invalid erase unit address"); + if(addr < fp->start || addr > fp->end || addr > f->size) + error(Ebadarg); + return addr; +} + +static Flashregion* +flashregion(Flash *f, ulong a) +{ + int i; + Flashregion *r; + + for(i=0; i<f->nr; i++){ + r = &f->regions[i]; + if(r->start <= a && a < r->end) + return r; + } + return nil; +} + +Dev flashdevtab = { + 'F', + "flash", + + flashreset, + devinit, + devshutdown, + flashattach, + flashwalk, + flashstat, + flashopen, + devcreate, + flashclose, + flashread, + devbread, + flashwrite, + devbwrite, + devremove, + devwstat, +}; + +/* + * called by flash card types named in link section (eg, flashamd.c) + */ +void +addflashcard(char *name, int (*reset)(Flash*)) +{ + Flashtype *f, **l; + + f = (Flashtype*)malloc(sizeof(*f)); + f->name = name; + f->reset = reset; + f->next = nil; + for(l = &flash.types; *l != nil; l = &(*l)->next) + ; + *l = f; +} + +static long +readflash(Flash *f, void *buf, long offset, int n) +{ + int r, width, wmask; + uchar tmp[16]; + uchar *p; + ulong o; + + if(offset < 0 || offset+n > f->size) + error(Ebadarg); + qlock(f); + if(waserror()){ + qunlock(f); + nexterror(); + } + if(f->read != nil){ + width = f->width; + wmask = width-1; + p = buf; + if(offset & wmask) { + o = offset & ~wmask; + if(f->read(f, o, (ulong*)tmp, width) < 0) + error(Eio); + memmove(tmp, (uchar*)f->addr + o, width); + for(; n > 0 && offset & wmask; n--) + *p++ = tmp[offset++ & wmask]; + } + r = n & wmask; + n &= ~wmask; + if(n){ + if(f->read(f, offset, (ulong*)p, n) < 0) + error(Eio); + offset += n; + p += n; + } + if(r){ + if(f->read(f, offset, (ulong*)tmp, width)) + error(Eio); + memmove(p, tmp, r); + } + }else + /* assumes hardware supports byte access */ + memmove(buf, (uchar*)f->addr+offset, n); + poperror(); + qunlock(f); + return n; +} + +static long +writeflash(Flash *f, long offset, void *buf, int n) +{ + uchar tmp[16]; + uchar *p; + ulong o; + int r, width, wmask; + Flashregion *rg; + + if(f->write == nil || offset < 0 || offset+n > f->size) + error(Ebadarg); + rg = flashregion(f, offset); + if(f->protect && rg != nil && rg->start == 0 && offset < rg->erasesize) + error(Eprotect); + width = f->width; + wmask = width-1; + qlock(f); + archflashwp(f, 0); + if(waserror()){ + archflashwp(f, 1); + qunlock(f); + nexterror(); + } + p = buf; + if(offset&wmask){ + o = offset & ~wmask; + if(f->read != nil){ + if(f->read(f, o, tmp, width) < 0) + error(Eio); + }else + memmove(tmp, (uchar*)f->addr+o, width); + for(; n > 0 && offset&wmask; n--) + tmp[offset++&wmask] = *p++; + if(f->write(f, o, tmp, width) < 0) + error(Eio); + } + r = n&wmask; + n &= ~wmask; + if(n){ + if(f->write(f, offset, p, n) < 0) + error(Eio); + offset += n; + p += n; + } + if(r){ + if(f->read != nil){ + if(f->read(f, offset, tmp, width) < 0) + error(Eio); + }else + memmove(tmp, (uchar*)f->addr+offset, width); + memmove(tmp, p, r); + if(f->write(f, offset, tmp, width) < 0) + error(Eio); + } + poperror(); + archflashwp(f, 1); + qunlock(f); + return n; +} + +static void +eraseflash(Flash *f, Flashregion *r, ulong addr) +{ + int rv; + + if(f->protect && r != nil && r->start == 0 && addr < r->erasesize) + error(Eprotect); + qlock(f); + archflashwp(f, 0); + if(waserror()){ + archflashwp(f, 1); + qunlock(f); + nexterror(); + } + if(r == nil){ + if(f->eraseall != nil) + rv = f->eraseall(f); + else + rv = -1; + }else + rv = f->erasezone(f, r, addr); + if(rv < 0) + error(Eio); + poperror(); + archflashwp(f, 1); + qunlock(f); +} + +/* + * flash access taking width and interleave into account + */ +int +flashget(Flash *f, ulong a) +{ + switch(f->width){ + default: + return ((uchar*)f->addr)[a<<f->bshift]; + case 2: + return ((ushort*)f->addr)[a]; + case 4: + return ((ulong*)f->addr)[a]; + } +} + +void +flashput(Flash *f, ulong a, int v) +{ + switch(f->width){ + default: + ((uchar*)f->addr)[a<<f->bshift] = v; + break; + case 2: + ((ushort*)f->addr)[a] = v; + break; + case 4: + ((ulong*)f->addr)[a] = v; + break; + } + coherence(); +} |