From 8a1055ed1b26d80555f7a7ba92399715620875b7 Mon Sep 17 00:00:00 2001 From: joevt Date: Sun, 10 Mar 2024 22:10:28 -0700 Subject: [PATCH] sc53c94: Add DBDMA support. - For pdm/amic, real_dma_xfer is called when SCSI_DMA_Ctrl has the run bit set. - For tnt/grandcentral, dma_wait is called when the DBDMA is started (run bit is set). It will call real_dma_xfer when the phase and sequence are DATA_IN/RCV_DATA or DATA_OUT/SEND_DATA. - dma_wait and real_dma_xfer uses a one shot timer instead of a loop to continue doing DMA while also giving time to the CPU. This and the above changes handles the case where the DBDMA is started before setting up the transfer phase and sequence. - dma_stop will stop the one shot timer when the DBDMA channel is stopped. --- devices/common/scsi/sc53c94.cpp | 77 ++++++++++++++++++++++++++------- devices/common/scsi/sc53c94.h | 13 +++++- 2 files changed, 73 insertions(+), 17 deletions(-) diff --git a/devices/common/scsi/sc53c94.cpp b/devices/common/scsi/sc53c94.cpp index 43294b7..514e331 100644 --- a/devices/common/scsi/sc53c94.cpp +++ b/devices/common/scsi/sc53c94.cpp @@ -657,7 +657,7 @@ void Sc53C94::real_dma_xfer_out() { // transfer data from host's memory to target - while (this->xfer_count) { + if (this->xfer_count) { uint32_t got_bytes; uint8_t* src_ptr; this->dma_ch->pull_data(std::min((int)this->xfer_count, DATA_FIFO_MAX), @@ -674,6 +674,16 @@ void Sc53C94::real_dma_xfer_out() this->sequencer(); } } + + if (this->xfer_count) { + this->dma_timer_id = TimerManager::get_instance()->add_oneshot_timer( + 10000, + [this]() { + // re-enter the sequencer with the state specified in next_state + this->dma_timer_id = 0; + this->real_dma_xfer_out(); + }); + } } void Sc53C94::real_dma_xfer_in() @@ -682,25 +692,60 @@ void Sc53C94::real_dma_xfer_in() // transfer data from target to host's memory - while (this->xfer_count) { - if (this->data_fifo_pos) { - this->dma_ch->push_data((char*)this->data_fifo, this->data_fifo_pos); + if (this->xfer_count && this->data_fifo_pos) { + this->dma_ch->push_data((char*)this->data_fifo, this->data_fifo_pos); - this->xfer_count -= this->data_fifo_pos; - this->data_fifo_pos = 0; - if (!this->xfer_count) { - is_done = true; - this->status |= STAT_TC; // signal zero transfer count - this->cur_state = SeqState::XFER_END; - this->sequencer(); - } - } - - // see if we need to refill FIFO - if (!this->data_fifo_pos && !is_done) { + this->xfer_count -= this->data_fifo_pos; + this->data_fifo_pos = 0; + if (!this->xfer_count) { + is_done = true; + this->status |= STAT_TC; // signal zero transfer count + this->cur_state = SeqState::XFER_END; this->sequencer(); } } + + // see if we need to refill FIFO + if (!this->data_fifo_pos && !is_done) { + this->sequencer(); + this->dma_timer_id = TimerManager::get_instance()->add_oneshot_timer( + 10000, + [this]() { + // re-enter the sequencer with the state specified in next_state + this->dma_timer_id = 0; + this->real_dma_xfer_in(); + }); + } +} + +void Sc53C94::dma_wait() { + if (this->cur_bus_phase == ScsiPhase::DATA_IN && this->cur_state == SeqState::RCV_DATA) { + real_dma_xfer_in(); + } + else if (this->cur_bus_phase == ScsiPhase::DATA_OUT && this->cur_state == SeqState::SEND_DATA) { + real_dma_xfer_out(); + } + else { + this->dma_timer_id = TimerManager::get_instance()->add_oneshot_timer( + 10000, + [this]() { + this->dma_timer_id = 0; + this->dma_wait(); + }); + } +} + +void Sc53C94::dma_start() +{ + dma_wait(); +} + +void Sc53C94::dma_stop() +{ + if (this->dma_timer_id) { + TimerManager::get_instance()->cancel_timer(this->dma_timer_id); + this->dma_timer_id = 0; + } } static const PropMap Sc53C94_properties = { diff --git a/devices/common/scsi/sc53c94.h b/devices/common/scsi/sc53c94.h index 0c65b0b..48a40ee 100644 --- a/devices/common/scsi/sc53c94.h +++ b/devices/common/scsi/sc53c94.h @@ -30,12 +30,12 @@ along with this program. If not, see . #define SC_53C94_H #include +#include #include #include #include -class DmaBidirChannel; class InterruptCtrl; /** 53C94 read registers */ @@ -209,8 +209,18 @@ public: void real_dma_xfer_out(); void real_dma_xfer_in(); + void dma_start(); + void dma_wait(); + void dma_stop(); void set_dma_channel(DmaBidirChannel *dma_ch) { this->dma_ch = dma_ch; + auto dbdma_ch = dynamic_cast(dma_ch); + if (dbdma_ch) { + dbdma_ch->set_callbacks( + std::bind(&Sc53C94::dma_start, this), + std::bind(&Sc53C94::dma_stop, this) + ); + } }; void set_drq_callback(DrqCb cb) { @@ -282,6 +292,7 @@ private: // DMA related stuff DmaBidirChannel* dma_ch = nullptr; DrqCb drq_cb = nullptr; + uint32_t dma_timer_id = 0; }; #endif // SC_53C94_H