xref: /inferno-os/os/port/devflash.c (revision 7e00430948d8af545f880e82bb30cd3ee50deb04)
1 #include	"u.h"
2 #include	"../port/lib.h"
3 #include	"mem.h"
4 #include	"dat.h"
5 #include	"fns.h"
6 #include	"../port/error.h"
7 
8 /*
9  * flash memory
10  */
11 
12 #include "../port/flashif.h"
13 
14 typedef struct Flashtype Flashtype;
15 struct Flashtype {
16 	char*	name;
17 	int	(*reset)(Flash*);
18 	Flashtype*	next;
19 };
20 
21 enum {
22 	Nbanks = 2,
23 };
24 
25 static struct
26 {
27 	Flash*	card[Nbanks];	/* actual card type, reset for access */
28 	Flashtype*	types;	/* possible card types */
29 }flash;
30 
31 enum{
32 	Qtopdir,
33 	Qflashdir,
34 	Qdata,
35 	Qctl,
36 };
37 
38 #define	TYPE(q)	((ulong)(q) & 0xFF)
39 #define	PART(q)	((ulong)(q)>>8)
40 #define	QID(p,t)	(((p)<<8) | (t))
41 
42 static	Flashregion*	flashregion(Flash*, ulong);
43 static	char*	flashnewpart(Flash*, char*, ulong, ulong);
44 static	ulong	flashaddr(Flash*, Flashpart*, char*);
45 static	void	protect(Flash*, ulong);
46 static	void	eraseflash(Flash*, Flashregion*, ulong);
47 static	long	readflash(Flash*, void*, long, int);
48 static	long	writeflash(Flash*, long, void*,int);
49 
50 static char Eprotect[] = "flash region protected";
51 
52 static int
53 flash2gen(Chan *c, ulong p, Dir *dp)
54 {
55 	Flashpart *fp;
56 	Flash *f;
57 	Qid q;
58 	int mode;
59 
60 	f = flash.card[c->dev];
61 	fp = &f->part[PART(p)];
62 	if(fp->name == nil)
63 		return 0;
64 	mkqid(&q, p, 0, QTFILE);
65 	switch(TYPE(p)){
66 	case Qdata:
67 		mode = 0660;
68 		if(f->write == nil)
69 			mode = 0440;
70 		devdir(c, q, fp->name, fp->end-fp->start, eve, mode, dp);
71 		return 1;
72 	case Qctl:
73 		snprint(up->genbuf, sizeof(up->genbuf), "%sctl", fp->name);
74 		devdir(c, q, up->genbuf, 0, eve, 0660, dp);
75 		return 1;
76 	default:
77 		return -1;
78 	}
79 }
80 
81 static int
82 flashgen(Chan *c, char*, Dirtab*, int, int s, Dir *dp)
83 {
84 	Qid q;
85 	char *n;
86 
87 	if(s == DEVDOTDOT){
88 		mkqid(&q, QID(0, Qtopdir), 0, QTDIR);
89 		n = "#F";
90 		if(c->dev != 0){
91 			sprint(up->genbuf, "#F%ld", c->dev);
92 			n = up->genbuf;
93 		}
94 		devdir(c, q, n, 0, eve, 0555, dp);
95 		return 1;
96 	}
97 	switch(TYPE(c->qid.path)){
98 	case Qtopdir:
99 		if(s != 0)
100 			break;
101 		mkqid(&q, QID(0, Qflashdir), 0, QTDIR);
102 		n = "flash";
103 		if(c->dev != 0){
104 			sprint(up->genbuf, "flash%ld", c->dev);
105 			n = up->genbuf;
106 		}
107 		devdir(c, q, n, 0, eve, 0555, dp);
108 		return 1;
109 	case Qflashdir:
110 		if(s >= 2*nelem(flash.card[c->dev]->part))
111 			return -1;
112 		return flash2gen(c, QID(s>>1, s&1?Qctl:Qdata), dp);
113 	case Qctl:
114 	case Qdata:
115 		return flash2gen(c, (ulong)c->qid.path, dp);
116 	}
117 	return -1;
118 }
119 
120 static void
121 flashreset(void)
122 {
123 	Flash *f;
124 	Flashtype *t;
125 	char *e;
126 	int bank;
127 
128 	for(bank = 0; bank < Nbanks; bank++){
129 		f = malloc(sizeof(*f));
130 		if(f == nil){
131 			print("#F%d: can't allocate Flash data\n", bank);
132 			return;
133 		}
134 		f->cmask = ~(ulong)0;
135 		if(archflashreset(bank, f) < 0 || f->type == nil || f->addr == nil){
136 			free(f);
137 			return;
138 		}
139 		for(t = flash.types; t != nil; t = t->next)
140 			if(strcmp(f->type, t->name) == 0)
141 				break;
142 		if(t == nil){
143 			iprint("#F%d: no flash driver for type %s (addr %p)\n", bank, f->type, f->addr);
144 			free(f);
145 			return;
146 		}
147 		f->reset = t->reset;
148 		f->protect = 1;
149 		if(f->reset(f) == 0){
150 			flash.card[bank] = f;
151 			iprint("#F%d: %s addr 0x%lux len %lud width %d interleave %d\n", bank, f->type, PADDR(f->addr), f->size, f->width, f->interleave);
152 			e = flashnewpart(f, "flash", 0, f->size);
153 			if(e != nil)
154 				panic("#F%d: couldn't init table: %s\n", bank, e);	/* shouldn't happen */
155 		}else
156 			iprint("#F%d: reset failed (%s)\n", bank, f->type);
157 	}
158 }
159 
160 static Chan*
161 flashattach(char *spec)
162 {
163 	Flash *f;
164 	int bank;
165 	Chan *c;
166 
167 	bank = strtol(spec, nil, 0);
168 	if(bank < 0 || bank >= Nbanks ||
169 	   (f = flash.card[bank]) == nil ||
170 	   f->attach != nil && f->attach(f) < 0)
171 		error(Enodev);
172 	c = devattach('F', spec);
173 	c->dev = bank;
174 	return c;
175 }
176 
177 static Walkqid*
178 flashwalk(Chan *c, Chan *nc, char **name, int nname)
179 {
180 	return devwalk(c, nc, name, nname, nil, 0, flashgen);
181 }
182 
183 static int
184 flashstat(Chan *c, uchar *dp, int n)
185 {
186 	return devstat(c, dp, n, nil, 0, flashgen);
187 }
188 
189 static Chan*
190 flashopen(Chan *c, int omode)
191 {
192 	omode = openmode(omode);
193 	switch(TYPE(c->qid.path)){
194 	case Qdata:
195 	case Qctl:
196 		if(flash.card[c->dev] == nil)
197 			error(Enodev);
198 		break;
199 	}
200 	return devopen(c, omode, nil, 0, flashgen);
201 }
202 
203 static void
204 flashclose(Chan*)
205 {
206 }
207 
208 static long
209 flashread(Chan *c, void *buf, long n, vlong offset)
210 {
211 	Flash *f;
212 	char *s, *o;
213 	Flashpart *fp;
214 	Flashregion *r;
215 	int i;
216 	ulong start, end;
217 
218 	if(c->qid.type & QTDIR)
219 		return devdirread(c, buf, n, nil, 0, flashgen);
220 
221 	f = flash.card[c->dev];
222 	fp = &f->part[PART(c->qid.path)];
223 	if(fp->name == nil)
224 		error(Egreg);
225 	switch(TYPE(c->qid.path)){
226 	case Qdata:
227 		offset += fp->start;
228 		if(offset >= fp->end)
229 			return 0;
230 		if(offset+n > fp->end)
231 			n = fp->end - offset;
232 		n = readflash(f, buf, offset, n);
233 		if(n < 0)
234 			error(Eio);
235 		return n;
236 	case Qctl:
237 		s = malloc(READSTR);
238 		if(waserror()){
239 			free(s);
240 			nexterror();
241 		}
242 		o = seprint(s, s+READSTR, "%#2.2ux %#4.4ux %d %q\n",
243 			f->id, f->devid, f->width, f->sort!=nil? f->sort: "nor");
244 		for(i=0; i<f->nr; i++){
245 			r = &f->regions[i];
246 			if(r->start < fp->end && fp->start < r->end){
247 				start = r->start;
248 				if(fp->start > start)
249 					start = fp->start;
250 				end = r->end;
251 				if(fp->end < end)
252 					end = fp->end;
253 				o = seprint(o, s+READSTR, "%#8.8lux %#8.8lux %#8.8lux", start, end, r->erasesize);
254 				if(r->pagesize)
255 					o = seprint(o, s+READSTR, " %#8.8lux", r->pagesize);
256 				o = seprint(o, s+READSTR, "\n");
257 			}
258 		}
259 		n = readstr(offset, buf, n, s);
260 		poperror();
261 		free(s);
262 		return n;
263 	}
264 	error(Egreg);
265 	return 0;		/* not reached */
266 }
267 
268 enum {
269 	CMerase,
270 	CMadd,
271 	CMremove,
272 	CMsync,
273 	CMprotectboot,
274 };
275 
276 static Cmdtab flashcmds[] = {
277 	{CMerase, "erase", 2},
278 	{CMadd, "add", 0},
279 	{CMremove, "remove", 2},
280 	{CMsync, "sync", 0},
281 	{CMprotectboot, "protectboot", 0},
282 };
283 
284 static long
285 flashwrite(Chan *c, void *buf, long n, vlong offset)
286 {
287 	Cmdbuf *cb;
288 	Cmdtab *ct;
289 	ulong addr, start, end;
290 	char *e;
291 	Flashpart *fp;
292 	Flashregion *r;
293 	Flash *f;
294 
295 	f = flash.card[c->dev];
296 	fp = &f->part[PART(c->qid.path)];
297 	if(fp->name == nil)
298 		error(Egreg);
299 	switch(TYPE(c->qid.path)){
300 	case Qdata:
301 		if(f->write == nil)
302 			error(Eperm);
303 		offset += fp->start;
304 		if(offset >= fp->end)
305 			return 0;
306 		if(offset+n > fp->end)
307 			n = fp->end - offset;
308 		n = writeflash(f, offset, buf, n);
309 		if(n < 0)
310 			error(Eio);
311 		return n;
312 	case Qctl:
313 		cb = parsecmd(buf, n);
314 		if(waserror()){
315 			free(cb);
316 			nexterror();
317 		}
318 		ct = lookupcmd(cb, flashcmds, nelem(flashcmds));
319 		switch(ct->index){
320 		case CMerase:
321 			if(strcmp(cb->f[1], "all") != 0){
322 				addr = flashaddr(f, fp, cb->f[1]);
323 				r = flashregion(f, addr);
324 				if(r == nil)
325 					error("nonexistent flash region");
326 				if(addr%r->erasesize != 0)
327 					error("invalid erase block address");
328 				eraseflash(f, r, addr);
329 			}else if(fp->start == 0 && fp->end == f->size && f->eraseall != nil){
330 				eraseflash(f, nil, 0);
331 			}else{
332 				for(addr = fp->start; addr < fp->end; addr += r->erasesize){
333 					r = flashregion(f, addr);
334 					if(r == nil)
335 						error("nonexistent flash region");
336 					if(addr%r->erasesize != 0)
337 						error("invalid erase block address");
338 					eraseflash(f, r, addr);
339 				}
340 			}
341 			break;
342 		case CMadd:
343 			if(cb->nf < 3)
344 				error(Ebadarg);
345 			start = flashaddr(f, fp, cb->f[2]);
346 			if(cb->nf > 3 && strcmp(cb->f[3], "end") != 0)
347 				end = flashaddr(f, fp, cb->f[3]);
348 			else
349 				end = fp->end;
350 			if(start > end || start >= fp->end || end > fp->end)
351 				error(Ebadarg);
352 			e = flashnewpart(f, cb->f[1], start, end);
353 			if(e != nil)
354 				error(e);
355 			break;
356 		case CMremove:
357 			/* TO DO */
358 			break;
359 		case CMprotectboot:
360 			if(cb->nf > 1 && strcmp(cb->f[1], "off") == 0)
361 				f->protect = 0;
362 			else
363 				f->protect = 1;
364 			break;
365 		case CMsync:
366 			/* TO DO? */
367 			break;
368 		default:
369 			error(Ebadarg);
370 		}
371 		poperror();
372 		free(cb);
373 		return n;
374 	}
375 	error(Egreg);
376 	return 0;		/* not reached */
377 }
378 
379 static char*
380 flashnewpart(Flash *f, char *name, ulong start, ulong end)
381 {
382 	Flashpart *fp, *empty;
383 	int i;
384 
385 	empty = nil;
386 	for(i = 0; i < nelem(f->part); i++){
387 		fp = &f->part[i];
388 		if(fp->name == nil){
389 			if(empty == nil)
390 				empty = fp;
391 		}else if(strcmp(fp->name, name) == 0)
392 			return Eexist;
393 	}
394 	if((fp = empty) == nil)
395 		return "partition table full";
396 	fp->name = strdup(name);
397 	if(fp->name == nil)
398 		return Enomem;
399 	fp->start = start;
400 	fp->end = end;
401 	return nil;
402 }
403 
404 static ulong
405 flashaddr(Flash *f, Flashpart *fp, char *s)
406 {
407 	Flashregion *r;
408 	ulong addr;
409 
410 	addr = strtoul(s, &s, 0);
411 	if(*s)
412 		error(Ebadarg);
413 	if(fp->name == nil)
414 		error("partition removed");
415 	addr += fp->start;
416 	r = flashregion(f, addr);
417 	if(r != nil && addr%r->erasesize != 0)
418 		error("invalid erase unit address");
419 	if(addr < fp->start || addr > fp->end || addr > f->size)
420 		error(Ebadarg);
421 	return addr;
422 }
423 
424 static Flashregion*
425 flashregion(Flash *f, ulong a)
426 {
427 	int i;
428 	Flashregion *r;
429 
430 	for(i=0; i<f->nr; i++){
431 		r = &f->regions[i];
432 		if(r->start <= a && a < r->end)
433 			return r;
434 	}
435 	return nil;
436 }
437 
438 Dev flashdevtab = {
439 	'F',
440 	"flash",
441 
442 	flashreset,
443 	devinit,
444 	devshutdown,
445 	flashattach,
446 	flashwalk,
447 	flashstat,
448 	flashopen,
449 	devcreate,
450 	flashclose,
451 	flashread,
452 	devbread,
453 	flashwrite,
454 	devbwrite,
455 	devremove,
456 	devwstat,
457 };
458 
459 /*
460  * called by flash card types named in link section (eg, flashamd.c)
461  */
462 void
463 addflashcard(char *name, int (*reset)(Flash*))
464 {
465 	Flashtype *f, **l;
466 
467 	f = (Flashtype*)malloc(sizeof(*f));
468 	f->name = name;
469 	f->reset = reset;
470 	f->next = nil;
471 	for(l = &flash.types; *l != nil; l = &(*l)->next)
472 		;
473 	*l = f;
474 }
475 
476 static long
477 readflash(Flash *f, void *buf, long offset, int n)
478 {
479 	int r, width, wmask;
480 	uchar tmp[16];
481 	uchar *p;
482 	ulong o;
483 
484 	if(offset < 0 || offset+n > f->size)
485 		error(Ebadarg);
486 	qlock(f);
487 	if(waserror()){
488 		qunlock(f);
489 		nexterror();
490 	}
491 	if(f->read != nil){
492 		width = f->width;
493 		wmask = width-1;
494 		p = buf;
495 		if(offset&wmask) {
496 			o = offset & ~wmask;
497 			if(f->read(f, o, (ulong*)tmp, width) < 0)
498 				error(Eio);
499 			memmove(tmp, (uchar*)f->addr+o, width);
500 			for(; n > 0 && offset&wmask; n--)
501 				*p++ = tmp[offset++&wmask];
502 		}
503 		r = n&wmask;
504 		n &= ~wmask;
505 		if(n){
506 			if(f->read(f, offset, (ulong*)p, n) < 0)
507 				error(Eio);
508 			offset += n;
509 			p += n;
510 		}
511 		if(r){
512 			if(f->read(f, offset, (ulong*)tmp, width))
513 				error(Eio);
514 			memmove(p, tmp, r);
515 		}
516 	}else
517 		memmove(buf, (uchar*)f->addr+offset, n);	/* assumes hardware supports byte access */
518 	poperror();
519 	qunlock(f);
520 	return n;
521 }
522 
523 static long
524 writeflash(Flash *f, long offset, void *buf, int n)
525 {
526 	uchar tmp[16];
527 	uchar *p;
528 	ulong o;
529 	int r, width, wmask;
530 	Flashregion *rg;
531 
532 	if(f->write == nil || offset < 0 || offset+n > f->size)
533 		error(Ebadarg);
534 	rg = flashregion(f, offset);
535 	if(f->protect && rg != nil && rg->start == 0 && offset < rg->erasesize)
536 		error(Eprotect);
537 	width = f->width;
538 	wmask = width-1;
539 	qlock(f);
540 	archflashwp(f, 0);
541 	if(waserror()){
542 		archflashwp(f, 1);
543 		qunlock(f);
544 		nexterror();
545 	}
546 	p = buf;
547 	if(offset&wmask){
548 		o = offset & ~wmask;
549 		if(f->read != nil){
550 			if(f->read(f, o, tmp, width) < 0)
551 				error(Eio);
552 		}else
553 			memmove(tmp, (uchar*)f->addr+o, width);
554 		for(; n > 0 && offset&wmask; n--)
555 			tmp[offset++&wmask] = *p++;
556 		if(f->write(f, o, tmp, width) < 0)
557 			error(Eio);
558 	}
559 	r = n&wmask;
560 	n &= ~wmask;
561 	if(n){
562 		if(f->write(f, offset, p, n) < 0)
563 			error(Eio);
564 		offset += n;
565 		p += n;
566 	}
567 	if(r){
568 		if(f->read != nil){
569 			if(f->read(f, offset, tmp, width) < 0)
570 				error(Eio);
571 		}else
572 			memmove(tmp, (uchar*)f->addr+offset, width);
573 		memmove(tmp, p, r);
574 		if(f->write(f, offset, tmp, width) < 0)
575 			error(Eio);
576 	}
577 	poperror();
578 	archflashwp(f, 1);
579 	qunlock(f);
580 	return n;
581 }
582 
583 static void
584 eraseflash(Flash *f, Flashregion *r, ulong addr)
585 {
586 	int rv;
587 
588 	if(f->protect && r != nil && r->start == 0 && addr < r->erasesize)
589 		error(Eprotect);
590 	qlock(f);
591 	archflashwp(f, 0);
592 	if(waserror()){
593 		archflashwp(f, 1);
594 		qunlock(f);
595 		nexterror();
596 	}
597 	if(r == nil){
598 		if(f->eraseall != nil)
599 			rv = f->eraseall(f);
600 		else
601 			rv = -1;
602 	}else
603 		rv = f->erasezone(f, r, addr);
604 	if(rv < 0)
605 		error(Eio);
606 	poperror();
607 	archflashwp(f, 1);
608 	qunlock(f);
609 }
610 
611 /*
612  * flash access taking width and interleave into account
613  */
614 int
615 flashget(Flash *f, ulong a)
616 {
617 	switch(f->width){
618 	default:
619 		return ((uchar*)f->addr)[a<<f->bshift];
620 	case 2:
621 		return ((ushort*)f->addr)[a];
622 	case 4:
623 		return ((ulong*)f->addr)[a];
624 	}
625 }
626 
627 void
628 flashput(Flash *f, ulong a, int v)
629 {
630 	switch(f->width){
631 	default:
632 		((uchar*)f->addr)[a<<f->bshift] = v;
633 		break;
634 	case 2:
635 		((ushort*)f->addr)[a] = v;
636 		break;
637 	case 4:
638 		((ulong*)f->addr)[a] = v;
639 		break;
640 	}
641 }
642