summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/generic_spi_flash.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/generic_spi_flash.cpp')
-rw-r--r--src/devices/machine/generic_spi_flash.cpp462
1 files changed, 322 insertions, 140 deletions
diff --git a/src/devices/machine/generic_spi_flash.cpp b/src/devices/machine/generic_spi_flash.cpp
index 7421004bf2e..6f24a33ef9e 100644
--- a/src/devices/machine/generic_spi_flash.cpp
+++ b/src/devices/machine/generic_spi_flash.cpp
@@ -1,5 +1,8 @@
// license:BSD-3-Clause
// copyright-holders:David Haywood
+
+// HLE-like implementation for SPI Flash ROMs using byte interface rather than SPI signals
+
#include "emu.h"
#include "generic_spi_flash.h"
@@ -9,12 +12,17 @@
#include "logmacro.h"
-DEFINE_DEVICE_TYPE(GENERIC_SPI_FLASH, generic_spi_flash_device, "generic_spi_flash", "Generic SPI Flash handling")
+DEFINE_DEVICE_TYPE(GENERIC_SPI_FLASH, generic_spi_flash_device, "generic_spi_flash", "Generic Byte HLE SPI Flash handling")
-generic_spi_flash_device::generic_spi_flash_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+generic_spi_flash_device::generic_spi_flash_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
: device_t(mconfig, GENERIC_SPI_FLASH, tag, owner, clock)
, device_nvram_interface(mconfig, *this)
+ , m_multibyte_status_read(1)
+ , m_multibyte_status_write(1)
{
+ m_idbytes[0] = 0xfe;
+ m_idbytes[1] = 0xfe;
+ m_idbytes[2] = 0x00;
}
void generic_spi_flash_device::device_start()
@@ -22,7 +30,6 @@ void generic_spi_flash_device::device_start()
save_item(NAME(m_spiaddr));
save_item(NAME(m_spi_state));
save_item(NAME(m_spilatch));
- save_item(NAME(m_spidir));
}
void generic_spi_flash_device::device_reset()
@@ -30,152 +37,327 @@ void generic_spi_flash_device::device_reset()
m_spiaddr = 0;
m_spi_state = 0;
m_spilatch = 0;
- m_spidir = false;
-}
-
-void generic_spi_flash_device::write(uint8_t data)
-{
- if (!m_spidir) // Send to SPI
- {
- switch (m_spi_state)
- {
- case READY_FOR_COMMAND:
- if (data == 0x03)
- {
- m_spi_state = READY_FOR_ADDRESS2;
- }
- else if (data == 0x05)
- {
- m_spi_state = READY_FOR_STATUS_READ;
- }
- else if (data == 0x0b)
- {
- m_spi_state = READY_FOR_HSADDRESS2;
- }
- else if (data == 0x06)
- {
- // write enable
- m_spi_state = READY_FOR_COMMAND;
- }
- else if (data == 0x04)
- {
- // write disable
- m_spi_state = READY_FOR_COMMAND;
- }
- else if (data == 0x02)
- {
- // page program
- m_spi_state = READY_FOR_WRITEADDRESS2;
- }
- else if (data == 0x20)
- {
- // erase 4k sector
- m_spi_state = READY_FOR_COMMAND;
- }
- else
- {
- fatalerror("SPI set to unknown mode %02x\n", data);
- }
- break;
-
- case READY_FOR_WRITEADDRESS2:
- m_spiaddr = (m_spiaddr & 0x00ffff) | (data << 16);
- m_spi_state = READY_FOR_WRITEADDRESS1;
- break;
-
- case READY_FOR_WRITEADDRESS1:
- m_spiaddr = (m_spiaddr & 0xff00ff) | (data << 8);
- m_spi_state = READY_FOR_WRITEADDRESS0;
- break;
-
- case READY_FOR_WRITEADDRESS0:
- m_spiaddr = (m_spiaddr & 0xffff00) | (data);
- m_spi_state = READY_FOR_WRITE;
- LOGMASKED(LOG_SPI, "SPI set to page WRITE mode with address %08x\n", m_spiaddr);
- break;
-
- case READY_FOR_WRITE:
- LOGMASKED(LOG_SPI, "Write SPI data %02x\n", data);
-
- m_spiptr[(m_spiaddr++) & (m_length-1)] = data;
-
- break;
-
-
- case READY_FOR_ADDRESS2:
- m_spiaddr = (m_spiaddr & 0x00ffff) | (data << 16);
- m_spi_state = READY_FOR_ADDRESS1;
- break;
-
- case READY_FOR_ADDRESS1:
- m_spiaddr = (m_spiaddr & 0xff00ff) | (data << 8);
- m_spi_state = READY_FOR_ADDRESS0;
- break;
-
- case READY_FOR_ADDRESS0:
- m_spiaddr = (m_spiaddr & 0xffff00) | (data);
- m_spi_state = READY_FOR_READ;
- m_spidir = 1;
- LOGMASKED(LOG_SPI, "SPI set to READ mode with address %08x\n", m_spiaddr);
- break;
-
- case READY_FOR_HSADDRESS2:
- m_spiaddr = (m_spiaddr & 0x00ffff) | (data << 16);
- m_spi_state = READY_FOR_HSADDRESS1;
- break;
-
- case READY_FOR_HSADDRESS1:
- m_spiaddr = (m_spiaddr & 0xff00ff) | (data << 8);
- m_spi_state = READY_FOR_HSADDRESS0;
- break;
-
- case READY_FOR_HSADDRESS0:
- m_spiaddr = (m_spiaddr & 0xffff00) | (data);
- m_spi_state = READY_FOR_HSDUMMY;
- break;
-
- case READY_FOR_HSDUMMY:
- m_spi_state = READY_FOR_READ;
- m_spidir = 1;
- LOGMASKED(LOG_SPI, "SPI set to High Speed READ mode with address %08x\n", m_spiaddr);
- break;
-
- case READY_FOR_SECTORERASEADDRESS2:
- m_spiaddr = (m_spiaddr & 0x00ffff) | (data << 16);
- m_spi_state = READY_FOR_SECTORERASEADDRESS1;
- break;
-
- case READY_FOR_SECTORERASEADDRESS1:
- m_spiaddr = (m_spiaddr & 0xff00ff) | (data << 8);
- m_spi_state = READY_FOR_SECTORERASEADDRESS0;
- break;
-
- case READY_FOR_SECTORERASEADDRESS0:
- m_spiaddr = (m_spiaddr & 0xffff00) | (data);
- LOGMASKED(LOG_SPI, "SPI set to Erase Sector with address %08x\n", m_spiaddr);
- break;
-
- }
+}
+
+void generic_spi_flash_device::get_command(u8 data)
+{
+ if (data == COMMAND_01_WRSR)
+ {
+ LOGMASKED(LOG_SPI, "Set SPI to WRSR, 1 or 2 params required\n");
+ m_spi_state = COMMAND_01_WRSR;
+ }
+ else if (data == COMMAND_9F_RDID)
+ {
+ LOGMASKED(LOG_SPI, "Set SPI to RDID (Read Identification)\n");
+ m_spi_state = COMMAND_9F_RDID;
+ }
+ else if (data == COMMAND_03_READ)
+ {
+ LOGMASKED(LOG_SPI, "Set SPI to READ (normal - 3 params needed)\n");
+ m_spi_state = COMMAND_03_READ;
+ }
+ else if (data == COMMAND_05_RDSR)
+ {
+ LOGMASKED(LOG_SPI, "Set SPI to RDSR (Read Status Register)\n");
+ m_spi_state = COMMAND_05_RDSR;
+ }
+ else if (data == COMMAND_0B_FAST_READ)
+ {
+ LOGMASKED(LOG_SPI, "Set SPI to FAST READ (fast - 4 params needed)\n");
+ m_spi_state = COMMAND_0B_FAST_READ;
+ }
+ else if (data == COMMAND_06_WREN)
+ {
+ LOGMASKED(LOG_SPI, "Set SPI to WREN (Write Enable)\n");
+ m_spi_state = READY_FOR_COMMAND;
+ }
+ else if (data == COMMAND_04_WRDI)
+ {
+ LOGMASKED(LOG_SPI, "Set SPI to WRDI (Write Disable)\n");
+ m_spi_state = READY_FOR_COMMAND;
+ }
+ else if (data == COMMAND_02_PP)
+ {
+ LOGMASKED(LOG_SPI, "Set SPI to PP (Page Program)\n");
+ m_spi_state = COMMAND_02_PP;
+ }
+ else if (data == COMMAND_20_SE)
+ {
+ LOGMASKED(LOG_SPI, "Set SPI to SE (Sector Erase)\n");
+ m_spi_state = COMMAND_20_SE;
+ }
+ else if (data == COMMAND_90_REMS)
+ {
+ LOGMASKED(LOG_SPI, "Set SPI to REMS (Read Electronic Manufacturer & Device ID)\n");
+ m_spi_state = COMMAND_90_REMS;
+ }
+ else if (data == COMMAND_AB_RDP)
+ {
+ LOGMASKED(LOG_SPI, "Set SPI to RDP (Release from deep power down)\n");
+ m_spi_state = READY_FOR_COMMAND;
+ }
+ else if (data == COMMAND_B9_DP)
+ {
+ LOGMASKED(LOG_SPI, "Set SPI to DP (deep power down)\n");
+ m_spi_state = READY_FOR_COMMAND;
+ }
+ else if (data == COMMAND_EB_4READ)
+ {
+ LOGMASKED(LOG_SPI, "Set SPI to 4READ (Quad I/O read with configurable dummy bytes)\n");
+ m_spi_state = COMMAND_EB_4READ;
}
else
{
- if (m_spi_state == READY_FOR_READ)
- {
- m_spilatch = m_spiptr[(m_spiaddr++) & (m_length-1)];
- }
- else if (m_spi_state == READY_FOR_STATUS_READ)
- {
- m_spilatch = 0x00;
- }
+ fatalerror("SPI set to unknown/unhandled command %02x\n", data);
+ }
+
+ m_spi_state_step = 0;
+}
+
+void generic_spi_flash_device::process_read_command(u8 data)
+{
+ switch (m_spi_state_step)
+ {
+ case 0x00:
+ m_spiaddr = (m_spiaddr & 0x00ffff) | (data << 16); m_spi_state_step++;
+ break;
+ case 0x01:
+ m_spiaddr = (m_spiaddr & 0xff00ff) | (data << 8); m_spi_state_step++;
+ break;
+ case 0x02:
+ m_spiaddr = (m_spiaddr & 0xffff00) | (data); m_spi_state_step++;
+ break;
+ default:
+ m_spilatch = m_spiptr[(m_spiaddr++) & (m_length - 1)];
+ break;
+ }
+}
+
+void generic_spi_flash_device::process_hsread_command(u8 data)
+{
+ switch (m_spi_state_step)
+ {
+ case 0x00:
+ m_spiaddr = (m_spiaddr & 0x00ffff) | (data << 16); m_spi_state_step++;
+ break;
+ case 0x01:
+ m_spiaddr = (m_spiaddr & 0xff00ff) | (data << 8); m_spi_state_step++;
+ break;
+ case 0x02:
+ m_spiaddr = (m_spiaddr & 0xffff00) | (data); m_spi_state_step++;
+ break;
+ case 0x03:
+ /* dummy */ m_spi_state_step++;
+ break;
+ default:
+ m_spilatch = m_spiptr[(m_spiaddr++) & (m_length - 1)];
+ break;
+ }
+}
+
+// has configurable dummy bytes?
+void generic_spi_flash_device::process_read4_command(u8 data)
+{
+ switch (m_spi_state_step)
+ {
+ case 0x00:
+ m_spiaddr = (m_spiaddr & 0x00ffff) | (data << 16); m_spi_state_step++;
+ break;
+ case 0x01:
+ m_spiaddr = (m_spiaddr & 0xff00ff) | (data << 8); m_spi_state_step++;
+ break;
+ case 0x02:
+ m_spiaddr = (m_spiaddr & 0xffff00) | (data); m_spi_state_step++;
+ break;
+ case 0x03: case 0x04: case 0x05:
+ /* dummy */ m_spi_state_step++;
+ break;
+ default:
+ m_spilatch = m_spiptr[(m_spiaddr++) & (m_length - 1)];
+ break;
+ }
+}
+
+void generic_spi_flash_device::process_write_command(u8 data)
+{
+ switch (m_spi_state_step)
+ {
+ case 0x00:
+ m_spiaddr = (m_spiaddr & 0x00ffff) | (data << 16); m_spi_state_step++;
+ break;
+ case 0x01:
+ m_spiaddr = (m_spiaddr & 0xff00ff) | (data << 8); m_spi_state_step++;
+ break;
+ case 0x02:
+ m_spiaddr = (m_spiaddr & 0xffff00) | (data); m_spi_state_step++;
+ break;
+ default:
+ LOGMASKED(LOG_SPI, "Write SPI data %02x\n", data);
+ m_spiptr[(m_spiaddr++) & (m_length - 1)] = data;
+ break;
+ }
+}
+
+void generic_spi_flash_device::process_sector_erase_command(u8 data)
+{
+ switch (m_spi_state_step)
+ {
+ case 0x00:
+ m_spiaddr = (m_spiaddr & 0x00ffff) | (data << 16); m_spi_state_step++;
+ break;
+ case 0x01:
+ m_spiaddr = (m_spiaddr & 0xff00ff) | (data << 8); m_spi_state_step++;
+ break;
+ case 0x02:
+ m_spiaddr = (m_spiaddr & 0xffff00) | (data); m_spi_state_step++;
+ LOGMASKED(LOG_SPI, "SPI set to Erase Sector with address %08x\n", m_spiaddr);
+ break;
+ default:
+ LOGMASKED(LOG_SPI, "%s unexpected byte %02x when writing sector erase address\n", data);
+ break;
+ }
+}
+
+void generic_spi_flash_device::process_status_write_command(u8 data)
+{
+ switch (m_spi_state_step)
+ {
+ case 0x00:
+ LOGMASKED(LOG_SPI, "status write step 1\n");
+ if (m_multibyte_status_write != 0)
+ m_spi_state_step++;
else
- {
- m_spilatch = 0x00;
- }
+ m_spi_state = READY_FOR_COMMAND;
+ break;
+
+ case 0x01:
+ LOGMASKED(LOG_SPI, "status write step 2\n");
+ m_spi_state = READY_FOR_COMMAND;
+ break;
}
}
+void generic_spi_flash_device::process_status_read_command(u8 data)
+{
+ switch (m_spi_state_step)
+ {
+ case 0x00:
+ LOGMASKED(LOG_SPI, "status read step 1\n");
+ m_spilatch = 0x00;
+ if (m_multibyte_status_read != 0)
+ m_spi_state_step++;
+ else
+ m_spi_state = READY_FOR_COMMAND;
+ break;
+
+ case 0x01:
+ LOGMASKED(LOG_SPI, "status read step 2\n");
+ m_spilatch = 0x00;
+ m_spi_state = READY_FOR_COMMAND;
+ break;
+ }
+}
+void generic_spi_flash_device::process_status_rems_command(u8 data)
+{
+ switch (m_spi_state_step)
+ {
+ case 0x00:
+ LOGMASKED(LOG_SPI, "REMS step 1\n");
+ m_spi_state_step++;
+ break;
+
+ case 0x01:
+ LOGMASKED(LOG_SPI, "REMS step 2\n");
+ m_spi_state_step++;
+ break;
+
+ case 0x02:
+ LOGMASKED(LOG_SPI, "REMS step 3\n");
+ m_spi_state_step++;
+ break;
+
+ case 0x03:
+ LOGMASKED(LOG_SPI, "REMS step 4\n");
+ m_spi_state_step++;
+ break;
+
+ case 0x04:
+ LOGMASKED(LOG_SPI, "REMS step 5\n");
+ m_spi_state = READY_FOR_COMMAND;
+ break;
+ }
+}
+
+void generic_spi_flash_device::process_status_rdid_command(u8 data)
+{
+ switch (m_spi_state_step)
+ {
+ case 0x00:
+ m_spilatch = m_idbytes[0];
+ m_spi_state_step++;
+ break;
+
+ case 0x01:
+ m_spilatch = m_idbytes[1];
+ m_spi_state_step++;
+ break;
+
+ case 0x02:
+ m_spilatch = m_idbytes[2];
+ m_spi_state = READY_FOR_COMMAND;
+ break;
+ }
+}
+
+void generic_spi_flash_device::write(u8 data)
+{
+ // not all commands have extra params/reads
+ switch (m_spi_state)
+ {
+ case READY_FOR_COMMAND:
+ get_command(data);
+ break;
+
+ case COMMAND_01_WRSR:
+ process_status_write_command(data);
+ break;
+
+ case COMMAND_02_PP:
+ process_write_command(data);
+ break;
+
+ case COMMAND_03_READ:
+ process_read_command(data);
+ break;
+
+ case COMMAND_05_RDSR:
+ process_status_read_command(data);
+ break;
+
+ case COMMAND_0B_FAST_READ:
+ process_hsread_command(data);
+ break;
+
+ case COMMAND_20_SE:
+ process_sector_erase_command(data);
+ break;
+
+ case COMMAND_90_REMS:
+ process_status_rems_command(data);
+ break;
+
+ case COMMAND_9F_RDID:
+ process_status_rdid_command(data);
+ break;
+
+ case COMMAND_EB_4READ:
+ process_read4_command(data);
+ break;
+ }
+}
+
void generic_spi_flash_device::nvram_default()
{
}