xref: /plan9/sys/src/9/ppc/devflash.c (revision 2cca75a1b2b8c6083390679d69d5c50cf66d9a01)
1458db832SDavid du Colombier #include	"u.h"
2458db832SDavid du Colombier #include	"../port/lib.h"
3458db832SDavid du Colombier #include	"mem.h"
4458db832SDavid du Colombier #include	"dat.h"
5458db832SDavid du Colombier #include	"fns.h"
6458db832SDavid du Colombier #include	"../port/error.h"
7458db832SDavid du Colombier 
8458db832SDavid du Colombier enum {
9458db832SDavid du Colombier 	Nflash = 2,
10458db832SDavid du Colombier 	Maxwchunk=	1024,	/* maximum chunk written by one call to falg->write */
11458db832SDavid du Colombier };
12458db832SDavid du Colombier 
13458db832SDavid du Colombier 
14458db832SDavid du Colombier /*
15458db832SDavid du Colombier  *  Flashes are either 8 or 16 bits wide.  On some installations (e.g., the
16458db832SDavid du Colombier  *  bitsy, they are interleaved: address 0 is in the first chip, address 2
17458db832SDavid du Colombier  *  on the second, address 4 on the first, etc.
18458db832SDavid du Colombier  *  We define Funit as the unit that matches the width of a single flash chip,
19458db832SDavid du Colombier  *  so Funit is either `uchar' or `ushort' (I haven't seen 32-bit wide flashes),
20458db832SDavid du Colombier  *  and we define Fword as the unit that matches a set of interleaved Funits.
21458db832SDavid du Colombier  *  We access interleaved flashes simultaneously, by doing single reads and
22458db832SDavid du Colombier  *  writes to both.  The macro `mirror' takes a command and replicates it for
23458db832SDavid du Colombier  *  this purpose.
24458db832SDavid du Colombier  *  The Blast board has a non-interleaved 16-bit wide flash.  When doing
25458db832SDavid du Colombier  *  writes to it, we must swap bytes.
26458db832SDavid du Colombier  */
27458db832SDavid du Colombier 
28458db832SDavid du Colombier typedef struct FlashAlg FlashAlg;
29458db832SDavid du Colombier typedef struct Flash Flash;
30458db832SDavid du Colombier typedef struct FlashRegion FlashRegion;
31458db832SDavid du Colombier 
32458db832SDavid du Colombier #ifdef WIDTH8
33458db832SDavid du Colombier 	typedef		uchar		Funit;		/* Width of the flash (uchar or ushort) */
34458db832SDavid du Colombier #	define		toendian(x)	(x)			/* Little or big endianness */
35458db832SDavid du Colombier #	define		fromendian(x)	(x)
36458db832SDavid du Colombier #	define		reg(x)		((x)<<1)
37458db832SDavid du Colombier #	ifdef INTERLEAVED
38458db832SDavid du Colombier #		define	mirror(x)		((x)<<8|(x))	/* Double query for interleaved flashes */
39458db832SDavid du Colombier 		typedef	ushort		Fword;		/* Width after interleaving */
40458db832SDavid du Colombier #		define	Wshift		1
41458db832SDavid du Colombier #	else
42458db832SDavid du Colombier #		define 	mirror(x)		(x)
43458db832SDavid du Colombier 		typedef	uchar		Fword;
44458db832SDavid du Colombier #		define	Wshift		0
45458db832SDavid du Colombier #	endif
46458db832SDavid du Colombier #else
47458db832SDavid du Colombier 	typedef		ushort		Funit;
48458db832SDavid du Colombier #	define		toendian(x)	((x)<<8)
49458db832SDavid du Colombier #	define		fromendian(x)	((x)>>8)
50458db832SDavid du Colombier #	define		reg(x)		(x)
51458db832SDavid du Colombier #	ifdef INTERLEAVED
52458db832SDavid du Colombier #		define	mirror(x)		(toendian(x)<<16|toendian(x))
53458db832SDavid du Colombier 		typedef	ulong		Fword;
54458db832SDavid du Colombier #		define	Wshift		2
55458db832SDavid du Colombier #	else
56458db832SDavid du Colombier #		define mirror(x)		toendian(x)
57458db832SDavid du Colombier 		typedef	ushort		Fword;
58458db832SDavid du Colombier #		define	Wshift		1
59458db832SDavid du Colombier #	endif
60458db832SDavid du Colombier #endif
61458db832SDavid du Colombier 
62458db832SDavid du Colombier /* this defines a contiguous set of erase blocks of one size */
63458db832SDavid du Colombier struct FlashRegion
64458db832SDavid du Colombier {
65458db832SDavid du Colombier 	ulong	addr;	/* start of region */
66458db832SDavid du Colombier 	ulong	end;		/* end of region + 1 */
67458db832SDavid du Colombier 	ulong	n;		/* number of blocks */
68458db832SDavid du Colombier 	ulong	size;		/* size of each block */
69458db832SDavid du Colombier };
70458db832SDavid du Colombier 
71458db832SDavid du Colombier struct Flash
72458db832SDavid du Colombier {
73458db832SDavid du Colombier 	ISAConf;					/* contains size */
74458db832SDavid du Colombier 	RWlock;
75458db832SDavid du Colombier 	Fword		*p;
76458db832SDavid du Colombier 	ushort		algid;		/* access algorithm */
77458db832SDavid du Colombier 	FlashAlg		*alg;
78458db832SDavid du Colombier 	ushort		manid;		/* manufacturer id */
79458db832SDavid du Colombier 	ushort		devid;		/* device id */
80458db832SDavid du Colombier 	int			wbsize;		/* size of write buffer */
81458db832SDavid du Colombier 	ulong		nr;			/* number of regions */
82458db832SDavid du Colombier 	uchar		bootprotect;
83458db832SDavid du Colombier 	ulong		offset;		/* beginning offset of this flash */
84458db832SDavid du Colombier 	FlashRegion	r[32];
85458db832SDavid du Colombier };
86458db832SDavid du Colombier 
87458db832SDavid du Colombier /* this defines a particular access algorithm */
88458db832SDavid du Colombier struct FlashAlg
89458db832SDavid du Colombier {
90458db832SDavid du Colombier 	int	id;
91458db832SDavid du Colombier 	char	*name;
92458db832SDavid du Colombier 	void	(*identify)(Flash*);	/* identify device */
93458db832SDavid du Colombier 	void	(*erase)(Flash*, ulong);	/* erase a region */
94458db832SDavid du Colombier 	void	(*write)(Flash*, void*, long, ulong);	/* write a region */
95458db832SDavid du Colombier };
96458db832SDavid du Colombier 
97458db832SDavid du Colombier static void	ise_id(Flash*);
98458db832SDavid du Colombier static void	ise_erase(Flash*, ulong);
99458db832SDavid du Colombier static void	ise_write(Flash*, void*, long, ulong);
100458db832SDavid du Colombier 
101458db832SDavid du Colombier static void	afs_id(Flash*);
102458db832SDavid du Colombier static void	afs_erase(Flash*, ulong);
103458db832SDavid du Colombier static void	afs_write(Flash*, void*, long, ulong);
104458db832SDavid du Colombier 
105458db832SDavid du Colombier static ulong	blockstart(Flash*, ulong);
106458db832SDavid du Colombier static ulong	blockend(Flash*, ulong);
107458db832SDavid du Colombier 
108458db832SDavid du Colombier FlashAlg falg[] =
109458db832SDavid du Colombier {
110458db832SDavid du Colombier 	{ 1,	"Intel/Sharp Extended",	ise_id, ise_erase, ise_write	},
111458db832SDavid du Colombier 	{ 2,	"AMD/Fujitsu Standard",	afs_id, afs_erase, afs_write	},
112458db832SDavid du Colombier };
113458db832SDavid du Colombier 
114458db832SDavid du Colombier Flash flashes[Nflash];
115458db832SDavid du Colombier 
116458db832SDavid du Colombier /*
117458db832SDavid du Colombier  *  common flash interface
118458db832SDavid du Colombier  */
119458db832SDavid du Colombier static uchar
cfigetc(Flash * flash,int off)120458db832SDavid du Colombier cfigetc(Flash *flash, int off)
121458db832SDavid du Colombier {
122458db832SDavid du Colombier 	uchar rv;
123458db832SDavid du Colombier 
124458db832SDavid du Colombier 	flash->p[reg(0x55)] = mirror(0x98);
125458db832SDavid du Colombier 	rv = fromendian(flash->p[reg(off)]);
126458db832SDavid du Colombier 	flash->p[reg(0x55)] = mirror(0xFF);
127458db832SDavid du Colombier 	return rv;
128458db832SDavid du Colombier }
129458db832SDavid du Colombier 
130458db832SDavid du Colombier static ushort
cfigets(Flash * flash,int off)131458db832SDavid du Colombier cfigets(Flash *flash, int off)
132458db832SDavid du Colombier {
133458db832SDavid du Colombier 	return (cfigetc(flash, off+1)<<8)|cfigetc(flash, off);
134458db832SDavid du Colombier }
135458db832SDavid du Colombier 
136458db832SDavid du Colombier static ulong
cfigetl(Flash * flash,int off)137458db832SDavid du Colombier cfigetl(Flash *flash, int off)
138458db832SDavid du Colombier {
139458db832SDavid du Colombier 	return (cfigetc(flash, off+3)<<24)|(cfigetc(flash, off+2)<<16)|
140458db832SDavid du Colombier 		(cfigetc(flash, off+1)<<8)|cfigetc(flash, off);
141458db832SDavid du Colombier }
142458db832SDavid du Colombier 
143458db832SDavid du Colombier static void
cfiquery(Flash * flash)144458db832SDavid du Colombier cfiquery(Flash *flash)
145458db832SDavid du Colombier {
146458db832SDavid du Colombier 	uchar q, r, y;
147458db832SDavid du Colombier 	ulong x, addr;
148458db832SDavid du Colombier 
149458db832SDavid du Colombier 	q = cfigetc(flash, 0x10);
150458db832SDavid du Colombier 	r = cfigetc(flash, 0x11);
151458db832SDavid du Colombier 	y = cfigetc(flash, 0x12);
152458db832SDavid du Colombier 	if(q != 'Q' || r != 'R' || y != 'Y'){
153458db832SDavid du Colombier 		print("cfi query failed: %ux %ux %ux\n", q, r, y);
154458db832SDavid du Colombier 		return;
155458db832SDavid du Colombier 	}
156458db832SDavid du Colombier 	flash->algid = cfigetc(flash, 0x13);
157458db832SDavid du Colombier 	flash->size = (sizeof(Fword)/sizeof(Funit)) * (1<<(cfigetc(flash, 0x27)));
158458db832SDavid du Colombier 	flash->wbsize = (sizeof(Fword)/sizeof(Funit)) * (1<<(cfigetc(flash, 0x2a)));
159458db832SDavid du Colombier 	flash->nr = cfigetc(flash, 0x2c);
160458db832SDavid du Colombier 	if(flash->nr > nelem(flash->r)){
161458db832SDavid du Colombier 		print("cfi reports > %d regions\n", nelem(flash->r));
162458db832SDavid du Colombier 		flash->nr = nelem(flash->r);
163458db832SDavid du Colombier 	}
164458db832SDavid du Colombier 	addr = 0;
165458db832SDavid du Colombier 	for(q = 0; q < flash->nr; q++){
166458db832SDavid du Colombier 		x = cfigetl(flash, q+0x2d);
167458db832SDavid du Colombier 		flash->r[q].size = (sizeof(Fword)/sizeof(Funit)) * 256 * (x>>16);
168458db832SDavid du Colombier 		flash->r[q].n = (x&0xffff)+1;
169458db832SDavid du Colombier 		flash->r[q].addr = addr;
170458db832SDavid du Colombier 		addr += flash->r[q].size*flash->r[q].n;
171458db832SDavid du Colombier 		flash->r[q].end = addr;
172458db832SDavid du Colombier 	}
173458db832SDavid du Colombier }
174458db832SDavid du Colombier 
175458db832SDavid du Colombier /*
176458db832SDavid du Colombier  *  flash device interface
177458db832SDavid du Colombier  */
178458db832SDavid du Colombier 
179458db832SDavid du Colombier enum
180458db832SDavid du Colombier {
181458db832SDavid du Colombier 	Qtopdir,
182458db832SDavid du Colombier 	Q2nddir,
183458db832SDavid du Colombier 	Qfctl,
184458db832SDavid du Colombier 	Qfdata,
185458db832SDavid du Colombier 
186458db832SDavid du Colombier 	Maxpart= 8,
187458db832SDavid du Colombier };
188458db832SDavid du Colombier 
189458db832SDavid du Colombier 
190458db832SDavid du Colombier typedef struct FPart FPart;
191458db832SDavid du Colombier struct FPart
192458db832SDavid du Colombier {
193458db832SDavid du Colombier 	Flash	*flash;
194458db832SDavid du Colombier 	char		*name;
195458db832SDavid du Colombier 	char		*ctlname;
196458db832SDavid du Colombier 	ulong	start;
197458db832SDavid du Colombier 	ulong	end;
198458db832SDavid du Colombier };
199458db832SDavid du Colombier static FPart	part[Maxpart];
200458db832SDavid du Colombier 
201458db832SDavid du Colombier #define FQID(p,q)	((p)<<8|(q))
202458db832SDavid du Colombier #define FTYPE(q)	((q) & 0xff)
203458db832SDavid du Colombier #define FPART(q)	(&part[(q) >>8])
204458db832SDavid du Colombier 
205458db832SDavid du Colombier static int
gen(Chan * c,char *,Dirtab *,int,int i,Dir * dp)206458db832SDavid du Colombier gen(Chan *c, char*, Dirtab*, int, int i, Dir *dp)
207458db832SDavid du Colombier {
208458db832SDavid du Colombier 	Qid q;
209458db832SDavid du Colombier 	FPart *fp;
210458db832SDavid du Colombier 
211458db832SDavid du Colombier 	q.vers = 0;
212458db832SDavid du Colombier 
213458db832SDavid du Colombier 	/* top level directory contains the name of the network */
214458db832SDavid du Colombier 	if(c->qid.path == Qtopdir){
215458db832SDavid du Colombier 		switch(i){
216458db832SDavid du Colombier 		case DEVDOTDOT:
217458db832SDavid du Colombier 			q.path = Qtopdir;
218458db832SDavid du Colombier 			q.type = QTDIR;
219458db832SDavid du Colombier 			devdir(c, q, "#F", 0, eve, DMDIR|0555, dp);
220458db832SDavid du Colombier 			break;
221458db832SDavid du Colombier 		case 0:
222458db832SDavid du Colombier 			q.path = Q2nddir;
223458db832SDavid du Colombier 			q.type = QTDIR;
224458db832SDavid du Colombier 			devdir(c, q, "flash", 0, eve, DMDIR|0555, dp);
225458db832SDavid du Colombier 			break;
226458db832SDavid du Colombier 		default:
227458db832SDavid du Colombier 			return -1;
228458db832SDavid du Colombier 		}
229458db832SDavid du Colombier 		return 1;
230458db832SDavid du Colombier 	}
231458db832SDavid du Colombier 
232458db832SDavid du Colombier 	/* second level contains all partitions and their control files */
233458db832SDavid du Colombier 	switch(i) {
234458db832SDavid du Colombier 	case DEVDOTDOT:
235458db832SDavid du Colombier 		q.path = Qtopdir;
236458db832SDavid du Colombier 		q.type = QTDIR;
237458db832SDavid du Colombier 		devdir(c, q, "#F", 0, eve, DMDIR|0555, dp);
238458db832SDavid du Colombier 		break;
239458db832SDavid du Colombier 	default:
240458db832SDavid du Colombier 		if(i >= 2*Maxpart)
241458db832SDavid du Colombier 			return -1;
242458db832SDavid du Colombier 		fp = &part[i>>1];
243458db832SDavid du Colombier 		if(fp->name == nil)
244458db832SDavid du Colombier 			return 0;
245458db832SDavid du Colombier 		if(i & 1){
246458db832SDavid du Colombier 			q.path = FQID(i>>1, Qfdata);
247458db832SDavid du Colombier 			q.type = QTFILE;
248458db832SDavid du Colombier 			devdir(c, q, fp->name, fp->end-fp->start, eve, 0660, dp);
249458db832SDavid du Colombier 		} else {
250458db832SDavid du Colombier 			q.path = FQID(i>>1, Qfctl);
251458db832SDavid du Colombier 			q.type = QTFILE;
252458db832SDavid du Colombier 			devdir(c, q, fp->ctlname, 0, eve, 0660, dp);
253458db832SDavid du Colombier 		}
254458db832SDavid du Colombier 		break;
255458db832SDavid du Colombier 	}
256458db832SDavid du Colombier 	return 1;
257458db832SDavid du Colombier }
258458db832SDavid du Colombier 
259458db832SDavid du Colombier static Flash *
findflash(ulong addr)260458db832SDavid du Colombier findflash(ulong addr)
261458db832SDavid du Colombier {
262458db832SDavid du Colombier 	Flash *flash;
263458db832SDavid du Colombier 
264458db832SDavid du Colombier 	for (flash = flashes; flash < flashes + Nflash; flash++)
265458db832SDavid du Colombier 		if(addr >= flash->offset && addr < flash->offset + flash->size)
266458db832SDavid du Colombier 			return flash;
267458db832SDavid du Colombier 	return nil;
268458db832SDavid du Colombier }
269458db832SDavid du Colombier 
270458db832SDavid du Colombier static FPart*
findpart(char * name)271458db832SDavid du Colombier findpart(char *name)
272458db832SDavid du Colombier {
273458db832SDavid du Colombier 	int i;
274458db832SDavid du Colombier 
275458db832SDavid du Colombier 	for(i = 0; i < Maxpart; i++)
276458db832SDavid du Colombier 		if(part[i].name != nil && strcmp(name, part[i].name) == 0)
277458db832SDavid du Colombier 			break;
278458db832SDavid du Colombier 	if(i >= Maxpart)
279458db832SDavid du Colombier 		return nil;
280458db832SDavid du Colombier 	return &part[i];
281458db832SDavid du Colombier }
282458db832SDavid du Colombier 
283458db832SDavid du Colombier static void
addpart(FPart * fp,char * name,ulong start,ulong end)284458db832SDavid du Colombier addpart(FPart *fp, char *name, ulong start, ulong end)
285458db832SDavid du Colombier {
286458db832SDavid du Colombier 	int i;
287458db832SDavid du Colombier 	char ctlname[64];
288458db832SDavid du Colombier 	Flash *flash;
289458db832SDavid du Colombier 	if (start > end)
290458db832SDavid du Colombier 		error(Ebadarg);
291458db832SDavid du Colombier 	if(fp == nil){
292458db832SDavid du Colombier 		flash = findflash(start);
293458db832SDavid du Colombier 		if (flash == nil || end > flash->offset + flash->size)
294458db832SDavid du Colombier 			error(Ebadarg);
295458db832SDavid du Colombier 		start -= flash->offset;
296458db832SDavid du Colombier 		end -= flash->offset;
297458db832SDavid du Colombier 	} else {
298458db832SDavid du Colombier 		start += fp->start;
299458db832SDavid du Colombier 		end += fp->start;
300458db832SDavid du Colombier 		if(start >= fp->end || end > fp->end){
301458db832SDavid du Colombier 			error(Ebadarg);
302458db832SDavid du Colombier 		}
303458db832SDavid du Colombier 		flash = fp->flash;
304458db832SDavid du Colombier 	}
305458db832SDavid du Colombier 	if(blockstart(flash, start) != start)
306458db832SDavid du Colombier 		error("must start on erase boundary");
307458db832SDavid du Colombier 	if(blockstart(flash, end) != end && end != flash->size)
308458db832SDavid du Colombier 		error("must end on erase boundary");
309458db832SDavid du Colombier 
310458db832SDavid du Colombier 	fp = findpart(name);
311458db832SDavid du Colombier 	if(fp != nil)
312458db832SDavid du Colombier 		error(Eexist);
313458db832SDavid du Colombier 	for(i = 0; i < Maxpart; i++)
314458db832SDavid du Colombier 		if(part[i].name == nil)
315458db832SDavid du Colombier 			break;
316458db832SDavid du Colombier 	if(i == Maxpart)
317458db832SDavid du Colombier 		error("no more partitions");
318458db832SDavid du Colombier 	fp = &part[i];
319458db832SDavid du Colombier 	kstrdup(&fp->name, name);
320458db832SDavid du Colombier 	snprint(ctlname, sizeof ctlname, "%sctl", name);
321458db832SDavid du Colombier 	kstrdup(&fp->ctlname, ctlname);
322458db832SDavid du Colombier 	fp->flash = flash;
323458db832SDavid du Colombier 	fp->start = start;
324458db832SDavid du Colombier 	fp->end = end;
325458db832SDavid du Colombier }
326458db832SDavid du Colombier 
327458db832SDavid du Colombier static void
rempart(FPart * fp)328458db832SDavid du Colombier rempart(FPart *fp)
329458db832SDavid du Colombier {
330458db832SDavid du Colombier 	char *p, *cp;
331458db832SDavid du Colombier 
332458db832SDavid du Colombier 	p = fp->name;
333458db832SDavid du Colombier 	fp->name = nil;
334458db832SDavid du Colombier 	cp = fp->ctlname;
335458db832SDavid du Colombier 	fp->ctlname = nil;
336458db832SDavid du Colombier 	free(p);
337458db832SDavid du Colombier 	free(cp);
338458db832SDavid du Colombier }
339458db832SDavid du Colombier 
340458db832SDavid du Colombier void
flashinit(void)341458db832SDavid du Colombier flashinit(void)
342458db832SDavid du Colombier {
343458db832SDavid du Colombier 	int i, ctlrno;
344458db832SDavid du Colombier 	char *fname;
345458db832SDavid du Colombier 	ulong offset;
346458db832SDavid du Colombier 	Flash *flash;
347458db832SDavid du Colombier 
348458db832SDavid du Colombier 	offset = 0;
349458db832SDavid du Colombier 	for (ctlrno = 0; ctlrno < Nflash; ctlrno++){
350458db832SDavid du Colombier 		flash = flashes + ctlrno;
351458db832SDavid du Colombier 		if(isaconfig("flash", ctlrno, flash) == 0)
352458db832SDavid du Colombier 			continue;
353458db832SDavid du Colombier 		flash->p = (Fword*)flash->mem;
354458db832SDavid du Colombier 		cfiquery(flash);
355458db832SDavid du Colombier 		for(i = 0; i < nelem(falg); i++)
356458db832SDavid du Colombier 			if(flash->algid == falg[i].id){
357458db832SDavid du Colombier 				flash->alg = &falg[i];
358458db832SDavid du Colombier 				(*flash->alg->identify)(flash);
359458db832SDavid du Colombier 				break;
360458db832SDavid du Colombier 			}
361458db832SDavid du Colombier 		flash->bootprotect = 1;
362458db832SDavid du Colombier 		flash->offset = offset;
363458db832SDavid du Colombier 		fname = malloc(8);
364458db832SDavid du Colombier 		sprint(fname, "flash%d", ctlrno);
365458db832SDavid du Colombier 		addpart(nil, fname, offset, offset + flash->size);
366458db832SDavid du Colombier 		offset += flash->size;
367458db832SDavid du Colombier 	}
368458db832SDavid du Colombier }
369458db832SDavid du Colombier 
370458db832SDavid du Colombier static Chan*
flashattach(char * spec)371458db832SDavid du Colombier flashattach(char* spec)
372458db832SDavid du Colombier {
373458db832SDavid du Colombier 	return devattach('F', spec);
374458db832SDavid du Colombier }
375458db832SDavid du Colombier 
376458db832SDavid du Colombier static Walkqid*
flashwalk(Chan * c,Chan * nc,char ** name,int nname)377458db832SDavid du Colombier flashwalk(Chan *c, Chan *nc, char **name, int nname)
378458db832SDavid du Colombier {
379458db832SDavid du Colombier 	return devwalk(c, nc, name, nname, nil, 0, gen);
380458db832SDavid du Colombier }
381458db832SDavid du Colombier 
382458db832SDavid du Colombier static int
flashstat(Chan * c,uchar * db,int n)383458db832SDavid du Colombier flashstat(Chan *c, uchar *db, int n)
384458db832SDavid du Colombier {
385458db832SDavid du Colombier 	return devstat(c, db, n, nil, 0, gen);
386458db832SDavid du Colombier }
387458db832SDavid du Colombier 
388458db832SDavid du Colombier static Chan*
flashopen(Chan * c,int omode)389458db832SDavid du Colombier flashopen(Chan* c, int omode)
390458db832SDavid du Colombier {
391458db832SDavid du Colombier 	omode = openmode(omode);
392458db832SDavid du Colombier 	if(strcmp(up->user, eve)!=0)
393458db832SDavid du Colombier 		error(Eperm);
394458db832SDavid du Colombier 	return devopen(c, omode, nil, 0, gen);
395458db832SDavid du Colombier }
396458db832SDavid du Colombier 
397458db832SDavid du Colombier static void
flashclose(Chan *)398458db832SDavid du Colombier flashclose(Chan*)
399458db832SDavid du Colombier {
400458db832SDavid du Colombier }
401458db832SDavid du Colombier 
402458db832SDavid du Colombier static long
flashctlread(FPart * fp,void * a,long n,vlong off)403458db832SDavid du Colombier flashctlread(FPart *fp, void* a, long n, vlong off)
404458db832SDavid du Colombier {
405458db832SDavid du Colombier 	char *buf, *p, *e;
406458db832SDavid du Colombier 	int i;
407458db832SDavid du Colombier 	ulong addr, end;
408458db832SDavid du Colombier 	Flash *flash;
409458db832SDavid du Colombier 
410458db832SDavid du Colombier 	flash = fp->flash;
411458db832SDavid du Colombier 	buf = smalloc(1024);
412458db832SDavid du Colombier 	e = buf + 1024;
413458db832SDavid du Colombier 	p = seprint(buf, e, "0x%-9lux 0x%-9lux 0x%-9lux 0x%-9x 0x%-9ux 0x%-9ux\n",
414458db832SDavid du Colombier 		flash->offset, fp->start, fp->end-fp->start, flash->wbsize, flash->manid, flash->devid);
415458db832SDavid du Colombier 	addr = fp->start;
416458db832SDavid du Colombier 	for(i = 0; i < flash->nr && addr < fp->end; i++)
417458db832SDavid du Colombier 		if(flash->r[i].addr <= addr && flash->r[i].end > addr){
418458db832SDavid du Colombier 			if(fp->end <= flash->r[i].end)
419458db832SDavid du Colombier 				end = fp->end;
420458db832SDavid du Colombier 			else
421458db832SDavid du Colombier 				end = flash->r[i].end;
422458db832SDavid du Colombier 			p = seprint(p, e, "0x%-9lux 0x%-9lux 0x%-9lux\n", addr,
423458db832SDavid du Colombier 				(end-addr)/flash->r[i].size, flash->r[i].size);
424458db832SDavid du Colombier 			addr = end;
425458db832SDavid du Colombier 		}
426458db832SDavid du Colombier 	n = readstr(off, a, n, buf);
427458db832SDavid du Colombier 	free(buf);
428458db832SDavid du Colombier 	return n;
429458db832SDavid du Colombier }
430458db832SDavid du Colombier 
431458db832SDavid du Colombier static long
flashdataread(FPart * fp,void * a,long n,vlong off)432458db832SDavid du Colombier flashdataread(FPart *fp, void* a, long n, vlong off)
433458db832SDavid du Colombier {
434458db832SDavid du Colombier 	Flash *flash;
435458db832SDavid du Colombier 
436458db832SDavid du Colombier 	flash = fp->flash;
437458db832SDavid du Colombier 	rlock(flash);
438458db832SDavid du Colombier 	if(waserror()){
439458db832SDavid du Colombier 		runlock(flash);
440458db832SDavid du Colombier 		nexterror();
441458db832SDavid du Colombier 	}
442458db832SDavid du Colombier 	if(fp->name == nil)
443458db832SDavid du Colombier 		error("partition vanished");
444458db832SDavid du Colombier 	if(!iseve())
445458db832SDavid du Colombier 		error(Eperm);
446458db832SDavid du Colombier 	off += fp->start;
447458db832SDavid du Colombier 	if(off >= fp->end)
448458db832SDavid du Colombier 		n = 0;
449458db832SDavid du Colombier 	if(off+n >= fp->end)
450458db832SDavid du Colombier 		n = fp->end - off;
451458db832SDavid du Colombier 	if(n > 0)
452458db832SDavid du Colombier 		memmove(a, ((uchar*)flash->mem)+off, n);
453458db832SDavid du Colombier 	runlock(flash);
454458db832SDavid du Colombier 	poperror();
455458db832SDavid du Colombier 
456458db832SDavid du Colombier 	return n;
457458db832SDavid du Colombier }
458458db832SDavid du Colombier 
459458db832SDavid du Colombier static long
flashread(Chan * c,void * a,long n,vlong off)460458db832SDavid du Colombier flashread(Chan* c, void* a, long n, vlong off)
461458db832SDavid du Colombier {
462458db832SDavid du Colombier 	int t;
463458db832SDavid du Colombier 
464458db832SDavid du Colombier 	if(c->qid.type == QTDIR)
465458db832SDavid du Colombier 		return devdirread(c, a, n, nil, 0, gen);
466458db832SDavid du Colombier 	t = FTYPE(c->qid.path);
467458db832SDavid du Colombier 	switch(t){
468458db832SDavid du Colombier 	default:
469458db832SDavid du Colombier 		error(Eperm);
470458db832SDavid du Colombier 	case Qfctl:
471458db832SDavid du Colombier 		n = flashctlread(FPART(c->qid.path), a, n, off);
472458db832SDavid du Colombier 		break;
473458db832SDavid du Colombier 	case Qfdata:
474458db832SDavid du Colombier 		n = flashdataread(FPART(c->qid.path), a, n, off);
475458db832SDavid du Colombier 		break;
476458db832SDavid du Colombier 	}
477458db832SDavid du Colombier 	return n;
478458db832SDavid du Colombier }
479458db832SDavid du Colombier 
480458db832SDavid du Colombier static void
bootprotect(ulong addr)481458db832SDavid du Colombier bootprotect(ulong addr)
482458db832SDavid du Colombier {
483458db832SDavid du Colombier 	FlashRegion *r;
484458db832SDavid du Colombier 	Flash *flash;
485458db832SDavid du Colombier 
486458db832SDavid du Colombier 	flash = findflash(addr);
487458db832SDavid du Colombier 	if (flash == nil)
488458db832SDavid du Colombier 		error(Ebadarg);
489458db832SDavid du Colombier 	if(flash->bootprotect == 0)
490458db832SDavid du Colombier 		return;
491458db832SDavid du Colombier 	if(flash->nr == 0)
492458db832SDavid du Colombier 		error("writing over boot loader disallowed");
493458db832SDavid du Colombier 	r = flash->r;
494458db832SDavid du Colombier 	if(addr >= r->addr && addr < r->addr + r->size)
495458db832SDavid du Colombier 		error("writing over boot loader disallowed");
496458db832SDavid du Colombier }
497458db832SDavid du Colombier 
498458db832SDavid du Colombier static ulong
blockstart(Flash * flash,ulong addr)499458db832SDavid du Colombier blockstart(Flash *flash, ulong addr)
500458db832SDavid du Colombier {
501458db832SDavid du Colombier 	FlashRegion *r, *e;
502458db832SDavid du Colombier 	ulong x;
503458db832SDavid du Colombier 
504458db832SDavid du Colombier 	r = flash->r;
505458db832SDavid du Colombier 	for(e = &flash->r[flash->nr]; r < e; r++){
506458db832SDavid du Colombier 		if(addr >= r->addr && addr < r->end){
507458db832SDavid du Colombier 			x = addr - r->addr;
508458db832SDavid du Colombier 			x /= r->size;
509458db832SDavid du Colombier 			return r->addr + x*r->size;
510458db832SDavid du Colombier 		}
511458db832SDavid du Colombier 	}
512458db832SDavid du Colombier 
513458db832SDavid du Colombier 	return (ulong)-1;
514458db832SDavid du Colombier }
515458db832SDavid du Colombier 
516458db832SDavid du Colombier static ulong
blockend(Flash * flash,ulong addr)517458db832SDavid du Colombier blockend(Flash *flash, ulong addr)
518458db832SDavid du Colombier {
519458db832SDavid du Colombier 	FlashRegion *r, *e;
520458db832SDavid du Colombier 	ulong x;
521458db832SDavid du Colombier 
522458db832SDavid du Colombier 	r = flash->r;
523458db832SDavid du Colombier 	for(e = &flash->r[flash->nr]; r < e; r++)
524458db832SDavid du Colombier 		if(addr >= r->addr && addr < r->end){
525458db832SDavid du Colombier 			x = addr - r->addr;
526458db832SDavid du Colombier 			x /= r->size;
527458db832SDavid du Colombier 			return r->addr + (x+1)*r->size;
528458db832SDavid du Colombier 		}
529458db832SDavid du Colombier 
530458db832SDavid du Colombier 	return (ulong)-1;
531458db832SDavid du Colombier }
532458db832SDavid du Colombier 
533458db832SDavid du Colombier static long
flashctlwrite(FPart * fp,char * p,long n)534458db832SDavid du Colombier flashctlwrite(FPart *fp, char *p, long n)
535458db832SDavid du Colombier {
536458db832SDavid du Colombier 	Cmdbuf *cmd;
537458db832SDavid du Colombier 	ulong off;
538458db832SDavid du Colombier 	Flash *flash;
539458db832SDavid du Colombier 
540458db832SDavid du Colombier 	if(fp == nil)
541458db832SDavid du Colombier 		panic("flashctlwrite");
542458db832SDavid du Colombier 
543458db832SDavid du Colombier 	flash = fp->flash;
544458db832SDavid du Colombier 	cmd = parsecmd(p, n);
545458db832SDavid du Colombier 	wlock(flash);
546458db832SDavid du Colombier 	if(waserror()){
547458db832SDavid du Colombier 		wunlock(flash);
548458db832SDavid du Colombier 		nexterror();
549458db832SDavid du Colombier 	}
550458db832SDavid du Colombier 	if(strcmp(cmd->f[0], "erase") == 0){
551458db832SDavid du Colombier 		switch(cmd->nf){
552458db832SDavid du Colombier 		case 2:
553458db832SDavid du Colombier 			/* erase a single block in the partition */
554458db832SDavid du Colombier 			off = atoi(cmd->f[1]);
555458db832SDavid du Colombier 			off += fp->start;
556458db832SDavid du Colombier 			if(off >= fp->end)
557458db832SDavid du Colombier 				error("region not in partition");
558458db832SDavid du Colombier 			if(off != blockstart(flash, off))
559458db832SDavid du Colombier 				error("erase must be a block boundary");
560458db832SDavid du Colombier 			bootprotect(off);
561458db832SDavid du Colombier 			(*flash->alg->erase)(flash, off);
562458db832SDavid du Colombier 			break;
563458db832SDavid du Colombier 		case 1:
564458db832SDavid du Colombier 			/* erase the whole partition */
565458db832SDavid du Colombier 			bootprotect(fp->start);
566458db832SDavid du Colombier 			for(off = fp->start; off < fp->end; off = blockend(flash, off))
567458db832SDavid du Colombier 				(*flash->alg->erase)(flash, off);
568458db832SDavid du Colombier 			break;
569458db832SDavid du Colombier 		default:
570458db832SDavid du Colombier 			error(Ebadarg);
571458db832SDavid du Colombier 		}
572458db832SDavid du Colombier 	} else if(strcmp(cmd->f[0], "add") == 0){
573458db832SDavid du Colombier 		if(cmd->nf != 4)
574458db832SDavid du Colombier 			error(Ebadarg);
575458db832SDavid du Colombier 		addpart(fp, cmd->f[1], strtoul(cmd->f[2], nil, 0), strtoul(cmd->f[3], nil, 0));
576458db832SDavid du Colombier 	} else if(strcmp(cmd->f[0], "remove") == 0){
577458db832SDavid du Colombier 		rempart(fp);
578458db832SDavid du Colombier 	} else if(strcmp(cmd->f[0], "protectboot") == 0){
579458db832SDavid du Colombier 		if(cmd->nf == 0 || strcmp(cmd->f[1], "off") != 0)
580458db832SDavid du Colombier 			flash->bootprotect = 1;
581458db832SDavid du Colombier 		else
582458db832SDavid du Colombier 			flash->bootprotect = 0;
583458db832SDavid du Colombier 	} else
584458db832SDavid du Colombier 		error(Ebadarg);
585458db832SDavid du Colombier 	poperror();
586458db832SDavid du Colombier 	wunlock(flash);
587458db832SDavid du Colombier 	free(cmd);
588458db832SDavid du Colombier 
589458db832SDavid du Colombier 	return n;
590458db832SDavid du Colombier }
591458db832SDavid du Colombier 
592458db832SDavid du Colombier static long
flashdatawrite(FPart * fp,uchar * p,long n,long off)593458db832SDavid du Colombier flashdatawrite(FPart *fp, uchar *p, long n, long off)
594458db832SDavid du Colombier {
595458db832SDavid du Colombier 	uchar *end;
596458db832SDavid du Colombier 	int m;
597458db832SDavid du Colombier 	int on;
598458db832SDavid du Colombier 	long ooff;
599458db832SDavid du Colombier 	uchar *buf;
600458db832SDavid du Colombier 	Flash *flash;
601458db832SDavid du Colombier 
602458db832SDavid du Colombier 	if(fp == nil)
603458db832SDavid du Colombier 		panic("flashdatawrite");
604458db832SDavid du Colombier 
605458db832SDavid du Colombier 	flash = fp->flash;
606458db832SDavid du Colombier 	buf = nil;
607458db832SDavid du Colombier 	wlock(flash);
608458db832SDavid du Colombier 	if(waserror()){
609458db832SDavid du Colombier 		wunlock(flash);
610458db832SDavid du Colombier 		if(buf != nil)
611458db832SDavid du Colombier 			free(buf);
612458db832SDavid du Colombier 		nexterror();
613458db832SDavid du Colombier 	}
614458db832SDavid du Colombier 
615458db832SDavid du Colombier 	if(fp->name == nil)
616458db832SDavid du Colombier 		error("partition vanished");
617458db832SDavid du Colombier 	if(!iseve())
618458db832SDavid du Colombier 		error(Eperm);
619458db832SDavid du Colombier 
620458db832SDavid du Colombier 	/* can't cross partition boundaries */
621458db832SDavid du Colombier 	off += fp->start;
622458db832SDavid du Colombier 	if(off >= fp->end || off+n > fp->end || n <= 0)
623458db832SDavid du Colombier 		error(Ebadarg);
624458db832SDavid du Colombier 
625458db832SDavid du Colombier 	/* make sure we're not writing the boot sector */
626458db832SDavid du Colombier 	bootprotect(off);
627458db832SDavid du Colombier 
628458db832SDavid du Colombier 	on = n;
629458db832SDavid du Colombier 
630458db832SDavid du Colombier 	/*
631458db832SDavid du Colombier 	 *  get the data into kernel memory to avoid faults during writing.
632458db832SDavid du Colombier 	 *  if write is not on a quad boundary or not a multiple of 4 bytes,
633458db832SDavid du Colombier 	 *  extend with data already in flash.
634458db832SDavid du Colombier 	 */
635458db832SDavid du Colombier 	buf = smalloc(n+8);
636458db832SDavid du Colombier 	m = off & 3;
637458db832SDavid du Colombier 	if(m){
638458db832SDavid du Colombier 		*(ulong*)buf = flash->p[off>>Wshift];
639458db832SDavid du Colombier 		n += m;
640458db832SDavid du Colombier 		off -= m;
641458db832SDavid du Colombier 	}
642458db832SDavid du Colombier 	if(n & 3){
643458db832SDavid du Colombier 		n -= n & 3;
644458db832SDavid du Colombier 		*(ulong*)(&buf[n]) = flash->p[(off+n)>>Wshift];
645458db832SDavid du Colombier 		n += 4;
646458db832SDavid du Colombier 	}
647458db832SDavid du Colombier 	memmove(&buf[m], p, on);
648458db832SDavid du Colombier 
649458db832SDavid du Colombier 	/* (*flash->alg->write) can't cross blocks */
650458db832SDavid du Colombier 	ooff = off;
651458db832SDavid du Colombier 	p = buf;
652458db832SDavid du Colombier 	for(end = p + n; p < end; p += m){
653458db832SDavid du Colombier 		m = blockend(flash, off) - off;
654458db832SDavid du Colombier 		if(m > end - p)
655458db832SDavid du Colombier 			m = end - p;
656458db832SDavid du Colombier 		if(m > Maxwchunk)
657458db832SDavid du Colombier 			m = Maxwchunk;
658458db832SDavid du Colombier 		(*flash->alg->write)(flash, p, m, off);
659458db832SDavid du Colombier 		off += m;
660458db832SDavid du Colombier 	}
661458db832SDavid du Colombier 
662458db832SDavid du Colombier 	/* make sure write succeeded */
663458db832SDavid du Colombier 	if(memcmp(buf, &flash->p[ooff>>Wshift], n) != 0)
664458db832SDavid du Colombier 		error("written bytes don't match");
665458db832SDavid du Colombier 
666458db832SDavid du Colombier 	wunlock(flash);
667458db832SDavid du Colombier 	free(buf);
668458db832SDavid du Colombier 	poperror();
669458db832SDavid du Colombier 
670458db832SDavid du Colombier 	return on;
671458db832SDavid du Colombier }
672458db832SDavid du Colombier 
673458db832SDavid du Colombier static long
flashwrite(Chan * c,void * a,long n,vlong off)674458db832SDavid du Colombier flashwrite(Chan* c, void* a, long n, vlong off)
675458db832SDavid du Colombier {
676458db832SDavid du Colombier 	int t;
677458db832SDavid du Colombier 
678458db832SDavid du Colombier 	if(c->qid.type == QTDIR)
679458db832SDavid du Colombier 		error(Eperm);
680458db832SDavid du Colombier 
681458db832SDavid du Colombier 	if(!iseve())
682458db832SDavid du Colombier 		error(Eperm);
683458db832SDavid du Colombier 
684458db832SDavid du Colombier 	t = FTYPE(c->qid.path);
685458db832SDavid du Colombier 	switch(t){
686458db832SDavid du Colombier 	default:
687458db832SDavid du Colombier 		panic("flashwrite");
688458db832SDavid du Colombier 	case Qfctl:
689458db832SDavid du Colombier 		n = flashctlwrite(FPART(c->qid.path), a, n);
690458db832SDavid du Colombier 		break;
691458db832SDavid du Colombier 	case Qfdata:
692458db832SDavid du Colombier 		n = flashdatawrite(FPART(c->qid.path), a, n, off);
693458db832SDavid du Colombier 		break;
694458db832SDavid du Colombier 	}
695458db832SDavid du Colombier 	return n;
696458db832SDavid du Colombier }
697458db832SDavid du Colombier 
698458db832SDavid du Colombier Dev flashdevtab = {
699458db832SDavid du Colombier 	'F',
700458db832SDavid du Colombier 	"flash",
701458db832SDavid du Colombier 
702458db832SDavid du Colombier 	devreset,
703458db832SDavid du Colombier 	flashinit,
704458db832SDavid du Colombier 	devshutdown,
705458db832SDavid du Colombier 	flashattach,
706458db832SDavid du Colombier 	flashwalk,
707458db832SDavid du Colombier 	flashstat,
708458db832SDavid du Colombier 	flashopen,
709458db832SDavid du Colombier 	devcreate,
710458db832SDavid du Colombier 	flashclose,
711458db832SDavid du Colombier 	flashread,
712458db832SDavid du Colombier 	devbread,
713458db832SDavid du Colombier 	flashwrite,
714458db832SDavid du Colombier 	devbwrite,
715458db832SDavid du Colombier 	devremove,
716458db832SDavid du Colombier 	devwstat,
717458db832SDavid du Colombier };
718458db832SDavid du Colombier 
719458db832SDavid du Colombier enum
720458db832SDavid du Colombier {
721458db832SDavid du Colombier 	/* status register */
722458db832SDavid du Colombier 	ISEs_lockerr=		1<<1,
723458db832SDavid du Colombier 	ISEs_powererr=		1<<3,
724458db832SDavid du Colombier 	ISEs_progerr=		1<<4,
725458db832SDavid du Colombier 	ISEs_eraseerr=		1<<5,
726458db832SDavid du Colombier 	ISEs_ready=		1<<7,
727458db832SDavid du Colombier 	ISEs_err= (ISEs_lockerr|ISEs_powererr|ISEs_progerr|ISEs_eraseerr),
728458db832SDavid du Colombier 
729458db832SDavid du Colombier 	/* extended status register */
730458db832SDavid du Colombier 	ISExs_bufavail=		1<<7,
731458db832SDavid du Colombier };
732458db832SDavid du Colombier 
733458db832SDavid du Colombier /* intel/sharp extended command set */
734458db832SDavid du Colombier static void
ise_reset(Flash * flash)735458db832SDavid du Colombier ise_reset(Flash* flash)
736458db832SDavid du Colombier {
737458db832SDavid du Colombier 	flash->p[reg(0xaa)] = mirror(0xff);	/* reset */
738458db832SDavid du Colombier }
739458db832SDavid du Colombier 
740458db832SDavid du Colombier static void
ise_id(Flash * flash)741458db832SDavid du Colombier ise_id(Flash* flash)
742458db832SDavid du Colombier {
743458db832SDavid du Colombier 	ise_reset(flash);
744458db832SDavid du Colombier 	flash->p[reg(0xaaa)] = mirror(0x90);	/* uncover vendor info */
745458db832SDavid du Colombier 	flash->manid = fromendian(flash->p[reg(0x0)]);
746458db832SDavid du Colombier 	flash->devid = fromendian(flash->p[reg(0x1)]);
747458db832SDavid du Colombier 	ise_reset(flash);
748458db832SDavid du Colombier }
749458db832SDavid du Colombier 
750458db832SDavid du Colombier static void
ise_clearerror(Flash * flash)751458db832SDavid du Colombier ise_clearerror(Flash* flash)
752458db832SDavid du Colombier {
753458db832SDavid du Colombier 	flash->p[reg(0x200)] = mirror(0x50);
754458db832SDavid du Colombier 
755458db832SDavid du Colombier }
756458db832SDavid du Colombier 
757458db832SDavid du Colombier static void
ise_error(int bank,ulong status)758458db832SDavid du Colombier ise_error(int bank, ulong status)
759458db832SDavid du Colombier {
760458db832SDavid du Colombier 	char err[64];
761458db832SDavid du Colombier 
762458db832SDavid du Colombier 	if(status & (ISEs_lockerr)){
763458db832SDavid du Colombier 		sprint(err, "flash%d: block locked %lux", bank, status);
764458db832SDavid du Colombier 		error(err);
765458db832SDavid du Colombier 	}
766458db832SDavid du Colombier 	if(status & (ISEs_powererr)){
767458db832SDavid du Colombier 		sprint(err, "flash%d: low prog voltage %lux", bank, status);
768458db832SDavid du Colombier 		error(err);
769458db832SDavid du Colombier 	}
770458db832SDavid du Colombier 	if(status & (ISEs_progerr|ISEs_eraseerr)){
771458db832SDavid du Colombier 		sprint(err, "flash%d: i/o error %lux", bank, status);
772458db832SDavid du Colombier 		error(err);
773458db832SDavid du Colombier 	}
774458db832SDavid du Colombier }
775458db832SDavid du Colombier static void
ise_erase(Flash * flash,ulong addr)776458db832SDavid du Colombier ise_erase(Flash *flash, ulong addr)
777458db832SDavid du Colombier {
778458db832SDavid du Colombier 	ulong start;
779458db832SDavid du Colombier 	ulong x;
780458db832SDavid du Colombier 
781458db832SDavid du Colombier 	addr >>= Wshift;
782458db832SDavid du Colombier 
783458db832SDavid du Colombier 	flashprogpower(1);
784458db832SDavid du Colombier 	flash->p[addr] = mirror(0x20);
785458db832SDavid du Colombier 	flash->p[addr] = mirror(0xd0);
786458db832SDavid du Colombier 	start = m->ticks;
787458db832SDavid du Colombier 	do {
788458db832SDavid du Colombier 		x = fromendian(flash->p[addr]);
789458db832SDavid du Colombier 		if((x & mirror(ISEs_ready)) == mirror(ISEs_ready))
790458db832SDavid du Colombier 			break;
791458db832SDavid du Colombier 	} while(TK2MS(m->ticks-start) < 1500);
792458db832SDavid du Colombier 	flashprogpower(0);
793458db832SDavid du Colombier 
794458db832SDavid du Colombier 	ise_clearerror(flash);
795458db832SDavid du Colombier 	ise_error(0, x);
796458db832SDavid du Colombier 	ise_error(1, x>>16);
797458db832SDavid du Colombier 
798458db832SDavid du Colombier 	ise_reset(flash);
799458db832SDavid du Colombier }
800458db832SDavid du Colombier /*
801458db832SDavid du Colombier  *  the flash spec claimes writing goes faster if we use
802458db832SDavid du Colombier  *  the write buffer.  We fill the write buffer and then
803458db832SDavid du Colombier  *  issue the write request.  After the write request,
804458db832SDavid du Colombier  *  subsequent reads will yield the status register.
805458db832SDavid du Colombier  *
806458db832SDavid du Colombier  *  returns the status, even on timeouts.
807458db832SDavid du Colombier  *
808458db832SDavid du Colombier  *  NOTE: I tried starting back to back buffered writes
809458db832SDavid du Colombier  *	without reading the status in between, as the
810458db832SDavid du Colombier  *	flowchart in the intel data sheet suggests.
811458db832SDavid du Colombier  *	However, it always responded with an illegal
812458db832SDavid du Colombier  *	command sequence, so I must be missing something.
813458db832SDavid du Colombier  *	If someone learns better, please email me, though
814458db832SDavid du Colombier  *	I doubt it will be much faster. -  presotto@bell-labs.com
815458db832SDavid du Colombier  */
816*2cca75a1SDavid du Colombier static long
ise_wbwrite(Flash * flash,Fword * p,int n,ulong off,ulong baddr,ulong * status)817458db832SDavid du Colombier ise_wbwrite(Flash *flash, Fword *p, int n, ulong off, ulong baddr, ulong *status)
818458db832SDavid du Colombier {
819458db832SDavid du Colombier 	Fword x;
820458db832SDavid du Colombier 	ulong start;
821458db832SDavid du Colombier 	int i;
822458db832SDavid du Colombier 	int s;
823458db832SDavid du Colombier 
824458db832SDavid du Colombier 	/* put flash into write buffer mode */
825458db832SDavid du Colombier 	start = m->ticks;
826458db832SDavid du Colombier 	for(;;) {
827458db832SDavid du Colombier 		s = splhi();
828458db832SDavid du Colombier 		/* request write buffer mode */
829458db832SDavid du Colombier 		flash->p[baddr] = mirror(0xe8);
830458db832SDavid du Colombier 
831458db832SDavid du Colombier 		/* look at extended status reg for status */
832458db832SDavid du Colombier 		if((flash->p[baddr] & mirror(1<<7)) == mirror(1<<7))
833458db832SDavid du Colombier 			break;
834458db832SDavid du Colombier 		splx(s);
835458db832SDavid du Colombier 
836458db832SDavid du Colombier 		/* didn't work, keep trying for 2 secs */
837458db832SDavid du Colombier 		if(TK2MS(m->ticks-start) > 2000){
838458db832SDavid du Colombier 			/* set up to read status */
839458db832SDavid du Colombier 			flash->p[baddr] = mirror(0x70);
840458db832SDavid du Colombier 			*status = fromendian(flash->p[baddr]);
841458db832SDavid du Colombier 			pprint("write buffered cmd timed out\n");
842458db832SDavid du Colombier 			return -1;
843458db832SDavid du Colombier 		}
844458db832SDavid du Colombier 	}
845458db832SDavid du Colombier 
846458db832SDavid du Colombier 	/* fill write buffer */
847458db832SDavid du Colombier 	flash->p[baddr] = mirror(n-1);
848458db832SDavid du Colombier 	for(i = 0; i < n; i++)
849458db832SDavid du Colombier 		flash->p[off+i] = *p++;
850458db832SDavid du Colombier 
851458db832SDavid du Colombier 	/* program from buffer */
852458db832SDavid du Colombier 	flash->p[baddr] = mirror(0xd0);
853458db832SDavid du Colombier 	splx(s);
854458db832SDavid du Colombier 
855458db832SDavid du Colombier 	/* wait till the programming is done */
856458db832SDavid du Colombier 	start = m->ticks;
857458db832SDavid du Colombier 	for(;;) {
858458db832SDavid du Colombier 		x = flash->p[baddr];	/* read status register */
859458db832SDavid du Colombier 		*status = fromendian(x);
860458db832SDavid du Colombier 		if((x & mirror(ISEs_ready)) == mirror(ISEs_ready))
861458db832SDavid du Colombier 			break;
862458db832SDavid du Colombier 		if(TK2MS(m->ticks-start) > 2000){
863458db832SDavid du Colombier 			pprint("read status timed out\n");
864458db832SDavid du Colombier 			return -1;
865458db832SDavid du Colombier 		}
866458db832SDavid du Colombier 	}
867458db832SDavid du Colombier 	if(x & mirror(ISEs_err))
868458db832SDavid du Colombier 		return -1;
869458db832SDavid du Colombier 
870458db832SDavid du Colombier 	return n;
871458db832SDavid du Colombier }
872458db832SDavid du Colombier 
873458db832SDavid du Colombier static void
ise_write(Flash * flash,void * a,long n,ulong off)874458db832SDavid du Colombier ise_write(Flash *flash, void *a, long n, ulong off)
875458db832SDavid du Colombier {
876458db832SDavid du Colombier 	Fword *p, *end;
877458db832SDavid du Colombier 	int i, wbsize;
878458db832SDavid du Colombier 	ulong x, baddr;
879458db832SDavid du Colombier 
880458db832SDavid du Colombier  	/* everything in terms of Fwords */
881458db832SDavid du Colombier 	wbsize = flash->wbsize >> Wshift;
882458db832SDavid du Colombier 	baddr = blockstart(flash, off) >> Wshift;
883458db832SDavid du Colombier 	off >>= Wshift;
884458db832SDavid du Colombier 	n >>= Wshift;
885458db832SDavid du Colombier 	p = a;
886458db832SDavid du Colombier 
887458db832SDavid du Colombier 	/* first see if write will succeed */
888458db832SDavid du Colombier 	for(i = 0; i < n; i++)
889458db832SDavid du Colombier 		if((p[i] & flash->p[off+i]) != p[i])
890458db832SDavid du Colombier 			error("flash needs erase");
891458db832SDavid du Colombier 
892458db832SDavid du Colombier 	if(waserror()){
893458db832SDavid du Colombier 		ise_reset(flash);
894458db832SDavid du Colombier 		flashprogpower(0);
895458db832SDavid du Colombier 		nexterror();
896458db832SDavid du Colombier 	}
897458db832SDavid du Colombier 	flashprogpower(1);
898458db832SDavid du Colombier 
899458db832SDavid du Colombier 	/*
900458db832SDavid du Colombier 	 *  use the first write to reach
901458db832SDavid du Colombier  	 *  a write buffer boundary.  the intel maunal
902458db832SDavid du Colombier 	 *  says writes starting at wb boundaries
903458db832SDavid du Colombier 	 *  maximize speed.
904458db832SDavid du Colombier 	 */
905458db832SDavid du Colombier 	i = wbsize - (off & (wbsize-1));
906458db832SDavid du Colombier 	for(end = p + n; p < end;){
907458db832SDavid du Colombier 		if(i > end - p)
908458db832SDavid du Colombier 			i = end - p;
909458db832SDavid du Colombier 
910458db832SDavid du Colombier 		if(ise_wbwrite(flash, p, i, off, baddr, &x) < 0)
911458db832SDavid du Colombier 			break;
912458db832SDavid du Colombier 
913458db832SDavid du Colombier 		off += i;
914458db832SDavid du Colombier 		p += i;
915458db832SDavid du Colombier 		i = wbsize;
916458db832SDavid du Colombier 	}
917458db832SDavid du Colombier 
918458db832SDavid du Colombier 	ise_clearerror(flash);
919458db832SDavid du Colombier 	ise_error(0, x);
920458db832SDavid du Colombier 	ise_error(1, x>>16);
921458db832SDavid du Colombier 
922458db832SDavid du Colombier 	ise_reset(flash);
923458db832SDavid du Colombier 	flashprogpower(0);
924458db832SDavid du Colombier 	poperror();
925458db832SDavid du Colombier }
926458db832SDavid du Colombier 
927458db832SDavid du Colombier /* amd/fujitsu standard command set
928458db832SDavid du Colombier  *	I don't have an amd chipset to work with
929458db832SDavid du Colombier  *	so I'm loathe to write this yet.  If someone
930458db832SDavid du Colombier  *	else does, please send it to me and I'll
931458db832SDavid du Colombier  *	incorporate it -- presotto@bell-labs.com
932458db832SDavid du Colombier  */
933458db832SDavid du Colombier static void
afs_reset(Flash * flash)934458db832SDavid du Colombier afs_reset(Flash *flash)
935458db832SDavid du Colombier {
936458db832SDavid du Colombier 	flash->p[reg(0xaa)] = mirror(0xf0);	/* reset */
937458db832SDavid du Colombier }
938458db832SDavid du Colombier static void
afs_id(Flash * flash)939458db832SDavid du Colombier afs_id(Flash *flash)
940458db832SDavid du Colombier {
941458db832SDavid du Colombier 	afs_reset(flash);
942458db832SDavid du Colombier 	flash->p[reg(0xaa)] = mirror(0xf0);	/* reset */
943458db832SDavid du Colombier 	flash->p[reg(0xaaa)] = mirror(0xaa);	/* query vendor block */
944458db832SDavid du Colombier 	flash->p[reg(0x554)] = mirror(0x55);
945458db832SDavid du Colombier 	flash->p[reg(0xaaa)] = mirror(0x90);
946458db832SDavid du Colombier 	flash->manid = fromendian(flash->p[reg(0x00)]);
947458db832SDavid du Colombier 	afs_reset(flash);
948458db832SDavid du Colombier 	flash->p[reg(0xaaa)] = mirror(0xaa);	/* query vendor block */
949458db832SDavid du Colombier 	flash->p[reg(0x554)] = mirror(0x55);
950458db832SDavid du Colombier 	flash->p[reg(0xaaa)] = mirror(0x90);
951458db832SDavid du Colombier 	flash->devid = fromendian(flash->p[reg(0x02)]);
952458db832SDavid du Colombier 	afs_reset(flash);
953458db832SDavid du Colombier }
954458db832SDavid du Colombier static void
afs_erase(Flash *,ulong)955458db832SDavid du Colombier afs_erase(Flash*, ulong)
956458db832SDavid du Colombier {
957458db832SDavid du Colombier 	error("amd/fujistsu erase not implemented");
958458db832SDavid du Colombier }
959458db832SDavid du Colombier static void
afs_write(Flash *,void *,long,ulong)960458db832SDavid du Colombier afs_write(Flash*, void*, long, ulong)
961458db832SDavid du Colombier {
962458db832SDavid du Colombier 	error("amd/fujistsu write not implemented");
963458db832SDavid du Colombier }
964