| plan 9 kernel history: overview | file list | diff list |
2000/1117/bitsy/devflash.c (diff list | history)
| 2000/1111/sys/src/9/bitsy/devflash.c:6,23 – 2000/1117/sys/src/9/bitsy/devflash.c:6,11 (short | long | prev | next) | ||
| 2000/1031 | #include "io.h" #include "../port/error.h" | |
| 2000/1107 |
| |
| 2000/1111/sys/src/9/bitsy/devflash.c:27,94 – 2000/1117/sys/src/9/bitsy/devflash.c:15,133 | ||
| 2000/1107 | * sectors for each request erase request. */ | |
| 2000/1117 | #define mirror(x) (((x)<<16)|(x)) /* this defines a contiguous set of erase blocks of one size */ typedef struct FlashRegion FlashRegion; struct FlashRegion | |
| 2000/1107 | { | |
| 2000/1117 | ulong addr; /* start of region */ ulong end; /* end of region + 1 */ ulong n; /* number of blocks */ ulong size; /* size of each block */ | |
| 2000/1107 | }; | |
| 2000/1117 | /* this defines a particular access algorithm */ typedef struct FlashAlg FlashAlg; struct FlashAlg | |
| 2000/1107 | { | |
| 2000/1117 | int id; char *name; void (*identify)(void); /* identify device */ void (*erase)(ulong); /* erase a region */ void (*write)(void*, long, ulong); /* write a region */ | |
| 2000/1107 | }; | |
| 2000/1117 | static void ise_id(void); static void ise_erase(ulong); static void ise_write(void*, long, ulong); static void afs_id(void); static void afs_erase(ulong); static void afs_write(void*, long, ulong); FlashAlg falg[] = | |
| 2000/1107 | { | |
| 2000/1111 |
| |
| 2000/1117 | { 1, "Intel/Sharp Extended", ise_id, ise_erase, ise_write }, { 2, "AMD/Fujitsu Standard", afs_id, afs_erase, afs_write }, | |
| 2000/1111 | }; | |
| 2000/1117 | struct { RWlock; ulong *p; ushort algid; /* access algorithm */ FlashAlg *alg; ushort manid; /* manufacturer id */ ushort devid; /* device id */ ulong size; /* size in bytes */ int wbsize; /* size of write buffer */ ulong nr; /* number of regions */ uchar bootprotect; FlashRegion r[32]; ulong *wb; /* staging area for write buffer */ } flash; | |
| 2000/1111 |
| |
| 2000/1117 | /* * common flash interface */ static uchar cfigetc(int off) | |
| 2000/1111 | { | |
| 2000/1117 | uchar rv; | |
| 2000/1111 |
| |
| 2000/1117 | flash.p[0x55] = mirror(0x98); rv = flash.p[off]; flash.p[0x55] = mirror(0xFF); return rv; | |
| 2000/1111 | } | |
| 2000/1117 | static ushort cfigets(int off) | |
| 2000/1111 | { | |
| 2000/1117 | return (cfigetc(off+1)<<8)|cfigetc(off); | |
| 2000/1111 | } | |
| 2000/1117 | static ulong cfigetl(int off) { return (cfigetc(off+3)<<24)|(cfigetc(off+2)<<16)| (cfigetc(off+1)<<8)|cfigetc(off); } static void cfiquery(void) { uchar q, r, y; ulong x, addr; q = cfigetc(0x10); r = cfigetc(0x11); y = cfigetc(0x12); if(q != 'Q' || r != 'R' || y != 'Y'){ print("cfi query failed: %ux %ux %ux\n", q, r, y); return; } flash.algid = cfigetc(0x13); flash.size = 1<<(cfigetc(0x27)+1); flash.wbsize = 1<<(cfigetc(0x2a)+1); flash.nr = cfigetc(0x2c); if(flash.nr > nelem(flash.r)){ print("cfi reports > %d regions\n", nelem(flash.r)); flash.nr = nelem(flash.r); } addr = 0; for(q = 0; q < flash.nr; q++){ x = cfigetl(q+0x2d); flash.r[q].size = 2*256*(x>>16); flash.r[q].n = (x&0xffff)+1; flash.r[q].addr = addr; addr += flash.r[q].size*flash.r[q].n; flash.r[q].end = addr; } flash.wb = malloc(flash.wbsize); } | |
| 2000/1111 | /* * flash device interface */ | |
| 2000/1111/sys/src/9/bitsy/devflash.c:95,124 – 2000/1117/sys/src/9/bitsy/devflash.c:134,168 | ||
| 2000/1111 | enum { | |
| 2000/1117 | Qfctl=1, Qfdata, | |
| 2000/1111 | }; Dirtab flashdir[]={ | |
| 2000/1117 | "flashctl", { Qfctl, 0 }, 0, 0664, "flashdata", { Qfdata, 0 }, 0, 0660, | |
| 2000/1111 | }; void flashinit(void) { | |
| 2000/1117 | int i; flash.p = (ulong*)FLASHZERO; | |
| 2000/1111 | cfiquery(); | |
| 2000/1117 | for(i = 0; i < nelem(falg); i++) if(flash.algid == falg[i].id){ flash.alg = &falg[i]; (*flash.alg->identify)(); break; } flash.bootprotect = 1; | |
| 2000/1111 | } static Chan* flashattach(char* spec) { | |
| 2000/1117 | return devattach('F', spec); | |
| 2000/1111 | } static int | |
| 2000/1111/sys/src/9/bitsy/devflash.c:150,165 – 2000/1117/sys/src/9/bitsy/devflash.c:194,378 | ||
| 2000/1111 | static long flashread(Chan* c, void* a, long n, vlong off) { | |
| 2000/1117 | char *buf, *p, *e; int i; if(c->qid.path&CHDIR) return devdirread(c, a, n, flashdir, nelem(flashdir), devgen); switch(c->qid.path){ default: error(Eperm); case Qfctl: buf = smalloc(1024); e = buf + 1024; p = seprint(buf, e, "0x%-9lux 0x%-9lux 0x%-9lux 0x%-9lux\n", flash.size, flash.wbsize, flash.manid, flash.devid); for(i = 0; i < flash.nr; i++) p = seprint(p, e, "0x%-9lux 0x%-9lux 0x%-9lux\n", flash.r[i].addr, flash.r[i].n, flash.r[i].size); n = readstr(off, a, n, buf); free(buf); break; case Qfdata: if(!iseve()) error(Eperm); if(off >= flash.size) return 0; if(off + n > flash.size) n = flash.size - off; rlock(&flash); if(waserror()){ runlock(&flash); nexterror(); } memmove(a, ((uchar*)FLASHZERO)+off, n); runlock(&flash); poperror(); break; } | |
| 2000/1111 | return n; } | |
| 2000/1117 | static void bootprotect(ulong addr) { FlashRegion *r; if(flash.bootprotect == 0) return; if(flash.nr == 0) error("writing over boot loader disallowed"); r = flash.r; if(addr >= r->addr && addr < r->addr + r->size) error("writing over boot loader disallowed"); } ulong blockstart(ulong addr) { FlashRegion *r, *e; ulong x; r = flash.r; for(e = &flash.r[flash.nr]; r < e; r++) if(addr >= r->addr && addr < r->end){ x = addr - r->addr; x /= r->size; return r->addr + x*r->size; } return (ulong)-1; } ulong blockend(ulong addr) { FlashRegion *r, *e; ulong x; r = flash.r; for(e = &flash.r[flash.nr]; r < e; r++) if(addr >= r->addr && addr < r->end){ x = addr - r->addr; x /= r->size; return r->addr + (x+1)*r->size; } return (ulong)-1; } static long flashctlwrite(char *p, long n) { Cmdbuf *cmd; ulong addr; cmd = parsecmd(p, n); wlock(&flash); if(waserror()){ wunlock(&flash); nexterror(); } if(strcmp(cmd->f[0], "erase") == 0){ if(cmd->nf != 2) error(Ebadarg); addr = atoi(cmd->f[1]); if(addr != blockstart(addr)) error("erase must be a block boundary"); bootprotect(addr); (*flash.alg->erase)(addr); } else if(strcmp(cmd->f[0], "protectboot") == 0){ if(cmd->nf == 0 || strcmp(cmd->f[1], "off") != 0) flash.bootprotect = 1; else flash.bootprotect = 0; } else error(Ebadarg); poperror(); wunlock(&flash); free(cmd); return n; } static long flashdatawrite(uchar *p, long n, long off) { uchar *end; int m; long ooff = off; uchar *op = p; if((off & 0x3) || (n & 0x3)) error("only quad writes"); if(off >= flash.size || off+n > flash.size || n <= 0) error(Ebadarg); wlock(&flash); if(waserror()){ wunlock(&flash); nexterror(); } /* make sure we're not writing the boot sector */ bootprotect(off); /* (*flash.alg->write) can't cross blocks */ for(end = p + n; p < end; p += m){ m = blockend(off) - off; if(m > end - p) m = end - p; (*flash.alg->write)(p, m, off); off += m; } /* make sure write succeeded */ if(memcmp(op, &flash.p[ooff>>2], n) != 0) error("written bytes don't match"); wunlock(&flash); poperror(); return n; } | |
| 2000/1111 | static long | |
| 2000/1117 | flashwrite(Chan* c, void* a, long n, vlong off) | |
| 2000/1111 | { | |
| 2000/1117 | if(c->qid.path & CHDIR) error(Eperm); if(!iseve()) error(Eperm); switch(c->qid.path){ default: panic("flashwrite"); case Qfctl: return flashctlwrite(a, n); case Qfdata: return flashdatawrite(a, n, off); } | |
| 2000/1111 | return n; } | |
| 2000/1111/sys/src/9/bitsy/devflash.c:183,185 – 2000/1117/sys/src/9/bitsy/devflash.c:396,615 | ||
| 2000/1111 | devremove, devwstat, | |
| 2000/1107 | }; | |
| 2000/1117 | /* intel/sharp extended command set */ static void ise_reset(void) { flash.p[0x55] = mirror(0xff); /* reset */ } static void ise_id(void) { ise_reset(); flash.p[0x555] = mirror(0x90); /* uncover vendor info */ flash.manid = flash.p[00]; flash.devid = flash.p[01]; ise_reset(); } static void ise_clearerror(void) { flash.p[0x100] = mirror(0x50); } static void ise_error(int bank, ulong status) { char err[ERRLEN]; if(status & (1<<3)){ sprint(err, "flash%d: low prog voltage", bank); error(err); } if(status & (1<<1)){ sprint(err, "flash%d: block locked", bank); error(err); } if(status & (1<<5)){ sprint(err, "flash%d: i/o error", bank); error(err); } } static void ise_erase(ulong addr) { ulong start; ulong x; addr >>= 2; /* convert to ulong offset */ flashprogpower(1); flash.p[addr] = mirror(0x20); flash.p[addr] = mirror(0xd0); start = m->ticks; do { x = flash.p[addr]; if((x & mirror(1<<7)) == mirror(1<<7)) break; } while(TK2MS(m->ticks-start) < 1500); flashprogpower(0); ise_clearerror(); ise_error(0, x); ise_error(1, x>>16); ise_reset(); } /* * flash writing goes about 16 times faster if we use * the write buffer. We fill the write buffer and then * issue the write request. After the write request, * subsequent reads will yield the status register or, * since error bits are sticky, another write buffer can * be filled and written. * * On timeout, we issue a read status register request so * that the status register can be read no matter how we * exit. */ static int ise_wbwrite(ulong *p, int n, ulong off) { ulong start; int i; /* copy out of user space to avoid faults later */ memmove(flash.wb, p, n*4); p = flash.wb; /* put flash into write buffer mode */ start = m->ticks; for(;;) { /* request write buffer mode */ flash.p[off] = mirror(0xe8); /* look at extended status reg for status */ if((flash.p[off] & mirror(1<<7)) == mirror(1<<7)) break; /* didn't work, keep trying for 2 secs */ if(TK2MS(m->ticks-start) > 2000){ /* set up to read status */ flash.p[off] = mirror(0x70); return -1; } } /* fill write buffer */ flash.p[off] = mirror(n-1); for(i = 0; i < n; i++) flash.p[off+i] = *p++; /* program from buffer */ flash.p[off] = mirror(0xd0); /* subsequent reads will return status about the write */ return n; } static void ise_write(void *a, long n, ulong off) { ulong *p, *end; int i, wbsize; ulong x, start, ooff; /* everything in terms of ulongs */ wbsize = flash.wbsize>>2; off >>= 2; n >>= 2; p = a; ooff = off; /* first see if write will succeed */ for(i = 0; i < n; i++) if((p[i] & flash.p[off+i]) != p[i]) error("flash needs erase"); if(waserror()){ ise_reset(); flashprogpower(0); nexterror(); } flashprogpower(1); /* * use the first write to reach * a write buffer boundary. the intel maunal * says writes startng at wb boundaries * maximize speed. */ i = wbsize - (off & (wbsize-1)); for(end = p + n; p < end;){ if(i > end - p) i = end - p; if(ise_wbwrite(p, i, off) != i) break; off += i; p += i; i = wbsize; } /* wait till the programming is done */ start = m->ticks; do { x = flash.p[ooff]; if((x & mirror(1<<7)) == mirror(1<<7)) break; } while(TK2MS(m->ticks-start) < 1000); ise_clearerror(); ise_error(0, x); ise_error(1, x>>16); ise_reset(); flashprogpower(0); poperror(); } /* amd/fujitsu standard command set * I don't have an amd chipset to work with * so I'm loathe to write this yet. If someone * else does, please send it to me and I'll * incorporate it -- presotto@bell-labs.com */ static void afs_reset(void) { flash.p[0x55] = mirror(0xf0); /* reset */ } static void afs_id(void) { afs_reset(); flash.p[0x55] = mirror(0xf0); /* reset */ flash.p[0x555] = mirror(0xaa); /* query vendor block */ flash.p[0x2aa] = mirror(0x55); flash.p[0x555] = mirror(0x90); flash.manid = flash.p[00]; afs_reset(); flash.p[0x555] = mirror(0xaa); /* query vendor block */ flash.p[0x2aa] = mirror(0x55); flash.p[0x555] = mirror(0x90); flash.devid = flash.p[01]; afs_reset(); } static void afs_erase(ulong) { error("amd/fujistsu erase not implemented"); } static void afs_write(void*, long, ulong) { error("amd/fujistsu write not implemented"); } | |