support simple writes
[flashkit-mdc.git] / flashkit.c
index 3e687fc..1dc54d9 100644 (file)
@@ -119,17 +119,38 @@ static int read_serial(int fd, void *data, size_t size)
        return 0;
 }
 
-static void set_addr(int fd, uint32_t addr)
+/* addr arg is always byte address */
+static void set_addr8(int fd, uint32_t addr)
 {
        uint8_t cmd[6] = {
-               CMD_ADDR, addr >> 17,
-               CMD_ADDR, addr >> 9,
-               CMD_ADDR, addr >> 1
+               CMD_ADDR, addr >> 16,
+               CMD_ADDR, addr >> 8,
+               CMD_ADDR, addr >> 0
        };
        write_serial(fd, cmd, sizeof(cmd));
 }
 
-static uint16_t read_word(int fd, uint32_t addr)
+static void set_addr16(int fd, uint32_t addr)
+{
+       set_addr8(fd, addr >> 1);
+}
+
+static uint16_t read_bus8(int fd, uint32_t addr)
+{
+       uint8_t cmd[7] = {
+               CMD_ADDR, addr >> 16,
+               CMD_ADDR, addr >> 8,
+               CMD_ADDR, addr >> 0,
+               CMD_RD | PAR_SINGE | PAR_MODE8
+       };
+       uint8_t r;
+
+       write_serial(fd, cmd, sizeof(cmd));
+       read_serial(fd, &r, sizeof(r));
+       return r;
+}
+
+static uint16_t read_bus16(int fd, uint32_t addr)
 {
        uint8_t cmd[7] = {
                CMD_ADDR, addr >> 17,
@@ -144,7 +165,20 @@ static uint16_t read_word(int fd, uint32_t addr)
        return ntohs(r);
 }
 
-static void write_word(int fd, uint32_t addr, uint16_t d)
+static void write_bus8(int fd, uint32_t addr, uint16_t d)
+{
+       uint8_t cmd[8] = {
+               CMD_ADDR, addr >> 16,
+               CMD_ADDR, addr >> 8,
+               CMD_ADDR, addr >> 0,
+               CMD_WR | PAR_SINGE | PAR_MODE8,
+               d
+       };
+
+       write_serial(fd, cmd, sizeof(cmd));
+}
+
+static void write_bus16(int fd, uint32_t addr, uint16_t d)
 {
        uint8_t cmd[9] = {
                CMD_ADDR, addr >> 17,
@@ -157,42 +191,71 @@ static void write_word(int fd, uint32_t addr, uint16_t d)
        write_serial(fd, cmd, sizeof(cmd));
 }
 
-static void read_block(int fd, void *dst, uint32_t size)
+static void read_block8(int fd, void *dst, uint32_t size)
 {
+       // PAR_MODE8 does not work, so read as 16bit and throw away MSB
+       uint8_t tmp[0x10000], *d8 = dst;
        uint8_t cmd[5] = {
-               CMD_LEN, size >> 9,
-               CMD_LEN, size >> 1,
+               CMD_LEN, size >> 8,
+               CMD_LEN, size >> 0,
                CMD_RD | PAR_INC
        };
+       int i;
 
        assert(size <= 0x10000);
        write_serial(fd, cmd, sizeof(cmd));
        read_serial(fd, dst, size);
+       read_serial(fd, tmp, size);
+
+       for (i = 0; i < size / 2; i++)
+               d8[i] = d8[i * 2 + 1];
+       d8 += size / 2;
+       for (i = 0; i < size / 2; i++)
+               d8[i] = tmp[i * 2 + 1];
 }
 
-static uint16_t flash_seq_r(int fd, uint8_t cmd, uint32_t addr)
+static void read_block16(int fd, void *dst, uint32_t size)
 {
-       // unlock
-       write_word(fd, 0xaaa, 0xaa);
-       write_word(fd, 0x555, 0x55);
+       uint8_t cmd[5] = {
+               CMD_LEN, size >> 9,
+               CMD_LEN, size >> 1,
+               CMD_RD | PAR_INC
+       };
 
-       write_word(fd, 0xaaa, cmd);
-       return read_word(fd, addr);
+       assert(size <= 0x10000);
+       write_serial(fd, cmd, sizeof(cmd));
+       read_serial(fd, dst, size);
 }
 
-static void flash_seq_erase(int fd, uint32_t addr)
+static void flash_seq_write8(int fd, uint32_t addr, const uint8_t *d)
 {
-       // printf("erase %06x\n", addr);
-       write_word(fd, 0xaaa, 0xaa);
-       write_word(fd, 0x555, 0x55);
-       write_word(fd, 0xaaa, 0x80);
+       uint8_t cmd[] = {
+               // unlock
+               CMD_ADDR, 0,
+               CMD_ADDR, 0x0a,
+               CMD_ADDR, 0xaa,
+               CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
+               CMD_ADDR, 0,
+               CMD_ADDR, 0x05,
+               CMD_ADDR, 0x55,
+               CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
+               // program setup
+               CMD_ADDR, 0,
+               CMD_ADDR, 0x0a,
+               CMD_ADDR, 0xaa,
+               CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
+               // program data
+               CMD_ADDR, addr >> 16,
+               CMD_ADDR, addr >> 8,
+               CMD_ADDR, addr >> 0,
+               CMD_WR | PAR_SINGE | PAR_MODE8, *d,
+               CMD_RY
+       };
 
-       write_word(fd, 0xaaa, 0xaa);
-       write_word(fd, 0x555, 0x55);
-       write_word(fd, addr, 0x30);
+       write_serial(fd, cmd, sizeof(cmd));
 }
 
-static void flash_seq_write(int fd, uint32_t addr, uint8_t *d)
+static void flash_seq_write16(int fd, uint32_t addr, const uint8_t *d)
 {
        uint8_t cmd[] = {
                // unlock
@@ -220,6 +283,44 @@ static void flash_seq_write(int fd, uint32_t addr, uint8_t *d)
        write_serial(fd, cmd, sizeof(cmd));
 }
 
+static const struct iof
+{
+       void     (*set_addr)(int fd, uint32_t addr);
+       uint16_t (*read_bus)(int fd, uint32_t addr);
+       void     (*write_bus)(int fd, uint32_t addr, uint16_t d);
+       void     (*read_block)(int fd, void *dst, uint32_t size);
+       void     (*flash_seq_write)(int fd, uint32_t addr, const uint8_t *d);
+}
+io_ops[] =
+{
+       { set_addr8,  read_bus8,  write_bus8,  read_block8,  flash_seq_write8  },
+       { set_addr16, read_bus16, write_bus16, read_block16, flash_seq_write16 },
+};
+
+static const struct iof *io = &io_ops[1];
+
+static uint16_t flash_seq_r(int fd, uint8_t cmd, uint32_t addr)
+{
+       // unlock
+       io->write_bus(fd, 0xaaa, 0xaa);
+       io->write_bus(fd, 0x555, 0x55);
+
+       io->write_bus(fd, 0xaaa, cmd);
+       return io->read_bus(fd, addr);
+}
+
+static void flash_seq_erase(int fd, uint32_t addr)
+{
+       // printf("erase %06x\n", addr);
+       io->write_bus(fd, 0xaaa, 0xaa);
+       io->write_bus(fd, 0x555, 0x55);
+       io->write_bus(fd, 0xaaa, 0x80);
+
+       io->write_bus(fd, 0xaaa, 0xaa);
+       io->write_bus(fd, 0x555, 0x55);
+       io->write_bus(fd, addr, 0x30);
+}
+
 // status wait + dummy read to cause a wait?
 static uint16_t ry_read(int fd)
 {
@@ -259,29 +360,29 @@ static void read_info(int fd)
        uint32_t i, a;
 
        info.mid = flash_seq_r(fd, 0x90, 0); // autoselect
-       info.did = read_word(fd, 2);
+       info.did = io->read_bus(fd, 2);
 
        // could enter CFI directly, but there seems to be a "stack"
        // of modes, so 2 exits would be needed
-       write_word(fd, 0, 0xf0);
+       io->write_bus(fd, 0, 0xf0);
 
-       write_word(fd, 0xaa, 0x98); // CFI Query
-       resp[0] = read_word(fd, 0x20);
-       resp[1] = read_word(fd, 0x22);
-       resp[2] = read_word(fd, 0x24);
+       io->write_bus(fd, 0xaa, 0x98); // CFI Query
+       resp[0] = io->read_bus(fd, 0x20);
+       resp[1] = io->read_bus(fd, 0x22);
+       resp[2] = io->read_bus(fd, 0x24);
        if (memcmp(resp, qry, sizeof(resp))) {
                fprintf(stderr, "unexpected CFI response: %04x %04x %04x\n",
                        resp[0], resp[1], resp[2]);
                exit(1);
        }
-       info.size = 1u << read_word(fd, 0x4e);
-       info.region_cnt = read_word(fd, 0x58);
+       info.size = 1u << io->read_bus(fd, 0x4e);
+       info.region_cnt = io->read_bus(fd, 0x58);
        assert(0 < info.region_cnt && info.region_cnt <= 4);
        for (i = 0, a = 0x5a; i < info.region_cnt; i++, a += 8) {
-               info.region[i].block_count = read_word(fd, a + 0) + 1;
-               info.region[i].block_count += read_word(fd, a + 2) << 8;
-               info.region[i].block_size = read_word(fd, a + 4) << 8;
-               info.region[i].block_size |= read_word(fd, a + 6) << 16;
+               info.region[i].block_count = io->read_bus(fd, a + 0) + 1;
+               info.region[i].block_count += io->read_bus(fd, a + 2) << 8;
+               info.region[i].block_size = io->read_bus(fd, a + 4) << 8;
+               info.region[i].block_size |= io->read_bus(fd, a + 6) << 16;
                info.region[i].start = total;
                info.region[i].size =
                        info.region[i].block_size * info.region[i].block_count;
@@ -289,7 +390,7 @@ static void read_info(int fd)
                total += info.region[i].size;
        }
 
-       write_word(fd, 0, 0xf0); // flash reset
+       io->write_bus(fd, 0, 0xf0); // flash reset
 
        printf("Flash info:\n");
        printf("Manufacturer ID: %04x\n", info.mid);
@@ -328,10 +429,7 @@ static void print_progress(uint32_t done, uint32_t total)
 {
        int i, step;
 
-       printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b");
-       printf("\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); /* 20 */
-       printf("\b\b\b\b\b\b");
-       printf("%06x/%06x |", done, total);
+       printf("\r%06x/%06x |", done, total);
 
        step = (total + 19) / 20;
        for (i = step; i <= total; i += step)
@@ -342,15 +440,37 @@ static void print_progress(uint32_t done, uint32_t total)
                fputc('\n', stdout);
 }
 
+static FILE *open_prep_read(const char *fname, long *size)
+{
+       FILE *f = fopen(fname, "rb");
+       if (!f) {
+               fprintf(stderr, "fopen %s: ", fname);
+               perror("");
+               exit(1);
+       }
+       if (*size <= 0) {
+               fseek(f, 0, SEEK_END);
+               *size = ftell(f);
+               fseek(f, 0, SEEK_SET);
+       }
+       if (*size <= 0) {
+               fprintf(stderr, "size of %s is %ld\n", fname, *size);
+               exit(1);
+       }
+       return f;
+}
+
 static void usage(const char *argv0)
 {
        printf("usage:\n"
                "%s [options]\n"
                "  -d <ttydevice>      (default /dev/ttyUSB0)\n"
                "  -r <file> [size]    dump the cart (default 4MB)\n"
-               "  -w <file> [size]    flash the cart (file size)\n"
+               "  -w <file> [size]    program the flash (def. file size)\n"
+               "  -s <file> [size]    simple write (SRAM, etc, def. file size)\n"
                "  -e <size>           erase (rounds to block size)\n"
-               "  -a <start_address>  cart start address (default 0)\n"
+               "  -a <start_address>  read/write start address (default 0)\n"
+               "  -8                  8bit flash\n"
                "  -v                  verify written data\n"
                "  -i                  get info about the flash chip\n"
                , argv0);
@@ -381,16 +501,20 @@ int main(int argc, char *argv[])
        const char *portname = "/dev/ttyUSB0";
        const char *fname_w = NULL;
        const char *fname_r = NULL;
+       const char *fname_ws = NULL;
        long size_w = 0;
        long size_r = 0;
+       long size_ws = 0;
        long size_e = 0;
        long size_v = 0;
        long len, address_in = 0;
        long a, a_blk, end;
        int do_info = 0;
        int do_verify = 0;
+       int write_step = 2;
        FILE *f_w = NULL;
        FILE *f_r = NULL;
+       FILE *f_ws = NULL;
        uint8_t id[2] = { 0, 0 };
        uint8_t cmd;
        uint16_t rv;
@@ -423,6 +547,15 @@ int main(int argc, char *argv[])
                        }
                        continue;
                }
+               if (!strcmp(argv[arg], "-s")) {
+                       fname_ws = getarg(argc, argv, ++arg);
+                       if (arg + 1 < argc && argv[arg + 1][0] != '-') {
+                               size_ws = strtol(argv[++arg], NULL, 0);
+                               if (size_ws <= 0)
+                                       invarg(argc, argv, arg);
+                       }
+                       continue;
+               }
                if (!strcmp(argv[arg], "-a")) {
                        address_in = strtol(getarg(argc, argv, ++arg), NULL, 0);
                        if (address_in < 0 || (address_in & 1))
@@ -435,6 +568,11 @@ int main(int argc, char *argv[])
                                invarg(argc, argv, arg);
                        continue;
                }
+               if (!strcmp(argv[arg], "-8")) {
+                       io = &io_ops[0];
+                       write_step = 1;
+                       continue;
+               }
                if (!strcmp(argv[arg], "-v")) {
                        do_verify = 1;
                        continue;
@@ -450,22 +588,7 @@ int main(int argc, char *argv[])
                size_r = 0x400000;
 
        if (fname_w) {
-               f_w = fopen(fname_w, "rb");
-               if (!f_w) {
-                       fprintf(stderr, "fopen %s: ", fname_w);
-                       perror("");
-                       return 1;
-               }
-               if (size_w <= 0) {
-                       fseek(f_w, 0, SEEK_END);
-                       size_w = ftell(f_w);
-                       fseek(f_w, 0, SEEK_SET);
-               }
-               if (size_w <= 0) {
-                       fprintf(stderr, "size of %s is %ld\n",
-                               fname_w, size_w);
-                       return 1;
-               }
+               f_w = open_prep_read(fname_w, &size_w);
                if (size_e < size_w)
                        size_e = size_w;
                if (do_verify)
@@ -479,6 +602,9 @@ int main(int argc, char *argv[])
                return 1;
        }
 
+       if (fname_ws)
+               f_ws = open_prep_read(fname_ws, &size_ws);
+
        setup(fd);
 
        cmd = CMD_RD | PAR_SINGE | PAR_DEV_ID;
@@ -491,7 +617,7 @@ int main(int argc, char *argv[])
        printf("flashkit id: %02x\n", id[0]);
 
        set_delay(fd, 1);
-       write_word(fd, 0, 0xf0); // flash reset
+       io->write_bus(fd, 0, 0xf0); // flash reset
 
        if (do_info || size_e)
                read_info(fd);
@@ -519,30 +645,51 @@ int main(int argc, char *argv[])
        if (f_w != NULL) {
                uint8_t b[2];
                set_delay(fd, 0);
-               printf("writing %ld bytes:\n", size_w);
-               for (a = 0; a < size_w; a += 2) {
+               printf("flashing %ld bytes:\n", size_w);
+               for (a = 0; a < size_w; a += write_step) {
                        ssize_t r;
 
                        b[1] = 0xff;
-                       len = min(size_w - a, 2);
+                       len = min(size_w - a, write_step);
                        r = fread(b, 1, len, f_w);
                        if (r != len) {
                                perror("\nfread");
                                return 1;
                        }
-                       flash_seq_write(fd, address_in + a, b);
+                       io->flash_seq_write(fd, address_in + a, b);
 
-                       if (!(a & 0x3fe))
+                       if (!(a & 0x3ff))
                                print_progress(a, size_w);
                }
                print_progress(a, size_w);
                rv = ry_read(fd);
-               if (rv != ((b[0] << 8) | b[1]))
+               if (write_step == 2 && rv != ((b[0] << 8) | b[1]))
                        fprintf(stderr, "warning: last bytes: %04x %02x%02x\n",
                                rv, b[0], b[1]);
                rewind(f_w);
                set_delay(fd, 1);
        }
+       if (f_ws != NULL) {
+               printf("writing %ld bytes:\n", size_ws);
+               for (a = 0; a < size_ws; a += write_step) {
+                       uint16_t b = 0xffff;
+                       ssize_t r;
+
+                       len = min(size_ws - a, write_step);
+                       r = fread(&b, 1, len, f_ws);
+                       if (r != len) {
+                               perror("\nfread");
+                               return 1;
+                       }
+                       if (write_step == 2)
+                               b = htons(b);
+                       io->write_bus(fd, address_in + a, b);
+
+                       if (!(a & 0x3ff))
+                               print_progress(a, size_ws);
+               }
+               print_progress(a, size_ws);
+       }
 
        if (fname_r || size_v) {
                long blks, blks_v, done, verify_diff = 0;
@@ -561,9 +708,9 @@ int main(int argc, char *argv[])
 
                printf("reading %ld bytes:\n", max(size_r, size_v));
                print_progress(0, blks * sizeof(g_block));
-               set_addr(fd, address_in);
+               io->set_addr(fd, address_in);
                for (done = 0; done < size_r || done < size_v; ) {
-                       read_block(fd, g_block, sizeof(g_block));
+                       io->read_block(fd, g_block, sizeof(g_block));
                        if (f_r && done < size_r) {
                                len = min(size_r - done, sizeof(g_block));
                                if (fwrite(g_block, 1, len, f_r) != len) {