xref: /plan9-contrib/sys/src/9/kw/flashkw.c (revision b649930dd34d6bab2bb3fa27632347c57745aead)
114cf0019SDavid du Colombier /*
214cf0019SDavid du Colombier  * sheevaplug nand flash driver
314cf0019SDavid du Colombier  *
414cf0019SDavid du Colombier  * for now separate from (inferno's) os/port/flashnand.c because the flash
514cf0019SDavid du Colombier  * seems newer, and has different commands, but that is nand-chip specific,
614cf0019SDavid du Colombier  * not sheevaplug-specific.  they should be merged in future.
714cf0019SDavid du Colombier  *
814cf0019SDavid du Colombier  * the sheevaplug has a hynix 4gbit flash chip: hy27uf084g2m.
914cf0019SDavid du Colombier  * 2048 byte pages, with 64 spare bytes each; erase block size is 128k.
1014cf0019SDavid du Colombier  *
1114cf0019SDavid du Colombier  * it has a "glueless" interface, at 0xf9000000.  that's the address
1214cf0019SDavid du Colombier  * of the data register.  the command and address registers are those
1314cf0019SDavid du Colombier  * or'ed with 1 and 2 respectively.
1414cf0019SDavid du Colombier  *
1514cf0019SDavid du Colombier  * linux uses this layout for the nand flash (from address 0 onwards):
1614cf0019SDavid du Colombier  *	1mb for u-boot
1714cf0019SDavid du Colombier  *	4mb for kernel
1814cf0019SDavid du Colombier  *	507mb for file system
1914cf0019SDavid du Colombier  *
2014cf0019SDavid du Colombier  * this is not so relevant here except for ecc.  the first two areas
2114cf0019SDavid du Colombier  * (u-boot and kernel) are expected to have 4-bit ecc per 512 bytes
2214cf0019SDavid du Colombier  * (but calculated from last byte to first), bad erase blocks skipped.
2314cf0019SDavid du Colombier  * the file system area has 1-bit ecc per 256 bytes.
2414cf0019SDavid du Colombier  */
2514cf0019SDavid du Colombier 
2614cf0019SDavid du Colombier #include	"u.h"
2714cf0019SDavid du Colombier #include	"../port/lib.h"
2814cf0019SDavid du Colombier #include	"mem.h"
2914cf0019SDavid du Colombier #include	"dat.h"
3014cf0019SDavid du Colombier #include	"fns.h"
3114cf0019SDavid du Colombier #include	"io.h"
3214cf0019SDavid du Colombier #include	"../port/error.h"
3314cf0019SDavid du Colombier 
3406f6463aSDavid du Colombier #include	"../port/flashif.h"
3506f6463aSDavid du Colombier #include	"../port/nandecc.h"
3614cf0019SDavid du Colombier 
3714cf0019SDavid du Colombier enum {
3814cf0019SDavid du Colombier 	Debug		= 0,
3914cf0019SDavid du Colombier 
402d1bf19dSDavid du Colombier 	Nopage		= ~0ul,		/* cache is empty */
412d1bf19dSDavid du Colombier 
4214cf0019SDavid du Colombier 	/* vendors */
4314cf0019SDavid du Colombier 	Hynix		= 0xad,
4414cf0019SDavid du Colombier 	Samsung		= 0xec,
4514cf0019SDavid du Colombier 
4614cf0019SDavid du Colombier 	/* chips */
4714cf0019SDavid du Colombier 	Hy27UF084G2M	= 0xdc,
4814cf0019SDavid du Colombier 
4914cf0019SDavid du Colombier 	NandActCEBoot	= 1<<1,
5014cf0019SDavid du Colombier };
5114cf0019SDavid du Colombier 
5214cf0019SDavid du Colombier typedef struct Nandreg Nandreg;
5314cf0019SDavid du Colombier typedef struct Nandtab Nandtab;
5414cf0019SDavid du Colombier typedef struct Cache Cache;
5514cf0019SDavid du Colombier 
5614cf0019SDavid du Colombier struct Nandreg {			/* hw registers */
5714cf0019SDavid du Colombier 	ulong	rdparms;
5814cf0019SDavid du Colombier 	ulong	wrparms;
5914cf0019SDavid du Colombier 	uchar	_pad0[0x70 - 0x20];
6014cf0019SDavid du Colombier 	ulong	ctl;
6114cf0019SDavid du Colombier };
6214cf0019SDavid du Colombier 
6314cf0019SDavid du Colombier struct Nandtab {
6414cf0019SDavid du Colombier 	int	vid;
6514cf0019SDavid du Colombier 	int	did;
6614cf0019SDavid du Colombier 	vlong	size;
6714cf0019SDavid du Colombier 	char*	name;
6814cf0019SDavid du Colombier };
6914cf0019SDavid du Colombier 
7014cf0019SDavid du Colombier struct Cache {
7114cf0019SDavid du Colombier 	Flash	*flif;
7214cf0019SDavid du Colombier 	ulong	pageno;
7314cf0019SDavid du Colombier 	ulong	pgsize;			/* r->pagesize */
7414cf0019SDavid du Colombier 	char	*page;			/* of pgsize bytes */
7514cf0019SDavid du Colombier };
7614cf0019SDavid du Colombier 
7714cf0019SDavid du Colombier enum {
7814cf0019SDavid du Colombier 	/* commands */
7914cf0019SDavid du Colombier 	Readstatus	= 0x70,
8014cf0019SDavid du Colombier 	Readid		= 0x90,	/* needs 1 0-address write */
8114cf0019SDavid du Colombier 	Resetf		= 0xff,
8214cf0019SDavid du Colombier 
8314cf0019SDavid du Colombier 	/*
8414cf0019SDavid du Colombier 	 * needs 5 address writes followed by Readstart,
8514cf0019SDavid du Colombier 	 * Readstartcache or Restartcopy.
8614cf0019SDavid du Colombier 	 */
8714cf0019SDavid du Colombier 	Read		= 0x00,
8814cf0019SDavid du Colombier 	Readstart	= 0x30,
8914cf0019SDavid du Colombier 	Readstartcache	= 0x31,
9014cf0019SDavid du Colombier 	Readstartcopy	= 0x35,
9114cf0019SDavid du Colombier 	/* after Readstartcache, to stop reading next pages */
9214cf0019SDavid du Colombier 	Readstopcache	= 0x34,
9314cf0019SDavid du Colombier 
9414cf0019SDavid du Colombier 	/* needs 5 address writes, the data, and -start or -cache */
9514cf0019SDavid du Colombier 	Program		= 0x80,
9614cf0019SDavid du Colombier 	Programstart	= 0x10,
9714cf0019SDavid du Colombier 	Programcache	= 0x15,
9814cf0019SDavid du Colombier 
9914cf0019SDavid du Colombier 	Copyback	= 0x85,	/* followed by Programstart */
10014cf0019SDavid du Colombier 
10114cf0019SDavid du Colombier 	/* 3 address writes for block followed by Erasestart */
10214cf0019SDavid du Colombier 	Erase		= 0x60,
10314cf0019SDavid du Colombier 	Erasestart	= 0xd0,
10414cf0019SDavid du Colombier 
10514cf0019SDavid du Colombier 	Randomread	= 0x85,
10614cf0019SDavid du Colombier 	Randomwrite	= 0x05,
10714cf0019SDavid du Colombier 	Randomwritestart= 0xe0,
10814cf0019SDavid du Colombier 
10914cf0019SDavid du Colombier 	/* status bits */
11014cf0019SDavid du Colombier 	SFail		= 1<<0,
11114cf0019SDavid du Colombier 	SCachefail	= 1<<1,
1124b9994c9SDavid du Colombier 	SIdle		= 1<<5,		/* doesn't seem to come on ever */
11314cf0019SDavid du Colombier 	SReady		= 1<<6,
11414cf0019SDavid du Colombier 	SNotprotected	= 1<<7,
1154b9994c9SDavid du Colombier 
1164b9994c9SDavid du Colombier 	Srdymask	= SReady,	/* was SIdle|SReady */
11714cf0019SDavid du Colombier };
11814cf0019SDavid du Colombier 
11914cf0019SDavid du Colombier Nandtab nandtab[] = {
12014cf0019SDavid du Colombier 	{Hynix,		Hy27UF084G2M,	512*MB,	"Hy27UF084G2M"},
12114cf0019SDavid du Colombier 	{Samsung,	0xdc,		512*MB,	"Samsung 2Gb"},
12214cf0019SDavid du Colombier };
12314cf0019SDavid du Colombier 
12414cf0019SDavid du Colombier static Cache cache;
12514cf0019SDavid du Colombier 
12614cf0019SDavid du Colombier static void
nandcmd(Flash * f,uchar b)12714cf0019SDavid du Colombier nandcmd(Flash *f, uchar b)
12814cf0019SDavid du Colombier {
12914cf0019SDavid du Colombier 	uchar *p = (uchar *)((ulong)f->addr|1);
13014cf0019SDavid du Colombier 
13114cf0019SDavid du Colombier 	*p = b;
13214cf0019SDavid du Colombier 	coherence();
13314cf0019SDavid du Colombier }
13414cf0019SDavid du Colombier 
13514cf0019SDavid du Colombier static void
nandaddr(Flash * f,uchar b)13614cf0019SDavid du Colombier nandaddr(Flash *f, uchar b)
13714cf0019SDavid du Colombier {
13814cf0019SDavid du Colombier 	uchar *p = (uchar *)((ulong)f->addr|2);
13914cf0019SDavid du Colombier 
14014cf0019SDavid du Colombier 	*p = b;
14114cf0019SDavid du Colombier 	coherence();
14214cf0019SDavid du Colombier }
14314cf0019SDavid du Colombier 
14414cf0019SDavid du Colombier static uchar
nandread(Flash * f)14514cf0019SDavid du Colombier nandread(Flash *f)
14614cf0019SDavid du Colombier {
14714cf0019SDavid du Colombier 	return *(uchar *)f->addr;
14814cf0019SDavid du Colombier }
14914cf0019SDavid du Colombier 
15014cf0019SDavid du Colombier static void
nandreadn(Flash * f,uchar * buf,long n)15114cf0019SDavid du Colombier nandreadn(Flash *f, uchar *buf, long n)
15214cf0019SDavid du Colombier {
15314cf0019SDavid du Colombier 	uchar *p = f->addr;
15414cf0019SDavid du Colombier 
15514cf0019SDavid du Colombier 	while(n-- > 0)
15614cf0019SDavid du Colombier 		*buf++ = *p;
15714cf0019SDavid du Colombier }
15814cf0019SDavid du Colombier 
15914cf0019SDavid du Colombier static void
nandwrite(Flash * f,uchar b)16014cf0019SDavid du Colombier nandwrite(Flash *f, uchar b)
16114cf0019SDavid du Colombier {
16214cf0019SDavid du Colombier 	*(uchar *)f->addr = b;
16314cf0019SDavid du Colombier 	coherence();
16414cf0019SDavid du Colombier }
16514cf0019SDavid du Colombier 
16614cf0019SDavid du Colombier static void
nandwriten(Flash * f,uchar * buf,long n)16714cf0019SDavid du Colombier nandwriten(Flash *f, uchar *buf, long n)
16814cf0019SDavid du Colombier {
16914cf0019SDavid du Colombier 	uchar *p = f->addr;
17014cf0019SDavid du Colombier 
17114cf0019SDavid du Colombier 	while(n-- > 0)
17214cf0019SDavid du Colombier 		*p = *buf++;
17314cf0019SDavid du Colombier 	coherence();
17414cf0019SDavid du Colombier }
17514cf0019SDavid du Colombier 
17614cf0019SDavid du Colombier static void
nandclaim(Flash *)17714cf0019SDavid du Colombier nandclaim(Flash*)
17814cf0019SDavid du Colombier {
1797365b686SDavid du Colombier 	Nandreg *nand = (Nandreg*)soc.nand;
1807365b686SDavid du Colombier 
1817365b686SDavid du Colombier 	nand->ctl |= NandActCEBoot;
18214cf0019SDavid du Colombier 	coherence();
18314cf0019SDavid du Colombier }
18414cf0019SDavid du Colombier 
18514cf0019SDavid du Colombier static void
nandunclaim(Flash *)18614cf0019SDavid du Colombier nandunclaim(Flash*)
18714cf0019SDavid du Colombier {
1887365b686SDavid du Colombier 	Nandreg *nand = (Nandreg*)soc.nand;
1897365b686SDavid du Colombier 
1907365b686SDavid du Colombier 	nand->ctl &= ~NandActCEBoot;
19114cf0019SDavid du Colombier 	coherence();
19214cf0019SDavid du Colombier }
19314cf0019SDavid du Colombier 
19406f6463aSDavid du Colombier 
19506f6463aSDavid du Colombier Nandtab *
findflash(Flash * f,uintptr pa,uchar * id4p)19606f6463aSDavid du Colombier findflash(Flash *f, uintptr pa, uchar *id4p)
19714cf0019SDavid du Colombier {
19814cf0019SDavid du Colombier 	int i;
19906f6463aSDavid du Colombier 	ulong sts;
20014cf0019SDavid du Colombier 	uchar maker, device, id3, id4;
20114cf0019SDavid du Colombier 	Nandtab *chip;
20214cf0019SDavid du Colombier 
20306f6463aSDavid du Colombier 	mmuidmap(pa, 16);
20406f6463aSDavid du Colombier 	f->addr = (void *)pa;
20506f6463aSDavid du Colombier 
20606f6463aSDavid du Colombier 	/* make sure controller is idle */
20706f6463aSDavid du Colombier 	nandclaim(f);
20806f6463aSDavid du Colombier 	nandcmd(f, Resetf);
20906f6463aSDavid du Colombier 	nandunclaim(f);
21006f6463aSDavid du Colombier 
21106f6463aSDavid du Colombier 	nandclaim(f);
21206f6463aSDavid du Colombier 	nandcmd(f, Readstatus);
21306f6463aSDavid du Colombier 	sts = nandread(f);
21406f6463aSDavid du Colombier 	nandunclaim(f);
21506f6463aSDavid du Colombier 	for (i = 10; i > 0 && !(sts & SReady); i--) {
21606f6463aSDavid du Colombier 		delay(50);
21706f6463aSDavid du Colombier 		nandclaim(f);
21806f6463aSDavid du Colombier 		nandcmd(f, Readstatus);
21906f6463aSDavid du Colombier 		sts = nandread(f);
22006f6463aSDavid du Colombier 		nandunclaim(f);
22106f6463aSDavid du Colombier 	}
222*409b3affSDavid du Colombier 	if(!(sts & SReady)) {
223*409b3affSDavid du Colombier 		if (Debug)
224*409b3affSDavid du Colombier 			print("flashkw: ctlr %#p not ready\n", pa);
22506f6463aSDavid du Colombier 		return nil;
226*409b3affSDavid du Colombier 	}
22706f6463aSDavid du Colombier 
22814cf0019SDavid du Colombier 	nandclaim(f);
22914cf0019SDavid du Colombier 	nandcmd(f, Readid);
23014cf0019SDavid du Colombier 	nandaddr(f, 0);
23114cf0019SDavid du Colombier 	maker = nandread(f);
23214cf0019SDavid du Colombier 	device = nandread(f);
23314cf0019SDavid du Colombier 	id3 = nandread(f);
23414cf0019SDavid du Colombier 	USED(id3);
23514cf0019SDavid du Colombier 	id4 = nandread(f);
23614cf0019SDavid du Colombier 	nandunclaim(f);
23706f6463aSDavid du Colombier 	if (id4p)
23806f6463aSDavid du Colombier 		*id4p = id4;
23914cf0019SDavid du Colombier 
24014cf0019SDavid du Colombier 	for(i = 0; i < nelem(nandtab); i++) {
24114cf0019SDavid du Colombier 		chip = &nandtab[i];
24214cf0019SDavid du Colombier 		if(chip->vid == maker && chip->did == device)
24306f6463aSDavid du Colombier 			return chip;
24414cf0019SDavid du Colombier 	}
245*409b3affSDavid du Colombier 	print("flashkw: unknown chip: vid %#ux did %#ux\n", maker, device);
24606f6463aSDavid du Colombier 	return nil;
24714cf0019SDavid du Colombier }
24814cf0019SDavid du Colombier 
24906f6463aSDavid du Colombier int
flashat(Flash * f,uintptr pa)25006f6463aSDavid du Colombier flashat(Flash *f, uintptr pa)
25106f6463aSDavid du Colombier {
25206f6463aSDavid du Colombier 	return findflash(f, pa, nil) != nil;
25306f6463aSDavid du Colombier }
25406f6463aSDavid du Colombier 
25506f6463aSDavid du Colombier static int
idchip(Flash * f)25606f6463aSDavid du Colombier idchip(Flash *f)
25706f6463aSDavid du Colombier {
25806f6463aSDavid du Colombier 	uchar id4;
25906f6463aSDavid du Colombier 	Flashregion *r;
26006f6463aSDavid du Colombier 	Nandtab *chip;
26106f6463aSDavid du Colombier 	static int blocksizes[4] = { 64*1024, 128*1024, 256*1024, 0 };
26206f6463aSDavid du Colombier 	static int pagesizes[4] = { 1024, 2*1024, 0, 0 };
26306f6463aSDavid du Colombier 	static int spares[2] = { 8, 16 };		/* per 512 bytes */
26406f6463aSDavid du Colombier 
26506f6463aSDavid du Colombier 	f->id = 0;
26606f6463aSDavid du Colombier 	f->devid = 0;
26706f6463aSDavid du Colombier 	f->width = 1;
26806f6463aSDavid du Colombier 	chip = findflash(f, (uintptr)f->addr, &id4);
26906f6463aSDavid du Colombier 	if (chip == nil)
27006f6463aSDavid du Colombier 		return -1;
27106f6463aSDavid du Colombier 	f->id = chip->vid;
27206f6463aSDavid du Colombier 	f->devid = chip->did;
27306f6463aSDavid du Colombier 	f->size = chip->size;
27414cf0019SDavid du Colombier 	f->width = 1;
27514cf0019SDavid du Colombier 	f->nr = 1;
27614cf0019SDavid du Colombier 
27714cf0019SDavid du Colombier 	r = &f->regions[0];
27814cf0019SDavid du Colombier 	r->pagesize = pagesizes[id4 & MASK(2)];
27914cf0019SDavid du Colombier 	r->erasesize = blocksizes[(id4 >> 4) & MASK(2)];
28006f6463aSDavid du Colombier 	if (r->pagesize == 0 || r->erasesize == 0) {
28106f6463aSDavid du Colombier 		iprint("flashkw: bogus flash sizes\n");
28206f6463aSDavid du Colombier 		return -1;
28306f6463aSDavid du Colombier 	}
28414cf0019SDavid du Colombier 	r->n = f->size / r->erasesize;
28514cf0019SDavid du Colombier 	r->start = 0;
28614cf0019SDavid du Colombier 	r->end = f->size;
287047f1f95SDavid du Colombier 	assert(ispow2(r->pagesize));
288047f1f95SDavid du Colombier 	r->pageshift = log2(r->pagesize);
289047f1f95SDavid du Colombier 	assert(ispow2(r->erasesize));
290047f1f95SDavid du Colombier 	r->eraseshift = log2(r->erasesize);
291047f1f95SDavid du Colombier 	assert(r->eraseshift >= r->pageshift);
29214cf0019SDavid du Colombier 	if (cache.page == nil) {
29314cf0019SDavid du Colombier 		cache.pgsize = r->pagesize;
29414cf0019SDavid du Colombier 		cache.page = smalloc(r->pagesize);
29514cf0019SDavid du Colombier 	}
29614cf0019SDavid du Colombier 
29714cf0019SDavid du Colombier 	r->spares = r->pagesize / 512 * spares[(id4 >> 2) & 1];
29814cf0019SDavid du Colombier 	print("#F0: kwnand: %s %,lud bytes pagesize %lud erasesize %,lud"
29914cf0019SDavid du Colombier 		" spares per page %lud\n", chip->name, f->size, r->pagesize,
30014cf0019SDavid du Colombier 		r->erasesize, r->spares);
30114cf0019SDavid du Colombier 	return 0;
30214cf0019SDavid du Colombier }
30314cf0019SDavid du Colombier 
30414cf0019SDavid du Colombier static int
ctlrwait(Flash * f)3054b9994c9SDavid du Colombier ctlrwait(Flash *f)
3064b9994c9SDavid du Colombier {
3074b9994c9SDavid du Colombier 	int sts, cnt;
3084b9994c9SDavid du Colombier 
3094b9994c9SDavid du Colombier 	nandclaim(f);
3104b9994c9SDavid du Colombier 	for (;;) {
3114b9994c9SDavid du Colombier 		nandcmd(f, Readstatus);
3124b9994c9SDavid du Colombier 		for(cnt = 100; cnt > 0 && (nandread(f) & Srdymask) != Srdymask;
3134b9994c9SDavid du Colombier 		    cnt--)
3144b9994c9SDavid du Colombier 			microdelay(50);
3154b9994c9SDavid du Colombier 		nandcmd(f, Readstatus);
3164b9994c9SDavid du Colombier 		sts = nandread(f);
3174b9994c9SDavid du Colombier 		if((sts & Srdymask) == Srdymask)
3184b9994c9SDavid du Colombier 			break;
3194b9994c9SDavid du Colombier 		print("flashkw: flash ctlr busy, sts %#ux: resetting\n", sts);
3204b9994c9SDavid du Colombier 		nandcmd(f, Resetf);
3214b9994c9SDavid du Colombier 	}
3224b9994c9SDavid du Colombier 	nandunclaim(f);
3234b9994c9SDavid du Colombier 	return 0;
3244b9994c9SDavid du Colombier }
3254b9994c9SDavid du Colombier 
3264b9994c9SDavid du Colombier static int
erasezone(Flash * f,Flashregion * r,ulong offset)32714cf0019SDavid du Colombier erasezone(Flash *f, Flashregion *r, ulong offset)
32814cf0019SDavid du Colombier {
32914cf0019SDavid du Colombier 	int i;
33014cf0019SDavid du Colombier 	ulong page, block;
33114cf0019SDavid du Colombier 	uchar s;
33214cf0019SDavid du Colombier 
33314cf0019SDavid du Colombier 	if (Debug) {
33414cf0019SDavid du Colombier 		print("flashkw: erasezone: offset %#lux, region nblocks %d,"
33514cf0019SDavid du Colombier 			" start %#lux, end %#lux\n", offset, r->n, r->start,
33614cf0019SDavid du Colombier 			r->end);
33714cf0019SDavid du Colombier 		print(" erasesize %lud, pagesize %lud\n",
33814cf0019SDavid du Colombier 			r->erasesize, r->pagesize);
33914cf0019SDavid du Colombier 	}
34014cf0019SDavid du Colombier 	assert(r->erasesize != 0);
34114cf0019SDavid du Colombier 	if(offset & (r->erasesize - 1)) {
34214cf0019SDavid du Colombier 		print("flashkw: erase offset %lud not block aligned\n", offset);
34314cf0019SDavid du Colombier 		return -1;
34414cf0019SDavid du Colombier 	}
345047f1f95SDavid du Colombier 	page = offset >> r->pageshift;
346047f1f95SDavid du Colombier 	block = page >> (r->eraseshift - r->pageshift);
34714cf0019SDavid du Colombier 	if (Debug)
34814cf0019SDavid du Colombier 		print("flashkw: erase: block %#lux\n", block);
34914cf0019SDavid du Colombier 
35014cf0019SDavid du Colombier 	/* make sure controller is idle */
3514b9994c9SDavid du Colombier 	if(ctlrwait(f) < 0) {
35214cf0019SDavid du Colombier 		print("flashkw: erase: flash busy\n");
35314cf0019SDavid du Colombier 		return -1;
35414cf0019SDavid du Colombier 	}
35514cf0019SDavid du Colombier 
35614cf0019SDavid du Colombier 	/* start erasing */
3574b9994c9SDavid du Colombier 	nandclaim(f);
35814cf0019SDavid du Colombier 	nandcmd(f, Erase);
35914cf0019SDavid du Colombier 	nandaddr(f, page>>0);
36014cf0019SDavid du Colombier 	nandaddr(f, page>>8);
36114cf0019SDavid du Colombier 	nandaddr(f, page>>16);
36214cf0019SDavid du Colombier 	nandcmd(f, Erasestart);
36314cf0019SDavid du Colombier 
3642d1bf19dSDavid du Colombier 	/* invalidate cache on any erasure (slight overkill) */
3652d1bf19dSDavid du Colombier 	cache.pageno = Nopage;
3662d1bf19dSDavid du Colombier 
36714cf0019SDavid du Colombier 	/* have to wait until flash is done.  typically ~2ms */
36814cf0019SDavid du Colombier 	delay(1);
36914cf0019SDavid du Colombier 	nandcmd(f, Readstatus);
37014cf0019SDavid du Colombier 	for(i = 0; i < 100; i++) {
37114cf0019SDavid du Colombier 		s = nandread(f);
37214cf0019SDavid du Colombier 		if(s & SReady) {
37314cf0019SDavid du Colombier 			nandunclaim(f);
37414cf0019SDavid du Colombier 			if(s & SFail) {
37514cf0019SDavid du Colombier 				print("flashkw: erase: failed, block %#lux\n",
37614cf0019SDavid du Colombier 					block);
37714cf0019SDavid du Colombier 				return -1;
37814cf0019SDavid du Colombier 			}
37914cf0019SDavid du Colombier 			return 0;
38014cf0019SDavid du Colombier 		}
38114cf0019SDavid du Colombier 		microdelay(50);
38214cf0019SDavid du Colombier 	}
38314cf0019SDavid du Colombier 	print("flashkw: erase timeout, block %#lux\n", block);
38414cf0019SDavid du Colombier 	nandunclaim(f);
38514cf0019SDavid du Colombier 	return -1;
38614cf0019SDavid du Colombier }
38714cf0019SDavid du Colombier 
38814cf0019SDavid du Colombier static void
flcachepage(Flash * f,ulong page,uchar * buf)38914cf0019SDavid du Colombier flcachepage(Flash *f, ulong page, uchar *buf)
39014cf0019SDavid du Colombier {
39114cf0019SDavid du Colombier 	Flashregion *r = &f->regions[0];
39214cf0019SDavid du Colombier 
39314cf0019SDavid du Colombier 	assert(cache.pgsize == r->pagesize);
39414cf0019SDavid du Colombier 	cache.flif = f;
39514cf0019SDavid du Colombier 	cache.pageno = page;
39614cf0019SDavid du Colombier 	/* permit i/o directly to or from the cache */
39714cf0019SDavid du Colombier 	if (buf != (uchar *)cache.page)
39814cf0019SDavid du Colombier 		memmove(cache.page, buf, cache.pgsize);
39914cf0019SDavid du Colombier }
40014cf0019SDavid du Colombier 
40114cf0019SDavid du Colombier static int
write1page(Flash * f,ulong offset,void * buf)40214cf0019SDavid du Colombier write1page(Flash *f, ulong offset, void *buf)
40314cf0019SDavid du Colombier {
40414cf0019SDavid du Colombier 	int i;
40514cf0019SDavid du Colombier 	ulong page, v;
40614cf0019SDavid du Colombier 	uchar s;
40714cf0019SDavid du Colombier 	uchar *eccp, *p;
40814cf0019SDavid du Colombier 	Flashregion *r = &f->regions[0];
40914cf0019SDavid du Colombier 	static uchar *oob;
41014cf0019SDavid du Colombier 
41114cf0019SDavid du Colombier 	if (oob == nil)
41214cf0019SDavid du Colombier 		oob = smalloc(r->spares);
41314cf0019SDavid du Colombier 
414047f1f95SDavid du Colombier 	page = offset >> r->pageshift;
41514cf0019SDavid du Colombier 	if (Debug)
41614cf0019SDavid du Colombier 		print("flashkw: write nand offset %#lux page %#lux\n",
41714cf0019SDavid du Colombier 			offset, page);
41814cf0019SDavid du Colombier 
41914cf0019SDavid du Colombier 	if(offset & (r->pagesize - 1)) {
42014cf0019SDavid du Colombier 		print("flashkw: write offset %lud not page aligned\n", offset);
42114cf0019SDavid du Colombier 		return -1;
42214cf0019SDavid du Colombier 	}
42314cf0019SDavid du Colombier 
42414cf0019SDavid du Colombier 	p = buf;
42514cf0019SDavid du Colombier 	memset(oob, 0xff, r->spares);
42614cf0019SDavid du Colombier 	assert(r->spares >= 24);
42714cf0019SDavid du Colombier 	eccp = oob + r->spares - 24;
42814cf0019SDavid du Colombier 	for(i = 0; i < r->pagesize / 256; i++) {
42914cf0019SDavid du Colombier 		v = nandecc(p);
43014cf0019SDavid du Colombier 		*eccp++ = v>>8;
43114cf0019SDavid du Colombier 		*eccp++ = v>>0;
43214cf0019SDavid du Colombier 		*eccp++ = v>>16;
43314cf0019SDavid du Colombier 		p += 256;
43414cf0019SDavid du Colombier 	}
43514cf0019SDavid du Colombier 
4364b9994c9SDavid du Colombier 	if(ctlrwait(f) < 0) {
43714cf0019SDavid du Colombier 		print("flashkw: write: nand not ready & idle\n");
43814cf0019SDavid du Colombier 		return -1;
43914cf0019SDavid du Colombier 	}
44014cf0019SDavid du Colombier 
44114cf0019SDavid du Colombier 	/* write, only whole pages for now, no sub-pages */
4424b9994c9SDavid du Colombier 	nandclaim(f);
44314cf0019SDavid du Colombier 	nandcmd(f, Program);
44414cf0019SDavid du Colombier 	nandaddr(f, 0);
44514cf0019SDavid du Colombier 	nandaddr(f, 0);
44614cf0019SDavid du Colombier 	nandaddr(f, page>>0);
44714cf0019SDavid du Colombier 	nandaddr(f, page>>8);
44814cf0019SDavid du Colombier 	nandaddr(f, page>>16);
44914cf0019SDavid du Colombier 	nandwriten(f, buf, r->pagesize);
45014cf0019SDavid du Colombier 	nandwriten(f, oob, r->spares);
45114cf0019SDavid du Colombier 	nandcmd(f, Programstart);
45214cf0019SDavid du Colombier 
45314cf0019SDavid du Colombier 	microdelay(100);
45414cf0019SDavid du Colombier 	nandcmd(f, Readstatus);
45514cf0019SDavid du Colombier 	for(i = 0; i < 100; i++) {
45614cf0019SDavid du Colombier 		s = nandread(f);
45714cf0019SDavid du Colombier 		if(s & SReady) {
45814cf0019SDavid du Colombier 			nandunclaim(f);
45914cf0019SDavid du Colombier 			if(s & SFail) {
46014cf0019SDavid du Colombier 				print("flashkw: write failed, page %#lux\n",
46114cf0019SDavid du Colombier 					page);
46214cf0019SDavid du Colombier 				return -1;
46314cf0019SDavid du Colombier 			}
46414cf0019SDavid du Colombier 			return 0;
46514cf0019SDavid du Colombier 		}
46614cf0019SDavid du Colombier 		microdelay(10);
46714cf0019SDavid du Colombier 	}
46814cf0019SDavid du Colombier 
46914cf0019SDavid du Colombier 	nandunclaim(f);
47014cf0019SDavid du Colombier 	flcachepage(f, page, buf);
47114cf0019SDavid du Colombier 	print("flashkw: write timeout for page %#lux\n", page);
47214cf0019SDavid du Colombier 	return -1;
47314cf0019SDavid du Colombier }
47414cf0019SDavid du Colombier 
47514cf0019SDavid du Colombier static int
read1page(Flash * f,ulong offset,void * buf)47614cf0019SDavid du Colombier read1page(Flash *f, ulong offset, void *buf)
47714cf0019SDavid du Colombier {
47814cf0019SDavid du Colombier 	int i;
47914cf0019SDavid du Colombier 	ulong addr, page, w;
48014cf0019SDavid du Colombier 	uchar *eccp, *p;
48114cf0019SDavid du Colombier 	Flashregion *r = &f->regions[0];
48214cf0019SDavid du Colombier 	static uchar *oob;
48314cf0019SDavid du Colombier 
48414cf0019SDavid du Colombier 	if (oob == nil)
48514cf0019SDavid du Colombier 		oob = smalloc(r->spares);
48614cf0019SDavid du Colombier 
48714cf0019SDavid du Colombier 	assert(r->pagesize != 0);
48814cf0019SDavid du Colombier 	addr = offset & (r->pagesize - 1);
489047f1f95SDavid du Colombier 	page = offset >> r->pageshift;
49014cf0019SDavid du Colombier 	if(addr != 0) {
49114cf0019SDavid du Colombier 		print("flashkw: read1page: read addr %#lux:"
49214cf0019SDavid du Colombier 			" must read aligned page\n", addr);
49314cf0019SDavid du Colombier 		return -1;
49414cf0019SDavid du Colombier 	}
49514cf0019SDavid du Colombier 
49614cf0019SDavid du Colombier 	/* satisfy request from cache if possible */
49714cf0019SDavid du Colombier 	if (f == cache.flif && page == cache.pageno &&
49814cf0019SDavid du Colombier 	    r->pagesize == cache.pgsize) {
49914cf0019SDavid du Colombier 		memmove(buf, cache.page, r->pagesize);
50014cf0019SDavid du Colombier 		return 0;
50114cf0019SDavid du Colombier 	}
50214cf0019SDavid du Colombier 
50314cf0019SDavid du Colombier 	if (Debug)
50414cf0019SDavid du Colombier 		print("flashkw: read offset %#lux addr %#lux page %#lux\n",
50514cf0019SDavid du Colombier 			offset, addr, page);
50614cf0019SDavid du Colombier 
50714cf0019SDavid du Colombier 	nandclaim(f);
50814cf0019SDavid du Colombier 	nandcmd(f, Read);
50914cf0019SDavid du Colombier 	nandaddr(f, addr>>0);
51014cf0019SDavid du Colombier 	nandaddr(f, addr>>8);
51114cf0019SDavid du Colombier 	nandaddr(f, page>>0);
51214cf0019SDavid du Colombier 	nandaddr(f, page>>8);
51314cf0019SDavid du Colombier 	nandaddr(f, page>>16);
51414cf0019SDavid du Colombier 	nandcmd(f, Readstart);
51514cf0019SDavid du Colombier 
51614cf0019SDavid du Colombier 	microdelay(50);
51714cf0019SDavid du Colombier 
51814cf0019SDavid du Colombier 	nandreadn(f, buf, r->pagesize);
51914cf0019SDavid du Colombier 	nandreadn(f, oob, r->spares);
52014cf0019SDavid du Colombier 
52114cf0019SDavid du Colombier 	nandunclaim(f);
52214cf0019SDavid du Colombier 
52314cf0019SDavid du Colombier 	/* verify/correct data. last 8*3 bytes is ecc, per 256 bytes. */
52414cf0019SDavid du Colombier 	p = buf;
52514cf0019SDavid du Colombier 	assert(r->spares >= 24);
52614cf0019SDavid du Colombier 	eccp = oob + r->spares - 24;
52714cf0019SDavid du Colombier 	for(i = 0; i < r->pagesize / 256; i++) {
52814cf0019SDavid du Colombier 		w = eccp[0] << 8 | eccp[1] << 0 | eccp[2] << 16;
52914cf0019SDavid du Colombier 		eccp += 3;
53014cf0019SDavid du Colombier 		switch(nandecccorrect(p, nandecc(p), &w, 1)) {
53114cf0019SDavid du Colombier 		case NandEccErrorBad:
53214cf0019SDavid du Colombier 			print("(page %d)\n", i);
53314cf0019SDavid du Colombier 			return -1;
53414cf0019SDavid du Colombier 		case NandEccErrorOneBit:
53514cf0019SDavid du Colombier 		case NandEccErrorOneBitInEcc:
53614cf0019SDavid du Colombier 			print("(page %d)\n", i);
53714cf0019SDavid du Colombier 			/* fall through */
53814cf0019SDavid du Colombier 		case NandEccErrorGood:
53914cf0019SDavid du Colombier 			break;
54014cf0019SDavid du Colombier 		}
54114cf0019SDavid du Colombier 		p += 256;
54214cf0019SDavid du Colombier 	}
543047f1f95SDavid du Colombier 
544047f1f95SDavid du Colombier 	flcachepage(f, page, buf);
54514cf0019SDavid du Colombier 	return 0;
54614cf0019SDavid du Colombier }
54714cf0019SDavid du Colombier 
54814cf0019SDavid du Colombier /*
54914cf0019SDavid du Colombier  * read a page at offset into cache, copy fragment from buf into it
55014cf0019SDavid du Colombier  * at pagoff, and rewrite that page.
55114cf0019SDavid du Colombier  */
55214cf0019SDavid du Colombier static int
rewrite(Flash * f,ulong offset,ulong pagoff,void * buf,ulong size)55314cf0019SDavid du Colombier rewrite(Flash *f, ulong offset, ulong pagoff, void *buf, ulong size)
55414cf0019SDavid du Colombier {
55514cf0019SDavid du Colombier 	if (read1page(f, offset, cache.page) < 0)
55614cf0019SDavid du Colombier 		return -1;
55714cf0019SDavid du Colombier 	memmove(&cache.page[pagoff], buf, size);
55814cf0019SDavid du Colombier 	return write1page(f, offset, cache.page);
55914cf0019SDavid du Colombier }
56014cf0019SDavid du Colombier 
56114cf0019SDavid du Colombier /* there are no alignment constraints on offset, buf, nor n */
56214cf0019SDavid du Colombier static int
write(Flash * f,ulong offset,void * buf,long n)56314cf0019SDavid du Colombier write(Flash *f, ulong offset, void *buf, long n)
56414cf0019SDavid du Colombier {
56514cf0019SDavid du Colombier 	uint un, frag, pagoff;
56614cf0019SDavid du Colombier 	ulong pgsize;
56714cf0019SDavid du Colombier 	uchar *p;
56814cf0019SDavid du Colombier 	Flashregion *r = &f->regions[0];
56914cf0019SDavid du Colombier 
57014cf0019SDavid du Colombier 	if(n <= 0)
57114cf0019SDavid du Colombier 		panic("flashkw: write: non-positive count %ld", n);
57214cf0019SDavid du Colombier 	un = n;
57314cf0019SDavid du Colombier 	assert(r->pagesize != 0);
57414cf0019SDavid du Colombier 	pgsize = r->pagesize;
57514cf0019SDavid du Colombier 
57614cf0019SDavid du Colombier 	/* if a partial first page exists, update the first page with it. */
57714cf0019SDavid du Colombier 	p = buf;
57814cf0019SDavid du Colombier 	pagoff = offset % pgsize;
57914cf0019SDavid du Colombier 	if (pagoff != 0) {
58014cf0019SDavid du Colombier 		frag = pgsize - pagoff;
58114cf0019SDavid du Colombier 		if (frag > un)		/* might not extend to end of page */
58214cf0019SDavid du Colombier 			frag = un;
58314cf0019SDavid du Colombier 		if (rewrite(f, offset - pagoff, pagoff, p, frag) < 0)
58414cf0019SDavid du Colombier 			return -1;
58514cf0019SDavid du Colombier 		offset += frag;
58614cf0019SDavid du Colombier 		p  += frag;
58714cf0019SDavid du Colombier 		un -= frag;
58814cf0019SDavid du Colombier 	}
58914cf0019SDavid du Colombier 
59014cf0019SDavid du Colombier 	/* copy whole pages */
59114cf0019SDavid du Colombier 	while (un >= pgsize) {
59214cf0019SDavid du Colombier 		if (write1page(f, offset, p) < 0)
59314cf0019SDavid du Colombier 			return -1;
59414cf0019SDavid du Colombier 		offset += pgsize;
59514cf0019SDavid du Colombier 		p  += pgsize;
59614cf0019SDavid du Colombier 		un -= pgsize;
59714cf0019SDavid du Colombier 	}
59814cf0019SDavid du Colombier 
59914cf0019SDavid du Colombier 	/* if a partial last page exists, update the last page with it. */
60014cf0019SDavid du Colombier 	if (un > 0)
60114cf0019SDavid du Colombier 		return rewrite(f, offset, 0, p, un);
60214cf0019SDavid du Colombier 	return 0;
60314cf0019SDavid du Colombier }
60414cf0019SDavid du Colombier 
60514cf0019SDavid du Colombier /* there are no alignment constraints on offset, buf, nor n */
60614cf0019SDavid du Colombier static int
read(Flash * f,ulong offset,void * buf,long n)60714cf0019SDavid du Colombier read(Flash *f, ulong offset, void *buf, long n)
60814cf0019SDavid du Colombier {
60914cf0019SDavid du Colombier 	uint un, frag, pagoff;
61014cf0019SDavid du Colombier 	ulong pgsize;
61114cf0019SDavid du Colombier 	uchar *p;
61214cf0019SDavid du Colombier 	Flashregion *r = &f->regions[0];
61314cf0019SDavid du Colombier 
61414cf0019SDavid du Colombier 	if(n <= 0)
61514cf0019SDavid du Colombier 		panic("flashkw: read: non-positive count %ld", n);
61614cf0019SDavid du Colombier 	un = n;
61714cf0019SDavid du Colombier 	assert(r->pagesize != 0);
61814cf0019SDavid du Colombier 	pgsize = r->pagesize;
61914cf0019SDavid du Colombier 
62014cf0019SDavid du Colombier 	/* if partial 1st page, read it into cache & copy fragment to buf */
62114cf0019SDavid du Colombier 	p = buf;
62214cf0019SDavid du Colombier 	pagoff = offset % pgsize;
62314cf0019SDavid du Colombier 	if (pagoff != 0) {
62414cf0019SDavid du Colombier 		frag = pgsize - pagoff;
62514cf0019SDavid du Colombier 		if (frag > un)		/* might not extend to end of page */
62614cf0019SDavid du Colombier 			frag = un;
62714cf0019SDavid du Colombier 		if (read1page(f, offset - pagoff, cache.page) < 0)
62814cf0019SDavid du Colombier 			return -1;
62914cf0019SDavid du Colombier 		offset += frag;
63014cf0019SDavid du Colombier 		memmove(p, &cache.page[pagoff], frag);
63114cf0019SDavid du Colombier 		p  += frag;
63214cf0019SDavid du Colombier 		un -= frag;
63314cf0019SDavid du Colombier 	}
63414cf0019SDavid du Colombier 
63514cf0019SDavid du Colombier 	/* copy whole pages */
63614cf0019SDavid du Colombier 	while (un >= pgsize) {
63714cf0019SDavid du Colombier 		if (read1page(f, offset, p) < 0)
63814cf0019SDavid du Colombier 			return -1;
63914cf0019SDavid du Colombier 		offset += pgsize;
64014cf0019SDavid du Colombier 		p  += pgsize;
64114cf0019SDavid du Colombier 		un -= pgsize;
64214cf0019SDavid du Colombier 	}
64314cf0019SDavid du Colombier 
64414cf0019SDavid du Colombier 	/* if partial last page, read into cache & copy initial fragment to buf */
64514cf0019SDavid du Colombier 	if (un > 0) {
64614cf0019SDavid du Colombier 		if (read1page(f, offset, cache.page) < 0)
64714cf0019SDavid du Colombier 			return -1;
64814cf0019SDavid du Colombier 		memmove(p, cache.page, un);
64914cf0019SDavid du Colombier 	}
65014cf0019SDavid du Colombier 	return 0;
65114cf0019SDavid du Colombier }
65214cf0019SDavid du Colombier 
65314cf0019SDavid du Colombier static int
reset(Flash * f)65414cf0019SDavid du Colombier reset(Flash *f)
65514cf0019SDavid du Colombier {
65614cf0019SDavid du Colombier 	if(f->data != nil)
65714cf0019SDavid du Colombier 		return 1;
65814cf0019SDavid du Colombier 	f->write = write;
65914cf0019SDavid du Colombier  	f->read = read;
66014cf0019SDavid du Colombier 	f->eraseall = nil;
66114cf0019SDavid du Colombier 	f->erasezone = erasezone;
66214cf0019SDavid du Colombier 	f->suspend = nil;
66314cf0019SDavid du Colombier 	f->resume = nil;
66414cf0019SDavid du Colombier 	f->sort = "nand";
6652d1bf19dSDavid du Colombier 	cache.pageno = Nopage;
66614cf0019SDavid du Colombier 	return idchip(f);
66714cf0019SDavid du Colombier }
66814cf0019SDavid du Colombier 
66914cf0019SDavid du Colombier void
flashkwlink(void)67014cf0019SDavid du Colombier flashkwlink(void)
67114cf0019SDavid du Colombier {
67214cf0019SDavid du Colombier 	addflashcard("nand", reset);
67314cf0019SDavid du Colombier }
674