shithub: purgatorio

ref: bd6c2aad586814b091ce5aca9d41cf2c51adb37b
dir: /os/port/flashintel/

View raw version

enum {
	DQ7 = I(0x80),
	DQ6 = I(0x40),
	DQ5 = I(0x20),
	DQ4 = I(0x10),
	DQ3 = I(0x08),
	DQ2 = I(0x04),
	DQ1 = I(0x02),
	DQ0 = I(0x01),
};

/*
 * intel algorithm
 */

enum {
	ReadID = I(0x90),
	ClearStatus = I(0x50),
	ReadStatus = I(0x70),
	Program = I(0x40),
	BlockErase = I(0x20),
	Confirm = I(0xD0),
};

#define	DPRINT	if(0)print
#define	EPRINT	if(1)print

static char*
intelwait(Flash *f, void *p, ulong ticks)
{
	ulong csr, mask;

	ticks += m->ticks+1;
	mask = f->cmask;
	for(;;){
		if(f->width == 2)
			csr = *(ushort*)p;
		else
			csr = *(ulong*)p;
		if((csr & mask) == (DQ7 & mask))
			break;
		sched();
		if(m->ticks >= ticks)
			return "flash write timed out";
	}
	if(csr & (DQ5|DQ4|DQ3|DQ1)){
		EPRINT("flash: error: %8.8lux %8.8lux\n", p, csr);
		if(csr & DQ1)
			return "flash block locked";
		if(csr & DQ3)
			return "low flash programming voltage";
		return Eio;
	}
	return nil;
}

static int
intelerase(Flash *f, Flashregion *r, ulong addr)
{
	int s;
	char *e;

	DPRINT("flash: erase zone %8.8lux\n", addr);
	if(addr & (r->erasesize-1))
		return -1;	/* bad zone */
	if(f->width == 2){
		ushort *p = (ushort*)((ulong)f->addr + addr);
		s = splhi();
		*p = BlockErase & f->cmask;
		*p = Confirm & f->cmask;
		splx(s);
		e = intelwait(f, p, MS2TK(8*1000));
		*p = ClearStatus & f->cmask;
		*p = ReadArray & f->cmask;
	}else{
		ulong *p = (ulong*)((ulong)f->addr + addr);
		s = splhi();
		*p = BlockErase & f->cmask;
		*p = Confirm & f->cmask;
		splx(s);
		e = intelwait(f, p, MS2TK(8*1000));
		*p = ClearStatus & f->cmask;
		*p = ReadArray & f->cmask;
	}
	if(e != nil)
		error(e);
	return 0;
}

static int
intelwrite2(Flash *f, ulong offset, void *buf, long n)
{
	ushort *a, *v;
	ulong w;
	int s;
	char *e;

	if(((ulong)f->addr|offset|n)&(f->width-1))
		return -1;
	a = (ushort*)((ulong)f->addr + offset);
	n /= f->width;
	v = buf;
	if(waserror()){
		*a = ClearStatus & f->cmask;
		*a = ReadArray & f->cmask;
		nexterror();
	}
	for(; --n >= 0; v++, a++){
		w = *a;
		DPRINT("flash: write %p %#ulx -> %#lux\n", a, w, (ulong)*v);
		if(w == *v)
			continue;	/* already set */
		if(~w & *v)
			error("flash not erased");
		s = splhi();
		*a = Program & f->cmask;	/* program */
		*a = *v;
		splx(s);
		microdelay(8);
		e = intelwait(f, a, 5);
		*a = ClearStatus & f->cmask;
		*a = ReadArray & f->cmask;
		if(e != nil)
			error(e);
		w = *a;
		if(w != *v){
			EPRINT("flash: write %p %#8.8lux -> %#8.8lux failed\n", a, w, (ulong)*v);
			error(Eio);
		}
	}
	poperror();
	return 0;
}

static int
intelwrite4(Flash *f, ulong offset, void *buf, long n)
{
	ulong *a, *v;
	ulong w;
	int s;
	char *e;

	if(((ulong)f->addr|offset|n)&(f->width-1))
		return -1;
	a = (ulong*)((ulong)f->addr + offset);
	n /= f->width;
	v = buf;
	if(waserror()){
		*a = ClearStatus & f->cmask;
		*a = ReadArray & f->cmask;
		nexterror();
	}
	for(; --n >= 0; v++, a++){
		w = *a;
		DPRINT("flash: write %p %#ulx -> %#lux\n", a, w, (ulong)*v);
		if(w == *v)
			continue;	/* already set */
		if(~w & *v)
			error("flash not erased");
		s = splhi();
		*a = Program;	/* program */
		*a = *v;
		splx(s);
		microdelay(8);
		e = intelwait(f, a, 5);
		*a = ClearStatus & f->cmask;
		*a = ReadArray & f->cmask;
		if(e != nil)
			error(e);
		w = *a;
		if(w != *v){
			EPRINT("flash: write %p %#8.8lux -> %#8.8lux failed\n", a, w, *v);
			error(Eio);
		}
	}
	poperror();
	return 0;
}