ref: 94443daf8e248e65afc8d3f17f26efea22748b51
dir: /os/port/flashamd29f0x0.c/
#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"
/*
* AMD29F0x0 with 4 interleaved to give 32 bits
*/
enum {
DQ7 = 0x80808080,
DQ6 = 0x40404040,
DQ5 = 0x20202020,
DQ3 = 0x08080808,
DQ2 = 0x04040404,
};
#define DPRINT if(0)print
#define EPRINT if(1)print
static char*
amdwait(ulong *p, ulong ticks)
{
ulong v0, v1;
ticks += m->ticks+1;
v0 = *p;
for(;;){
sched();
v1 = *p;
if((v1 & DQ6) == (v0 & DQ6))
break;
if((v1 & DQ5) == DQ5){
v0 = *p;
v1 = *p;
if((v1 & DQ6) == (v0 & DQ6))
break;
EPRINT("flash: DQ5 error: %8.8lux %8.8lux\n", v0, v1);
return "flash write error";
}
if(m->ticks >= ticks){
EPRINT("flash: timed out: %8.8lux\n", *p);
return "flash write timed out";
}
v0 = v1;
}
return nil;
}
static int
eraseall(Flash *f)
{
ulong *p;
int s;
char *e;
DPRINT("flash: erase all\n");
p = (ulong*)f->addr;
s = splhi();
*(p+0x555) = 0xAAAAAAAA;
*(p+0x2AA) = 0x55555555;
*(p+0x555) = 0x80808080;
*(p+0x555) = 0xAAAAAAAA;
*(p+0x2AA) = 0x55555555;
*(p+0x555) = 0x10101010; /* chip erase */
splx(s);
e = amdwait(p, MS2TK(64*1000));
*p = 0xF0F0F0F0; /* reset */
if(e != nil)
error(e);
return 0;
}
static int
erasezone(Flash *f, Flashregion *r, ulong addr)
{
ulong *p;
int s;
char *e;
DPRINT("flash: erase %8.8lux\n", addr);
if(addr & (r->erasesize-1))
return -1; /* bad zone */
p = (ulong*)f->addr;
s = splhi();
*(p+0x555) = 0xAAAAAAAA;
*(p+0x2AA) = 0x55555555;
*(p+0x555) = 0x80808080;
*(p+0x555) = 0xAAAAAAAA;
*(p+0x2AA) = 0x55555555;
p += addr>>2;
*p = 0x30303030; /* sector erase */
splx(s);
e = amdwait(p, MS2TK(8*1000));
*p = 0xF0F0F0F0; /* reset */
if(e != nil)
error(e);
return 0;
}
static int
write4(Flash *f, ulong offset, void *buf, long n)
{
ulong *p, *a, *v, w;
int s;
char *e;
p = (ulong*)f->addr;
if(((ulong)p|offset|n)&3)
return -1;
n >>= 2;
a = p + (offset>>2);
v = buf;
for(; --n >= 0; v++, a++){
w = *a;
DPRINT("flash: write %lux %lux -> %lux\n", (ulong)a, w, *v);
if(w == *v)
continue; /* already set */
if(~w & *v)
error("flash not erased");
s = splhi();
*(p+0x555) = 0xAAAAAAAA;
*(p+0x2AA) = 0x55555555;
*(p+0x555) = 0xA0A0A0A0; /* program */
*a = *v;
splx(s);
microdelay(8);
if(*a != *v){
microdelay(8);
while(*a != *v){
e = amdwait(a, 1);
if(e != nil)
error(e);
}
}
}
return 0;
}
static int
reset(Flash *f)
{
f->id = 0x01; /* can't use autoselect: might be running in flash */
f->devid = 0;
f->write = write4;
f->eraseall = eraseall;
f->erasezone = erasezone;
f->suspend = nil;
f->resume = nil;
f->width = 4;
f->interleave = 0; /* TO DO */
f->nr = 1;
f->regions[0] = (Flashregion){f->size/(4*64*1024), 0, f->size, 4*64*1024, 0};
*(ulong*)f->addr = 0xF0F0F0F0; /* reset (just in case) */
return 0;
}
void
flashamd29f0x0link(void)
{
addflashcard("AMD29F0x0", reset);
}