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