summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/spi_sdcard.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/spi_sdcard.cpp')
-rw-r--r--src/devices/machine/spi_sdcard.cpp311
1 files changed, 182 insertions, 129 deletions
diff --git a/src/devices/machine/spi_sdcard.cpp b/src/devices/machine/spi_sdcard.cpp
index d238317c789..4db4fc2c8f9 100644
--- a/src/devices/machine/spi_sdcard.cpp
+++ b/src/devices/machine/spi_sdcard.cpp
@@ -16,9 +16,9 @@
Multiple block read/write commands are not supported but would be straightforward to add.
- Refrences:
+ References:
https://www.sdcard.org/downloads/pls/ (Physical Layer Simplified Specification)
- REF: tags are refering to the spec form above. 'Physical Layer Simplified Specification v8.00'
+ REF: tags are referring to the spec form above. 'Physical Layer Simplified Specification v8.00'
http://www.dejazzer.com/ee379/lecture_notes/lec12_sd_card.pdf
https://embdev.net/attachment/39390/TOSHIBA_SD_Card_Specification.pdf
@@ -29,12 +29,13 @@
#include "spi_sdcard.h"
#include "imagedev/harddriv.h"
-#define LOG_GENERAL (1U << 0)
+#include "multibyte.h"
+
#define LOG_COMMAND (1U << 1)
#define LOG_SPI (1U << 2)
-//#define VERBOSE (LOG_COMMAND)
-#define LOG_OUTPUT_FUNC osd_printf_info
+//#define VERBOSE (LOG_GENERAL | LOG_COMMAND)
+//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"
@@ -49,8 +50,7 @@ spi_sdcard_device::spi_sdcard_device(const machine_config &mconfig, device_type
write_miso(*this),
m_image(*this, "image"),
m_state(SD_STATE_IDLE),
- m_harddisk(nullptr),
- m_ss(0), m_in_bit(0),
+ m_ss(0), m_in_bit(0), m_clk_state(0),
m_in_latch(0), m_out_latch(0xff), m_cur_bit(0),
m_out_count(0), m_out_ptr(0), m_write_ptr(0), m_blksize(512), m_blknext(0),
m_bACMD(false)
@@ -74,7 +74,6 @@ ALLOW_SAVE_TYPE(spi_sdcard_device::sd_type);
void spi_sdcard_device::device_start()
{
- write_miso.resolve_safe();
save_item(NAME(m_state));
save_item(NAME(m_in_latch));
save_item(NAME(m_out_latch));
@@ -82,6 +81,7 @@ void spi_sdcard_device::device_start()
save_item(NAME(m_out_count));
save_item(NAME(m_ss));
save_item(NAME(m_in_bit));
+ save_item(NAME(m_clk_state));
save_item(NAME(m_cur_bit));
save_item(NAME(m_write_ptr));
save_item(NAME(m_blksize));
@@ -94,7 +94,6 @@ void spi_sdcard_device::device_start()
void spi_sdcard_device::device_reset()
{
- m_harddisk = m_image->get_hard_disk_file();
}
void spi_sdcard_device::device_add_mconfig(machine_config &config)
@@ -111,118 +110,119 @@ void spi_sdcard_device::send_data(u16 count, sd_state new_state)
void spi_sdcard_device::spi_clock_w(int state)
{
- // only respond if selected
- if (m_ss)
+ // only respond if selected, and a clock edge
+ if (m_ss && state != m_clk_state)
{
- // We implmement SPI Mode 3 signalling, in which we latch the data on
+ // We implement SPI Mode 3 signalling, in which we latch the data on
// rising clock edges, and shift the data on falling clock edges.
// See http://www.dejazzer.com/ee379/lecture_notes/lec12_sd_card.pdf for details
- // on the 4 SPI signalling modes. SD Cards can work in ether Mode 0 or Mode 3,
+ // on the 4 SPI signalling modes. SD Cards can work in either Mode 0 or Mode 3,
// both of which shift on the falling edge and latch on the rising edge but
// have opposite CLK polarity.
-
if (state)
+ latch_in();
+ else
+ shift_out();
+ }
+ m_clk_state = state;
+}
+
+void spi_sdcard_device::latch_in()
+{
+ m_in_latch &= ~0x01;
+ m_in_latch |= m_in_bit;
+ LOGMASKED(LOG_SPI, "\tsdcard: L %02x (%d) (out %02x)\n", m_in_latch, m_cur_bit, m_out_latch);
+ m_cur_bit++;
+ if (m_cur_bit == 8)
+ {
+ LOGMASKED(LOG_SPI, "SDCARD: got %02x\n", m_in_latch);
+ for (u8 i = 0; i < 5; i++)
{
- m_in_latch &= ~0x01;
- m_in_latch |= m_in_bit;
- LOGMASKED(LOG_SPI, "\tsdcard: L %02x (%d) (out %02x)\n", m_in_latch, m_cur_bit, m_out_latch);
- m_cur_bit++;
- if (m_cur_bit == 8)
+ m_cmd[i] = m_cmd[i + 1];
+ }
+ m_cmd[5] = m_in_latch;
+
+ switch (m_state)
+ {
+ case SD_STATE_IDLE:
+ do_command();
+ break;
+
+ case SD_STATE_WRITE_WAITFE:
+ if (m_in_latch == 0xfe)
+ {
+ m_state = SD_STATE_WRITE_DATA;
+ m_out_latch = 0xff;
+ m_write_ptr = 0;
+ }
+ break;
+
+ case SD_STATE_WRITE_DATA:
+ m_data[m_write_ptr++] = m_in_latch;
+ if (m_write_ptr == (m_blksize + 2))
{
- LOGMASKED(LOG_SPI, "SDCARD: got %02x\n", m_in_latch);
- for (u8 i = 0; i < 5; i++)
+ LOG("writing LBA %x, data %02x %02x %02x %02x\n", m_blknext, m_data[0], m_data[1], m_data[2], m_data[3]);
+ if (m_image->write(m_blknext, &m_data[0]))
{
- m_cmd[i] = m_cmd[i + 1];
+ m_data[0] = DATA_RESPONSE_OK;
}
- m_cmd[5] = m_in_latch;
-
- switch (m_state)
+ else
{
- case SD_STATE_IDLE:
- do_command();
- break;
-
- case SD_STATE_WRITE_WAITFE:
- if (m_in_latch == 0xfe)
- {
- m_state = SD_STATE_WRITE_DATA;
- m_out_latch = 0xff;
- m_write_ptr = 0;
- change_state(SD_STATE_RCV);
- }
- break;
-
- case SD_STATE_WRITE_DATA:
- m_data[m_write_ptr++] = m_in_latch;
- if (m_write_ptr == (m_blksize + 2))
- {
- u32 blk = (u32(m_cmd[1]) << 24) | (u32(m_cmd[2]) << 16) | (u32(m_cmd[3]) << 8) | u32(m_cmd[4]);
- if (m_type == SD_TYPE_V2)
- {
- blk /= m_blksize;
- }
-
- LOGMASKED(LOG_GENERAL, "writing LBA %x, data %02x %02x %02x %02x\n", blk, m_data[0], m_data[1], m_data[2], m_data[3]);
- if (hard_disk_write(m_harddisk, blk, &m_data[0]))
- {
- m_data[0] = DATA_RESPONSE_OK;
- }
- else
- {
- m_data[0] = DATA_RESPONSE_IO_ERROR;
- }
- m_data[1] = 0x01;
-
- send_data(2, SD_STATE_IDLE);
- }
- break;
-
- case SD_STATE_DATA_MULTI:
- do_command();
- if (m_state == SD_STATE_DATA_MULTI && m_out_count == 0)
- {
- m_data[0] = 0xfe; // data token
- hard_disk_read(m_harddisk, m_blknext++, &m_data[1]);
- util::crc16_t crc16 = util::crc16_creator::simple(
- &m_data[1], m_blksize);
- m_data[m_blksize + 1] = (crc16 >> 8) & 0xff;
- m_data[m_blksize + 2] = (crc16 & 0xff);
- send_data(1 + m_blksize + 2, SD_STATE_DATA_MULTI);
- }
- break;
-
- default:
- if (((m_cmd[0] & 0x70) == 0x40) || (m_out_count == 0)) // CMD0 - GO_IDLE_STATE
- {
- do_command();
- }
- break;
+ m_data[0] = DATA_RESPONSE_IO_ERROR;
}
+ m_data[1] = 0x01;
+
+ send_data(2, SD_STATE_IDLE);
}
- }
- else
- {
- m_in_latch <<= 1;
- m_out_latch <<= 1;
- m_out_latch |= 1;
- LOGMASKED(LOG_SPI, "\tsdcard: S %02x %02x (%d)\n", m_in_latch,
- m_out_latch, m_cur_bit);
-
- m_cur_bit &= 0x07;
- if (m_cur_bit == 0)
+ break;
+
+ case SD_STATE_DATA_MULTI:
+ do_command();
+ if (m_state == SD_STATE_DATA_MULTI && m_out_count == 0)
{
- if (m_out_count > 0)
- {
- m_out_latch = m_data[m_out_ptr++];
- LOGMASKED(LOG_SPI, "SDCARD: latching %02x (start of shift)\n", m_out_latch);
- m_out_count--;
- }
+ m_data[0] = 0xfe; // data token
+ m_image->read(m_blknext++, &m_data[1]);
+ util::crc16_t crc16 = util::crc16_creator::simple(&m_data[1], m_blksize);
+ put_u16be(&m_data[m_blksize + 1], crc16);
+ send_data(1 + m_blksize + 2, SD_STATE_DATA_MULTI);
}
- write_miso(BIT(m_out_latch, 7));
+ break;
+
+ default:
+ if (((m_cmd[0] & 0x70) == 0x40) || (m_out_count == 0)) // CMD0 - GO_IDLE_STATE
+ {
+ do_command();
+ }
+ break;
}
}
}
+void spi_sdcard_device::shift_out()
+{
+ m_in_latch <<= 1;
+ m_out_latch <<= 1;
+ m_out_latch |= 1;
+ LOGMASKED(LOG_SPI, "\tsdcard: S %02x %02x (%d)\n", m_in_latch, m_out_latch, m_cur_bit);
+
+ m_cur_bit &= 0x07;
+ if (m_cur_bit == 0)
+ {
+ if (m_out_ptr < SPI_DELAY_RESPONSE)
+ {
+ m_out_ptr++;
+ }
+ else if (m_out_count > 0)
+ {
+ m_out_latch = m_data[m_out_ptr - SPI_DELAY_RESPONSE];
+ m_out_ptr++;
+ LOGMASKED(LOG_SPI, "SDCARD: latching %02x (start of shift)\n", m_out_latch);
+ m_out_count--;
+ }
+ }
+ write_miso(BIT(m_out_latch, 7));
+}
+
void spi_sdcard_device::do_command()
{
if (((m_cmd[0] & 0xc0) == 0x40) && (m_cmd[5] & 1))
@@ -232,7 +232,7 @@ void spi_sdcard_device::do_command()
switch (m_cmd[0] & 0x3f)
{
case 0: // CMD0 - GO_IDLE_STATE
- if (m_harddisk)
+ if (m_image->exists())
{
m_data[0] = 0x01;
send_data(1, SD_STATE_IDLE);
@@ -244,7 +244,12 @@ void spi_sdcard_device::do_command()
}
break;
- case 8: // CMD8 - SEND_IF_COND (SD v2 only)
+ case 1: // CMD1 - SEND_OP_COND
+ m_data[0] = 0x00;
+ send_data(1, SD_STATE_READY);
+ break;
+
+ case 8: // CMD8 - SEND_IF_COND (SD v2 only)
m_data[0] = 0x01;
m_data[1] = 0;
m_data[2] = 0;
@@ -253,9 +258,49 @@ void spi_sdcard_device::do_command()
send_data(5, SD_STATE_IDLE);
break;
- case 10: // CMD10 - SEND_CID
- m_data[0] = 0x01; // initial R1 response
- m_data[1] = 0x00; // throwaway byte before data transfer
+ case 9: // CMD9 - SEND_CSD
+ m_data[0] = 0x00;
+ m_data[1] = 0xff;
+ m_data[2] = 0xfe;
+
+ //if (m_type == SD_TYPE_V2) // CSD Version 1.0
+ {
+ u8 block_len = 9;
+ for (auto i = m_blksize >> 10; i; i >>= 1, ++block_len);
+
+ m_data[3] = 0x00; // 127: CSD_STRUCTURE:2 (00b) 0:6
+ m_data[4] = 0x0e; // 119: TAAC:8
+ m_data[5] = 0x00; // 111: NSAC:8
+ m_data[6] = 0x32; // 103: TRAN_SPEED:8 (32h or 5Ah)
+ m_data[7] = 0x5b; // 95: CCC:12 (01x110110101b)
+ m_data[8] = 0x50; // .. READ_BL_LN:4
+ m_data[8] |= block_len;
+ m_data[9] = 0x83; // 79: READ_BL_PARTIAL:1 (1b) WRITE_BLK_MISALIGN:1 READ_BLK_MISALIGN:1 DSR_IMP:1 0:2 C_SIZE:12
+ m_data[10] = 0xff; // ..
+ m_data[11] = 0xed; // .. VDD_R_CURR_MIN:3 VDD_R_CURR_MAX:3
+ m_data[12] = 0xb7; // 55: VDD_W_CURR_MIN:3 VDD_W_CURR_MAX:3 C_SIZE_MUL:3
+ m_data[13] = 0xbf; // .. ERASE_BLK_EN:1 SECTOR_SIZE:7
+ m_data[14] = 0xbf; // .. WP_GRP_SIZE:7
+ m_data[15] = 0x04; // 31: WP_GRP_ENABLE:1 0:2 R2W_FACTOR:3 WRITE_BL_LEN:4
+ m_data[15] |= (block_len >> 2);
+ m_data[16] = 0x00; /// .. WRITE_BL_PARTIAL:1 0:5
+ m_data[16] |= (block_len & 3) << 6;
+ m_data[17] = 0x00; // 15: FILE_FORMAT_GRP:1 COPY:1 PERM_WRITE_PROTECT:1 TMP_WRITE_PROTECT:1 FILE_FORMAT:2 WP_UPC:1 0:1
+ m_data[18] = 0x01; // 7: CRC7 1:1
+ }
+ /*
+ else // SD_TYPE_HC: CSD Version 2.0
+ {
+ m_data[3] = 0x40;
+ }
+ */
+
+ send_data(3 + 16, SD_STATE_STBY);
+ break;
+
+ case 10: // CMD10 - SEND_CID
+ m_data[0] = 0x00; // initial R1 response
+ m_data[1] = 0xff; // throwaway byte before data transfer
m_data[2] = 0xfe; // data token
m_data[3] = 'M'; // Manufacturer ID - we'll use M for MAME
m_data[4] = 'M'; // OEM ID - MD for MAMEdev
@@ -268,31 +313,31 @@ void spi_sdcard_device::do_command()
m_data[11] = 0x10; // Product Revision in BCD (1.0)
{
u32 uSerial = 0x12345678;
- m_data[12] = (uSerial >> 24) & 0xff; // PSN - Product Serial Number
- m_data[13] = (uSerial >> 16) & 0xff;
- m_data[14] = (uSerial >> 8) & 0xff;
- m_data[15] = (uSerial & 0xff);
+ put_u32be(&m_data[12], uSerial); // PSN - Product Serial Number
}
m_data[16] = 0x01; // MDT - Manufacturing Date
m_data[17] = 0x59; // 0x15 9 = 2021, September
m_data[18] = 0x00; // CRC7, bit 0 is always 0
{
util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], 16);
- m_data[19] = (crc16 >> 8) & 0xff;
- m_data[20] = (crc16 & 0xff);
+ put_u16be(&m_data[19], crc16);
}
send_data(3 + 16 + 2, SD_STATE_STBY);
break;
case 12: // CMD12 - STOP_TRANSMISSION
m_data[0] = 0;
- send_data(1,
- m_state == SD_STATE_RCV ? SD_STATE_PRG : SD_STATE_TRAN);
+ send_data(1, m_state == SD_STATE_RCV ? SD_STATE_PRG : SD_STATE_TRAN);
+ break;
+
+ case 13: // CMD13 - SEND_STATUS
+ m_data[0] = 0; // TODO
+ send_data(1, SD_STATE_STBY);
break;
case 16: // CMD16 - SET_BLOCKLEN
- m_blksize = (u16(m_cmd[3]) << 8) | u16(m_cmd[4]);
- if (hard_disk_set_block_size(m_harddisk, m_blksize))
+ m_blksize = get_u16be(&m_cmd[3]);
+ if (m_image->set_block_size(m_blksize))
{
m_data[0] = 0;
}
@@ -301,32 +346,29 @@ void spi_sdcard_device::do_command()
m_data[0] = 0xff; // indicate an error
// if false was returned, it means the hard disk is a CHD file, and we can't resize the
// blocks on CHD files.
- logerror("spi_sdcard: Couldn't change block size to %d, wrong "
- "CHD file?",
- m_blksize);
+ logerror("spi_sdcard: Couldn't change block size to %d, wrong CHD file?", m_blksize);
}
send_data(1, SD_STATE_TRAN);
break;
case 17: // CMD17 - READ_SINGLE_BLOCK
- if (m_harddisk)
+ if (m_image->exists())
{
m_data[0] = 0x00; // initial R1 response
// data token occurs some time after the R1 response. A2SD expects at least 1
// byte of space between R1 and the data packet.
m_data[1] = 0xff;
m_data[2] = 0xfe; // data token
- u32 blk = (u32(m_cmd[1]) << 24) | (u32(m_cmd[2]) << 16) | (u32(m_cmd[3]) << 8) | u32(m_cmd[4]);
+ u32 blk = get_u32be(&m_cmd[1]);
if (m_type == SD_TYPE_V2)
{
blk /= m_blksize;
}
- LOGMASKED(LOG_GENERAL, "reading LBA %x\n", blk);
- hard_disk_read(m_harddisk, blk, &m_data[3]);
+ LOG("reading LBA %x\n", blk);
+ m_image->read(blk, &m_data[3]);
{
util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], m_blksize);
- m_data[m_blksize + 3] = (crc16 >> 8) & 0xff;
- m_data[m_blksize + 4] = (crc16 & 0xff);
+ put_u16be(&m_data[m_blksize + 3], crc16);
}
send_data(3 + m_blksize + 2, SD_STATE_DATA);
}
@@ -338,13 +380,13 @@ void spi_sdcard_device::do_command()
break;
case 18: // CMD18 - CMD_READ_MULTIPLE_BLOCK
- if (m_harddisk)
+ if (m_image->exists())
{
m_data[0] = 0x00; // initial R1 response
// data token occurs some time after the R1 response. A2SD
// expects at least 1 byte of space between R1 and the data
// packet.
- m_blknext = (u32(m_cmd[1]) << 24) | (u32(m_cmd[2]) << 16) | (u32(m_cmd[3]) << 8) | u32(m_cmd[4]);
+ m_blknext = get_u32be(&m_cmd[1]);
if (m_type == SD_TYPE_V2)
{
m_blknext /= m_blksize;
@@ -359,7 +401,12 @@ void spi_sdcard_device::do_command()
case 24: // CMD24 - WRITE_BLOCK
m_data[0] = 0;
- send_data(1, SD_STATE_RCV);
+ m_blknext = get_u32be(&m_cmd[1]);
+ if (m_type == SD_TYPE_V2)
+ {
+ m_blknext /= m_blksize;
+ }
+ send_data(1, SD_STATE_WRITE_WAITFE);
break;
case 41:
@@ -396,6 +443,12 @@ void spi_sdcard_device::do_command()
send_data(5, SD_STATE_DATA);
break;
+ case 59: // CMD59 - CRC_ON_OFF
+ m_data[0] = 0;
+ // TODO CRC 1-on, 0-off
+ send_data(1, SD_STATE_STBY);
+ break;
+
default:
LOGMASKED(LOG_COMMAND, "SDCARD: Unsupported %02x\n", m_cmd[0] & 0x3f);
clean_cmd = false;