]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/port/devflash.c
devmnt: deal with partial response for Tversion request in mntversion()
[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 = smalloc(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         if(strlen(name)+3 >= sizeof(up->genbuf))
407                 return Etoolong;
408         kstrdup(&fp->name, name);
409         if(fp->name == nil)
410                 return Enomem;
411         fp->start = start;
412         fp->end = end;
413         return nil;
414 }
415
416 static ulong
417 flashaddr(Flash *f, Flashpart *fp, char *s)
418 {
419         Flashregion *r;
420         ulong addr;
421
422         addr = strtoul(s, &s, 0);
423         if(*s)
424                 error(Ebadarg);
425         if(fp->name == nil)
426                 error("partition removed");
427         addr += fp->start;
428         r = flashregion(f, addr);
429         if(r != nil && addr%r->erasesize != 0)
430                 error("invalid erase unit address");
431         if(addr < fp->start || addr > fp->end || addr > f->size)
432                 error(Ebadarg);
433         return addr;
434 }
435
436 static Flashregion*
437 flashregion(Flash *f, ulong a)
438 {
439         int i;
440         Flashregion *r;
441
442         for(i=0; i<f->nr; i++){
443                 r = &f->regions[i];
444                 if(r->start <= a && a < r->end)
445                         return r;
446         }
447         return nil;
448 }
449
450 Dev flashdevtab = {
451         'F',
452         "flash",
453
454         flashreset,
455         devinit,
456         devshutdown,
457         flashattach,
458         flashwalk,
459         flashstat,
460         flashopen,
461         devcreate,
462         flashclose,
463         flashread,
464         devbread,
465         flashwrite,
466         devbwrite,
467         devremove,
468         devwstat,
469 };
470
471 /*
472  * called by flash card types named in link section (eg, flashamd.c)
473  */
474 void
475 addflashcard(char *name, int (*reset)(Flash*))
476 {
477         Flashtype *f, **l;
478
479         f = malloc(sizeof(*f));
480         if(f == nil){
481                 print("addflashcard: no memory for Flashtype\n");
482                 return;
483         }
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 }