/*
* 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;
}