summaryrefslogtreecommitdiff
path: root/sys/src/9/port/devflash.c
diff options
context:
space:
mode:
authorTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
committerTaru Karttunen <taruti@taruti.net>2011-03-30 15:46:40 +0300
commite5888a1ffdae813d7575f5fb02275c6bb07e5199 (patch)
treed8d51eac403f07814b9e936eed0c9a79195e2450 /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-xsys/src/9/port/devflash.c654
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();
+}