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