add adapter handling for HiROM
[flashkit-mdc.git] / flashkit.c
CommitLineData
d9502b8d 1/*
2 * Tool for USB serial communication with Krikzz's FlashKit-MD
3 * Copyright (c) 2017 notaz
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining
6 * a copy of this software and associated documentation files (the
7 * "Software"), to deal in the Software without restriction, including
8 * without limitation the rights to use, copy, modify, merge, publish,
9 * distribute, sublicense, and/or sell copies of the Software, and to
10 * permit persons to whom the Software is furnished to do so, subject to
11 * the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be
14 * included in all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
20 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
21 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 * SOFTWARE.
24 */
25
26#include <stdio.h>
27#include <string.h>
28#include <stdlib.h>
29#include <stdint.h>
30#include <assert.h>
31#include <sys/types.h>
32#include <sys/stat.h>
33#include <fcntl.h>
34#include <arpa/inet.h> // hton
35#include <termios.h>
36#include <unistd.h>
37
38#ifndef min
39#define min(x, y) ((x) < (y) ? (x) : (y))
40#define max(x, y) ((x) > (y) ? (x) : (y))
41#endif
42
43enum dev_cmd {
44 CMD_ADDR = 0,
45 CMD_LEN = 1,
46 CMD_RD = 2,
47 CMD_WR = 3,
48 CMD_RY = 4,
49 CMD_DELAY = 5,
50};
11d3b79e 51#define PAR_MODE8 (1 << 4) /* but still drives noth LWR and UWR */
d9502b8d 52#define PAR_DEV_ID (1 << 5)
53#define PAR_SINGE (1 << 6)
54#define PAR_INC (1 << 7)
55
bc4a856a 56static struct flash_info {
57 uint32_t prog_addr;
58 uint16_t mid;
59 uint16_t did;
60 uint32_t size;
61 uint16_t region_cnt;
62 struct {
63 uint32_t block_size;
64 uint32_t block_count;
65 uint32_t start;
66 uint32_t size;
67 } region[4];
68} info;
69
d9502b8d 70static int setup(int fd)
71{
72 struct termios tty;
73 int ret;
74
75 memset(&tty, 0, sizeof(tty));
76
77 ret = tcgetattr(fd, &tty);
78 if (ret != 0)
79 {
80 perror("tcgetattr");
81 return 1;
82 }
83
84 tty.c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP
85 | INLCR | IGNCR | ICRNL | IXON);
86 tty.c_oflag &= ~OPOST;
87 tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN);
88 tty.c_cflag &= ~(CSIZE | PARENB);
89 tty.c_cflag |= CS8;
90
91 //tty.c_cc[VMIN] = 1;
92 //tty.c_cc[VTIME] = 5; // 0.5 seconds read timeout
93
94 ret = tcsetattr(fd, TCSANOW, &tty);
95 if (ret != 0) {
96 perror("tcsetattr");
97 return ret;
98 }
99
100 return 0;
101}
102
103static int write_serial(int fd, const void *data, size_t size)
104{
105 int ret;
106
107 ret = write(fd, data, size);
108 if (ret != size) {
109 fprintf(stderr, "write %d/%zd: ", ret, size);
110 perror("");
111 exit(1);
112 }
113
114 return 0;
115}
116
117static int read_serial(int fd, void *data, size_t size)
118{
119 size_t got = 0;
120 int ret;
121
122 while (got < size) {
123 ret = read(fd, (char *)data + got, size - got);
124 if (ret <= 0) {
125 fprintf(stderr, "read %d %zd/%zd: ",
126 ret, got, size);
127 perror("");
128 exit(1);
129 }
130 got += ret;
131 }
132
133 return 0;
134}
135
c097fb89 136/* addr arg is always byte address */
137static void set_addr8(int fd, uint32_t addr)
d9502b8d 138{
139 uint8_t cmd[6] = {
c097fb89 140 CMD_ADDR, addr >> 16,
141 CMD_ADDR, addr >> 8,
142 CMD_ADDR, addr >> 0
143 };
144 write_serial(fd, cmd, sizeof(cmd));
145}
146
147static void set_addr16(int fd, uint32_t addr)
148{
149 set_addr8(fd, addr >> 1);
150}
151
152static uint16_t read_bus8(int fd, uint32_t addr)
153{
154 uint8_t cmd[7] = {
155 CMD_ADDR, addr >> 16,
156 CMD_ADDR, addr >> 8,
157 CMD_ADDR, addr >> 0,
158 CMD_RD | PAR_SINGE | PAR_MODE8
d9502b8d 159 };
c097fb89 160 uint8_t r;
161
d9502b8d 162 write_serial(fd, cmd, sizeof(cmd));
c097fb89 163 read_serial(fd, &r, sizeof(r));
164 return r;
d9502b8d 165}
166
c097fb89 167static uint16_t read_bus16(int fd, uint32_t addr)
d9502b8d 168{
169 uint8_t cmd[7] = {
170 CMD_ADDR, addr >> 17,
171 CMD_ADDR, addr >> 9,
172 CMD_ADDR, addr >> 1,
173 CMD_RD | PAR_SINGE
174 };
175 uint16_t r;
176
177 write_serial(fd, cmd, sizeof(cmd));
178 read_serial(fd, &r, sizeof(r));
179 return ntohs(r);
180}
181
c097fb89 182static void write_bus8(int fd, uint32_t addr, uint16_t d)
183{
184 uint8_t cmd[8] = {
185 CMD_ADDR, addr >> 16,
186 CMD_ADDR, addr >> 8,
187 CMD_ADDR, addr >> 0,
188 CMD_WR | PAR_SINGE | PAR_MODE8,
189 d
190 };
191
192 write_serial(fd, cmd, sizeof(cmd));
193}
194
195static void write_bus16(int fd, uint32_t addr, uint16_t d)
d9502b8d 196{
197 uint8_t cmd[9] = {
198 CMD_ADDR, addr >> 17,
199 CMD_ADDR, addr >> 9,
200 CMD_ADDR, addr >> 1,
201 CMD_WR | PAR_SINGE,
202 d >> 8, d
203 };
204
205 write_serial(fd, cmd, sizeof(cmd));
206}
207
c097fb89 208static void read_block8(int fd, void *dst, uint32_t size)
d9502b8d 209{
c097fb89 210 // PAR_MODE8 does not work, so read as 16bit and throw away MSB
211 uint8_t tmp[0x10000], *d8 = dst;
d9502b8d 212 uint8_t cmd[5] = {
c097fb89 213 CMD_LEN, size >> 8,
214 CMD_LEN, size >> 0,
d9502b8d 215 CMD_RD | PAR_INC
216 };
c097fb89 217 int i;
d9502b8d 218
219 assert(size <= 0x10000);
220 write_serial(fd, cmd, sizeof(cmd));
221 read_serial(fd, dst, size);
c097fb89 222 read_serial(fd, tmp, size);
223
224 for (i = 0; i < size / 2; i++)
225 d8[i] = d8[i * 2 + 1];
226 d8 += size / 2;
227 for (i = 0; i < size / 2; i++)
228 d8[i] = tmp[i * 2 + 1];
d9502b8d 229}
230
c097fb89 231static void read_block16(int fd, void *dst, uint32_t size)
d9502b8d 232{
c097fb89 233 uint8_t cmd[5] = {
234 CMD_LEN, size >> 9,
235 CMD_LEN, size >> 1,
236 CMD_RD | PAR_INC
237 };
d9502b8d 238
c097fb89 239 assert(size <= 0x10000);
240 write_serial(fd, cmd, sizeof(cmd));
241 read_serial(fd, dst, size);
d9502b8d 242}
243
c097fb89 244static void flash_seq_write8(int fd, uint32_t addr, const uint8_t *d)
d9502b8d 245{
c097fb89 246 uint8_t cmd[] = {
247 // unlock
bc4a856a 248 CMD_ADDR, info.prog_addr >> 16,
249 CMD_ADDR, info.prog_addr >> 8,
250 CMD_ADDR, info.prog_addr >> 0,
c097fb89 251 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
bc4a856a 252 CMD_ADDR, info.prog_addr >> 17,
253 CMD_ADDR, info.prog_addr >> 9,
254 CMD_ADDR, info.prog_addr >> 1,
c097fb89 255 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
256 // program setup
bc4a856a 257 CMD_ADDR, info.prog_addr >> 16,
258 CMD_ADDR, info.prog_addr >> 8,
259 CMD_ADDR, info.prog_addr >> 0,
c097fb89 260 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
261 // program data
262 CMD_ADDR, addr >> 16,
263 CMD_ADDR, addr >> 8,
264 CMD_ADDR, addr >> 0,
265 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
266 CMD_RY
267 };
d9502b8d 268
bc4a856a 269 assert(info.prog_addr);
c097fb89 270 write_serial(fd, cmd, sizeof(cmd));
d9502b8d 271}
272
c097fb89 273static void flash_seq_write16(int fd, uint32_t addr, const uint8_t *d)
d9502b8d 274{
275 uint8_t cmd[] = {
276 // unlock
bc4a856a 277 CMD_ADDR, info.prog_addr >> 17,
278 CMD_ADDR, info.prog_addr >> 9,
279 CMD_ADDR, info.prog_addr >> 1,
d9502b8d 280 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
bc4a856a 281 CMD_ADDR, info.prog_addr >> 18,
282 CMD_ADDR, info.prog_addr >> 10,
283 CMD_ADDR, info.prog_addr >> 2,
d9502b8d 284 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
285 // program setup
bc4a856a 286 CMD_ADDR, info.prog_addr >> 17,
287 CMD_ADDR, info.prog_addr >> 9,
288 CMD_ADDR, info.prog_addr >> 1,
d9502b8d 289 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
290 // program data
291 CMD_ADDR, addr >> 17,
292 CMD_ADDR, addr >> 9,
293 CMD_ADDR, addr >> 1,
294 CMD_WR | PAR_SINGE, d[0], d[1],
295 CMD_RY
296 };
297
bc4a856a 298 assert(info.prog_addr);
d9502b8d 299 write_serial(fd, cmd, sizeof(cmd));
300}
301
563a41af 302// -- 8bit+LoROM --
303
304static uint32_t lorom_rom_addr(uint32_t a)
c5e4a2c2 305{
563a41af 306 return ((a & 0x7f8000) << 1) | 0x8000 | (a & 0x7fff);
307}
308
309static void set_addr8l(int fd, uint32_t a)
310{
311 set_addr8(fd, lorom_rom_addr(a));
312}
313
314static uint16_t read_bus8l(int fd, uint32_t a)
315{
316 return read_bus8(fd, lorom_rom_addr(a));
317}
318
319static void write_bus8l(int fd, uint32_t a, uint16_t d)
320{
321 write_bus8(fd, lorom_rom_addr(a), d);
322}
323
324static void flash_seq_write8l(int fd, uint32_t a, const uint8_t *d)
325{
326 a = lorom_rom_addr(a);
c5e4a2c2 327 uint8_t cmd[] = {
328 // unlock
329 CMD_ADDR, 0,
330 CMD_ADDR, 0x8a,
331 CMD_ADDR, 0xaa,
332 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
333 CMD_ADDR, 0,
334 CMD_ADDR, 0x85,
335 CMD_ADDR, 0x55,
336 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
337 // program setup
338 CMD_ADDR, 0,
339 CMD_ADDR, 0x8a,
340 CMD_ADDR, 0xaa,
341 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
342 // program data
563a41af 343 CMD_ADDR, a >> 16,
344 CMD_ADDR, a >> 8,
345 CMD_ADDR, a >> 0,
c5e4a2c2 346 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
347 CMD_RY
348 };
349
350 write_serial(fd, cmd, sizeof(cmd));
351}
352
563a41af 353// -- 8bit+LoROM+adapter --
354
355static uint32_t do_flipflops(int fd, uint32_t a)
356{
357 static uint32_t abits_now = ~0u; // A23, A22, A21
358 uint32_t abits = (a >> 21) & 7;
359
360 if (abits != abits_now) {
361 // printf("flipflops: %x->%x\n", abits_now, abits);
362 write_bus16(fd, 0xa13000, abits);
363 abits_now = abits;
364 }
365 return a & 0x1fffff;
366}
367
368static void set_addr8la(int fd, uint32_t a)
369{
370 set_addr8(fd, do_flipflops(fd, lorom_rom_addr(a)));
371}
372
373static uint16_t read_bus8la(int fd, uint32_t a)
374{
375 return read_bus8(fd, do_flipflops(fd, lorom_rom_addr(a)));
376}
377
378static void write_bus8la(int fd, uint32_t a, uint16_t d)
379{
380 write_bus8(fd, do_flipflops(fd, lorom_rom_addr(a)), d);
381}
382
383static void flash_seq_write8la(int fd, uint32_t a, const uint8_t *d)
384{
385 // we should clear flipflops for the flash commands, but this
386 // doesn't seem to be necessary as the flash chip seems to
387 // ignore the upper bits when looking for commands, and this
388 // extra clearing would slow things down
389 a = do_flipflops(fd, lorom_rom_addr(a));
390 uint8_t cmd[] = {
391 // unlock
392 CMD_ADDR, 0,
393 CMD_ADDR, 0x8a,
394 CMD_ADDR, 0xaa,
395 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
396 CMD_ADDR, 0,
397 CMD_ADDR, 0x85,
398 CMD_ADDR, 0x55,
399 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
400 // program setup
401 CMD_ADDR, 0,
402 CMD_ADDR, 0x8a,
403 CMD_ADDR, 0xaa,
404 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
405 // program data
406 CMD_ADDR, a >> 16,
407 CMD_ADDR, a >> 8,
408 CMD_ADDR, a >> 0,
409 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
410 CMD_RY
411 };
412
413 write_serial(fd, cmd, sizeof(cmd));
414}
415
416// -- 8bit+LoROM+adapter+sram --
417
418static uint32_t lorom_sram_addr(uint32_t a)
419{
420 return a | 0x600000;
421}
422
423static void set_addr8las(int fd, uint32_t a)
424{
425 set_addr8(fd, do_flipflops(fd, lorom_sram_addr(a)));
426}
427
428static uint16_t read_bus8las(int fd, uint32_t a)
429{
430 return read_bus8(fd, do_flipflops(fd, lorom_sram_addr(a)));
431}
432
433static void write_bus8las(int fd, uint32_t a, uint16_t d)
434{
435 write_bus8(fd, do_flipflops(fd, lorom_sram_addr(a)), d);
436}
437
438static void flash_seq_write8las(int fd, uint32_t a, const uint8_t *d)
439{
440}
441
f4924e98 442// -- 8bit+adapter --
443
444static void set_addr8a(int fd, uint32_t a)
445{
446 set_addr8(fd, do_flipflops(fd, a));
447}
448
449static uint16_t read_bus8a(int fd, uint32_t a)
450{
451 return read_bus8(fd, do_flipflops(fd, a));
452}
453
454static void write_bus8a(int fd, uint32_t a, uint16_t d)
455{
456 write_bus8(fd, do_flipflops(fd, a), d);
457}
458
459static void flash_seq_write8a(int fd, uint32_t a, const uint8_t *d)
460{
461 // no flipflop clearing, see flash_seq_write8la
462 a = do_flipflops(fd, a);
463 uint8_t cmd[] = {
464 // unlock
465 CMD_ADDR, 0,
466 CMD_ADDR, 0x8a,
467 CMD_ADDR, 0xaa,
468 CMD_WR | PAR_SINGE | PAR_MODE8, 0xaa,
469 CMD_ADDR, 0,
470 CMD_ADDR, 0x85,
471 CMD_ADDR, 0x55,
472 CMD_WR | PAR_SINGE | PAR_MODE8, 0x55,
473 // program setup
474 CMD_ADDR, 0,
475 CMD_ADDR, 0x8a,
476 CMD_ADDR, 0xaa,
477 CMD_WR | PAR_SINGE | PAR_MODE8, 0xa0,
478 // program data
479 CMD_ADDR, a >> 16,
480 CMD_ADDR, a >> 8,
481 CMD_ADDR, a >> 0,
482 CMD_WR | PAR_SINGE | PAR_MODE8, *d,
483 CMD_RY
484 };
485
486 write_serial(fd, cmd, sizeof(cmd));
487}
488
489// -- 8bit+adapter+nocart --
490
491static void set_addr8an(int fd, uint32_t a)
492{
493 set_addr8(fd, do_flipflops(fd, a) | 0x200000);
494}
495
496static uint16_t read_bus8an(int fd, uint32_t a)
497{
498 return read_bus8(fd, do_flipflops(fd, a) | 0x200000);
499}
500
501static void write_bus8an(int fd, uint32_t a, uint16_t d)
502{
503 write_bus8(fd, do_flipflops(fd, a) | 0x200000, d);
504}
505
506static void flash_seq_write8an(int fd, uint32_t a, const uint8_t *d)
507{
508}
509
c5e4a2c2 510#define N0 ""
511#define N1 "8bit"
512#define N2 "8bit+LoROM"
513#define N3 "8bit+LoROM+adapter"
563a41af 514#define N4 "8bit+LoROM+adapter+sram"
f4924e98 515#define N5 "8bit+adapter (use '-a 0x400000' for HiROM)"
516#define N6 "8bit+adapter+nocart"
c097fb89 517static const struct iof
518{
c5e4a2c2 519 const char *name;
520 int addrs_remapped;
c097fb89 521 void (*set_addr)(int fd, uint32_t addr);
522 uint16_t (*read_bus)(int fd, uint32_t addr);
523 void (*write_bus)(int fd, uint32_t addr, uint16_t d);
524 void (*read_block)(int fd, void *dst, uint32_t size);
525 void (*flash_seq_write)(int fd, uint32_t addr, const uint8_t *d);
526}
527io_ops[] =
528{
563a41af 529 { N0, 0, set_addr16, read_bus16, write_bus16, read_block16, flash_seq_write16 },
530 { N1, 0, set_addr8, read_bus8, write_bus8, read_block8, flash_seq_write8 },
531 { N2, 1, set_addr8l, read_bus8l, write_bus8l, read_block8, flash_seq_write8l },
532 { N3, 1, set_addr8la, read_bus8la, write_bus8la, read_block8, flash_seq_write8la },
533 { N4, 0, set_addr8las, read_bus8las, write_bus8las, read_block8, flash_seq_write8las },
f4924e98 534 { N5, 0, set_addr8a, read_bus8a, write_bus8a, read_block8, flash_seq_write8a },
535 { N6, 0, set_addr8an, read_bus8an, write_bus8an, read_block8, flash_seq_write8an },
c097fb89 536};
537
c5e4a2c2 538static const struct iof *io = &io_ops[0];
c097fb89 539
540static uint16_t flash_seq_r(int fd, uint8_t cmd, uint32_t addr)
541{
542 // unlock
bc4a856a 543 assert(info.prog_addr);
544 io->write_bus(fd, info.prog_addr >> 0, 0xaa);
545 io->write_bus(fd, info.prog_addr >> 1, 0x55);
c097fb89 546
bc4a856a 547 io->write_bus(fd, info.prog_addr >> 0, cmd);
c097fb89 548 return io->read_bus(fd, addr);
549}
550
c5e4a2c2 551static void flash_seq_erase_d(int fd, uint32_t addr, uint8_t d)
c097fb89 552{
553 // printf("erase %06x\n", addr);
bc4a856a 554 assert(info.prog_addr);
555 io->write_bus(fd, info.prog_addr >> 0, 0xaa);
556 io->write_bus(fd, info.prog_addr >> 1, 0x55);
557 io->write_bus(fd, info.prog_addr >> 0, 0x80);
c097fb89 558
bc4a856a 559 io->write_bus(fd, info.prog_addr >> 0, 0xaa);
560 io->write_bus(fd, info.prog_addr >> 1, 0x55);
c5e4a2c2 561 io->write_bus(fd, addr, d);
562}
563
564static void flash_seq_erase(int fd, uint32_t addr)
565{
566 flash_seq_erase_d(fd, addr, 0x30);
567}
568
569static void flash_seq_erase_full(int fd)
570{
bc4a856a 571 flash_seq_erase_d(fd, info.prog_addr, 0x10);
c097fb89 572}
573
d9502b8d 574// status wait + dummy read to cause a wait?
575static uint16_t ry_read(int fd)
576{
577 uint8_t cmd[2] = { CMD_RY, CMD_RD | PAR_SINGE };
578 uint16_t rv = 0;
579
580 write_serial(fd, cmd, sizeof(cmd));
581 read_serial(fd, &rv, sizeof(rv));
582 return ntohs(rv);
583}
584
585static void set_delay(int fd, uint8_t delay)
586{
587 uint8_t cmd[2] = { CMD_DELAY, delay };
588
589 write_serial(fd, cmd, sizeof(cmd));
590}
591
d9502b8d 592static void read_info(int fd)
593{
594 static const uint16_t qry[3] = { 'Q', 'R', 'Y' };
bc4a856a 595 uint16_t resp_cfi[3], sst_mid = ~0;
d9502b8d 596 uint32_t total = 0;
d9502b8d 597 uint32_t i, a;
598
d9502b8d 599
bc4a856a 600 // see if this chip understands CFI (common flash interface)
601 io->write_bus(fd, 0xaa, 0x98);
602 resp_cfi[0] = io->read_bus(fd, 0x20);
603 resp_cfi[1] = io->read_bus(fd, 0x22);
604 resp_cfi[2] = io->read_bus(fd, 0x24);
605 if (memcmp(resp_cfi, qry, sizeof(resp_cfi)) == 0)
606 {
607 info.size = 1u << io->read_bus(fd, 0x4e);
608 info.region_cnt = io->read_bus(fd, 0x58);
609 assert(0 < info.region_cnt && info.region_cnt <= 4);
610 for (i = 0, a = 0x5a; i < info.region_cnt; i++, a += 8) {
611 info.region[i].block_count = io->read_bus(fd, a + 0) + 1;
612 info.region[i].block_count += io->read_bus(fd, a + 2) << 8;
613 info.region[i].block_size = io->read_bus(fd, a + 4) << 8;
614 info.region[i].block_size |= io->read_bus(fd, a + 6) << 16;
615 info.region[i].start = total;
616 info.region[i].size =
617 info.region[i].block_size * info.region[i].block_count;
618 assert(info.region[i].size);
619 total += info.region[i].size;
620 }
621 if (info.size != total)
622 fprintf(stderr, "warning: total is %u, bad CFI?\n", total);
d9502b8d 623
bc4a856a 624 info.prog_addr = 0xaaa;
625 io->write_bus(fd, 0, 0xf0);
626 info.mid = flash_seq_r(fd, 0x90, 0); // autoselect
627 info.did = io->read_bus(fd, 2);
d9502b8d 628 }
bc4a856a 629 else
630 {
631 // try the SST protocol
632 info.prog_addr = 0x5555;
633 sst_mid = flash_seq_r(fd, 0x90, 0);
634 if (sst_mid == 0xbf)
635 {
636 info.mid = sst_mid;
637 info.did = io->read_bus(fd, 0x01);
638 switch (info.did) {
639 case 0xb5:
640 case 0xb6:
641 case 0xb7:
642 info.size = 128 * 1024 << (info.did - 0xb5);
643 break;
644 default:
645 fprintf(stderr, "unrecognized SST device %02x\n", info.did);
646 exit(1);
647 }
648 info.region_cnt = 1;
649 info.region[0].block_count = info.size / 4096;
650 info.region[0].block_size = 4096;
651 info.region[0].start = 0;
652 info.region[0].size = info.size;
653 }
654 else
655 info.prog_addr = 0;
d9502b8d 656 }
657
c097fb89 658 io->write_bus(fd, 0, 0xf0); // flash reset
d9502b8d 659
bc4a856a 660 if (info.prog_addr == 0) {
661 fprintf(stderr, "unable to identify the flash chip :(\n");
662 fprintf(stderr, "CFI response: %02x %02x %02x\n",
663 resp_cfi[0], resp_cfi[1], resp_cfi[2]);
664 fprintf(stderr, "SST MID: %02x\n", sst_mid);
665 exit(1);
666 }
667
d9502b8d 668 printf("Flash info:\n");
669 printf("Manufacturer ID: %04x\n", info.mid);
670 printf("Device ID: %04x\n", info.did);
671 printf("size: %u\n", info.size);
672 printf("Erase Block Regions: %u\n", info.region_cnt);
673 for (i = 0; i < info.region_cnt; i++)
674 printf(" %5u x %u\n", info.region[i].block_size,
675 info.region[i].block_count);
d9502b8d 676}
677
678static uint32_t get_block_addr(uint32_t addr, uint32_t blk_offset)
679{
f34ac796 680 uint32_t i, base, faddr;
d9502b8d 681
682 assert(info.region_cnt);
f34ac796 683 assert(info.size);
684
685 // get a flash address to allow mapper hardware
686 faddr = addr & (info.size - 1);
687 base = addr & ~(info.size - 1);
688
d9502b8d 689 for (i = 0; i < info.region_cnt; i++) {
f34ac796 690 if (info.region[i].start <= faddr
691 && faddr < info.region[i].start + info.region[i].size)
d9502b8d 692 {
f34ac796 693 uint32_t blk = (faddr - info.region[i].start)
d9502b8d 694 / info.region[i].block_size
695 + blk_offset;
f34ac796 696 return base + info.region[i].start
d9502b8d 697 + blk * info.region[i].block_size;
698 }
699 }
700
701 fprintf(stderr, "\naddress out of range: 0x%x\n", addr);
702 exit(1);
703}
704
705static void print_progress(uint32_t done, uint32_t total)
706{
707 int i, step;
708
c097fb89 709 printf("\r%06x/%06x |", done, total);
d9502b8d 710
711 step = (total + 19) / 20;
712 for (i = step; i <= total; i += step)
713 fputc(done >= i ? '=' : '-', stdout);
714 printf("| %3d%%", done * 100 / total);
715 fflush(stdout);
716 if (done >= total)
717 fputc('\n', stdout);
718}
719
78ad8139 720static FILE *open_prep_read(const char *fname, long *size)
721{
722 FILE *f = fopen(fname, "rb");
723 if (!f) {
724 fprintf(stderr, "fopen %s: ", fname);
725 perror("");
726 exit(1);
727 }
728 if (*size <= 0) {
729 fseek(f, 0, SEEK_END);
730 *size = ftell(f);
731 fseek(f, 0, SEEK_SET);
732 }
733 if (*size <= 0) {
734 fprintf(stderr, "size of %s is %ld\n", fname, *size);
735 exit(1);
736 }
737 return f;
738}
739
11d3b79e 740static const char *portname =
741#ifdef __APPLE__
742 "/dev/cu.usbserial-AL0254JM";
743#else
744 "/dev/ttyUSB0";
745#endif
af236294 746
d9502b8d 747static void usage(const char *argv0)
748{
c5e4a2c2 749 size_t i;
750
d9502b8d 751 printf("usage:\n"
752 "%s [options]\n"
af236294 753 " -d <ttydevice> (default %s)\n"
d9502b8d 754 " -r <file> [size] dump the cart (default 4MB)\n"
78ad8139 755 " -w <file> [size] program the flash (def. file size)\n"
756 " -s <file> [size] simple write (SRAM, etc, def. file size)\n"
c5e4a2c2 757 " -e <size> erase (rounds to block size); can specify 'full'\n"
78ad8139 758 " -a <start_address> read/write start address (default 0)\n"
c5e4a2c2 759 " -m <n> use an address mapper n, one of:\n"
af236294 760 , argv0, portname);
c5e4a2c2 761 for (i = 1; i < sizeof(io_ops) / sizeof(io_ops[0]); i++)
762 printf(
763 " %zd: %s\n", i, io_ops[i].name);
764 printf( " -v verify written data\n"
765 " -i get info about the flash chip\n");
d9502b8d 766 exit(1);
767}
768
769static void invarg(int argc, char *argv[], int arg)
770{
771 if (arg < argc)
772 fprintf(stderr, "invalid arg %d: \"%s\"\n", arg, argv[arg]);
773 else
774 fprintf(stderr, "missing required argument %d\n", arg);
775 exit(1);
776}
777
778static void *getarg(int argc, char *argv[], int arg)
779{
780 if (arg >= argc)
781 invarg(argc, argv, arg);
782 return argv[arg];
783}
784
c5e4a2c2 785static long getarg_l(int argc, char *argv[], int arg)
786{
787 char *endp = NULL;
788 long r;
789
790 if (arg >= argc)
791 invarg(argc, argv, arg);
792 r = strtol(argv[arg], &endp, 0);
793 if (endp == NULL || *endp != 0)
794 invarg(argc, argv, arg);
795 return r;
796}
797
798// 32K to easily handle SNES LoROM
799static uint8_t g_block[0x8000];
800static uint8_t g_block2[sizeof(g_block)];
d9502b8d 801
802int main(int argc, char *argv[])
803{
d9502b8d 804 const char *fname_w = NULL;
805 const char *fname_r = NULL;
78ad8139 806 const char *fname_ws = NULL;
d9502b8d 807 long size_w = 0;
808 long size_r = 0;
78ad8139 809 long size_ws = 0;
d9502b8d 810 long size_e = 0;
811 long size_v = 0;
812 long len, address_in = 0;
813 long a, a_blk, end;
814 int do_info = 0;
815 int do_verify = 0;
c097fb89 816 int write_step = 2;
d9502b8d 817 FILE *f_w = NULL;
818 FILE *f_r = NULL;
78ad8139 819 FILE *f_ws = NULL;
d9502b8d 820 uint8_t id[2] = { 0, 0 };
821 uint8_t cmd;
822 uint16_t rv;
823 int arg = 1;
824 int fd;
825
826 if (argc < 2 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help"))
827 usage(argv[0]);
828
829 for (arg = 1; arg < argc; arg++) {
830 if (!strcmp(argv[arg], "-d")) {
831 portname = getarg(argc, argv, ++arg);
832 continue;
833 }
834 if (!strcmp(argv[arg], "-r")) {
835 fname_r = getarg(argc, argv, ++arg);
836 if (arg + 1 < argc && argv[arg + 1][0] != '-') {
c5e4a2c2 837 size_r = getarg_l(argc, argv, ++arg);
d9502b8d 838 if (size_r <= 0)
839 invarg(argc, argv, arg);
840 }
841 continue;
842 }
843 if (!strcmp(argv[arg], "-w")) {
844 fname_w = getarg(argc, argv, ++arg);
845 if (arg + 1 < argc && argv[arg + 1][0] != '-') {
c5e4a2c2 846 size_w = getarg_l(argc, argv, ++arg);
d9502b8d 847 if (size_w <= 0)
848 invarg(argc, argv, arg);
849 }
850 continue;
851 }
78ad8139 852 if (!strcmp(argv[arg], "-s")) {
853 fname_ws = getarg(argc, argv, ++arg);
854 if (arg + 1 < argc && argv[arg + 1][0] != '-') {
c5e4a2c2 855 size_ws = getarg_l(argc, argv, ++arg);
78ad8139 856 if (size_ws <= 0)
857 invarg(argc, argv, arg);
858 }
859 continue;
860 }
d9502b8d 861 if (!strcmp(argv[arg], "-a")) {
c5e4a2c2 862 address_in = getarg_l(argc, argv, ++arg);
863 if (address_in < 0)
d9502b8d 864 invarg(argc, argv, arg);
865 continue;
866 }
867 if (!strcmp(argv[arg], "-e")) {
c5e4a2c2 868 arg++;
869 if (!strcmp(getarg(argc, argv, arg), "full"))
870 size_e = -1;
871 else {
872 size_e = getarg_l(argc, argv, arg);
873 if (size_e <= 0)
874 invarg(argc, argv, arg);
875 }
d9502b8d 876 continue;
877 }
c5e4a2c2 878 if (!strcmp(argv[arg], "-m")) {
879 long v = getarg_l(argc, argv, ++arg);
880 if ((size_t)v >= sizeof(io_ops) / sizeof(io_ops[0]))
881 invarg(argc, argv, arg);
882 io = &io_ops[v];
883 if (v != 0)
884 write_step = 1;
c097fb89 885 continue;
886 }
d9502b8d 887 if (!strcmp(argv[arg], "-v")) {
888 do_verify = 1;
889 continue;
890 }
891 if (!strcmp(argv[arg], "-i")) {
892 do_info = 1;
893 continue;
894 }
895 invarg(argc, argv, arg);
896 }
897
898 if (fname_r && size_r == 0)
899 size_r = 0x400000;
900
901 if (fname_w) {
78ad8139 902 f_w = open_prep_read(fname_w, &size_w);
c5e4a2c2 903 if (size_e == 0 && io->addrs_remapped)
904 size_e = -1;
905 if (size_e != -1 && size_e < size_w)
d9502b8d 906 size_e = size_w;
907 if (do_verify)
908 size_v = size_w;
909 }
910
911 fd = open(portname, O_RDWR | O_NOCTTY | O_SYNC);
912 if (fd < 0) {
913 fprintf(stderr, "open %s: ", portname);
914 perror("");
915 return 1;
916 }
917
78ad8139 918 if (fname_ws)
919 f_ws = open_prep_read(fname_ws, &size_ws);
920
d9502b8d 921 setup(fd);
922
923 cmd = CMD_RD | PAR_SINGE | PAR_DEV_ID;
924 write_serial(fd, &cmd, sizeof(cmd));
925 read_serial(fd, id, sizeof(id));
926 if (id[0] != id[1] || id[0] == 0) {
927 fprintf(stderr, "unexpected id: %02x %02x\n", id[0], id[1]);
928 return 1;
929 }
930 printf("flashkit id: %02x\n", id[0]);
931
932 set_delay(fd, 1);
5a2c48a5 933
934 if (do_info || size_e || f_w)
935 io->write_bus(fd, 0, 0xf0); // flash reset
d9502b8d 936
937 if (do_info || size_e)
938 read_info(fd);
939
c5e4a2c2 940 if (size_e == -1) {
941 printf("performing full erase..."); fflush(stdout);
942 flash_seq_erase_full(fd);
943 rv = ry_read(fd);
944 if (rv != 0xffff) {
945 fprintf(stderr, "\nerase error: %04x\n", rv);
946 return 1;
947 }
948 printf(" done.\n");
949 }
950 else if (size_e) {
d9502b8d 951 // set_delay(fd, 0); // ?
952 a_blk = get_block_addr(address_in, 0);
953 end = address_in + size_e;
954
955 printf("erasing %ld bytes:\n", size_e);
956 print_progress(0, size_e);
957 for (a = address_in; a < end; ) {
958 flash_seq_erase(fd, a_blk);
959 rv = ry_read(fd);
960 if (rv != 0xffff) {
961 fprintf(stderr, "\nerase error: %lx %04x\n",
962 a_blk, rv);
c5e4a2c2 963 return 1;
d9502b8d 964 }
965
966 a_blk = get_block_addr(a_blk, 1);
967 a += a_blk - a;
968 print_progress(a - address_in, size_e);
969 }
970 }
971 if (f_w != NULL) {
972 uint8_t b[2];
973 set_delay(fd, 0);
78ad8139 974 printf("flashing %ld bytes:\n", size_w);
c097fb89 975 for (a = 0; a < size_w; a += write_step) {
d9502b8d 976 ssize_t r;
977
978 b[1] = 0xff;
c097fb89 979 len = min(size_w - a, write_step);
d9502b8d 980 r = fread(b, 1, len, f_w);
981 if (r != len) {
982 perror("\nfread");
983 return 1;
984 }
c097fb89 985 io->flash_seq_write(fd, address_in + a, b);
d9502b8d 986
c097fb89 987 if (!(a & 0x3ff))
d9502b8d 988 print_progress(a, size_w);
989 }
990 print_progress(a, size_w);
991 rv = ry_read(fd);
c097fb89 992 if (write_step == 2 && rv != ((b[0] << 8) | b[1]))
d9502b8d 993 fprintf(stderr, "warning: last bytes: %04x %02x%02x\n",
994 rv, b[0], b[1]);
995 rewind(f_w);
996 set_delay(fd, 1);
997 }
78ad8139 998 if (f_ws != NULL) {
999 printf("writing %ld bytes:\n", size_ws);
1000 for (a = 0; a < size_ws; a += write_step) {
1001 uint16_t b = 0xffff;
1002 ssize_t r;
1003
1004 len = min(size_ws - a, write_step);
1005 r = fread(&b, 1, len, f_ws);
1006 if (r != len) {
1007 perror("\nfread");
1008 return 1;
1009 }
1010 if (write_step == 2)
1011 b = htons(b);
1012 io->write_bus(fd, address_in + a, b);
1013
1014 if (!(a & 0x3ff))
1015 print_progress(a, size_ws);
1016 }
1017 print_progress(a, size_ws);
1018 }
d9502b8d 1019
1020 if (fname_r || size_v) {
1021 long blks, blks_v, done, verify_diff = 0;
1022
1023 blks = (size_r + sizeof(g_block) - 1) / sizeof(g_block);
1024 blks_v = (size_v + sizeof(g_block) - 1) / sizeof(g_block);
1025 blks = max(blks, blks_v);
1026 if (fname_r) {
1027 f_r = fopen(fname_r, "wb");
1028 if (!f_r) {
1029 fprintf(stderr, "fopen %s: ", fname_r);
1030 perror("");
1031 return 1;
1032 }
1033 }
1034
1035 printf("reading %ld bytes:\n", max(size_r, size_v));
1036 print_progress(0, blks * sizeof(g_block));
f4924e98 1037 a_blk = -1;
d9502b8d 1038 for (done = 0; done < size_r || done < size_v; ) {
f4924e98 1039 a = address_in + done;
1040 if (io->addrs_remapped || (a >> 21) != a_blk) {
1041 a_blk = a >> 21;
1042 io->set_addr(fd, a);
1043 }
c097fb89 1044 io->read_block(fd, g_block, sizeof(g_block));
d9502b8d 1045 if (f_r && done < size_r) {
1046 len = min(size_r - done, sizeof(g_block));
1047 if (fwrite(g_block, 1, len, f_r) != len) {
1048 perror("fwrite");
1049 return 1;
1050 }
1051 }
1052 if (done < size_v) {
1053 len = min(size_v - done, sizeof(g_block));
1054 if (fread(g_block2, 1, len, f_w) != len) {
1055 perror("fread");
1056 return 1;
1057 }
1058 verify_diff |= memcmp(g_block, g_block2, len);
1059 }
1060 done += sizeof(g_block);
1061 print_progress(done, blks * sizeof(g_block));
1062 }
1063 if (verify_diff) {
1064 fprintf(stderr, "verify FAILED\n");
1065 return 1;
1066 }
1067 }
1068 if (f_r)
1069 fclose(f_r);
1070 if (f_w)
1071 fclose(f_w);
1072
1073 return 0;
1074}