summaryrefslogtreecommitdiff
path: root/os/pc/flashzpc.c
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 21:39:35 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 21:39:35 +0000
commit74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a (patch)
treec6e220ba61db3a6ea4052e6841296d829654e664 /os/pc/flashzpc.c
parent46439007cf417cbd9ac8049bb4122c890097a0fa (diff)
20060303
Diffstat (limited to 'os/pc/flashzpc.c')
-rw-r--r--os/pc/flashzpc.c371
1 files changed, 371 insertions, 0 deletions
diff --git a/os/pc/flashzpc.c b/os/pc/flashzpc.c
new file mode 100644
index 00000000..c360f7bf
--- /dev/null
+++ b/os/pc/flashzpc.c
@@ -0,0 +1,371 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+
+#include "flashif.h"
+
+#define FLASHMEM 0xfff80000
+#define FLASHPGSZ 0x40000
+#define FLASHBKSZ (FLASHPGSZ>>2)
+#define LOG2FPGSZ 18
+#define FLASHEND (FLASHMEM+FLASHPGSZ)
+#define SYSREG0 0x78
+#define SYSREG1 0x878
+
+/* Intel28F016SA flash memory family (8SA and (DD)32SA as well) in byte mode */
+
+/*
+ * word mode does not work - a 2 byte write to a location results in the lower address
+ * byte being unchanged (4 byte writes are even stranger) and no indication of error.
+ * Perhaps the bridge is interfering with the address lines.
+ * Looks like the BIOS code doesn't use it either but that's not certain.
+ */
+
+/*
+ * When port 0x78 bit 2 is set to 1 (flash device 1)
+ * 0xfff80000-0xfffbffff seems to be free but has dos block headers
+ * 0xfffc0000-0xfffdffff seems to be the DOS P: drive
+ * 0xfffe0000-0xffffffff is the BIOS
+ * When port 0x78 bit 2 is set to 0 (flash device 0)
+ * 0xfff80000-0xffffffff is a mixture of used and unused DOS blocks and apparently
+ * many copies of the BIOS
+ *
+ * In the absence of information from Ziatech and to preserve the BIOS and DOS sections,
+ * this driver only uses the first range for a total of 8 x 0x40000 = 2Mb
+ */
+
+enum {
+ DQ7 = 0x80,
+ DQ6 = 0x40,
+ DQ5 = 0x20,
+ DQ4 = 0x10,
+ DQ3 = 0x08,
+ DQ2 = 0x04,
+ DQ1 = 0x02,
+ DQ0 = 0x01,
+};
+
+enum {
+ FLRDM = 0xFF, /* read */
+ FLWTM = 0x10, /* write/program */
+ FLCLR = 0x50, /* clear SR */
+ FLBE1 = 0x20, /* block erase */
+ FLBE2 = 0xD0, /* block erase */
+ FLRSR = 0x70, /* read SR */
+ FLDID = 0x90, /* read id */
+};
+
+#define DPRINT if(0)print
+#define EPRINT if(1)print
+
+static int
+zpcwait(uchar *p, ulong ticks)
+{
+ uchar csr;
+
+ ticks += m->ticks+1;
+ while((*p & DQ7) != DQ7){
+ sched();
+ if(m->ticks >= ticks){
+ EPRINT("flash: timed out: %8.8lux\n", (ulong)*p);
+ return -1;
+ }
+ }
+ csr = *p;
+ if(csr & (DQ5|DQ4|DQ3)){
+ EPRINT("flash: DQ5 error: %8.8lux %8.8lux\n", p, (ulong)csr);
+ return 0;
+ }
+ return 1;
+}
+
+static int
+eraseall(Flash *f)
+{
+ uchar r;
+ uchar *p;
+ int i, j, s;
+
+ DPRINT("flash: erase all\n");
+ for (i = 0; i < 8; i++) { /* page */
+ /* set page */
+ r = inb(SYSREG0);
+ r &= 0x8f;
+ r |= i<<4;
+ outb(SYSREG0, r);
+ p = (uchar *)f->addr;
+ for (j = 0; j < 4; j++) { /* block within page */
+ DPRINT("erasing page %d block %d addr %lux\n", i, j, p);
+ s = splhi();
+ *p = FLBE1;
+ *p = FLBE2;
+ splx(s);
+ if(zpcwait(p, MS2TK(16*1000)) <= 0){
+ *p = FLCLR; /* clr SR */
+ *p = FLRDM; /* read mode */
+ f->unusable = ~0;
+ return -1;
+ }
+ *p = FLCLR;
+ *p = FLRDM;
+ p += FLASHPGSZ>>2;
+ }
+ }
+ return 0;
+}
+
+static int
+erasezone(Flash *f, int zone)
+{
+ uchar r;
+ uchar *p;
+ int s, pg, blk;
+
+ DPRINT("flash: erase zone %d\n", zone);
+ if(zone & ~31) {
+ EPRINT("flash: bad erasezone %d\n", zone);
+ return -1; /* bad zone */
+ }
+ pg = zone>>2;
+ blk = zone&3;
+ /* set page */
+ r = inb(SYSREG0);
+ r &= 0x8f;
+ r |= pg<<4;
+ outb(SYSREG0, r);
+ p = (uchar *)f->addr + blk*(FLASHPGSZ>>2);
+ DPRINT("erasing zone %d pg %d blk %d addr %lux\n", zone, pg, blk, p);
+ s = splhi();
+ *p = FLBE1;
+ *p = FLBE2;
+ splx(s);
+ if(zpcwait(p, MS2TK(8*1000)) <= 0){
+ *p = FLCLR;
+ *p = FLRDM; /* reset */
+ f->unusable |= 1<<zone;
+ return -1;
+ }
+ *p = FLCLR;
+ *p = FLRDM;
+ return 0;
+}
+
+static int
+readx(Flash *f, ulong offset, void *buf, long n)
+{
+ uchar r;
+ ulong pg, o;
+ long m;
+ uchar *p = buf;
+
+ pg = offset>>LOG2FPGSZ;
+ o = offset&(FLASHPGSZ-1);
+ while (n > 0) {
+ if (pg < 0 || pg > 7) {
+ EPRINT("flash: bad read %ld %ld\n", offset, n);
+ return -1;
+ }
+ /* set page */
+ r = inb(SYSREG0);
+ r &= 0x8f;
+ r |= pg<<4;
+ outb(SYSREG0, r);
+ if (o+n > FLASHPGSZ)
+ m = FLASHPGSZ-o;
+ else
+ m = n;
+ DPRINT("flash: read page %ld offset %lux buf %lux n %ld\n", pg, o, p-(uchar*)buf, m);
+ memmove(p, (uchar *)f->addr + o, m);
+ p += m;
+ n -= m;
+ pg++;
+ o = 0;
+ }
+ return 0;
+}
+
+static int
+writex(Flash *f, ulong offset, void *buf, long n)
+{
+ int i, s;
+ uchar r;
+ ulong pg, o;
+ long m;
+ uchar *a, *v = buf;
+
+ DPRINT("flash: writex\n");
+ pg = offset>>LOG2FPGSZ;
+ o = offset&(FLASHPGSZ-1);
+ while (n > 0) {
+ if (pg < 0 || pg > 7) {
+ EPRINT("flash: bad write %ld %ld\n", offset, n);
+ return -1;
+ }
+ /* set page */
+ r = inb(SYSREG0);
+ r &= 0x8f;
+ r |= pg<<4;
+ outb(SYSREG0, r);
+ if (o+n > FLASHPGSZ)
+ m = FLASHPGSZ-o;
+ else
+ m = n;
+ a = (uchar *)f->addr + o;
+ DPRINT("flash: write page %ld offset %lux buf %lux n %ld\n", pg, o, v-(uchar*)buf, m);
+ for (i = 0; i < m; i++, v++, a++) {
+ if (~*a & *v) {
+ EPRINT("flash: bad write: %lux %lux -> %lux\n", (ulong)a, (ulong)*a, (ulong)*v);
+ return -1;
+ }
+ if (*a == *v)
+ continue;
+ s = splhi();
+ *a = FLWTM; /* program */
+ *a = *v;
+ splx(s);
+ microdelay(8);
+ if(zpcwait(a, 5) <= 0){
+ *a = FLCLR; /* clr SR */
+ *a = FLRDM; /* read mode */
+ f->unusable = ~0;
+ return -1;
+ }
+ *a = FLCLR;
+ *a = FLRDM;
+ if (*a != *v) {
+ EPRINT("flash: write %lux %lux -> %lux failed\n", (ulong)a, (ulong)*a, (ulong)*v);
+ return -1;
+ }
+ }
+ n -= m;
+ pg++;
+ o = 0;
+ }
+ return 0;
+}
+
+#ifdef ZERO
+/* search the whole of flash */
+static void
+flashsearch(Flash *f)
+{
+ int d, m, p, b, n, i;
+ uchar r, buf[64];
+
+ for (d = 0; d < 2; d++) { /* flash device */
+ r = inb(SYSREG0);
+ r &= 0xfb;
+ r |= (d<<2);
+ outb(SYSREG0, r);
+ for (m = 0; m < 2; m++) { /* lower/upper mem */
+ if (m == 0)
+ f->addr = (void *)FLASHMEM;
+ else
+ f->addr = (void *)FLASHEND;
+ for (p = 0; p < 8; p++) { /* page */
+ for (b = 0; b < 4; b++) { /* block */
+ n = readx(f, (4*p+b)*FLASHBKSZ, buf, 64);
+ if (n != 0) {
+ print("bad read in search %d\n", n);
+ goto end;
+ }
+ print("%d %d %d %d : ", d, m, p, b);
+ if (buf[0] == 0x5a && buf[1] == 0x54) { /* DOS block */
+ n = 0;
+ for (i = 0; i < 64; i++) {
+ if (buf[i] == 0xff)
+ n++;
+ }
+ if (n == 64-28)
+ print("un");
+ print("used dos\n");
+ }
+ else if (buf[0] == 0x55 && buf[1] == 0xaa)
+ print("bios start\n");
+ else
+ print("bios ?\n");
+ }
+ }
+ }
+ }
+end:
+ r = inb(SYSREG0);
+ r |= 4;
+ outb(SYSREG0, r);
+ f->addr = (void *)FLASHMEM;
+}
+#endif
+
+static int
+reset(Flash *f)
+{
+ uchar r;
+ int s;
+ ulong pa;
+ Pcidev *bridge;
+
+ /* get bridge device */
+ bridge = pcimatch(nil, 0x8086, 0x7000); /* Intel PIIX3 ISA bridge device */
+ if (bridge == nil) {
+ EPRINT("flash : failed to find bridge device\n");
+ return 1;
+ }
+ /* enable extended BIOS and read/write */
+ s = splhi();
+ r = pcicfgr8(bridge, 0x4e);
+ r |= 0x84;
+ pcicfgw8(bridge, 0x4e, r);
+ splx(s);
+ /* set system register bits */
+ r = inb(SYSREG0);
+ r |= 0x86; /* chip enable, non-BIOS part, set r/w */
+ outb(SYSREG0, r);
+ /*
+ * might have to grab memory starting at PADDR(FLASHMEM) ie 0x7ff80000
+ * because if this is mapped via virtual address FLASHMEM we would get a
+ * a kernel panic in mmukmap().
+ * va = 0xfff80000 pa = 0xfff80000 for flash
+ * va = 0xfff80000 pa = 0x7ff80000 if lower memory grabbed by anything
+ */
+ /*
+ * upafree(FLASHMEM, FLASHPGSZ);
+ * pa = upamalloc(FLASHMEM, FLASHPGSZ, 0);
+ * if (pa != FLASHMEM)
+ * error
+ */
+ pa = mmukmap(FLASHMEM, FLASHMEM, FLASHPGSZ);
+ if (pa != FLASHEND) {
+ EPRINT("failed to map flash memory");
+ return 1;
+ }
+/*
+ pa = mmukmap(FLASHEND, FLASHEND, FLASHPGSZ);
+ if (pa != 0) {
+ EPRINT("failed to map flash memory");
+ return 1;
+ }
+*/
+ f->id = 0x0089; /* can't use autoselect: might be running in flash */
+ f->devid = 0x66a0;
+ f->read = readx;
+ f->write = writex;
+ f->eraseall = eraseall;
+ f->erasezone = erasezone;
+ f->suspend = nil;
+ f->resume = nil;
+ f->width = 1; /* must be 1 since devflash.c must not read directly */
+ f->erasesize = 64*1024;
+ *(uchar*)f->addr = FLCLR; /* clear status registers */
+ *(uchar*)f->addr = FLRDM; /* reset to read mode */
+ return 0;
+}
+
+void
+flashzpclink(void)
+{
+ addflashcard("DD28F032SA", reset);
+}