]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/ppc/devflash.c
Import sources from 2011-03-30 iso image - lib
[plan9front.git] / sys / src / 9 / ppc / devflash.c
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 enum {
9         Nflash = 2,
10         Maxwchunk=      1024,   /* maximum chunk written by one call to falg->write */
11 };
12
13
14 /*
15  *  Flashes are either 8 or 16 bits wide.  On some installations (e.g., the
16  *  bitsy, they are interleaved: address 0 is in the first chip, address 2
17  *  on the second, address 4 on the first, etc.
18  *  We define Funit as the unit that matches the width of a single flash chip,
19  *  so Funit is either `uchar' or `ushort' (I haven't seen 32-bit wide flashes),
20  *  and we define Fword as the unit that matches a set of interleaved Funits.
21  *  We access interleaved flashes simultaneously, by doing single reads and
22  *  writes to both.  The macro `mirror' takes a command and replicates it for
23  *  this purpose.
24  *  The Blast board has a non-interleaved 16-bit wide flash.  When doing
25  *  writes to it, we must swap bytes.
26  */
27
28 typedef struct FlashAlg FlashAlg;
29 typedef struct Flash Flash;
30 typedef struct FlashRegion FlashRegion;
31
32 #ifdef WIDTH8
33         typedef         uchar           Funit;          /* Width of the flash (uchar or ushort) */
34 #       define          toendian(x)     (x)                     /* Little or big endianness */
35 #       define          fromendian(x)   (x)
36 #       define          reg(x)          ((x)<<1)
37 #       ifdef INTERLEAVED
38 #               define  mirror(x)               ((x)<<8|(x))    /* Double query for interleaved flashes */
39                 typedef ushort          Fword;          /* Width after interleaving */
40 #               define  Wshift          1
41 #       else
42 #               define  mirror(x)               (x)
43                 typedef uchar           Fword;
44 #               define  Wshift          0
45 #       endif
46 #else
47         typedef         ushort          Funit;
48 #       define          toendian(x)     ((x)<<8)
49 #       define          fromendian(x)   ((x)>>8)
50 #       define          reg(x)          (x)
51 #       ifdef INTERLEAVED
52 #               define  mirror(x)               (toendian(x)<<16|toendian(x))
53                 typedef ulong           Fword;
54 #               define  Wshift          2
55 #       else
56 #               define mirror(x)                toendian(x)
57                 typedef ushort          Fword;
58 #               define  Wshift          1
59 #       endif
60 #endif
61
62 /* this defines a contiguous set of erase blocks of one size */
63 struct FlashRegion
64 {
65         ulong   addr;   /* start of region */
66         ulong   end;            /* end of region + 1 */
67         ulong   n;              /* number of blocks */
68         ulong   size;           /* size of each block */
69 };
70
71 struct Flash
72 {
73         ISAConf;                                        /* contains size */
74         RWlock;
75         Fword           *p;
76         ushort          algid;          /* access algorithm */
77         FlashAlg                *alg;
78         ushort          manid;          /* manufacturer id */
79         ushort          devid;          /* device id */
80         int                     wbsize;         /* size of write buffer */ 
81         ulong           nr;                     /* number of regions */
82         uchar           bootprotect;
83         ulong           offset;         /* beginning offset of this flash */
84         FlashRegion     r[32];
85 };
86
87 /* this defines a particular access algorithm */
88 struct FlashAlg
89 {
90         int     id;
91         char    *name;
92         void    (*identify)(Flash*);    /* identify device */
93         void    (*erase)(Flash*, ulong);        /* erase a region */
94         void    (*write)(Flash*, void*, long, ulong);   /* write a region */
95 };
96
97 static void     ise_id(Flash*);
98 static void     ise_erase(Flash*, ulong);
99 static void     ise_write(Flash*, void*, long, ulong);
100
101 static void     afs_id(Flash*);
102 static void     afs_erase(Flash*, ulong);
103 static void     afs_write(Flash*, void*, long, ulong);
104
105 static ulong    blockstart(Flash*, ulong);
106 static ulong    blockend(Flash*, ulong);
107
108 FlashAlg falg[] =
109 {
110         { 1,    "Intel/Sharp Extended", ise_id, ise_erase, ise_write    },
111         { 2,    "AMD/Fujitsu Standard", afs_id, afs_erase, afs_write    },
112 };
113
114 Flash flashes[Nflash];
115
116 /*
117  *  common flash interface
118  */
119 static uchar
120 cfigetc(Flash *flash, int off)
121 {
122         uchar rv;
123
124         flash->p[reg(0x55)] = mirror(0x98);
125         rv = fromendian(flash->p[reg(off)]);
126         flash->p[reg(0x55)] = mirror(0xFF);
127         return rv;
128 }
129
130 static ushort
131 cfigets(Flash *flash, int off)
132 {
133         return (cfigetc(flash, off+1)<<8)|cfigetc(flash, off);
134 }
135
136 static ulong
137 cfigetl(Flash *flash, int off)
138 {
139         return (cfigetc(flash, off+3)<<24)|(cfigetc(flash, off+2)<<16)|
140                 (cfigetc(flash, off+1)<<8)|cfigetc(flash, off);
141 }
142
143 static void
144 cfiquery(Flash *flash)
145 {
146         uchar q, r, y;
147         ulong x, addr;
148
149         q = cfigetc(flash, 0x10);
150         r = cfigetc(flash, 0x11);
151         y = cfigetc(flash, 0x12);
152         if(q != 'Q' || r != 'R' || y != 'Y'){
153                 print("cfi query failed: %ux %ux %ux\n", q, r, y);
154                 return;
155         }
156         flash->algid = cfigetc(flash, 0x13);
157         flash->size = (sizeof(Fword)/sizeof(Funit)) * (1<<(cfigetc(flash, 0x27)));
158         flash->wbsize = (sizeof(Fword)/sizeof(Funit)) * (1<<(cfigetc(flash, 0x2a)));
159         flash->nr = cfigetc(flash, 0x2c);
160         if(flash->nr > nelem(flash->r)){
161                 print("cfi reports > %d regions\n", nelem(flash->r));
162                 flash->nr = nelem(flash->r);
163         }
164         addr = 0;
165         for(q = 0; q < flash->nr; q++){
166                 x = cfigetl(flash, q+0x2d);
167                 flash->r[q].size = (sizeof(Fword)/sizeof(Funit)) * 256 * (x>>16);
168                 flash->r[q].n = (x&0xffff)+1;
169                 flash->r[q].addr = addr;
170                 addr += flash->r[q].size*flash->r[q].n;
171                 flash->r[q].end = addr;
172         }
173 }
174
175 /*
176  *  flash device interface
177  */
178
179 enum
180 {
181         Qtopdir,
182         Q2nddir,
183         Qfctl,
184         Qfdata,
185
186         Maxpart= 8,
187 };
188
189
190 typedef struct FPart FPart;
191 struct FPart
192 {
193         Flash   *flash;
194         char            *name;
195         char            *ctlname;
196         ulong   start;
197         ulong   end;
198 };
199 static FPart    part[Maxpart];
200
201 #define FQID(p,q)       ((p)<<8|(q))
202 #define FTYPE(q)        ((q) & 0xff)
203 #define FPART(q)        (&part[(q) >>8])
204
205 static int
206 gen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
207 {
208         Qid q;
209         FPart *fp;
210
211         q.vers = 0;
212
213         /* top level directory contains the name of the network */
214         if(c->qid.path == Qtopdir){
215                 switch(i){
216                 case DEVDOTDOT:
217                         q.path = Qtopdir;
218                         q.type = QTDIR;
219                         devdir(c, q, "#F", 0, eve, DMDIR|0555, dp);
220                         break;
221                 case 0:
222                         q.path = Q2nddir;
223                         q.type = QTDIR;
224                         devdir(c, q, "flash", 0, eve, DMDIR|0555, dp);
225                         break;
226                 default:
227                         return -1;
228                 }
229                 return 1;
230         }
231
232         /* second level contains all partitions and their control files */
233         switch(i) {
234         case DEVDOTDOT:
235                 q.path = Qtopdir;
236                 q.type = QTDIR;
237                 devdir(c, q, "#F", 0, eve, DMDIR|0555, dp);
238                 break;
239         default:
240                 if(i >= 2*Maxpart)
241                         return -1;
242                 fp = &part[i>>1];
243                 if(fp->name == nil)
244                         return 0;
245                 if(i & 1){
246                         q.path = FQID(i>>1, Qfdata);
247                         q.type = QTFILE;
248                         devdir(c, q, fp->name, fp->end-fp->start, eve, 0660, dp);
249                 } else {
250                         q.path = FQID(i>>1, Qfctl);
251                         q.type = QTFILE;
252                         devdir(c, q, fp->ctlname, 0, eve, 0660, dp);
253                 }
254                 break;
255         }
256         return 1;
257 }
258
259 static Flash *
260 findflash(ulong addr)
261 {
262         Flash *flash;
263
264         for (flash = flashes; flash < flashes + Nflash; flash++)
265                 if(addr >= flash->offset && addr < flash->offset + flash->size)
266                         return flash;
267         return nil;
268 }
269
270 static FPart*
271 findpart(char *name)
272 {
273         int i;
274
275         for(i = 0; i < Maxpart; i++)
276                 if(part[i].name != nil && strcmp(name, part[i].name) == 0)
277                         break;
278         if(i >= Maxpart)
279                 return nil;
280         return &part[i];
281 }
282
283 static void
284 addpart(FPart *fp, char *name, ulong start, ulong end)
285 {
286         int i;
287         char ctlname[64];
288         Flash *flash;
289         if (start > end)
290                 error(Ebadarg);
291         if(fp == nil){
292                 flash = findflash(start);
293                 if (flash == nil || end > flash->offset + flash->size)
294                         error(Ebadarg);
295                 start -= flash->offset;
296                 end -= flash->offset;
297         } else {
298                 start += fp->start;
299                 end += fp->start;
300                 if(start >= fp->end || end > fp->end){
301                         error(Ebadarg);
302                 }
303                 flash = fp->flash;
304         }
305         if(blockstart(flash, start) != start)
306                 error("must start on erase boundary");
307         if(blockstart(flash, end) != end && end != flash->size)
308                 error("must end on erase boundary");
309
310         fp = findpart(name);
311         if(fp != nil)
312                 error(Eexist);
313         for(i = 0; i < Maxpart; i++)
314                 if(part[i].name == nil)
315                         break;
316         if(i == Maxpart)
317                 error("no more partitions");
318         fp = &part[i];
319         kstrdup(&fp->name, name);
320         snprint(ctlname, sizeof ctlname, "%sctl", name);
321         kstrdup(&fp->ctlname, ctlname);
322         fp->flash = flash;
323         fp->start = start;
324         fp->end = end;
325 }
326
327 static void
328 rempart(FPart *fp)
329 {
330         char *p, *cp;
331
332         p = fp->name;
333         fp->name = nil;
334         cp = fp->ctlname;
335         fp->ctlname = nil;
336         free(p);
337         free(cp);
338 }
339
340 void
341 flashinit(void)
342 {
343         int i, ctlrno;
344         char *fname;
345         ulong offset;
346         Flash *flash;
347
348         offset = 0;
349         for (ctlrno = 0; ctlrno < Nflash; ctlrno++){
350                 flash = flashes + ctlrno;
351                 if(isaconfig("flash", ctlrno, flash) == 0)
352                         continue;
353                 flash->p = (Fword*)flash->mem;
354                 cfiquery(flash);
355                 for(i = 0; i < nelem(falg); i++)
356                         if(flash->algid == falg[i].id){
357                                 flash->alg = &falg[i];
358                                 (*flash->alg->identify)(flash);
359                                 break;
360                         }
361                 flash->bootprotect = 1;
362                 flash->offset = offset;
363                 fname = malloc(8);
364                 sprint(fname, "flash%d", ctlrno);
365                 addpart(nil, fname, offset, offset + flash->size);
366                 offset += flash->size;
367         }
368 }
369
370 static Chan*
371 flashattach(char* spec)
372 {
373         return devattach('F', spec);
374 }
375
376 static Walkqid*
377 flashwalk(Chan *c, Chan *nc, char **name, int nname)
378 {
379         return devwalk(c, nc, name, nname, nil, 0, gen);
380 }
381
382 static int       
383 flashstat(Chan *c, uchar *db, int n)
384 {
385         return devstat(c, db, n, nil, 0, gen);
386 }
387
388 static Chan*
389 flashopen(Chan* c, int omode)
390 {
391         omode = openmode(omode);
392         if(strcmp(up->user, eve)!=0)
393                 error(Eperm);
394         return devopen(c, omode, nil, 0, gen);
395 }
396
397 static void      
398 flashclose(Chan*)
399 {
400 }
401
402 static long
403 flashctlread(FPart *fp, void* a, long n, vlong off)
404 {
405         char *buf, *p, *e;
406         int i;
407         ulong addr, end;
408         Flash *flash;
409
410         flash = fp->flash;
411         buf = smalloc(1024);
412         e = buf + 1024;
413         p = seprint(buf, e, "0x%-9lux 0x%-9lux 0x%-9lux 0x%-9x 0x%-9ux 0x%-9ux\n",
414                 flash->offset, fp->start, fp->end-fp->start, flash->wbsize, flash->manid, flash->devid);
415         addr = fp->start;
416         for(i = 0; i < flash->nr && addr < fp->end; i++)
417                 if(flash->r[i].addr <= addr && flash->r[i].end > addr){
418                         if(fp->end <= flash->r[i].end)
419                                 end = fp->end;
420                         else
421                                 end = flash->r[i].end;
422                         p = seprint(p, e, "0x%-9lux 0x%-9lux 0x%-9lux\n", addr,
423                                 (end-addr)/flash->r[i].size, flash->r[i].size);
424                         addr = end;
425                 }
426         n = readstr(off, a, n, buf);
427         free(buf);
428         return n;
429 }
430
431 static long
432 flashdataread(FPart *fp, void* a, long n, vlong off)
433 {
434         Flash *flash;
435
436         flash = fp->flash;
437         rlock(flash);
438         if(waserror()){
439                 runlock(flash);
440                 nexterror();
441         }
442         if(fp->name == nil)
443                 error("partition vanished");
444         if(!iseve())
445                 error(Eperm);
446         off += fp->start;
447         if(off >= fp->end)
448                 n = 0;
449         if(off+n >= fp->end)
450                 n = fp->end - off;
451         if(n > 0)
452                 memmove(a, ((uchar*)flash->mem)+off, n);
453         runlock(flash);
454         poperror();
455
456         return n;
457 }
458
459 static long      
460 flashread(Chan* c, void* a, long n, vlong off)
461 {
462         int t;
463
464         if(c->qid.type == QTDIR)
465                 return devdirread(c, a, n, nil, 0, gen);
466         t = FTYPE(c->qid.path);
467         switch(t){
468         default:
469                 error(Eperm);
470         case Qfctl:
471                 n = flashctlread(FPART(c->qid.path), a, n, off);
472                 break;
473         case Qfdata:
474                 n = flashdataread(FPART(c->qid.path), a, n, off);
475                 break;
476         }
477         return n;
478 }
479
480 static void
481 bootprotect(ulong addr)
482 {
483         FlashRegion *r;
484         Flash *flash;
485
486         flash = findflash(addr);
487         if (flash == nil)
488                 error(Ebadarg);
489         if(flash->bootprotect == 0)
490                 return;
491         if(flash->nr == 0)
492                 error("writing over boot loader disallowed");
493         r = flash->r;
494         if(addr >= r->addr && addr < r->addr + r->size)
495                 error("writing over boot loader disallowed");
496 }
497
498 static ulong
499 blockstart(Flash *flash, ulong addr)
500 {
501         FlashRegion *r, *e;
502         ulong x;
503
504         r = flash->r;
505         for(e = &flash->r[flash->nr]; r < e; r++){
506                 if(addr >= r->addr && addr < r->end){
507                         x = addr - r->addr;
508                         x /= r->size;
509                         return r->addr + x*r->size;
510                 }
511         }
512                         
513         return (ulong)-1;
514 }
515
516 static ulong
517 blockend(Flash *flash, ulong addr)
518 {
519         FlashRegion *r, *e;
520         ulong x;
521
522         r = flash->r;
523         for(e = &flash->r[flash->nr]; r < e; r++)
524                 if(addr >= r->addr && addr < r->end){
525                         x = addr - r->addr;
526                         x /= r->size;
527                         return r->addr + (x+1)*r->size;
528                 }
529                         
530         return (ulong)-1;
531 }
532
533 static long
534 flashctlwrite(FPart *fp, char *p, long n)
535 {
536         Cmdbuf *cmd;
537         ulong off;
538         Flash *flash;
539
540         if(fp == nil)
541                 panic("flashctlwrite");
542
543         flash = fp->flash;
544         cmd = parsecmd(p, n);
545         wlock(flash);
546         if(waserror()){
547                 wunlock(flash);
548                 nexterror();
549         }
550         if(strcmp(cmd->f[0], "erase") == 0){
551                 switch(cmd->nf){
552                 case 2:
553                         /* erase a single block in the partition */
554                         off = atoi(cmd->f[1]);
555                         off += fp->start;
556                         if(off >= fp->end)
557                                 error("region not in partition");
558                         if(off != blockstart(flash, off))
559                                 error("erase must be a block boundary");
560                         bootprotect(off);
561                         (*flash->alg->erase)(flash, off);
562                         break;
563                 case 1:
564                         /* erase the whole partition */
565                         bootprotect(fp->start);
566                         for(off = fp->start; off < fp->end; off = blockend(flash, off))
567                                 (*flash->alg->erase)(flash, off);
568                         break;
569                 default:
570                         error(Ebadarg);
571                 }
572         } else if(strcmp(cmd->f[0], "add") == 0){
573                 if(cmd->nf != 4)
574                         error(Ebadarg);
575                 addpart(fp, cmd->f[1], strtoul(cmd->f[2], nil, 0), strtoul(cmd->f[3], nil, 0));
576         } else if(strcmp(cmd->f[0], "remove") == 0){
577                 rempart(fp);
578         } else if(strcmp(cmd->f[0], "protectboot") == 0){
579                 if(cmd->nf == 0 || strcmp(cmd->f[1], "off") != 0)
580                         flash->bootprotect = 1;
581                 else
582                         flash->bootprotect = 0;
583         } else
584                 error(Ebadarg);
585         poperror();
586         wunlock(flash);
587         free(cmd);
588
589         return n;
590 }
591
592 static long
593 flashdatawrite(FPart *fp, uchar *p, long n, long off)
594 {
595         uchar *end;
596         int m;
597         int on;
598         long ooff;
599         uchar *buf;
600         Flash *flash;
601
602         if(fp == nil)
603                 panic("flashdatawrite");
604
605         flash = fp->flash;
606         buf = nil;
607         wlock(flash);
608         if(waserror()){
609                 wunlock(flash);
610                 if(buf != nil)
611                         free(buf);
612                 nexterror();
613         }
614
615         if(fp->name == nil)
616                 error("partition vanished");
617         if(!iseve())
618                 error(Eperm);
619
620         /* can't cross partition boundaries */
621         off += fp->start;
622         if(off >= fp->end || off+n > fp->end || n <= 0)
623                 error(Ebadarg);
624
625         /* make sure we're not writing the boot sector */
626         bootprotect(off);
627
628         on = n;
629
630         /*
631          *  get the data into kernel memory to avoid faults during writing.
632          *  if write is not on a quad boundary or not a multiple of 4 bytes,
633          *  extend with data already in flash.
634          */
635         buf = smalloc(n+8);
636         m = off & 3;
637         if(m){
638                 *(ulong*)buf = flash->p[off>>Wshift];
639                 n += m;
640                 off -= m;
641         }
642         if(n & 3){
643                 n -= n & 3;
644                 *(ulong*)(&buf[n]) = flash->p[(off+n)>>Wshift];
645                 n += 4;
646         }
647         memmove(&buf[m], p, on);
648
649         /* (*flash->alg->write) can't cross blocks */
650         ooff = off;
651         p = buf;
652         for(end = p + n; p < end; p += m){
653                 m = blockend(flash, off) - off;
654                 if(m > end - p)
655                         m = end - p;
656                 if(m > Maxwchunk)
657                         m = Maxwchunk;
658                 (*flash->alg->write)(flash, p, m, off);
659                 off += m;
660         }
661
662         /* make sure write succeeded */
663         if(memcmp(buf, &flash->p[ooff>>Wshift], n) != 0)
664                 error("written bytes don't match");
665
666         wunlock(flash);
667         free(buf);
668         poperror();
669
670         return on;
671 }
672
673 static long      
674 flashwrite(Chan* c, void* a, long n, vlong off)
675 {
676         int t;
677
678         if(c->qid.type == QTDIR)
679                 error(Eperm);
680
681         if(!iseve())
682                 error(Eperm);
683
684         t = FTYPE(c->qid.path);
685         switch(t){
686         default:
687                 panic("flashwrite");
688         case Qfctl:
689                 n = flashctlwrite(FPART(c->qid.path), a, n);
690                 break;
691         case Qfdata:
692                 n = flashdatawrite(FPART(c->qid.path), a, n, off);
693                 break;
694         }
695         return n;
696 }
697
698 Dev flashdevtab = {
699         'F',
700         "flash",
701
702         devreset,
703         flashinit,
704         devshutdown,
705         flashattach,
706         flashwalk,
707         flashstat,
708         flashopen,
709         devcreate,
710         flashclose,
711         flashread,
712         devbread,
713         flashwrite,
714         devbwrite,
715         devremove,
716         devwstat,
717 };
718
719 enum
720 {
721         /* status register */
722         ISEs_lockerr=           1<<1,
723         ISEs_powererr=          1<<3,
724         ISEs_progerr=           1<<4,
725         ISEs_eraseerr=          1<<5,
726         ISEs_ready=             1<<7,
727         ISEs_err= (ISEs_lockerr|ISEs_powererr|ISEs_progerr|ISEs_eraseerr),
728
729         /* extended status register */
730         ISExs_bufavail=         1<<7,
731 };
732
733 /* intel/sharp extended command set */
734 static void
735 ise_reset(Flash* flash)
736 {
737         flash->p[reg(0xaa)] = mirror(0xff);     /* reset */
738 }
739
740 static void
741 ise_id(Flash* flash)
742 {
743         ise_reset(flash);
744         flash->p[reg(0xaaa)] = mirror(0x90);    /* uncover vendor info */
745         flash->manid = fromendian(flash->p[reg(0x0)]);
746         flash->devid = fromendian(flash->p[reg(0x1)]);
747         ise_reset(flash);
748 }
749
750 static void
751 ise_clearerror(Flash* flash)
752 {
753         flash->p[reg(0x200)] = mirror(0x50);
754
755 }
756
757 static void
758 ise_error(int bank, ulong status)
759 {
760         char err[64];
761
762         if(status & (ISEs_lockerr)){
763                 sprint(err, "flash%d: block locked %lux", bank, status);
764                 error(err);
765         }
766         if(status & (ISEs_powererr)){
767                 sprint(err, "flash%d: low prog voltage %lux", bank, status);
768                 error(err);
769         }
770         if(status & (ISEs_progerr|ISEs_eraseerr)){
771                 sprint(err, "flash%d: i/o error %lux", bank, status);
772                 error(err);
773         }
774 }
775 static void
776 ise_erase(Flash *flash, ulong addr)
777 {
778         ulong start;
779         ulong x;
780
781         addr >>= Wshift;
782
783         flashprogpower(1);
784         flash->p[addr] = mirror(0x20);
785         flash->p[addr] = mirror(0xd0);
786         start = m->ticks;
787         do {
788                 x = fromendian(flash->p[addr]);
789                 if((x & mirror(ISEs_ready)) == mirror(ISEs_ready))
790                         break;
791         } while(TK2MS(m->ticks-start) < 1500);
792         flashprogpower(0);
793
794         ise_clearerror(flash);
795         ise_error(0, x);
796         ise_error(1, x>>16);
797
798         ise_reset(flash);
799 }
800 /*
801  *  the flash spec claimes writing goes faster if we use
802  *  the write buffer.  We fill the write buffer and then
803  *  issue the write request.  After the write request,
804  *  subsequent reads will yield the status register.
805  *
806  *  returns the status, even on timeouts.
807  *
808  *  NOTE: I tried starting back to back buffered writes
809  *      without reading the status in between, as the
810  *      flowchart in the intel data sheet suggests.
811  *      However, it always responded with an illegal
812  *      command sequence, so I must be missing something.
813  *      If someone learns better, please email me, though
814  *      I doubt it will be much faster. -  presotto@bell-labs.com
815  */
816 static long
817 ise_wbwrite(Flash *flash, Fword *p, int n, ulong off, ulong baddr, ulong *status)
818 {
819         Fword x;
820         ulong start;
821         int i;
822         int s;
823
824         /* put flash into write buffer mode */
825         start = m->ticks;
826         for(;;) {
827                 s = splhi();
828                 /* request write buffer mode */
829                 flash->p[baddr] = mirror(0xe8);
830
831                 /* look at extended status reg for status */
832                 if((flash->p[baddr] & mirror(1<<7)) == mirror(1<<7))
833                         break;
834                 splx(s);
835
836                 /* didn't work, keep trying for 2 secs */
837                 if(TK2MS(m->ticks-start) > 2000){
838                         /* set up to read status */
839                         flash->p[baddr] = mirror(0x70);
840                         *status = fromendian(flash->p[baddr]);
841                         pprint("write buffered cmd timed out\n");
842                         return -1;
843                 }
844         }
845
846         /* fill write buffer */
847         flash->p[baddr] = mirror(n-1);
848         for(i = 0; i < n; i++)
849                 flash->p[off+i] = *p++;
850
851         /* program from buffer */
852         flash->p[baddr] = mirror(0xd0);
853         splx(s);
854
855         /* wait till the programming is done */
856         start = m->ticks;
857         for(;;) {
858                 x = flash->p[baddr];    /* read status register */
859                 *status = fromendian(x);
860                 if((x & mirror(ISEs_ready)) == mirror(ISEs_ready))
861                         break;
862                 if(TK2MS(m->ticks-start) > 2000){
863                         pprint("read status timed out\n");
864                         return -1;
865                 }
866         }
867         if(x & mirror(ISEs_err))
868                 return -1;
869
870         return n;
871 }
872
873 static void
874 ise_write(Flash *flash, void *a, long n, ulong off)
875 {
876         Fword *p, *end;
877         int i, wbsize;
878         ulong x, baddr;
879
880         /* everything in terms of Fwords */
881         wbsize = flash->wbsize >> Wshift;
882         baddr = blockstart(flash, off) >> Wshift;
883         off >>= Wshift;
884         n >>= Wshift;
885         p = a;
886
887         /* first see if write will succeed */
888         for(i = 0; i < n; i++)
889                 if((p[i] & flash->p[off+i]) != p[i])
890                         error("flash needs erase");
891
892         if(waserror()){
893                 ise_reset(flash);
894                 flashprogpower(0);
895                 nexterror();
896         }
897         flashprogpower(1);
898
899         /*
900          *  use the first write to reach
901          *  a write buffer boundary.  the intel maunal
902          *  says writes starting at wb boundaries
903          *  maximize speed.
904          */
905         i = wbsize - (off & (wbsize-1));
906         for(end = p + n; p < end;){
907                 if(i > end - p)
908                         i = end - p;
909
910                 if(ise_wbwrite(flash, p, i, off, baddr, &x) < 0)
911                         break;
912
913                 off += i;
914                 p += i;
915                 i = wbsize;
916         }
917
918         ise_clearerror(flash);
919         ise_error(0, x);
920         ise_error(1, x>>16);
921
922         ise_reset(flash);
923         flashprogpower(0);
924         poperror();
925 }
926
927 /* amd/fujitsu standard command set
928  *      I don't have an amd chipset to work with
929  *      so I'm loathe to write this yet.  If someone
930  *      else does, please send it to me and I'll
931  *      incorporate it -- presotto@bell-labs.com
932  */
933 static void
934 afs_reset(Flash *flash)
935 {
936         flash->p[reg(0xaa)] = mirror(0xf0);     /* reset */
937 }
938 static void
939 afs_id(Flash *flash)
940 {
941         afs_reset(flash);
942         flash->p[reg(0xaa)] = mirror(0xf0);     /* reset */
943         flash->p[reg(0xaaa)] = mirror(0xaa);    /* query vendor block */
944         flash->p[reg(0x554)] = mirror(0x55);
945         flash->p[reg(0xaaa)] = mirror(0x90);
946         flash->manid = fromendian(flash->p[reg(0x00)]);
947         afs_reset(flash);
948         flash->p[reg(0xaaa)] = mirror(0xaa);    /* query vendor block */
949         flash->p[reg(0x554)] = mirror(0x55);
950         flash->p[reg(0xaaa)] = mirror(0x90);
951         flash->devid = fromendian(flash->p[reg(0x02)]);
952         afs_reset(flash);
953 }
954 static void
955 afs_erase(Flash*, ulong)
956 {
957         error("amd/fujistsu erase not implemented");
958 }
959 static void
960 afs_write(Flash*, void*, long, ulong)
961 {
962         error("amd/fujistsu write not implemented");
963 }