xref: /plan9/sys/src/9/port/devflash.c (revision 4e3613ab15c331a9ada113286cc0f2a35bc0373d)
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