]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devflash.c
bind devshr to /shr
[plan9front.git] / sys / src / 9 / port / devflash.c
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 }