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