106f6463aSDavid du Colombier /*
206f6463aSDavid du Colombier * flash memory
306f6463aSDavid du Colombier */
406f6463aSDavid du Colombier
506f6463aSDavid du Colombier #include "u.h"
606f6463aSDavid du Colombier #include "../port/lib.h"
706f6463aSDavid du Colombier #include "mem.h"
806f6463aSDavid du Colombier #include "dat.h"
906f6463aSDavid du Colombier #include "fns.h"
1006f6463aSDavid du Colombier #include "../port/error.h"
1106f6463aSDavid du Colombier
1206f6463aSDavid du Colombier #include "../port/flashif.h"
1306f6463aSDavid du Colombier
1406f6463aSDavid du Colombier typedef struct Flashtype Flashtype;
1506f6463aSDavid du Colombier struct Flashtype {
1606f6463aSDavid du Colombier char* name;
1706f6463aSDavid du Colombier int (*reset)(Flash*);
1828620197SDavid du Colombier Flashtype* next;
1906f6463aSDavid du Colombier };
2006f6463aSDavid du Colombier
2106f6463aSDavid du Colombier enum {
2206f6463aSDavid du Colombier Nbanks = 2,
2306f6463aSDavid du Colombier };
2406f6463aSDavid du Colombier
2506f6463aSDavid du Colombier static struct
2606f6463aSDavid du Colombier {
2706f6463aSDavid du Colombier Flash* card[Nbanks]; /* actual card type, reset for access */
2806f6463aSDavid du Colombier Flashtype* types; /* possible card types */
2906f6463aSDavid du Colombier }flash;
3006f6463aSDavid du Colombier
3106f6463aSDavid du Colombier enum{
3206f6463aSDavid du Colombier Qtopdir,
3306f6463aSDavid du Colombier Qflashdir,
3406f6463aSDavid du Colombier Qdata,
3506f6463aSDavid du Colombier Qctl,
3606f6463aSDavid du Colombier };
3706f6463aSDavid du Colombier
3806f6463aSDavid du Colombier #define TYPE(q) ((ulong)(q) & 0xFF)
3906f6463aSDavid du Colombier #define PART(q) ((ulong)(q)>>8)
4006f6463aSDavid du Colombier #define QID(p,t) (((p)<<8) | (t))
4106f6463aSDavid du Colombier
4206f6463aSDavid du Colombier static Flashregion* flashregion(Flash*, ulong);
4306f6463aSDavid du Colombier static char* flashnewpart(Flash*, char*, ulong, ulong);
4406f6463aSDavid du Colombier static ulong flashaddr(Flash*, Flashpart*, char*);
4506f6463aSDavid du Colombier static void protect(Flash*, ulong);
4606f6463aSDavid du Colombier static void eraseflash(Flash*, Flashregion*, ulong);
4706f6463aSDavid du Colombier static long readflash(Flash*, void*, long, int);
4806f6463aSDavid du Colombier static long writeflash(Flash*, long, void*,int);
4906f6463aSDavid du Colombier
5006f6463aSDavid du Colombier static char Eprotect[] = "flash region protected";
5106f6463aSDavid du Colombier
5206f6463aSDavid du Colombier static int
flash2gen(Chan * c,ulong p,Dir * dp)5306f6463aSDavid du Colombier flash2gen(Chan *c, ulong p, Dir *dp)
5406f6463aSDavid du Colombier {
5506f6463aSDavid du Colombier Flashpart *fp;
5606f6463aSDavid du Colombier Flash *f;
5706f6463aSDavid du Colombier Qid q;
5806f6463aSDavid du Colombier int mode;
5906f6463aSDavid du Colombier
6006f6463aSDavid du Colombier f = flash.card[c->dev];
6106f6463aSDavid du Colombier fp = &f->part[PART(p)];
6206f6463aSDavid du Colombier if(fp->name == nil)
6306f6463aSDavid du Colombier return 0;
6406f6463aSDavid du Colombier mkqid(&q, p, 0, QTFILE);
6506f6463aSDavid du Colombier switch(TYPE(p)){
6606f6463aSDavid du Colombier case Qdata:
6706f6463aSDavid du Colombier mode = 0660;
6806f6463aSDavid du Colombier if(f->write == nil)
6906f6463aSDavid du Colombier mode = 0440;
7006f6463aSDavid du Colombier devdir(c, q, fp->name, fp->end-fp->start, eve, mode, dp);
7106f6463aSDavid du Colombier return 1;
7206f6463aSDavid du Colombier case Qctl:
7306f6463aSDavid du Colombier snprint(up->genbuf, sizeof(up->genbuf), "%sctl", fp->name);
745b0beceeSDavid du Colombier /* no harm in letting everybody read the ctl files */
755b0beceeSDavid du Colombier devdir(c, q, up->genbuf, 0, eve, 0664, dp);
7606f6463aSDavid du Colombier return 1;
7706f6463aSDavid du Colombier default:
7806f6463aSDavid du Colombier return -1;
7906f6463aSDavid du Colombier }
8006f6463aSDavid du Colombier }
8106f6463aSDavid du Colombier
8206f6463aSDavid du Colombier static int
flashgen(Chan * c,char *,Dirtab *,int,int s,Dir * dp)8306f6463aSDavid du Colombier flashgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
8406f6463aSDavid du Colombier {
8506f6463aSDavid du Colombier Qid q;
8606f6463aSDavid du Colombier char *n;
8706f6463aSDavid du Colombier
8806f6463aSDavid du Colombier if(s == DEVDOTDOT){
8906f6463aSDavid du Colombier mkqid(&q, QID(0, Qtopdir), 0, QTDIR);
9006f6463aSDavid du Colombier n = "#F";
9106f6463aSDavid du Colombier if(c->dev != 0){
92*4e3613abSDavid du Colombier snprint(up->genbuf, sizeof up->genbuf, "#F%ld", c->dev);
9306f6463aSDavid du Colombier n = up->genbuf;
9406f6463aSDavid du Colombier }
9506f6463aSDavid du Colombier devdir(c, q, n, 0, eve, 0555, dp);
9606f6463aSDavid du Colombier return 1;
9706f6463aSDavid du Colombier }
9806f6463aSDavid du Colombier switch(TYPE(c->qid.path)){
9906f6463aSDavid du Colombier case Qtopdir:
10006f6463aSDavid du Colombier if(s != 0)
10106f6463aSDavid du Colombier break;
10206f6463aSDavid du Colombier mkqid(&q, QID(0, Qflashdir), 0, QTDIR);
10306f6463aSDavid du Colombier n = "flash";
10406f6463aSDavid du Colombier if(c->dev != 0){
105*4e3613abSDavid du Colombier snprint(up->genbuf, sizeof up->genbuf, "flash%ld",
106*4e3613abSDavid du Colombier c->dev);
10706f6463aSDavid du Colombier n = up->genbuf;
10806f6463aSDavid du Colombier }
10906f6463aSDavid du Colombier devdir(c, q, n, 0, eve, 0555, dp);
11006f6463aSDavid du Colombier return 1;
11106f6463aSDavid du Colombier case Qflashdir:
11206f6463aSDavid du Colombier if(s >= 2*nelem(flash.card[c->dev]->part))
11306f6463aSDavid du Colombier return -1;
11406f6463aSDavid du Colombier return flash2gen(c, QID(s>>1, s&1?Qctl:Qdata), dp);
11506f6463aSDavid du Colombier case Qctl:
11606f6463aSDavid du Colombier case Qdata:
11706f6463aSDavid du Colombier return flash2gen(c, (ulong)c->qid.path, dp);
11806f6463aSDavid du Colombier }
11906f6463aSDavid du Colombier return -1;
12006f6463aSDavid du Colombier }
12106f6463aSDavid du Colombier
12206f6463aSDavid du Colombier static void
flashreset(void)12306f6463aSDavid du Colombier flashreset(void)
12406f6463aSDavid du Colombier {
12506f6463aSDavid du Colombier Flash *f;
12606f6463aSDavid du Colombier Flashtype *t;
12706f6463aSDavid du Colombier char *e;
12806f6463aSDavid du Colombier int bank;
12906f6463aSDavid du Colombier
13006f6463aSDavid du Colombier for(bank = 0; bank < Nbanks; bank++){
13106f6463aSDavid du Colombier f = malloc(sizeof(*f));
13206f6463aSDavid du Colombier if(f == nil){
13306f6463aSDavid du Colombier print("#F%d: can't allocate Flash data\n", bank);
13406f6463aSDavid du Colombier return;
13506f6463aSDavid du Colombier }
13606f6463aSDavid du Colombier f->cmask = ~(ulong)0;
13706f6463aSDavid du Colombier if(archflashreset(bank, f) < 0 || f->type == nil ||
13806f6463aSDavid du Colombier f->addr == nil){
13906f6463aSDavid du Colombier free(f);
14006f6463aSDavid du Colombier return;
14106f6463aSDavid du Colombier }
14206f6463aSDavid du Colombier for(t = flash.types; t != nil; t = t->next)
14306f6463aSDavid du Colombier if(strcmp(f->type, t->name) == 0)
14406f6463aSDavid du Colombier break;
14506f6463aSDavid du Colombier if(t == nil){
14606f6463aSDavid du Colombier iprint("#F%d: no flash driver for type %s (addr %p)\n",
14706f6463aSDavid du Colombier bank, f->type, f->addr);
14806f6463aSDavid du Colombier free(f);
14906f6463aSDavid du Colombier return;
15006f6463aSDavid du Colombier }
15106f6463aSDavid du Colombier f->reset = t->reset;
15206f6463aSDavid du Colombier f->protect = 1;
15306f6463aSDavid du Colombier if(f->reset(f) == 0){
15406f6463aSDavid du Colombier flash.card[bank] = f;
15506f6463aSDavid du Colombier iprint("#F%d: %s addr %#p len %lud width %d interleave %d\n",
15606f6463aSDavid du Colombier // bank, f->type, PADDR(f->addr), f->size,
15706f6463aSDavid du Colombier bank, f->type, f->addr, f->size,
15806f6463aSDavid du Colombier f->width, f->interleave);
15906f6463aSDavid du Colombier e = flashnewpart(f, "flash", 0, f->size);
16006f6463aSDavid du Colombier if(e != nil)
16106f6463aSDavid du Colombier panic("#F%d: couldn't init table: %s", bank, e);
16206f6463aSDavid du Colombier }else
16306f6463aSDavid du Colombier iprint("#F%d: %#p: reset failed (%s)\n",
16406f6463aSDavid du Colombier bank, f->addr, f->type);
16506f6463aSDavid du Colombier }
16606f6463aSDavid du Colombier }
16706f6463aSDavid du Colombier
16806f6463aSDavid du Colombier static Chan*
flashattach(char * spec)16906f6463aSDavid du Colombier flashattach(char *spec)
17006f6463aSDavid du Colombier {
17106f6463aSDavid du Colombier Flash *f;
17206f6463aSDavid du Colombier int bank;
17306f6463aSDavid du Colombier Chan *c;
17406f6463aSDavid du Colombier
17506f6463aSDavid du Colombier bank = strtol(spec, nil, 0);
17606f6463aSDavid du Colombier if(bank < 0 || bank >= Nbanks ||
17706f6463aSDavid du Colombier (f = flash.card[bank]) == nil ||
17806f6463aSDavid du Colombier f->attach != nil && f->attach(f) < 0)
17906f6463aSDavid du Colombier error(Enodev);
18006f6463aSDavid du Colombier c = devattach('F', spec);
18106f6463aSDavid du Colombier c->dev = bank;
18206f6463aSDavid du Colombier return c;
18306f6463aSDavid du Colombier }
18406f6463aSDavid du Colombier
18506f6463aSDavid du Colombier static Walkqid*
flashwalk(Chan * c,Chan * nc,char ** name,int nname)18606f6463aSDavid du Colombier flashwalk(Chan *c, Chan *nc, char **name, int nname)
18706f6463aSDavid du Colombier {
18806f6463aSDavid du Colombier return devwalk(c, nc, name, nname, nil, 0, flashgen);
18906f6463aSDavid du Colombier }
19006f6463aSDavid du Colombier
19106f6463aSDavid du Colombier static int
flashstat(Chan * c,uchar * dp,int n)19206f6463aSDavid du Colombier flashstat(Chan *c, uchar *dp, int n)
19306f6463aSDavid du Colombier {
19406f6463aSDavid du Colombier return devstat(c, dp, n, nil, 0, flashgen);
19506f6463aSDavid du Colombier }
19606f6463aSDavid du Colombier
19706f6463aSDavid du Colombier static Chan*
flashopen(Chan * c,int omode)19806f6463aSDavid du Colombier flashopen(Chan *c, int omode)
19906f6463aSDavid du Colombier {
20006f6463aSDavid du Colombier omode = openmode(omode);
20106f6463aSDavid du Colombier switch(TYPE(c->qid.path)){
20206f6463aSDavid du Colombier case Qdata:
20306f6463aSDavid du Colombier case Qctl:
20406f6463aSDavid du Colombier if(flash.card[c->dev] == nil)
20506f6463aSDavid du Colombier error(Enodev);
20606f6463aSDavid du Colombier break;
20706f6463aSDavid du Colombier }
20806f6463aSDavid du Colombier return devopen(c, omode, nil, 0, flashgen);
20906f6463aSDavid du Colombier }
21006f6463aSDavid du Colombier
21106f6463aSDavid du Colombier static void
flashclose(Chan *)21206f6463aSDavid du Colombier flashclose(Chan*)
21306f6463aSDavid du Colombier {
21406f6463aSDavid du Colombier }
21506f6463aSDavid du Colombier
21606f6463aSDavid du Colombier static long
flashread(Chan * c,void * buf,long n,vlong offset)21706f6463aSDavid du Colombier flashread(Chan *c, void *buf, long n, vlong offset)
21806f6463aSDavid du Colombier {
21906f6463aSDavid du Colombier Flash *f;
22006f6463aSDavid du Colombier Flashpart *fp;
22106f6463aSDavid du Colombier Flashregion *r;
22206f6463aSDavid du Colombier int i;
22306f6463aSDavid du Colombier ulong start, end;
22406f6463aSDavid du Colombier char *s, *o;
22506f6463aSDavid du Colombier
22606f6463aSDavid du Colombier if(c->qid.type & QTDIR)
22706f6463aSDavid du Colombier return devdirread(c, buf, n, nil, 0, flashgen);
22806f6463aSDavid du Colombier
22906f6463aSDavid du Colombier f = flash.card[c->dev];
23006f6463aSDavid du Colombier fp = &f->part[PART(c->qid.path)];
23106f6463aSDavid du Colombier if(fp->name == nil)
23206f6463aSDavid du Colombier error(Egreg);
23306f6463aSDavid du Colombier switch(TYPE(c->qid.path)){
23406f6463aSDavid du Colombier case Qdata:
23506f6463aSDavid du Colombier offset += fp->start;
23606f6463aSDavid du Colombier if(offset >= fp->end)
23706f6463aSDavid du Colombier return 0;
23806f6463aSDavid du Colombier if(offset+n > fp->end)
23906f6463aSDavid du Colombier n = fp->end - offset;
24006f6463aSDavid du Colombier n = readflash(f, buf, offset, n);
24106f6463aSDavid du Colombier if(n < 0)
24206f6463aSDavid du Colombier error(Eio);
24306f6463aSDavid du Colombier return n;
24406f6463aSDavid du Colombier case Qctl:
24506f6463aSDavid du Colombier s = malloc(READSTR);
246aa72973aSDavid du Colombier if(s == nil)
247aa72973aSDavid du Colombier error(Enomem);
24806f6463aSDavid du Colombier if(waserror()){
24906f6463aSDavid du Colombier free(s);
25006f6463aSDavid du Colombier nexterror();
25106f6463aSDavid du Colombier }
25206f6463aSDavid du Colombier o = seprint(s, s+READSTR, "%#2.2ux %#4.4ux %d %q\n",
25306f6463aSDavid du Colombier f->id, f->devid, f->width, f->sort!=nil? f->sort: "nor");
25406f6463aSDavid du Colombier for(i=0; i<f->nr; i++){
25506f6463aSDavid du Colombier r = &f->regions[i];
25606f6463aSDavid du Colombier if(r->start < fp->end && fp->start < r->end){
25706f6463aSDavid du Colombier start = r->start;
25806f6463aSDavid du Colombier if(fp->start > start)
25906f6463aSDavid du Colombier start = fp->start;
26006f6463aSDavid du Colombier end = r->end;
26106f6463aSDavid du Colombier if(fp->end < end)
26206f6463aSDavid du Colombier end = fp->end;
26306f6463aSDavid du Colombier o = seprint(o, s+READSTR, "%#8.8lux %#8.8lux %#8.8lux",
26406f6463aSDavid du Colombier start, end, r->erasesize);
26506f6463aSDavid du Colombier if(r->pagesize)
26606f6463aSDavid du Colombier o = seprint(o, s+READSTR, " %#8.8lux",
26706f6463aSDavid du Colombier r->pagesize);
26806f6463aSDavid du Colombier o = seprint(o, s+READSTR, "\n");
26906f6463aSDavid du Colombier }
27006f6463aSDavid du Colombier }
27106f6463aSDavid du Colombier n = readstr(offset, buf, n, s);
27206f6463aSDavid du Colombier poperror();
27306f6463aSDavid du Colombier free(s);
27406f6463aSDavid du Colombier return n;
27506f6463aSDavid du Colombier }
27606f6463aSDavid du Colombier error(Egreg);
27706f6463aSDavid du Colombier return 0; /* not reached */
27806f6463aSDavid du Colombier }
27906f6463aSDavid du Colombier
28006f6463aSDavid du Colombier enum {
28106f6463aSDavid du Colombier CMerase,
28206f6463aSDavid du Colombier CMadd,
28306f6463aSDavid du Colombier CMremove,
28406f6463aSDavid du Colombier CMsync,
28506f6463aSDavid du Colombier CMprotectboot,
28606f6463aSDavid du Colombier };
28706f6463aSDavid du Colombier
28806f6463aSDavid du Colombier static Cmdtab flashcmds[] = {
28906f6463aSDavid du Colombier {CMerase, "erase", 2},
29006f6463aSDavid du Colombier {CMadd, "add", 0},
29106f6463aSDavid du Colombier {CMremove, "remove", 2},
29206f6463aSDavid du Colombier {CMsync, "sync", 0},
29306f6463aSDavid du Colombier {CMprotectboot, "protectboot", 0},
29406f6463aSDavid du Colombier };
29506f6463aSDavid du Colombier
29606f6463aSDavid du Colombier static long
flashwrite(Chan * c,void * buf,long n,vlong offset)29706f6463aSDavid du Colombier flashwrite(Chan *c, void *buf, long n, vlong offset)
29806f6463aSDavid du Colombier {
29906f6463aSDavid du Colombier Cmdbuf *cb;
30006f6463aSDavid du Colombier Cmdtab *ct;
30106f6463aSDavid du Colombier ulong addr, start, end;
30206f6463aSDavid du Colombier char *e;
30306f6463aSDavid du Colombier Flashpart *fp;
30406f6463aSDavid du Colombier Flashregion *r;
30506f6463aSDavid du Colombier Flash *f;
30606f6463aSDavid du Colombier
30706f6463aSDavid du Colombier f = flash.card[c->dev];
30806f6463aSDavid du Colombier fp = &f->part[PART(c->qid.path)];
30906f6463aSDavid du Colombier if(fp->name == nil)
31006f6463aSDavid du Colombier error(Egreg);
31106f6463aSDavid du Colombier switch(TYPE(c->qid.path)){
31206f6463aSDavid du Colombier case Qdata:
31306f6463aSDavid du Colombier if(f->write == nil)
31406f6463aSDavid du Colombier error(Eperm);
31506f6463aSDavid du Colombier offset += fp->start;
31606f6463aSDavid du Colombier if(offset >= fp->end)
31706f6463aSDavid du Colombier return 0;
31806f6463aSDavid du Colombier if(offset+n > fp->end)
31906f6463aSDavid du Colombier n = fp->end - offset;
32006f6463aSDavid du Colombier n = writeflash(f, offset, buf, n);
32106f6463aSDavid du Colombier if(n < 0)
32206f6463aSDavid du Colombier error(Eio);
32306f6463aSDavid du Colombier return n;
32406f6463aSDavid du Colombier case Qctl:
32506f6463aSDavid du Colombier cb = parsecmd(buf, n);
32606f6463aSDavid du Colombier if(waserror()){
32706f6463aSDavid du Colombier free(cb);
32806f6463aSDavid du Colombier nexterror();
32906f6463aSDavid du Colombier }
33006f6463aSDavid du Colombier ct = lookupcmd(cb, flashcmds, nelem(flashcmds));
33106f6463aSDavid du Colombier switch(ct->index){
33206f6463aSDavid du Colombier case CMerase:
33306f6463aSDavid du Colombier if(strcmp(cb->f[1], "all") != 0){
33406f6463aSDavid du Colombier addr = flashaddr(f, fp, cb->f[1]);
33506f6463aSDavid du Colombier r = flashregion(f, addr);
33606f6463aSDavid du Colombier if(r == nil)
33706f6463aSDavid du Colombier error("nonexistent flash region");
33806f6463aSDavid du Colombier if(addr%r->erasesize != 0)
33906f6463aSDavid du Colombier error("invalid erase block address");
34006f6463aSDavid du Colombier eraseflash(f, r, addr);
34106f6463aSDavid du Colombier }else if(fp->start == 0 && fp->end == f->size &&
34206f6463aSDavid du Colombier f->eraseall != nil){
34306f6463aSDavid du Colombier eraseflash(f, nil, 0);
34406f6463aSDavid du Colombier }else{
34506f6463aSDavid du Colombier for(addr = fp->start; addr < fp->end;
34606f6463aSDavid du Colombier addr += r->erasesize){
34706f6463aSDavid du Colombier r = flashregion(f, addr);
34806f6463aSDavid du Colombier if(r == nil)
34906f6463aSDavid du Colombier error("nonexistent flash region");
35006f6463aSDavid du Colombier if(addr%r->erasesize != 0)
35106f6463aSDavid du Colombier error("invalid erase block address");
35206f6463aSDavid du Colombier eraseflash(f, r, addr);
35306f6463aSDavid du Colombier }
35406f6463aSDavid du Colombier }
35506f6463aSDavid du Colombier break;
35606f6463aSDavid du Colombier case CMadd:
35706f6463aSDavid du Colombier if(cb->nf < 3)
35806f6463aSDavid du Colombier error(Ebadarg);
35906f6463aSDavid du Colombier start = flashaddr(f, fp, cb->f[2]);
36006f6463aSDavid du Colombier if(cb->nf > 3 && strcmp(cb->f[3], "end") != 0)
36106f6463aSDavid du Colombier end = flashaddr(f, fp, cb->f[3]);
36206f6463aSDavid du Colombier else
36306f6463aSDavid du Colombier end = fp->end;
36406f6463aSDavid du Colombier if(start > end || start >= fp->end || end > fp->end)
36506f6463aSDavid du Colombier error(Ebadarg);
36606f6463aSDavid du Colombier e = flashnewpart(f, cb->f[1], start, end);
36706f6463aSDavid du Colombier if(e != nil)
36806f6463aSDavid du Colombier error(e);
36906f6463aSDavid du Colombier break;
37006f6463aSDavid du Colombier case CMremove:
37106f6463aSDavid du Colombier /* TO DO */
37206f6463aSDavid du Colombier break;
37306f6463aSDavid du Colombier case CMprotectboot:
37406f6463aSDavid du Colombier if(cb->nf > 1 && strcmp(cb->f[1], "off") == 0)
37506f6463aSDavid du Colombier f->protect = 0;
37606f6463aSDavid du Colombier else
37706f6463aSDavid du Colombier f->protect = 1;
37806f6463aSDavid du Colombier break;
37906f6463aSDavid du Colombier case CMsync:
38006f6463aSDavid du Colombier /* TO DO? */
38106f6463aSDavid du Colombier break;
38206f6463aSDavid du Colombier default:
38306f6463aSDavid du Colombier error(Ebadarg);
38406f6463aSDavid du Colombier }
38506f6463aSDavid du Colombier poperror();
38606f6463aSDavid du Colombier free(cb);
38706f6463aSDavid du Colombier return n;
38806f6463aSDavid du Colombier }
38906f6463aSDavid du Colombier error(Egreg);
39006f6463aSDavid du Colombier return 0; /* not reached */
39106f6463aSDavid du Colombier }
39206f6463aSDavid du Colombier
39306f6463aSDavid du Colombier static char*
flashnewpart(Flash * f,char * name,ulong start,ulong end)39406f6463aSDavid du Colombier flashnewpart(Flash *f, char *name, ulong start, ulong end)
39506f6463aSDavid du Colombier {
39606f6463aSDavid du Colombier Flashpart *fp, *empty;
39706f6463aSDavid du Colombier int i;
39806f6463aSDavid du Colombier
39906f6463aSDavid du Colombier empty = nil;
40006f6463aSDavid du Colombier for(i = 0; i < nelem(f->part); i++){
40106f6463aSDavid du Colombier fp = &f->part[i];
40206f6463aSDavid du Colombier if(fp->name == nil){
40306f6463aSDavid du Colombier if(empty == nil)
40406f6463aSDavid du Colombier empty = fp;
40506f6463aSDavid du Colombier }else if(strcmp(fp->name, name) == 0)
40606f6463aSDavid du Colombier return Eexist;
40706f6463aSDavid du Colombier }
40806f6463aSDavid du Colombier if((fp = empty) == nil)
40906f6463aSDavid du Colombier return "partition table full";
41006f6463aSDavid du Colombier // fp->name = nil;
41106f6463aSDavid du Colombier kstrdup(&fp->name, name);
41206f6463aSDavid du Colombier if(fp->name == nil)
41306f6463aSDavid du Colombier return Enomem;
41406f6463aSDavid du Colombier fp->start = start;
41506f6463aSDavid du Colombier fp->end = end;
41606f6463aSDavid du Colombier return nil;
41706f6463aSDavid du Colombier }
41806f6463aSDavid du Colombier
41906f6463aSDavid du Colombier static ulong
flashaddr(Flash * f,Flashpart * fp,char * s)42006f6463aSDavid du Colombier flashaddr(Flash *f, Flashpart *fp, char *s)
42106f6463aSDavid du Colombier {
42206f6463aSDavid du Colombier Flashregion *r;
42306f6463aSDavid du Colombier ulong addr;
42406f6463aSDavid du Colombier
42506f6463aSDavid du Colombier addr = strtoul(s, &s, 0);
42606f6463aSDavid du Colombier if(*s)
42706f6463aSDavid du Colombier error(Ebadarg);
42806f6463aSDavid du Colombier if(fp->name == nil)
42906f6463aSDavid du Colombier error("partition removed");
43006f6463aSDavid du Colombier addr += fp->start;
43106f6463aSDavid du Colombier r = flashregion(f, addr);
43206f6463aSDavid du Colombier if(r != nil && addr%r->erasesize != 0)
43306f6463aSDavid du Colombier error("invalid erase unit address");
43406f6463aSDavid du Colombier if(addr < fp->start || addr > fp->end || addr > f->size)
43506f6463aSDavid du Colombier error(Ebadarg);
43606f6463aSDavid du Colombier return addr;
43706f6463aSDavid du Colombier }
43806f6463aSDavid du Colombier
43906f6463aSDavid du Colombier static Flashregion*
flashregion(Flash * f,ulong a)44006f6463aSDavid du Colombier flashregion(Flash *f, ulong a)
44106f6463aSDavid du Colombier {
44206f6463aSDavid du Colombier int i;
44306f6463aSDavid du Colombier Flashregion *r;
44406f6463aSDavid du Colombier
44506f6463aSDavid du Colombier for(i=0; i<f->nr; i++){
44606f6463aSDavid du Colombier r = &f->regions[i];
44706f6463aSDavid du Colombier if(r->start <= a && a < r->end)
44806f6463aSDavid du Colombier return r;
44906f6463aSDavid du Colombier }
45006f6463aSDavid du Colombier return nil;
45106f6463aSDavid du Colombier }
45206f6463aSDavid du Colombier
45306f6463aSDavid du Colombier Dev flashdevtab = {
45406f6463aSDavid du Colombier 'F',
45506f6463aSDavid du Colombier "flash",
45606f6463aSDavid du Colombier
45706f6463aSDavid du Colombier flashreset,
45806f6463aSDavid du Colombier devinit,
45906f6463aSDavid du Colombier devshutdown,
46006f6463aSDavid du Colombier flashattach,
46106f6463aSDavid du Colombier flashwalk,
46206f6463aSDavid du Colombier flashstat,
46306f6463aSDavid du Colombier flashopen,
46406f6463aSDavid du Colombier devcreate,
46506f6463aSDavid du Colombier flashclose,
46606f6463aSDavid du Colombier flashread,
46706f6463aSDavid du Colombier devbread,
46806f6463aSDavid du Colombier flashwrite,
46906f6463aSDavid du Colombier devbwrite,
47006f6463aSDavid du Colombier devremove,
47106f6463aSDavid du Colombier devwstat,
47206f6463aSDavid du Colombier };
47306f6463aSDavid du Colombier
47406f6463aSDavid du Colombier /*
47506f6463aSDavid du Colombier * called by flash card types named in link section (eg, flashamd.c)
47606f6463aSDavid du Colombier */
47706f6463aSDavid du Colombier void
addflashcard(char * name,int (* reset)(Flash *))47806f6463aSDavid du Colombier addflashcard(char *name, int (*reset)(Flash*))
47906f6463aSDavid du Colombier {
48006f6463aSDavid du Colombier Flashtype *f, **l;
48106f6463aSDavid du Colombier
48206f6463aSDavid du Colombier f = (Flashtype*)malloc(sizeof(*f));
483aa72973aSDavid du Colombier if(f == nil)
484aa72973aSDavid du Colombier error(Enomem);
48506f6463aSDavid du Colombier f->name = name;
48606f6463aSDavid du Colombier f->reset = reset;
48706f6463aSDavid du Colombier f->next = nil;
48806f6463aSDavid du Colombier for(l = &flash.types; *l != nil; l = &(*l)->next)
48906f6463aSDavid du Colombier ;
49006f6463aSDavid du Colombier *l = f;
49106f6463aSDavid du Colombier }
49206f6463aSDavid du Colombier
49306f6463aSDavid du Colombier static long
readflash(Flash * f,void * buf,long offset,int n)49406f6463aSDavid du Colombier readflash(Flash *f, void *buf, long offset, int n)
49506f6463aSDavid du Colombier {
49606f6463aSDavid du Colombier int r, width, wmask;
49706f6463aSDavid du Colombier uchar tmp[16];
49806f6463aSDavid du Colombier uchar *p;
49906f6463aSDavid du Colombier ulong o;
50006f6463aSDavid du Colombier
50106f6463aSDavid du Colombier if(offset < 0 || offset+n > f->size)
50206f6463aSDavid du Colombier error(Ebadarg);
50306f6463aSDavid du Colombier qlock(f);
50406f6463aSDavid du Colombier if(waserror()){
50506f6463aSDavid du Colombier qunlock(f);
50606f6463aSDavid du Colombier nexterror();
50706f6463aSDavid du Colombier }
50806f6463aSDavid du Colombier if(f->read != nil){
50906f6463aSDavid du Colombier width = f->width;
51006f6463aSDavid du Colombier wmask = width-1;
51106f6463aSDavid du Colombier p = buf;
51206f6463aSDavid du Colombier if(offset & wmask) {
51306f6463aSDavid du Colombier o = offset & ~wmask;
51406f6463aSDavid du Colombier if(f->read(f, o, (ulong*)tmp, width) < 0)
51506f6463aSDavid du Colombier error(Eio);
51606f6463aSDavid du Colombier memmove(tmp, (uchar*)f->addr + o, width);
51706f6463aSDavid du Colombier for(; n > 0 && offset & wmask; n--)
51806f6463aSDavid du Colombier *p++ = tmp[offset++ & wmask];
51906f6463aSDavid du Colombier }
52006f6463aSDavid du Colombier r = n & wmask;
52106f6463aSDavid du Colombier n &= ~wmask;
52206f6463aSDavid du Colombier if(n){
52306f6463aSDavid du Colombier if(f->read(f, offset, (ulong*)p, n) < 0)
52406f6463aSDavid du Colombier error(Eio);
52506f6463aSDavid du Colombier offset += n;
52606f6463aSDavid du Colombier p += n;
52706f6463aSDavid du Colombier }
52806f6463aSDavid du Colombier if(r){
52906f6463aSDavid du Colombier if(f->read(f, offset, (ulong*)tmp, width))
53006f6463aSDavid du Colombier error(Eio);
53106f6463aSDavid du Colombier memmove(p, tmp, r);
53206f6463aSDavid du Colombier }
53306f6463aSDavid du Colombier }else
53406f6463aSDavid du Colombier /* assumes hardware supports byte access */
53506f6463aSDavid du Colombier memmove(buf, (uchar*)f->addr+offset, n);
53606f6463aSDavid du Colombier poperror();
53706f6463aSDavid du Colombier qunlock(f);
53806f6463aSDavid du Colombier return n;
53906f6463aSDavid du Colombier }
54006f6463aSDavid du Colombier
54106f6463aSDavid du Colombier static long
writeflash(Flash * f,long offset,void * buf,int n)54206f6463aSDavid du Colombier writeflash(Flash *f, long offset, void *buf, int n)
54306f6463aSDavid du Colombier {
54406f6463aSDavid du Colombier uchar tmp[16];
54506f6463aSDavid du Colombier uchar *p;
54606f6463aSDavid du Colombier ulong o;
54706f6463aSDavid du Colombier int r, width, wmask;
54806f6463aSDavid du Colombier Flashregion *rg;
54906f6463aSDavid du Colombier
55006f6463aSDavid du Colombier if(f->write == nil || offset < 0 || offset+n > f->size)
55106f6463aSDavid du Colombier error(Ebadarg);
55206f6463aSDavid du Colombier rg = flashregion(f, offset);
55306f6463aSDavid du Colombier if(f->protect && rg != nil && rg->start == 0 && offset < rg->erasesize)
55406f6463aSDavid du Colombier error(Eprotect);
55506f6463aSDavid du Colombier width = f->width;
55606f6463aSDavid du Colombier wmask = width-1;
55706f6463aSDavid du Colombier qlock(f);
55806f6463aSDavid du Colombier archflashwp(f, 0);
55906f6463aSDavid du Colombier if(waserror()){
56006f6463aSDavid du Colombier archflashwp(f, 1);
56106f6463aSDavid du Colombier qunlock(f);
56206f6463aSDavid du Colombier nexterror();
56306f6463aSDavid du Colombier }
56406f6463aSDavid du Colombier p = buf;
56506f6463aSDavid du Colombier if(offset&wmask){
56606f6463aSDavid du Colombier o = offset & ~wmask;
56706f6463aSDavid du Colombier if(f->read != nil){
56806f6463aSDavid du Colombier if(f->read(f, o, tmp, width) < 0)
56906f6463aSDavid du Colombier error(Eio);
57006f6463aSDavid du Colombier }else
57106f6463aSDavid du Colombier memmove(tmp, (uchar*)f->addr+o, width);
57206f6463aSDavid du Colombier for(; n > 0 && offset&wmask; n--)
57306f6463aSDavid du Colombier tmp[offset++&wmask] = *p++;
57406f6463aSDavid du Colombier if(f->write(f, o, tmp, width) < 0)
57506f6463aSDavid du Colombier error(Eio);
57606f6463aSDavid du Colombier }
57706f6463aSDavid du Colombier r = n&wmask;
57806f6463aSDavid du Colombier n &= ~wmask;
57906f6463aSDavid du Colombier if(n){
58006f6463aSDavid du Colombier if(f->write(f, offset, p, n) < 0)
58106f6463aSDavid du Colombier error(Eio);
58206f6463aSDavid du Colombier offset += n;
58306f6463aSDavid du Colombier p += n;
58406f6463aSDavid du Colombier }
58506f6463aSDavid du Colombier if(r){
58606f6463aSDavid du Colombier if(f->read != nil){
58706f6463aSDavid du Colombier if(f->read(f, offset, tmp, width) < 0)
58806f6463aSDavid du Colombier error(Eio);
58906f6463aSDavid du Colombier }else
59006f6463aSDavid du Colombier memmove(tmp, (uchar*)f->addr+offset, width);
59106f6463aSDavid du Colombier memmove(tmp, p, r);
59206f6463aSDavid du Colombier if(f->write(f, offset, tmp, width) < 0)
59306f6463aSDavid du Colombier error(Eio);
59406f6463aSDavid du Colombier }
59506f6463aSDavid du Colombier poperror();
59606f6463aSDavid du Colombier archflashwp(f, 1);
59706f6463aSDavid du Colombier qunlock(f);
59806f6463aSDavid du Colombier return n;
59906f6463aSDavid du Colombier }
60006f6463aSDavid du Colombier
60106f6463aSDavid du Colombier static void
eraseflash(Flash * f,Flashregion * r,ulong addr)60206f6463aSDavid du Colombier eraseflash(Flash *f, Flashregion *r, ulong addr)
60306f6463aSDavid du Colombier {
60406f6463aSDavid du Colombier int rv;
60506f6463aSDavid du Colombier
60606f6463aSDavid du Colombier if(f->protect && r != nil && r->start == 0 && addr < r->erasesize)
60706f6463aSDavid du Colombier error(Eprotect);
60806f6463aSDavid du Colombier qlock(f);
60906f6463aSDavid du Colombier archflashwp(f, 0);
61006f6463aSDavid du Colombier if(waserror()){
61106f6463aSDavid du Colombier archflashwp(f, 1);
61206f6463aSDavid du Colombier qunlock(f);
61306f6463aSDavid du Colombier nexterror();
61406f6463aSDavid du Colombier }
61506f6463aSDavid du Colombier if(r == nil){
61606f6463aSDavid du Colombier if(f->eraseall != nil)
61706f6463aSDavid du Colombier rv = f->eraseall(f);
61806f6463aSDavid du Colombier else
61906f6463aSDavid du Colombier rv = -1;
62006f6463aSDavid du Colombier }else
62106f6463aSDavid du Colombier rv = f->erasezone(f, r, addr);
62206f6463aSDavid du Colombier if(rv < 0)
62306f6463aSDavid du Colombier error(Eio);
62406f6463aSDavid du Colombier poperror();
62506f6463aSDavid du Colombier archflashwp(f, 1);
62606f6463aSDavid du Colombier qunlock(f);
62706f6463aSDavid du Colombier }
62806f6463aSDavid du Colombier
62906f6463aSDavid du Colombier /*
63006f6463aSDavid du Colombier * flash access taking width and interleave into account
63106f6463aSDavid du Colombier */
63206f6463aSDavid du Colombier int
flashget(Flash * f,ulong a)63306f6463aSDavid du Colombier flashget(Flash *f, ulong a)
63406f6463aSDavid du Colombier {
63506f6463aSDavid du Colombier switch(f->width){
63606f6463aSDavid du Colombier default:
63706f6463aSDavid du Colombier return ((uchar*)f->addr)[a<<f->bshift];
63806f6463aSDavid du Colombier case 2:
63906f6463aSDavid du Colombier return ((ushort*)f->addr)[a];
64006f6463aSDavid du Colombier case 4:
64106f6463aSDavid du Colombier return ((ulong*)f->addr)[a];
64206f6463aSDavid du Colombier }
64306f6463aSDavid du Colombier }
64406f6463aSDavid du Colombier
64506f6463aSDavid du Colombier void
flashput(Flash * f,ulong a,int v)64606f6463aSDavid du Colombier flashput(Flash *f, ulong a, int v)
64706f6463aSDavid du Colombier {
64806f6463aSDavid du Colombier switch(f->width){
64906f6463aSDavid du Colombier default:
65006f6463aSDavid du Colombier ((uchar*)f->addr)[a<<f->bshift] = v;
65106f6463aSDavid du Colombier break;
65206f6463aSDavid du Colombier case 2:
65306f6463aSDavid du Colombier ((ushort*)f->addr)[a] = v;
65406f6463aSDavid du Colombier break;
65506f6463aSDavid du Colombier case 4:
65606f6463aSDavid du Colombier ((ulong*)f->addr)[a] = v;
65706f6463aSDavid du Colombier break;
65806f6463aSDavid du Colombier }
65906f6463aSDavid du Colombier coherence();
66006f6463aSDavid du Colombier }
661