/* * JR-IDE Project * - (c) 2017 Alan Hightower * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #define JRIDE_PROBE_LITE 1 #include "jride.h" #include "flash_api.h" #ifndef MIN #define MIN(a,b) (((a) > (b)) ? (b) : (a)) #endif static volatile uint8_t __far *bios_window; static volatile uint8_t __far *remap_window; static void _flash_remap (uint8_t page) { #if JRIDE_ISA bios_window[JRIDE_FLASH_BASE_REG] = ((FP_SEG(remap_window) >> 9) & 0x0f) | 0x00; bios_window[JRIDE_FLASH_PAGE_REG] = page | 0x80; #else bios_window[JRIDE_FLASH_MAP_REG] = page | ((FP_SEG(remap_window) >> 5) & 0x060) | 0x80; #endif return; } static void _flash_unmap (void) { #if JRIDE_ISA bios_window[JRIDE_FLASH_PAGE_REG] = 0x00; #else bios_window[JRIDE_FLASH_MAP_REG] = 0x00; #endif return; } #define FLASH_PREAMBLE(address, byte) { \ _flash_remap (address >> JRIDE_FLASH_PAGE_LOG2); \ remap_window[address & JRIDE_FLASH_PAGE_MASK] = byte; } static int _flash_probe (void) { int size = 0; // Enter ID mode FLASH_PREAMBLE(0x5555, 0xaa); FLASH_PREAMBLE(0x2aaa, 0x55); FLASH_PREAMBLE(0x5555, 0x90); _flash_remap (0); switch (remap_window[0]) { case 0xbf: // Microchip & Greenliant (formerly SST) switch (remap_window[1]) { #if !JRIDE_PROBE_LITE case 0x07: size = 128; break; // Greenliant 1 MBit (GLS29EE010) case 0x13: size = 512; break; // Greenliant 4 Mbit (GLS29SF040) case 0x14: size = 512; break; // Greenliant 4 Mbit (GLS29VF040) case 0x24: size = 256; break; // Greenliant 2 Mbit (GLS29SF020) case 0x25: size = 256; break; // Greenliant 2 Mbit (GLS29VF020) case 0x5d: size = 64; break; // Greenliant 512 Kbit (GLS29EE512) case 0xa4: size = 64; break; // Greenliant 512 Kbit (GLS27SF512) case 0xa5: size = 128; break; // Greenliant 1 MBit (GLS27SF010) case 0xa6: size = 256; break; // Greenliant 2 MBit (GLS27SF020) #endif case 0xb4: size = 64; break; // Microchip/SST 512 Kb (SST39SF512) case 0xb5: size = 128; break; // Microchip/SST 1 Mbit (SST39SF010) case 0xb6: size = 256; break; // Microchip/SST 2 Mbit (SST39SF020) case 0xb7: size = 512; break; // Microchip/SST 4 Mbit (SST39SF040) } #if !JRIDE_PROBE_LITE case 0x1f: // Atmel switch (remap_window[1]) { case 0x03: size = 64; break; // Atmel 512 Kbit (AT49F/LV/BV512) case 0x04: size = 128; break; // Atmel 1 Mbit (AT49F/LV/BV001) case 0x05: size = 128; break; // Atmel 1 Mbit (AT49F/LV/BV001) case 0x07: size = 256; break; // Atmel 2 Mbit (AT49F/LV/BV002) case 0x08: size = 256; break; // Atmel 2 Mbit (AT49F/LV/BV002) case 0x5d: size = 64; break; // Atmel 512 Kbit (AT29C512) case 0xa4: size = 512; break; // Atmel 4 Mbit (AT29C040) case 0xb5: size = 512; break; // Atmel 4 Mbit (AT49F040) case 0xd5: size = 128; break; // Atmel 1 Mbit (AT29C010) case 0xda: size = 256; break; // Atmel 2 Mbit (AT29C020) } break; case 0x20: // STMicroelectronics (SGS/Thomson) switch (remap_window[1]) { case 0x20: size = 128; break; // ST 1 Mbit (M29F010) case 0x34: size = 256; break; // ST 2 Mbit (M29F002) case 0xb0: size = 256; break; // ST 2 Mbit (M29F002) case 0xe2: size = 512; break; // ST 4 Mbit (M29F040) } break; case 0x31: // ON Semiconductors (formerly Catalyst) switch (remap_window[1]) { case 0x94: size = 64; break; // ON 1 Mbit (CAT28F001) case 0x95: size = 64; break; // ON 1 Mbit (CAT28F001) case 0xb4: size = 128; break; // ON 1 Mbit (CAT28F010) case 0xb8: size = 64; break; // ON 512 Kbit (CAT28F512) case 0xbd: size = 256; break; // ON 2 Mbit (CAT28F020) } break; #endif } /* * Note: Many other manufacturers produced boot flash parts up to * F002 series including AMD (0x01), MXIC (0xc2), Fujitsu (0x04) * and others. */ // Return to normal mode FLASH_PREAMBLE(0x5555, 0xaa); FLASH_PREAMBLE(0x2aaa, 0x55); FLASH_PREAMBLE(0x5555, 0xf0); _flash_unmap (); return size; } #define SEG_ROM_START (JRIDE_ROM_RANGE_START >> 4) #define SEG_ROM_END (JRIDE_ROM_RANGE_END >> 4) #define SEG_ROM_STEP (JRIDE_ROM_WINDOW_SIZE >> 4) #define SEG_REMAP_START (JRIDE_FLASH_RANGE_START >> 4) #define SEG_REMAP_END (JRIDE_FLASH_RANGE_END >> 4) #define SEG_REMAP_STEP (JRIDE_FLASH_PAGE_SIZE >> 4) int jride_flash_poll (uint16_t *bios_seg, uint16_t *window_seg) { uint16_t segment = SEG_ROM_START, window; int size; // First gracefully look for the JR-IDE BIOS magic signature #if 0 for (window = SEG_ROM_START; window < SEG_ROM_END; window += SEG_ROM_STEP) { /* if found, segment = window & break */ } #endif for (; segment < SEG_ROM_END; segment += SEG_REMAP_STEP) { bios_window = (volatile uint8_t far *) MK_FP(segment, 0x0000); for (window = SEG_REMAP_START; window < SEG_REMAP_END; window += SEG_REMAP_STEP) { if (window == segment) continue; remap_window = (volatile uint8_t far *) MK_FP(window, 0x0000); if ((size = _flash_probe ())) { if (bios_seg) *bios_seg = (uint16_t) FP_SEG(bios_window); if (window_seg) *window_seg = (uint16_t) FP_SEG(remap_window); _flash_unmap (); return size; } } } _flash_unmap (); return 0; } void jride_flash_erase (uint8_t page, uint8_t sector) { printf ("Erasing sector %d / page %d\n", sector, page); FLASH_PREAMBLE(0x5555, 0xaa); FLASH_PREAMBLE(0x2aaa, 0x55); FLASH_PREAMBLE(0x5555, 0x80); FLASH_PREAMBLE(0x5555, 0xaa); FLASH_PREAMBLE(0x2aaa, 0x55); _flash_remap (page); remap_window[sector << 12] = 0x30; while (remap_window[sector << 12] != 0xff); _flash_unmap (); return; } void jride_flash_erase_all (void) { FLASH_PREAMBLE(0x5555, 0xaa); FLASH_PREAMBLE(0x2aaa, 0x55); FLASH_PREAMBLE(0x5555, 0x80); FLASH_PREAMBLE(0x5555, 0xaa); FLASH_PREAMBLE(0x2aaa, 0x55); FLASH_PREAMBLE(0x5555, 0x10); _flash_remap (0); while (remap_window[0] != 0xff); _flash_unmap (); return; } void jride_flash_read (uint8_t far *data, uint32_t offset, uint16_t remain) { uint16_t len, window_offset; while (remain) { window_offset = offset & JRIDE_FLASH_PAGE_MASK; len = MIN(JRIDE_FLASH_PAGE_SIZE - window_offset, remain); _flash_remap (offset >> JRIDE_FLASH_PAGE_LOG2); _fmemcpy (data, (void far *) &remap_window[window_offset], len); remain -= len; offset += len; data += len; } _flash_unmap (); return; } void jride_flash_write (uint32_t offset, uint8_t far *data, uint16_t remain) { uint8_t far *buffer = NULL; uint8_t far *src; volatile uint8_t far *dst; uint8_t erase, program; uint16_t i, sector, page; while (remain) { page = offset >> JRIDE_FLASH_PAGE_LOG2; sector = (offset >> 12) & ((1 << (JRIDE_FLASH_PAGE_LOG2 - 12)) - 1); dst = &remap_window[sector << 12]; _flash_remap (page); printf ("Setup %u/%u @ %04x:%04x - ", page, sector, FP_SEG(dst), FP_OFF(dst)); if (offset & 0x0fff) // leading partial sector update { uint16_t mid = offset & 0x0fff; uint16_t len = MIN(4096 - mid, remain); if (!buffer) buffer = (uint8_t far *) _fmalloc (4096); jride_flash_read (buffer, offset & ~0x0fff, mid); _fmemcpy (&buffer[mid], data, len); src = buffer; remain -= len; offset += len; data += len; } else if (remain < 4096) // trailing partial sector update { uint16_t mid = remain; if (!buffer) buffer = (uint8_t far *) _fmalloc (4096); _fmemcpy (buffer, data, mid); jride_flash_read (&buffer[mid], offset + mid, 4096 - mid); src = buffer; remain -= mid; offset += mid; data += mid; } else // full sector update { src = data; remain -= 4096; offset += 4096; data += 4096; } erase = program = 0; for (i = 0; i < 0x1000; i++) { if (src[i] == dst[i]) continue; program |= ((dst[i] & src[i]) != dst[i]); if (erase && program) break; if (dst[i] != 0xff) { erase = 1; if (program) break; } } if (erase) jride_flash_erase (page, sector); if (program) { printf ("Programming sector %d / page %d\n", sector, page); for (i = 0; i < 0x1000; i++) { if (src[i] == dst[i]) continue; FLASH_PREAMBLE(0x5555, 0xaa); FLASH_PREAMBLE(0x2aaa, 0x55); FLASH_PREAMBLE(0x5555, 0xa0); _flash_remap (page); dst[i] = src[i]; while (src[i] != dst[i]); } } } if (buffer) _ffree (buffer); _flash_unmap (); return; }