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