123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669 |
- /*
- * sheevaplug nand flash driver
- *
- * for now separate from (inferno's) os/port/flashnand.c because the flash
- * seems newer, and has different commands, but that is nand-chip specific,
- * not sheevaplug-specific. they should be merged in future.
- *
- * the sheevaplug has a hynix 4gbit flash chip: hy27uf084g2m.
- * 2048 byte pages, with 64 spare bytes each; erase block size is 128k.
- *
- * it has a "glueless" interface, at 0xf9000000. that's the address
- * of the data register. the command and address registers are those
- * or'ed with 1 and 2 respectively.
- *
- * linux uses this layout for the nand flash (from address 0 onwards):
- * 1mb for u-boot
- * 4mb for kernel
- * 507mb for file system
- *
- * this is not so relevant here except for ecc. the first two areas
- * (u-boot and kernel) are expected to have 4-bit ecc per 512 bytes
- * (but calculated from last byte to first), bad erase blocks skipped.
- * the file system area has 1-bit ecc per 256 bytes.
- */
- #include "u.h"
- #include "../port/lib.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "io.h"
- #include "../port/error.h"
- #include "../port/flashif.h"
- #include "../port/nandecc.h"
- #define NANDFREG ((Nandreg*)AddrNandf)
- enum {
- Debug = 0,
- Nopage = ~0ul, /* cache is empty */
- /* vendors */
- Hynix = 0xad,
- Samsung = 0xec,
- /* chips */
- Hy27UF084G2M = 0xdc,
- NandActCEBoot = 1<<1,
- };
- typedef struct Nandreg Nandreg;
- typedef struct Nandtab Nandtab;
- typedef struct Cache Cache;
- struct Nandreg { /* hw registers */
- ulong rdparms;
- ulong wrparms;
- uchar _pad0[0x70 - 0x20];
- ulong ctl;
- };
- struct Nandtab {
- int vid;
- int did;
- vlong size;
- char* name;
- };
- struct Cache {
- Flash *flif;
- ulong pageno;
- ulong pgsize; /* r->pagesize */
- char *page; /* of pgsize bytes */
- };
- enum {
- /* commands */
- Readstatus = 0x70,
- Readid = 0x90, /* needs 1 0-address write */
- Resetf = 0xff,
- /*
- * needs 5 address writes followed by Readstart,
- * Readstartcache or Restartcopy.
- */
- Read = 0x00,
- Readstart = 0x30,
- Readstartcache = 0x31,
- Readstartcopy = 0x35,
- /* after Readstartcache, to stop reading next pages */
- Readstopcache = 0x34,
- /* needs 5 address writes, the data, and -start or -cache */
- Program = 0x80,
- Programstart = 0x10,
- Programcache = 0x15,
- Copyback = 0x85, /* followed by Programstart */
- /* 3 address writes for block followed by Erasestart */
- Erase = 0x60,
- Erasestart = 0xd0,
- Randomread = 0x85,
- Randomwrite = 0x05,
- Randomwritestart= 0xe0,
- /* status bits */
- SFail = 1<<0,
- SCachefail = 1<<1,
- SIdle = 1<<5, /* doesn't seem to come on ever */
- SReady = 1<<6,
- SNotprotected = 1<<7,
- Srdymask = SReady, /* was SIdle|SReady */
- };
- Nandtab nandtab[] = {
- {Hynix, Hy27UF084G2M, 512*MB, "Hy27UF084G2M"},
- {Samsung, 0xdc, 512*MB, "Samsung 2Gb"},
- };
- static Cache cache;
- static void
- nandcmd(Flash *f, uchar b)
- {
- uchar *p = (uchar *)((ulong)f->addr|1);
- *p = b;
- coherence();
- }
- static void
- nandaddr(Flash *f, uchar b)
- {
- uchar *p = (uchar *)((ulong)f->addr|2);
- *p = b;
- coherence();
- }
- static uchar
- nandread(Flash *f)
- {
- return *(uchar *)f->addr;
- }
- static void
- nandreadn(Flash *f, uchar *buf, long n)
- {
- uchar *p = f->addr;
- while(n-- > 0)
- *buf++ = *p;
- }
- static void
- nandwrite(Flash *f, uchar b)
- {
- *(uchar *)f->addr = b;
- coherence();
- }
- static void
- nandwriten(Flash *f, uchar *buf, long n)
- {
- uchar *p = f->addr;
- while(n-- > 0)
- *p = *buf++;
- coherence();
- }
- static void
- nandclaim(Flash*)
- {
- NANDFREG->ctl |= NandActCEBoot;
- coherence();
- }
- static void
- nandunclaim(Flash*)
- {
- NANDFREG->ctl &= ~NandActCEBoot;
- coherence();
- }
- void mmuidmap(uintptr phys, int mbs);
- Nandtab *
- findflash(Flash *f, uintptr pa, uchar *id4p)
- {
- int i;
- ulong sts;
- uchar maker, device, id3, id4;
- Nandtab *chip;
- mmuidmap(pa, 16);
- f->addr = (void *)pa;
- /* make sure controller is idle */
- nandclaim(f);
- nandcmd(f, Resetf);
- nandunclaim(f);
- nandclaim(f);
- nandcmd(f, Readstatus);
- sts = nandread(f);
- nandunclaim(f);
- for (i = 10; i > 0 && !(sts & SReady); i--) {
- delay(50);
- nandclaim(f);
- nandcmd(f, Readstatus);
- sts = nandread(f);
- nandunclaim(f);
- }
- if(!(sts & SReady))
- return nil;
- nandclaim(f);
- nandcmd(f, Readid);
- nandaddr(f, 0);
- maker = nandread(f);
- device = nandread(f);
- id3 = nandread(f);
- USED(id3);
- id4 = nandread(f);
- nandunclaim(f);
- if (id4p)
- *id4p = id4;
- for(i = 0; i < nelem(nandtab); i++) {
- chip = &nandtab[i];
- if(chip->vid == maker && chip->did == device)
- return chip;
- }
- return nil;
- }
- int
- flashat(Flash *f, uintptr pa)
- {
- return findflash(f, pa, nil) != nil;
- }
- static int
- idchip(Flash *f)
- {
- uchar id4;
- Flashregion *r;
- Nandtab *chip;
- static int blocksizes[4] = { 64*1024, 128*1024, 256*1024, 0 };
- static int pagesizes[4] = { 1024, 2*1024, 0, 0 };
- static int spares[2] = { 8, 16 }; /* per 512 bytes */
- f->id = 0;
- f->devid = 0;
- f->width = 1;
- chip = findflash(f, (uintptr)f->addr, &id4);
- if (chip == nil)
- return -1;
- f->id = chip->vid;
- f->devid = chip->did;
- f->size = chip->size;
- f->width = 1;
- f->nr = 1;
- r = &f->regions[0];
- r->pagesize = pagesizes[id4 & MASK(2)];
- r->erasesize = blocksizes[(id4 >> 4) & MASK(2)];
- if (r->pagesize == 0 || r->erasesize == 0) {
- iprint("flashkw: bogus flash sizes\n");
- return -1;
- }
- r->n = f->size / r->erasesize;
- r->start = 0;
- r->end = f->size;
- assert(ispow2(r->pagesize));
- r->pageshift = log2(r->pagesize);
- assert(ispow2(r->erasesize));
- r->eraseshift = log2(r->erasesize);
- assert(r->eraseshift >= r->pageshift);
- if (cache.page == nil) {
- cache.pgsize = r->pagesize;
- cache.page = smalloc(r->pagesize);
- }
- r->spares = r->pagesize / 512 * spares[(id4 >> 2) & 1];
- print("#F0: kwnand: %s %,lud bytes pagesize %lud erasesize %,lud"
- " spares per page %lud\n", chip->name, f->size, r->pagesize,
- r->erasesize, r->spares);
- return 0;
- }
- static int
- ctlrwait(Flash *f)
- {
- int sts, cnt;
- nandclaim(f);
- for (;;) {
- nandcmd(f, Readstatus);
- for(cnt = 100; cnt > 0 && (nandread(f) & Srdymask) != Srdymask;
- cnt--)
- microdelay(50);
- nandcmd(f, Readstatus);
- sts = nandread(f);
- if((sts & Srdymask) == Srdymask)
- break;
- print("flashkw: flash ctlr busy, sts %#ux: resetting\n", sts);
- nandcmd(f, Resetf);
- }
- nandunclaim(f);
- return 0;
- }
- static int
- erasezone(Flash *f, Flashregion *r, ulong offset)
- {
- int i;
- ulong page, block;
- uchar s;
- if (Debug) {
- print("flashkw: erasezone: offset %#lux, region nblocks %d,"
- " start %#lux, end %#lux\n", offset, r->n, r->start,
- r->end);
- print(" erasesize %lud, pagesize %lud\n",
- r->erasesize, r->pagesize);
- }
- assert(r->erasesize != 0);
- if(offset & (r->erasesize - 1)) {
- print("flashkw: erase offset %lud not block aligned\n", offset);
- return -1;
- }
- page = offset >> r->pageshift;
- block = page >> (r->eraseshift - r->pageshift);
- if (Debug)
- print("flashkw: erase: block %#lux\n", block);
- /* make sure controller is idle */
- if(ctlrwait(f) < 0) {
- print("flashkw: erase: flash busy\n");
- return -1;
- }
- /* start erasing */
- nandclaim(f);
- nandcmd(f, Erase);
- nandaddr(f, page>>0);
- nandaddr(f, page>>8);
- nandaddr(f, page>>16);
- nandcmd(f, Erasestart);
- /* invalidate cache on any erasure (slight overkill) */
- cache.pageno = Nopage;
- /* have to wait until flash is done. typically ~2ms */
- delay(1);
- nandcmd(f, Readstatus);
- for(i = 0; i < 100; i++) {
- s = nandread(f);
- if(s & SReady) {
- nandunclaim(f);
- if(s & SFail) {
- print("flashkw: erase: failed, block %#lux\n",
- block);
- return -1;
- }
- return 0;
- }
- microdelay(50);
- }
- print("flashkw: erase timeout, block %#lux\n", block);
- nandunclaim(f);
- return -1;
- }
- static void
- flcachepage(Flash *f, ulong page, uchar *buf)
- {
- Flashregion *r = &f->regions[0];
- assert(cache.pgsize == r->pagesize);
- cache.flif = f;
- cache.pageno = page;
- /* permit i/o directly to or from the cache */
- if (buf != (uchar *)cache.page)
- memmove(cache.page, buf, cache.pgsize);
- }
- static int
- write1page(Flash *f, ulong offset, void *buf)
- {
- int i;
- ulong page, v;
- uchar s;
- uchar *eccp, *p;
- Flashregion *r = &f->regions[0];
- static uchar *oob;
- if (oob == nil)
- oob = smalloc(r->spares);
- page = offset >> r->pageshift;
- if (Debug)
- print("flashkw: write nand offset %#lux page %#lux\n",
- offset, page);
- if(offset & (r->pagesize - 1)) {
- print("flashkw: write offset %lud not page aligned\n", offset);
- return -1;
- }
- p = buf;
- memset(oob, 0xff, r->spares);
- assert(r->spares >= 24);
- eccp = oob + r->spares - 24;
- for(i = 0; i < r->pagesize / 256; i++) {
- v = nandecc(p);
- *eccp++ = v>>8;
- *eccp++ = v>>0;
- *eccp++ = v>>16;
- p += 256;
- }
- if(ctlrwait(f) < 0) {
- print("flashkw: write: nand not ready & idle\n");
- return -1;
- }
- /* write, only whole pages for now, no sub-pages */
- nandclaim(f);
- nandcmd(f, Program);
- nandaddr(f, 0);
- nandaddr(f, 0);
- nandaddr(f, page>>0);
- nandaddr(f, page>>8);
- nandaddr(f, page>>16);
- nandwriten(f, buf, r->pagesize);
- nandwriten(f, oob, r->spares);
- nandcmd(f, Programstart);
- microdelay(100);
- nandcmd(f, Readstatus);
- for(i = 0; i < 100; i++) {
- s = nandread(f);
- if(s & SReady) {
- nandunclaim(f);
- if(s & SFail) {
- print("flashkw: write failed, page %#lux\n",
- page);
- return -1;
- }
- return 0;
- }
- microdelay(10);
- }
- nandunclaim(f);
- flcachepage(f, page, buf);
- print("flashkw: write timeout for page %#lux\n", page);
- return -1;
- }
- static int
- read1page(Flash *f, ulong offset, void *buf)
- {
- int i;
- ulong addr, page, w;
- uchar *eccp, *p;
- Flashregion *r = &f->regions[0];
- static uchar *oob;
- if (oob == nil)
- oob = smalloc(r->spares);
- assert(r->pagesize != 0);
- addr = offset & (r->pagesize - 1);
- page = offset >> r->pageshift;
- if(addr != 0) {
- print("flashkw: read1page: read addr %#lux:"
- " must read aligned page\n", addr);
- return -1;
- }
- /* satisfy request from cache if possible */
- if (f == cache.flif && page == cache.pageno &&
- r->pagesize == cache.pgsize) {
- memmove(buf, cache.page, r->pagesize);
- return 0;
- }
- if (Debug)
- print("flashkw: read offset %#lux addr %#lux page %#lux\n",
- offset, addr, page);
- nandclaim(f);
- nandcmd(f, Read);
- nandaddr(f, addr>>0);
- nandaddr(f, addr>>8);
- nandaddr(f, page>>0);
- nandaddr(f, page>>8);
- nandaddr(f, page>>16);
- nandcmd(f, Readstart);
- microdelay(50);
- nandreadn(f, buf, r->pagesize);
- nandreadn(f, oob, r->spares);
- nandunclaim(f);
- /* verify/correct data. last 8*3 bytes is ecc, per 256 bytes. */
- p = buf;
- assert(r->spares >= 24);
- eccp = oob + r->spares - 24;
- for(i = 0; i < r->pagesize / 256; i++) {
- w = eccp[0] << 8 | eccp[1] << 0 | eccp[2] << 16;
- eccp += 3;
- switch(nandecccorrect(p, nandecc(p), &w, 1)) {
- case NandEccErrorBad:
- print("(page %d)\n", i);
- return -1;
- case NandEccErrorOneBit:
- case NandEccErrorOneBitInEcc:
- print("(page %d)\n", i);
- /* fall through */
- case NandEccErrorGood:
- break;
- }
- p += 256;
- }
- flcachepage(f, page, buf);
- return 0;
- }
- /*
- * read a page at offset into cache, copy fragment from buf into it
- * at pagoff, and rewrite that page.
- */
- static int
- rewrite(Flash *f, ulong offset, ulong pagoff, void *buf, ulong size)
- {
- if (read1page(f, offset, cache.page) < 0)
- return -1;
- memmove(&cache.page[pagoff], buf, size);
- return write1page(f, offset, cache.page);
- }
- /* there are no alignment constraints on offset, buf, nor n */
- static int
- write(Flash *f, ulong offset, void *buf, long n)
- {
- uint un, frag, pagoff;
- ulong pgsize;
- uchar *p;
- Flashregion *r = &f->regions[0];
- if(n <= 0)
- panic("flashkw: write: non-positive count %ld", n);
- un = n;
- assert(r->pagesize != 0);
- pgsize = r->pagesize;
- /* if a partial first page exists, update the first page with it. */
- p = buf;
- pagoff = offset % pgsize;
- if (pagoff != 0) {
- frag = pgsize - pagoff;
- if (frag > un) /* might not extend to end of page */
- frag = un;
- if (rewrite(f, offset - pagoff, pagoff, p, frag) < 0)
- return -1;
- offset += frag;
- p += frag;
- un -= frag;
- }
- /* copy whole pages */
- while (un >= pgsize) {
- if (write1page(f, offset, p) < 0)
- return -1;
- offset += pgsize;
- p += pgsize;
- un -= pgsize;
- }
- /* if a partial last page exists, update the last page with it. */
- if (un > 0)
- return rewrite(f, offset, 0, p, un);
- return 0;
- }
- /* there are no alignment constraints on offset, buf, nor n */
- static int
- read(Flash *f, ulong offset, void *buf, long n)
- {
- uint un, frag, pagoff;
- ulong pgsize;
- uchar *p;
- Flashregion *r = &f->regions[0];
- if(n <= 0)
- panic("flashkw: read: non-positive count %ld", n);
- un = n;
- assert(r->pagesize != 0);
- pgsize = r->pagesize;
- /* if partial 1st page, read it into cache & copy fragment to buf */
- p = buf;
- pagoff = offset % pgsize;
- if (pagoff != 0) {
- frag = pgsize - pagoff;
- if (frag > un) /* might not extend to end of page */
- frag = un;
- if (read1page(f, offset - pagoff, cache.page) < 0)
- return -1;
- offset += frag;
- memmove(p, &cache.page[pagoff], frag);
- p += frag;
- un -= frag;
- }
- /* copy whole pages */
- while (un >= pgsize) {
- if (read1page(f, offset, p) < 0)
- return -1;
- offset += pgsize;
- p += pgsize;
- un -= pgsize;
- }
- /* if partial last page, read into cache & copy initial fragment to buf */
- if (un > 0) {
- if (read1page(f, offset, cache.page) < 0)
- return -1;
- memmove(p, cache.page, un);
- }
- return 0;
- }
- static int
- reset(Flash *f)
- {
- if(f->data != nil)
- return 1;
- f->write = write;
- f->read = read;
- f->eraseall = nil;
- f->erasezone = erasezone;
- f->suspend = nil;
- f->resume = nil;
- f->sort = "nand";
- cache.pageno = Nopage;
- return idchip(f);
- }
- void
- flashkwlink(void)
- {
- addflashcard("nand", reset);
- }
|