diff options
Diffstat (limited to 'os/port/flashamd29f0x0.c')
| -rw-r--r-- | os/port/flashamd29f0x0.c | 167 |
1 files changed, 167 insertions, 0 deletions
diff --git a/os/port/flashamd29f0x0.c b/os/port/flashamd29f0x0.c new file mode 100644 index 00000000..06798d05 --- /dev/null +++ b/os/port/flashamd29f0x0.c @@ -0,0 +1,167 @@ +#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); +} |
