dbdma: fix LOAD_QUAD and STORE_QUAD to work with MMIO.

This commit is contained in:
Maxim Poliakovski 2023-10-02 14:46:52 +02:00
parent 8cf290c034
commit f754f63f8f

View File

@ -38,12 +38,13 @@ void DMAChannel::set_callbacks(DbdmaCallback start_cb, DbdmaCallback stop_cb) {
/* Load DMACmd from physical memory. */ /* Load DMACmd from physical memory. */
void DMAChannel::fetch_cmd(uint32_t cmd_addr, DMACmd* p_cmd) { void DMAChannel::fetch_cmd(uint32_t cmd_addr, DMACmd* p_cmd) {
memcpy((uint8_t*)p_cmd, mmu_get_dma_mem(cmd_addr, 16, nullptr), 16); MapDmaResult res = mmu_map_dma_mem(cmd_addr, 16, false);
memcpy((uint8_t*)p_cmd, res.host_va, 16);
} }
uint8_t DMAChannel::interpret_cmd() { uint8_t DMAChannel::interpret_cmd() {
DMACmd cmd_struct; DMACmd cmd_struct;
bool is_writable, branch_taken = false; MapDmaResult res;
if (this->cmd_in_progress) { if (this->cmd_in_progress) {
// return current command if there is data to transfer // return current command if there is data to transfer
@ -68,7 +69,8 @@ uint8_t DMAChannel::interpret_cmd() {
LOG_F(ERROR, "Key > 0 not implemented"); LOG_F(ERROR, "Key > 0 not implemented");
break; break;
} }
this->queue_data = mmu_get_dma_mem(cmd_struct.address, cmd_struct.req_count, &is_writable); res = mmu_map_dma_mem(cmd_struct.address, cmd_struct.req_count, false);
this->queue_data = res.host_va;
this->queue_len = cmd_struct.req_count; this->queue_len = cmd_struct.req_count;
this->cmd_in_progress = true; this->cmd_in_progress = true;
break; break;
@ -100,10 +102,11 @@ uint8_t DMAChannel::interpret_cmd() {
void DMAChannel::finish_cmd() { void DMAChannel::finish_cmd() {
DMACmd cmd_struct; DMACmd cmd_struct;
bool is_writable, branch_taken = false; bool branch_taken = false;
// obtain real pointer to the descriptor of the command to be finished // obtain real pointer to the descriptor of the command to be finished
uint8_t *cmd_desc = mmu_get_dma_mem(this->cmd_ptr, 16, &is_writable); MapDmaResult res = mmu_map_dma_mem(this->cmd_ptr, 16, false);
uint8_t *cmd_desc = res.host_va;
// get command code // get command code
this->cur_cmd = cmd_desc[3] >> 4; this->cur_cmd = cmd_desc[3] >> 4;
@ -111,21 +114,32 @@ void DMAChannel::finish_cmd() {
// all commands except STOP update cmd.xferStatus and // all commands except STOP update cmd.xferStatus and
// perform actions under control of "i", "b" and "w" bits // perform actions under control of "i", "b" and "w" bits
if (this->cur_cmd < DBDMA_Cmd::STOP) { if (this->cur_cmd < DBDMA_Cmd::STOP) {
if (is_writable) // react to cmd.w (wait) bits
WRITE_WORD_LE_A(&cmd_desc[14], this->ch_stat | CH_STAT_ACTIVE);
if (cmd_desc[2] & 3) { if (cmd_desc[2] & 3) {
ABORT_F("DBDMA: cmd.w bit not implemented"); bool cond = true;
if ((cmd_desc[2] & 3) != 3) {
uint16_t wt_mask = this->wait_select >> 16;
cond = (this->ch_stat & wt_mask) == (this->wait_select & wt_mask);
if ((cmd_desc[2] & 3) == 2) {
cond = !cond; // wait if cond = false
}
} }
// react to cmd.b bit if (cond)
return;
}
if (res.is_writable)
WRITE_WORD_LE_A(&cmd_desc[14], this->ch_stat | CH_STAT_ACTIVE);
// react to cmd.b (branch) bits
if (cmd_desc[2] & 0xC) { if (cmd_desc[2] & 0xC) {
bool cond = true; bool cond = true;
if ((cmd_desc[2] & 0xC) != 0xC) { if ((cmd_desc[2] & 0xC) != 0xC) {
uint16_t br_mask = this->branch_select >> 16; uint16_t br_mask = this->branch_select >> 16;
cond = (this->ch_stat & br_mask) == (this->branch_select & br_mask); cond = (this->ch_stat & br_mask) == (this->branch_select & br_mask);
if ((cmd_desc[2] & 0xC) == 0x8) { // branch if cond cleared? if ((cmd_desc[2] & 0xC) == 0x8) {
cond = !cond; cond = !cond; // branch if cond = false
} }
} }
if (cond) { if (cond) {
@ -138,7 +152,7 @@ void DMAChannel::finish_cmd() {
} }
// all INPUT and OUTPUT commands update cmd.resCount // all INPUT and OUTPUT commands update cmd.resCount
if (this->cur_cmd < DBDMA_Cmd::STORE_QUAD && is_writable) { if (this->cur_cmd < DBDMA_Cmd::STORE_QUAD && res.is_writable) {
WRITE_WORD_LE_A(&cmd_desc[12], this->queue_len & 0xFFFFUL); WRITE_WORD_LE_A(&cmd_desc[12], this->queue_len & 0xFFFFUL);
} }
@ -149,8 +163,9 @@ void DMAChannel::finish_cmd() {
} }
void DMAChannel::xfer_quad(const DMACmd *cmd_desc, const bool is_store) { void DMAChannel::xfer_quad(const DMACmd *cmd_desc, const bool is_store) {
bool is_writable; MapDmaResult res;
uint8_t *src, *dst; uint8_t *src, *dst;
uint32_t addr;
// parse and fix reqCount // parse and fix reqCount
uint32_t xfer_size = cmd_desc->req_count & 7; uint32_t xfer_size = cmd_desc->req_count & 7;
@ -162,22 +177,33 @@ void DMAChannel::xfer_quad(const DMACmd *cmd_desc, const bool is_store) {
xfer_size = 1; xfer_size = 1;
} }
// prepare data pointers addr = cmd_desc->address & ~(xfer_size - 1);
if (is_store) {
src = mmu_get_dma_mem(this->cmd_ptr, 16, &is_writable);
src += 8; // move src to cmd.data32
dst = mmu_get_dma_mem(cmd_desc->address & ~(xfer_size - 1), xfer_size,
&is_writable);
} else {
src = mmu_get_dma_mem(cmd_desc->address & ~(xfer_size - 1), xfer_size,
&is_writable);
dst = mmu_get_dma_mem(this->cmd_ptr, 16, &is_writable);
dst += 8; // move dst to cmd.data32
}
// perform data transfer only if the destination is writable // prepare data pointers and perform data transfer
if (is_writable) { if (is_store) {
std::memcpy(dst, src, xfer_size); res = mmu_map_dma_mem(this->cmd_ptr, 16, false);
src = res.host_va + 8; // move src to cmd.data32
res = mmu_map_dma_mem(addr, xfer_size, true);
if (res.type & RT_MMIO) {
res.dev_obj->write(res.dev_base, addr - res.dev_base,
read_mem_rev(src, xfer_size), xfer_size);
} else if (res.is_writable) {
std::memcpy(res.host_va, src, xfer_size);
}
} else {
res = mmu_map_dma_mem(this->cmd_ptr, 16, false);
if (res.is_writable) {
dst = res.host_va + 8; // move dst to cmd.data32
res = mmu_map_dma_mem(addr, xfer_size, true);
if (res.type & RT_MMIO) {
write_mem_rev(dst,
res.dev_obj->read(res.dev_base, addr - res.dev_base, xfer_size),
xfer_size);
} else {
std::memcpy(dst, res.host_va, xfer_size);
}
}
} }
if (cmd_desc->cmd_bits & 0xC) if (cmd_desc->cmd_bits & 0xC)
@ -187,20 +213,20 @@ void DMAChannel::xfer_quad(const DMACmd *cmd_desc, const bool is_store) {
} }
void DMAChannel::update_irq() { void DMAChannel::update_irq() {
bool is_writable;
// obtain real pointer to the descriptor of the completed command // obtain real pointer to the descriptor of the completed command
uint8_t *cmd_desc = mmu_get_dma_mem(this->cmd_ptr, 16, &is_writable); MapDmaResult res = mmu_map_dma_mem(this->cmd_ptr, 16, false);
uint8_t *cmd_desc = res.host_va;
// STOP doesn't generate interrupts // STOP doesn't generate interrupts
if (this->cur_cmd < DBDMA_Cmd::STOP) { if (this->cur_cmd < DBDMA_Cmd::STOP) {
// react to cmd.i (interrupt) bits
if (cmd_desc[2] & 0x30) { if (cmd_desc[2] & 0x30) {
bool cond = true; bool cond = true;
if ((cmd_desc[2] & 0x30) != 0x30) { if ((cmd_desc[2] & 0x30) != 0x30) {
uint16_t int_mask = this->int_select >> 16; uint16_t int_mask = this->int_select >> 16;
cond = (this->ch_stat & int_mask) == (this->int_select & int_mask); cond = (this->ch_stat & int_mask) == (this->int_select & int_mask);
if ((cmd_desc[2] & 0x30) == 0x20) { // branch if cond cleared? if ((cmd_desc[2] & 0x30) == 0x20) {
cond = !cond; cond = !cond; // generate interrupt if cond = false
} }
} }
if (cond) { if (cond) {
@ -211,24 +237,22 @@ void DMAChannel::update_irq() {
} }
uint32_t DMAChannel::reg_read(uint32_t offset, int size) { uint32_t DMAChannel::reg_read(uint32_t offset, int size) {
uint32_t res = 0;
if (size != 4) { if (size != 4) {
ABORT_F("DBDMA: non-DWORD read from a DMA channel not supported"); ABORT_F("DBDMA: non-DWORD read from a DMA channel not supported");
} }
switch (offset) { switch (offset) {
case DMAReg::CH_CTRL: case DMAReg::CH_CTRL:
res = 0; // ChannelControl reads as 0 (DBDMA spec 5.5.1, table 74) return 0; // ChannelControl reads as 0 (DBDMA spec 5.5.1, table 74)
break;
case DMAReg::CH_STAT: case DMAReg::CH_STAT:
res = BYTESWAP_32(this->ch_stat); return BYTESWAP_32(this->ch_stat);
break; case DMAReg::CMD_PTR_LO:
return BYTESWAP_32(this->cmd_ptr);
default: default:
LOG_F(WARNING, "Unsupported DMA channel register 0x%X", offset); LOG_F(WARNING, "Unsupported DMA channel register 0x%X", offset);
} }
return res; return 0;
} }
void DMAChannel::reg_write(uint32_t offset, uint32_t value, int size) { void DMAChannel::reg_write(uint32_t offset, uint32_t value, int size) {