mirror of
https://github.com/holub/mame
synced 2025-04-20 15:32:45 +03:00
spi_sdcard: Added CMD1 SEND_OP_COND.
- Fixed CMD10 R1 response, not idle. - Delay SPI response by 1 byte, required for MMFS. - Only latch data on clock edges.
This commit is contained in:
parent
ad7cbf9c0a
commit
2236ea0ff4
@ -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
|
||||
@ -50,7 +50,7 @@ spi_sdcard_device::spi_sdcard_device(const machine_config &mconfig, device_type
|
||||
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)
|
||||
@ -82,6 +82,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));
|
||||
@ -111,118 +112,128 @@ 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)
|
||||
{
|
||||
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_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;
|
||||
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 (m_harddisk->write(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
|
||||
m_harddisk->read(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
latch_in();
|
||||
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);
|
||||
shift_out();
|
||||
}
|
||||
m_clk_state = state;
|
||||
}
|
||||
|
||||
m_cur_bit &= 0x07;
|
||||
if (m_cur_bit == 0)
|
||||
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_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)
|
||||
{
|
||||
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_state = SD_STATE_WRITE_DATA;
|
||||
m_out_latch = 0xff;
|
||||
m_write_ptr = 0;
|
||||
change_state(SD_STATE_RCV);
|
||||
}
|
||||
write_miso(BIT(m_out_latch, 7));
|
||||
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 (m_harddisk->write(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
|
||||
m_harddisk->read(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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
@ -244,7 +255,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 +269,9 @@ 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 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
|
||||
@ -286,13 +302,12 @@ void spi_sdcard_device::do_command()
|
||||
|
||||
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 16: // CMD16 - SET_BLOCKLEN
|
||||
m_blksize = (u16(m_cmd[3]) << 8) | u16(m_cmd[4]);
|
||||
if (m_harddisk->set_block_size(m_blksize))
|
||||
if (m_harddisk && m_harddisk->set_block_size(m_blksize))
|
||||
{
|
||||
m_data[0] = 0;
|
||||
}
|
||||
@ -301,9 +316,7 @@ 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;
|
||||
|
@ -53,20 +53,26 @@ private:
|
||||
SD_STATE_DIS,
|
||||
SD_STATE_INA,
|
||||
|
||||
//FIXME Existing states wich must be revisited
|
||||
//FIXME Existing states which must be revisited
|
||||
SD_STATE_WRITE_WAITFE,
|
||||
SD_STATE_WRITE_DATA
|
||||
};
|
||||
sd_state m_state;
|
||||
|
||||
// MMFS for Acorn machines expect dummy byte before response
|
||||
static constexpr int SPI_DELAY_RESPONSE = 1;
|
||||
|
||||
void send_data(u16 count, sd_state new_state);
|
||||
void do_command();
|
||||
void change_state(sd_state new_state);
|
||||
|
||||
void latch_in();
|
||||
void shift_out();
|
||||
|
||||
u8 m_data[520], m_cmd[6];
|
||||
hard_disk_file *m_harddisk;
|
||||
|
||||
int m_ss, m_in_bit;
|
||||
int m_ss, m_in_bit, m_clk_state;
|
||||
u8 m_in_latch, m_out_latch, m_cur_bit;
|
||||
u16 m_out_count, m_out_ptr, m_write_ptr, m_blksize;
|
||||
u32 m_blknext;
|
||||
|
Loading…
Reference in New Issue
Block a user