]> git.lizzy.rs Git - plan9front.git/blob - sys/src/9/kw/flashkw.c
Import sources from 2011-03-30 iso image - lib
[plan9front.git] / sys / src / 9 / kw / flashkw.c
1 /*
2  * sheevaplug nand flash driver
3  *
4  * for now separate from (inferno's) os/port/flashnand.c because the flash
5  * seems newer, and has different commands, but that is nand-chip specific,
6  * not sheevaplug-specific.  they should be merged in future.
7  *
8  * the sheevaplug has a hynix 4gbit flash chip: hy27uf084g2m.
9  * 2048 byte pages, with 64 spare bytes each; erase block size is 128k.
10  *
11  * it has a "glueless" interface, at 0xf9000000.  that's the address
12  * of the data register.  the command and address registers are those
13  * or'ed with 1 and 2 respectively.
14  *
15  * linux uses this layout for the nand flash (from address 0 onwards):
16  *      1mb for u-boot
17  *      4mb for kernel
18  *      507mb for file system
19  *
20  * this is not so relevant here except for ecc.  the first two areas
21  * (u-boot and kernel) are expected to have 4-bit ecc per 512 bytes
22  * (but calculated from last byte to first), bad erase blocks skipped.
23  * the file system area has 1-bit ecc per 256 bytes.
24  */
25
26 #include        "u.h"
27 #include        "../port/lib.h"
28 #include        "mem.h"
29 #include        "dat.h"
30 #include        "fns.h"
31 #include        "io.h"
32 #include        "../port/error.h"
33
34 #include        "../port/flashif.h"
35 #include        "../port/nandecc.h"
36
37 enum {
38         Debug           = 0,
39
40         Nopage          = ~0ul,         /* cache is empty */
41
42         /* vendors */
43         Hynix           = 0xad,
44         Samsung         = 0xec,
45
46         /* chips */
47         Hy27UF084G2M    = 0xdc,
48
49         NandActCEBoot   = 1<<1,
50 };
51
52 typedef struct Nandreg Nandreg;
53 typedef struct Nandtab Nandtab;
54 typedef struct Cache Cache;
55
56 struct Nandreg {                        /* hw registers */
57         ulong   rdparms;
58         ulong   wrparms;
59         uchar   _pad0[0x70 - 0x20];
60         ulong   ctl;
61 };
62
63 struct Nandtab {
64         int     vid;
65         int     did;
66         vlong   size;
67         char*   name;
68 };
69
70 struct Cache {
71         Flash   *flif;
72         ulong   pageno;
73         ulong   pgsize;                 /* r->pagesize */
74         char    *page;                  /* of pgsize bytes */
75 };
76
77 enum {
78         /* commands */
79         Readstatus      = 0x70,
80         Readid          = 0x90, /* needs 1 0-address write */
81         Resetf          = 0xff,
82
83         /*
84          * needs 5 address writes followed by Readstart,
85          * Readstartcache or Restartcopy.
86          */
87         Read            = 0x00,
88         Readstart       = 0x30,
89         Readstartcache  = 0x31,
90         Readstartcopy   = 0x35,
91         /* after Readstartcache, to stop reading next pages */
92         Readstopcache   = 0x34,
93
94         /* needs 5 address writes, the data, and -start or -cache */
95         Program         = 0x80,
96         Programstart    = 0x10,
97         Programcache    = 0x15,
98
99         Copyback        = 0x85, /* followed by Programstart */
100
101         /* 3 address writes for block followed by Erasestart */
102         Erase           = 0x60,
103         Erasestart      = 0xd0,
104
105         Randomread      = 0x85,
106         Randomwrite     = 0x05,
107         Randomwritestart= 0xe0,
108
109         /* status bits */
110         SFail           = 1<<0,
111         SCachefail      = 1<<1,
112         SIdle           = 1<<5,         /* doesn't seem to come on ever */
113         SReady          = 1<<6,
114         SNotprotected   = 1<<7,
115
116         Srdymask        = SReady,       /* was SIdle|SReady */
117 };
118
119 Nandtab nandtab[] = {
120         {Hynix,         Hy27UF084G2M,   512*MB, "Hy27UF084G2M"},
121         {Samsung,       0xdc,           512*MB, "Samsung 2Gb"},
122 };
123
124 static Cache cache;
125
126 static void
127 nandcmd(Flash *f, uchar b)
128 {
129         uchar *p = (uchar *)((ulong)f->addr|1);
130
131         *p = b;
132         coherence();
133 }
134
135 static void
136 nandaddr(Flash *f, uchar b)
137 {
138         uchar *p = (uchar *)((ulong)f->addr|2);
139
140         *p = b;
141         coherence();
142 }
143
144 static uchar
145 nandread(Flash *f)
146 {
147         return *(uchar *)f->addr;
148 }
149
150 static void
151 nandreadn(Flash *f, uchar *buf, long n)
152 {
153         uchar *p = f->addr;
154
155         while(n-- > 0)
156                 *buf++ = *p;
157 }
158
159 static void
160 nandwrite(Flash *f, uchar b)
161 {
162         *(uchar *)f->addr = b;
163         coherence();
164 }
165
166 static void
167 nandwriten(Flash *f, uchar *buf, long n)
168 {
169         uchar *p = f->addr;
170
171         while(n-- > 0)
172                 *p = *buf++;
173         coherence();
174 }
175
176 static void
177 nandclaim(Flash*)
178 {
179         Nandreg *nand = (Nandreg*)soc.nand;
180
181         nand->ctl |= NandActCEBoot;
182         coherence();
183 }
184
185 static void
186 nandunclaim(Flash*)
187 {
188         Nandreg *nand = (Nandreg*)soc.nand;
189
190         nand->ctl &= ~NandActCEBoot;
191         coherence();
192 }
193
194
195 void    mmuidmap(uintptr phys, int mbs);
196
197 Nandtab *
198 findflash(Flash *f, uintptr pa, uchar *id4p)
199 {
200         int i;
201         ulong sts;
202         uchar maker, device, id3, id4;
203         Nandtab *chip;
204
205         mmuidmap(pa, 16);
206         f->addr = (void *)pa;
207
208         /* make sure controller is idle */
209         nandclaim(f);
210         nandcmd(f, Resetf);
211         nandunclaim(f);
212
213         nandclaim(f);
214         nandcmd(f, Readstatus);
215         sts = nandread(f);
216         nandunclaim(f);
217         for (i = 10; i > 0 && !(sts & SReady); i--) {
218                 delay(50);
219                 nandclaim(f);
220                 nandcmd(f, Readstatus);
221                 sts = nandread(f);
222                 nandunclaim(f);
223         }
224         if(!(sts & SReady))
225                 return nil;
226
227         nandclaim(f);
228         nandcmd(f, Readid);
229         nandaddr(f, 0);
230         maker = nandread(f);
231         device = nandread(f);
232         id3 = nandread(f);
233         USED(id3);
234         id4 = nandread(f);
235         nandunclaim(f);
236         if (id4p)
237                 *id4p = id4;
238
239         for(i = 0; i < nelem(nandtab); i++) {
240                 chip = &nandtab[i];
241                 if(chip->vid == maker && chip->did == device)
242                         return chip;
243         }
244         return nil;
245 }
246
247 int
248 flashat(Flash *f, uintptr pa)
249 {
250         return findflash(f, pa, nil) != nil;
251 }
252
253 static int
254 idchip(Flash *f)
255 {
256         uchar id4;
257         Flashregion *r;
258         Nandtab *chip;
259         static int blocksizes[4] = { 64*1024, 128*1024, 256*1024, 0 };
260         static int pagesizes[4] = { 1024, 2*1024, 0, 0 };
261         static int spares[2] = { 8, 16 };               /* per 512 bytes */
262
263         f->id = 0;
264         f->devid = 0;
265         f->width = 1;
266         chip = findflash(f, (uintptr)f->addr, &id4);
267         if (chip == nil)
268                 return -1;
269         f->id = chip->vid;
270         f->devid = chip->did;
271         f->size = chip->size;
272         f->width = 1;
273         f->nr = 1;
274
275         r = &f->regions[0];
276         r->pagesize = pagesizes[id4 & MASK(2)];
277         r->erasesize = blocksizes[(id4 >> 4) & MASK(2)];
278         if (r->pagesize == 0 || r->erasesize == 0) {
279                 iprint("flashkw: bogus flash sizes\n");
280                 return -1;
281         }
282         r->n = f->size / r->erasesize;
283         r->start = 0;
284         r->end = f->size;
285         assert(ispow2(r->pagesize));
286         r->pageshift = log2(r->pagesize);
287         assert(ispow2(r->erasesize));
288         r->eraseshift = log2(r->erasesize);
289         assert(r->eraseshift >= r->pageshift);
290         if (cache.page == nil) {
291                 cache.pgsize = r->pagesize;
292                 cache.page = smalloc(r->pagesize);
293         }
294
295         r->spares = r->pagesize / 512 * spares[(id4 >> 2) & 1];
296         print("#F0: kwnand: %s %,lud bytes pagesize %lud erasesize %,lud"
297                 " spares per page %lud\n", chip->name, f->size, r->pagesize,
298                 r->erasesize, r->spares);
299         return 0;
300 }
301
302 static int
303 ctlrwait(Flash *f)
304 {
305         int sts, cnt;
306
307         nandclaim(f);
308         for (;;) {
309                 nandcmd(f, Readstatus);
310                 for(cnt = 100; cnt > 0 && (nandread(f) & Srdymask) != Srdymask;
311                     cnt--)
312                         microdelay(50);
313                 nandcmd(f, Readstatus);
314                 sts = nandread(f);
315                 if((sts & Srdymask) == Srdymask)
316                         break;
317                 print("flashkw: flash ctlr busy, sts %#ux: resetting\n", sts);
318                 nandcmd(f, Resetf);
319         }
320         nandunclaim(f);
321         return 0;
322 }
323
324 static int
325 erasezone(Flash *f, Flashregion *r, ulong offset)
326 {
327         int i;
328         ulong page, block;
329         uchar s;
330
331         if (Debug) {
332                 print("flashkw: erasezone: offset %#lux, region nblocks %d,"
333                         " start %#lux, end %#lux\n", offset, r->n, r->start,
334                         r->end);
335                 print(" erasesize %lud, pagesize %lud\n",
336                         r->erasesize, r->pagesize);
337         }
338         assert(r->erasesize != 0);
339         if(offset & (r->erasesize - 1)) {
340                 print("flashkw: erase offset %lud not block aligned\n", offset);
341                 return -1;
342         }
343         page = offset >> r->pageshift;
344         block = page >> (r->eraseshift - r->pageshift);
345         if (Debug)
346                 print("flashkw: erase: block %#lux\n", block);
347
348         /* make sure controller is idle */
349         if(ctlrwait(f) < 0) {
350                 print("flashkw: erase: flash busy\n");
351                 return -1;
352         }
353
354         /* start erasing */
355         nandclaim(f);
356         nandcmd(f, Erase);
357         nandaddr(f, page>>0);
358         nandaddr(f, page>>8);
359         nandaddr(f, page>>16);
360         nandcmd(f, Erasestart);
361
362         /* invalidate cache on any erasure (slight overkill) */
363         cache.pageno = Nopage;
364
365         /* have to wait until flash is done.  typically ~2ms */
366         delay(1);
367         nandcmd(f, Readstatus);
368         for(i = 0; i < 100; i++) {
369                 s = nandread(f);
370                 if(s & SReady) {
371                         nandunclaim(f);
372                         if(s & SFail) {
373                                 print("flashkw: erase: failed, block %#lux\n",
374                                         block);
375                                 return -1;
376                         }
377                         return 0;
378                 }
379                 microdelay(50);
380         }
381         print("flashkw: erase timeout, block %#lux\n", block);
382         nandunclaim(f);
383         return -1;
384 }
385
386 static void
387 flcachepage(Flash *f, ulong page, uchar *buf)
388 {
389         Flashregion *r = &f->regions[0];
390
391         assert(cache.pgsize == r->pagesize);
392         cache.flif = f;
393         cache.pageno = page;
394         /* permit i/o directly to or from the cache */
395         if (buf != (uchar *)cache.page)
396                 memmove(cache.page, buf, cache.pgsize);
397 }
398
399 static int
400 write1page(Flash *f, ulong offset, void *buf)
401 {
402         int i;
403         ulong page, v;
404         uchar s;
405         uchar *eccp, *p;
406         Flashregion *r = &f->regions[0];
407         static uchar *oob;
408
409         if (oob == nil)
410                 oob = smalloc(r->spares);
411
412         page = offset >> r->pageshift;
413         if (Debug)
414                 print("flashkw: write nand offset %#lux page %#lux\n",
415                         offset, page);
416
417         if(offset & (r->pagesize - 1)) {
418                 print("flashkw: write offset %lud not page aligned\n", offset);
419                 return -1;
420         }
421
422         p = buf;
423         memset(oob, 0xff, r->spares);
424         assert(r->spares >= 24);
425         eccp = oob + r->spares - 24;
426         for(i = 0; i < r->pagesize / 256; i++) {
427                 v = nandecc(p);
428                 *eccp++ = v>>8;
429                 *eccp++ = v>>0;
430                 *eccp++ = v>>16;
431                 p += 256;
432         }
433
434         if(ctlrwait(f) < 0) {
435                 print("flashkw: write: nand not ready & idle\n");
436                 return -1;
437         }
438
439         /* write, only whole pages for now, no sub-pages */
440         nandclaim(f);
441         nandcmd(f, Program);
442         nandaddr(f, 0);
443         nandaddr(f, 0);
444         nandaddr(f, page>>0);
445         nandaddr(f, page>>8);
446         nandaddr(f, page>>16);
447         nandwriten(f, buf, r->pagesize);
448         nandwriten(f, oob, r->spares);
449         nandcmd(f, Programstart);
450
451         microdelay(100);
452         nandcmd(f, Readstatus);
453         for(i = 0; i < 100; i++) {
454                 s = nandread(f);
455                 if(s & SReady) {
456                         nandunclaim(f);
457                         if(s & SFail) {
458                                 print("flashkw: write failed, page %#lux\n",
459                                         page);
460                                 return -1;
461                         }
462                         return 0;
463                 }
464                 microdelay(10);
465         }
466
467         nandunclaim(f);
468         flcachepage(f, page, buf);
469         print("flashkw: write timeout for page %#lux\n", page);
470         return -1;
471 }
472
473 static int
474 read1page(Flash *f, ulong offset, void *buf)
475 {
476         int i;
477         ulong addr, page, w;
478         uchar *eccp, *p;
479         Flashregion *r = &f->regions[0];
480         static uchar *oob;
481
482         if (oob == nil)
483                 oob = smalloc(r->spares);
484
485         assert(r->pagesize != 0);
486         addr = offset & (r->pagesize - 1);
487         page = offset >> r->pageshift;
488         if(addr != 0) {
489                 print("flashkw: read1page: read addr %#lux:"
490                         " must read aligned page\n", addr);
491                 return -1;
492         }
493
494         /* satisfy request from cache if possible */
495         if (f == cache.flif && page == cache.pageno &&
496             r->pagesize == cache.pgsize) {
497                 memmove(buf, cache.page, r->pagesize);
498                 return 0;
499         }
500
501         if (Debug)
502                 print("flashkw: read offset %#lux addr %#lux page %#lux\n",
503                         offset, addr, page);
504
505         nandclaim(f);
506         nandcmd(f, Read);
507         nandaddr(f, addr>>0);
508         nandaddr(f, addr>>8);
509         nandaddr(f, page>>0);
510         nandaddr(f, page>>8);
511         nandaddr(f, page>>16);
512         nandcmd(f, Readstart);
513
514         microdelay(50);
515
516         nandreadn(f, buf, r->pagesize);
517         nandreadn(f, oob, r->spares);
518
519         nandunclaim(f);
520
521         /* verify/correct data. last 8*3 bytes is ecc, per 256 bytes. */
522         p = buf;
523         assert(r->spares >= 24);
524         eccp = oob + r->spares - 24;
525         for(i = 0; i < r->pagesize / 256; i++) {
526                 w = eccp[0] << 8 | eccp[1] << 0 | eccp[2] << 16;
527                 eccp += 3;
528                 switch(nandecccorrect(p, nandecc(p), &w, 1)) {
529                 case NandEccErrorBad:
530                         print("(page %d)\n", i);
531                         return -1;
532                 case NandEccErrorOneBit:
533                 case NandEccErrorOneBitInEcc:
534                         print("(page %d)\n", i);
535                         /* fall through */
536                 case NandEccErrorGood:
537                         break;
538                 }
539                 p += 256;
540         }
541
542         flcachepage(f, page, buf);
543         return 0;
544 }
545
546 /*
547  * read a page at offset into cache, copy fragment from buf into it
548  * at pagoff, and rewrite that page.
549  */
550 static int
551 rewrite(Flash *f, ulong offset, ulong pagoff, void *buf, ulong size)
552 {
553         if (read1page(f, offset, cache.page) < 0)
554                 return -1;
555         memmove(&cache.page[pagoff], buf, size);
556         return write1page(f, offset, cache.page);
557 }
558
559 /* there are no alignment constraints on offset, buf, nor n */
560 static int
561 write(Flash *f, ulong offset, void *buf, long n)
562 {
563         uint un, frag, pagoff;
564         ulong pgsize;
565         uchar *p;
566         Flashregion *r = &f->regions[0];
567
568         if(n <= 0)
569                 panic("flashkw: write: non-positive count %ld", n);
570         un = n;
571         assert(r->pagesize != 0);
572         pgsize = r->pagesize;
573
574         /* if a partial first page exists, update the first page with it. */
575         p = buf;
576         pagoff = offset % pgsize;
577         if (pagoff != 0) {
578                 frag = pgsize - pagoff;
579                 if (frag > un)          /* might not extend to end of page */
580                         frag = un;
581                 if (rewrite(f, offset - pagoff, pagoff, p, frag) < 0)
582                         return -1;
583                 offset += frag;
584                 p  += frag;
585                 un -= frag;
586         }
587
588         /* copy whole pages */
589         while (un >= pgsize) {
590                 if (write1page(f, offset, p) < 0)
591                         return -1;
592                 offset += pgsize;
593                 p  += pgsize;
594                 un -= pgsize;
595         }
596
597         /* if a partial last page exists, update the last page with it. */
598         if (un > 0)
599                 return rewrite(f, offset, 0, p, un);
600         return 0;
601 }
602
603 /* there are no alignment constraints on offset, buf, nor n */
604 static int
605 read(Flash *f, ulong offset, void *buf, long n)
606 {
607         uint un, frag, pagoff;
608         ulong pgsize;
609         uchar *p;
610         Flashregion *r = &f->regions[0];
611
612         if(n <= 0)
613                 panic("flashkw: read: non-positive count %ld", n);
614         un = n;
615         assert(r->pagesize != 0);
616         pgsize = r->pagesize;
617
618         /* if partial 1st page, read it into cache & copy fragment to buf */
619         p = buf;
620         pagoff = offset % pgsize;
621         if (pagoff != 0) {
622                 frag = pgsize - pagoff;
623                 if (frag > un)          /* might not extend to end of page */
624                         frag = un;
625                 if (read1page(f, offset - pagoff, cache.page) < 0)
626                         return -1;
627                 offset += frag;
628                 memmove(p, &cache.page[pagoff], frag);
629                 p  += frag;
630                 un -= frag;
631         }
632
633         /* copy whole pages */
634         while (un >= pgsize) {
635                 if (read1page(f, offset, p) < 0)
636                         return -1;
637                 offset += pgsize;
638                 p  += pgsize;
639                 un -= pgsize;
640         }
641
642         /* if partial last page, read into cache & copy initial fragment to buf */
643         if (un > 0) {
644                 if (read1page(f, offset, cache.page) < 0)
645                         return -1;
646                 memmove(p, cache.page, un);
647         }
648         return 0;
649 }
650
651 static int
652 reset(Flash *f)
653 {
654         if(f->data != nil)
655                 return 1;
656         f->write = write;
657         f->read = read;
658         f->eraseall = nil;
659         f->erasezone = erasezone;
660         f->suspend = nil;
661         f->resume = nil;
662         f->sort = "nand";
663         cache.pageno = Nopage;
664         return idchip(f);
665 }
666
667 void
668 flashkwlink(void)
669 {
670         addflashcard("nand", reset);
671 }