On Tue, May 3, 2022 at 3:16 AM Frank Chang <frank.ch...@sifive.com> wrote:
> On Mon, May 2, 2022 at 6:29 PM Edgar E. Iglesias <edgar.igles...@gmail.com> > wrote: > >> On Thu, Apr 28, 2022 at 5:43 PM <frank.ch...@sifive.com> wrote: >> >>> From: Frank Chang <frank.ch...@sifive.com> >>> >>> Add Xilinx AXI CDMA model, which follows >>> AXI Central Direct Memory Access v4.1 spec: >>> https://docs.xilinx.com/v/u/en-US/pg034-axi-cdma >>> >>> Supports both Simple DMA and Scatter Gather modes. >>> >> >> Hi Frank, >> >> Thanks for modeling this! I have a couple of questions. >> > > Hi Edgar, > > Thanks for reviewing. > > >> >> Do you plan to submit a machine that uses this DMA? >> > > Currently, Xilinx CDMA is used in our internal platform only, which is not > upstream. > Do you have any suggestions for the existing machine that I can add Xilinx > CDMA to? > Or perhaps, ARM virt machine? > If there's a reference design somewhere we could use we could potentially create a new zynqmp or versal based machine. It would be great if you guys had a public RISCV design with the CDMA that we could model. > > >> >> The CDMA has a 32-bit AXI4-Lite port for register accesses (see page 6 >> and 8 in the spec you referenced), so axicdma_ops.impl.max should be 4 and >> you shouldn't need the read/write q versions. >> > > Okay, that's something I was not aware of. > > However, I have a question regarding the 64-bit address space. > > For 64-bit address space, i.e. xlnx,addrwidth = 64. > The CDMA spec says that: > "TAILDESC_PNTR[_MSB] register causes the AXI CDMA SG Engine > to start fetching descriptors starting from the CURDESC_PNTR register > value." > > It seems that DMA will start the transfer if either TAILDESC_PNTR or > TAILDESC_PNTR_MSB is written. > Then how can we guarantee that the full 64-bit address pointer is written > before the DMA transfer is started if we can't write both TAILDESC_PNTR > and TAILDESC_PNTR_MSB > at the same time? > This is described on pages 25 and 26: "When the AXI CDMA is in SG Mode and the address space is 32 bits (CDMACR.SGMode = 1), a write by the software application to the TAILDESC_PNTR register causes the AXI CDMA SG Engine to start fetching descriptors" I.e TAILDESC_PNTR only starts the DMA if 32bit addresses have been selected. If 64bit addresses are selected, TAILDESC_PNTR_MSB starts the DMA: "When the AXI CDMA is in SG Mode, and the address space is more than 32 bits, (CDMACR.SGMode = 1), a write by the software application to the TAILDESC_PNTR_MSB register causes the AXI CDMA SG Engine to start fetching descriptors" > > I'm also awarded that Xilinx CDMA Linux driver also has separate 32-bit > writes for a 64-bit address. > But wouldn't that cause, e.g. dmatest to be failed? > The driver probably relies on the interconnect to down-size the 64bit access to 2x32bit ones. QEMU will do the same so this shouldn't be a problem. Best regards, Edgar > > Regards, > Frank Chang > > >> >> Best regards, >> Edgar >> >> >> >>> >>> Signed-off-by: Frank Chang <frank.ch...@sifive.com> >>> Reviewed-by: Jim Shu <jim....@sifive.com> >>> --- >>> hw/dma/meson.build | 2 +- >>> hw/dma/xilinx_axicdma.c | 792 ++++++++++++++++++++++++++++++++ >>> include/hw/dma/xilinx_axicdma.h | 72 +++ >>> 3 files changed, 865 insertions(+), 1 deletion(-) >>> create mode 100644 hw/dma/xilinx_axicdma.c >>> create mode 100644 include/hw/dma/xilinx_axicdma.h >>> >>> diff --git a/hw/dma/meson.build b/hw/dma/meson.build >>> index f3f0661bc3..85edf80b82 100644 >>> --- a/hw/dma/meson.build >>> +++ b/hw/dma/meson.build >>> @@ -3,7 +3,7 @@ softmmu_ss.add(when: 'CONFIG_PL080', if_true: >>> files('pl080.c')) >>> softmmu_ss.add(when: 'CONFIG_PL330', if_true: files('pl330.c')) >>> softmmu_ss.add(when: 'CONFIG_I82374', if_true: files('i82374.c')) >>> softmmu_ss.add(when: 'CONFIG_I8257', if_true: files('i8257.c')) >>> -softmmu_ss.add(when: 'CONFIG_XILINX_AXI', if_true: >>> files('xilinx_axidma.c')) >>> +softmmu_ss.add(when: 'CONFIG_XILINX_AXI', if_true: >>> files('xilinx_axidma.c', 'xilinx_axicdma.c')) >>> softmmu_ss.add(when: 'CONFIG_ZYNQ_DEVCFG', if_true: >>> files('xlnx-zynq-devcfg.c')) >>> softmmu_ss.add(when: 'CONFIG_ETRAXFS', if_true: files('etraxfs_dma.c')) >>> softmmu_ss.add(when: 'CONFIG_STP2000', if_true: files('sparc32_dma.c')) >>> diff --git a/hw/dma/xilinx_axicdma.c b/hw/dma/xilinx_axicdma.c >>> new file mode 100644 >>> index 0000000000..50aec3ec45 >>> --- /dev/null >>> +++ b/hw/dma/xilinx_axicdma.c >>> @@ -0,0 +1,792 @@ >>> +/* >>> + * QEMU model of Xilinx AXI-CDMA block. >>> + * >>> + * Copyright (c) 2022 Frank Chang <frank.ch...@sifive.com>. >>> + * >>> + * Permission is hereby granted, free of charge, to any person >>> obtaining a copy >>> + * of this software and associated documentation files (the >>> "Software"), to deal >>> + * in the Software without restriction, including without limitation >>> the rights >>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or >>> sell >>> + * copies of the Software, and to permit persons to whom the Software is >>> + * furnished to do so, subject to the following conditions: >>> + * >>> + * The above copyright notice and this permission notice shall be >>> included in >>> + * all copies or substantial portions of the Software. >>> + * >>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, >>> EXPRESS OR >>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF >>> MERCHANTABILITY, >>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT >>> SHALL >>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR >>> OTHER >>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, >>> ARISING FROM, >>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER >>> DEALINGS IN >>> + * THE SOFTWARE. >>> + */ >>> + >>> +#include "qemu/osdep.h" >>> +#include "hw/hw.h" >>> +#include "hw/irq.h" >>> +#include "hw/ptimer.h" >>> +#include "hw/qdev-properties.h" >>> +#include "hw/sysbus.h" >>> +#include "qapi/error.h" >>> +#include "qemu/log.h" >>> +#include "qemu/module.h" >>> +#include "qemu/timer.h" >>> +#include "qom/object.h" >>> +#include "sysemu/dma.h" >>> +#include "hw/dma/xilinx_axicdma.h" >>> + >>> +#define R_CDMACR 0x00 >>> +#define R_CDMASR 0x04 >>> +#define R_CURDESC 0x08 >>> +#define R_CURDESC_MSB 0x0c >>> +#define R_TAILDESC 0x10 >>> +#define R_TAILDESC_MSB 0x14 >>> +#define R_SA 0x18 >>> +#define R_SA_MSB 0x1c >>> +#define R_DA 0x20 >>> +#define R_DA_MSB 0x24 >>> +#define R_BTT 0x28 >>> + >>> +#define R_MAX 0x30 >>> + >>> +/* CDMACR */ >>> +#define CDMACR_TAIL_PNTR_EN BIT(1) >>> +#define CDMACR_RESET BIT(2) >>> +#define CDMACR_SGMODE BIT(3) >>> +#define CDMACR_KEY_HOLE_READ BIT(4) >>> +#define CDMACR_KEY_HOLE_WRITE BIT(5) >>> +#define CDMACR_CYCLIC_BD_ENABLE BIT(6) >>> +#define CDMACR_IOC_IRQ_EN BIT(12) >>> +#define CDMACR_DLY_IRQ_EN BIT(13) >>> +#define CDMACR_ERR_IRQ_EN BIT(14) >>> + >>> +#define CDMACR_MASK 0xffff707c >>> + >>> +/* TailPntrEn = 1, IRQThreshold = 1. */ >>> +#define CDMACR_DEFAULT 0x10002 >>> + >>> +/* CDMASR */ >>> +#define CDMASR_IDLE BIT(1) >>> +#define CDMASR_SG_INCLUD BIT(3) >>> +#define CDMASR_DMA_INT_ERR BIT(4) >>> +#define CDMASR_DMA_SLV_ERR BIT(5) >>> +#define CDMASR_DMA_DEC_ERR BIT(6) >>> +#define CDMASR_SG_INT_ERR BIT(8) >>> +#define CDMASR_SG_SLV_ERR BIT(9) >>> +#define CDMASR_SG_DEC_ERR BIT(10) >>> +#define CDMASR_IOC_IRQ BIT(12) >>> +#define CDMASR_DLY_IRQ BIT(13) >>> +#define CDMASR_ERR_IRQ BIT(14) >>> + >>> +#define CDMASR_IRQ_THRES_SHIFT 16 >>> +#define CDMASR_IRQ_THRES_WIDTH 8 >>> +#define CDMASR_IRQ_DELAY_SHIFT 24 >>> +#define CDMASR_IRQ_DELAY_WIDTH 8 >>> + >>> +#define CDMASR_IRQ_MASK (CDMASR_IOC_IRQ | \ >>> + CDMASR_DLY_IRQ | \ >>> + CDMASR_ERR_IRQ) >>> + >>> +/* Idle = 1, SGIncld = 1, IRQThresholdSts = 1. */ >>> +#define CDMASR_DEFAULT 0x1000a >>> + >>> +#define CURDESC_MASK 0xffffffc0 >>> + >>> +#define TAILDESC_MASK 0xffffffc0 >>> + >>> +#define BTT_MAX_SIZE (1UL << 26) >>> +#define BTT_MASK (BTT_MAX_SIZE - 1) >>> + >>> +/* SDesc - Status */ >>> +#define SDEC_STATUS_DMA_INT_ERR BIT(28) >>> +#define SDEC_STATUS_DMA_SLV_ERR BIT(29) >>> +#define SDEC_STATUS_DMA_DEC_ERR BIT(30) >>> +#define SDEC_STATUS_DMA_CMPLT BIT(31) >>> + >>> + >>> +static void axicdma_set_dma_err(XilinxAXICDMA *s, uint32_t err); >>> +static void axicdma_set_sg_dma_err(XilinxAXICDMA *s, uint32_t err, >>> hwaddr addr); >>> +static void axicdma_set_sg_err(XilinxAXICDMA *s, uint32_t err); >>> + >>> +static void axicdma_update_irq(XilinxAXICDMA *s) >>> +{ >>> + uint32_t enable, pending; >>> + >>> + enable = s->control & CDMASR_IRQ_MASK; >>> + pending = s->status & CDMASR_IRQ_MASK; >>> + >>> + qemu_set_irq(s->irq, !!(enable & pending)); >>> +} >>> + >>> +static void axicdma_set_irq(XilinxAXICDMA *s, uint32_t irq, bool level) >>> +{ >>> + g_assert(irq == CDMASR_IOC_IRQ || >>> + irq == CDMASR_DLY_IRQ || >>> + irq == CDMASR_ERR_IRQ); >>> + >>> + if (level) { >>> + s->status |= irq; >>> + } else { >>> + s->status &= ~irq; >>> + } >>> + >>> + axicdma_update_irq(s); >>> +} >>> + >>> +static void axicdma_reload_complete_cnt(XilinxAXICDMA *s) >>> +{ >>> + s->complete_cnt = extract32(s->control, CDMASR_IRQ_THRES_SHIFT, >>> + CDMASR_IRQ_THRES_WIDTH); >>> +} >>> + >>> +static void timer_hit(void *opaque) >>> +{ >>> + XilinxAXICDMA *s = XILINX_AXI_CDMA(opaque); >>> + >>> + axicdma_set_irq(s, CDMASR_DLY_IRQ, true); >>> + axicdma_reload_complete_cnt(s); >>> +} >>> + >>> +static bool sdesc_load(XilinxAXICDMA *s, hwaddr addr) >>> +{ >>> + SDesc *d = &s->sdesc; >>> + MemTxResult ret; >>> + >>> + ret = address_space_read(s->as, addr, MEMTXATTRS_UNSPECIFIED, >>> + d, sizeof(SDesc)); >>> + if (ret != MEMTX_OK) { >>> + axicdma_set_sg_err(s, CDMASR_SG_DEC_ERR); >>> + return false; >>> + } >>> + >>> + /* Convert from LE into host endianness. */ >>> + d->nxtdesc = le64_to_cpu(d->nxtdesc); >>> + d->src = le64_to_cpu(d->src); >>> + d->dest = le64_to_cpu(d->dest); >>> + d->control = le32_to_cpu(d->control); >>> + d->status = le32_to_cpu(d->status); >>> + >>> + return true; >>> +} >>> + >>> +static bool sdesc_store(XilinxAXICDMA *s, hwaddr addr) >>> +{ >>> + SDesc *d = &s->sdesc; >>> + MemTxResult ret; >>> + >>> + /* Convert from host endianness into LE. */ >>> + d->nxtdesc = cpu_to_le64(d->nxtdesc); >>> + d->src = cpu_to_le64(d->src); >>> + d->dest = cpu_to_le64(d->dest); >>> + d->control = cpu_to_le32(d->control); >>> + d->status = cpu_to_le32(d->status); >>> + >>> + ret = address_space_write(s->as, addr, MEMTXATTRS_UNSPECIFIED, >>> + d, sizeof(SDesc)); >>> + if (ret != MEMTX_OK) { >>> + axicdma_set_sg_err(s, CDMASR_SG_DEC_ERR); >>> + return false; >>> + } >>> + >>> + return true; >>> +} >>> + >>> +static void sdesc_complete(XilinxAXICDMA *s) >>> +{ >>> + uint32_t irq_delay = extract32(s->control, CDMASR_IRQ_DELAY_SHIFT, >>> + CDMASR_IRQ_DELAY_WIDTH); >>> + >>> + if (irq_delay) { >>> + /* Restart the delayed timer. */ >>> + ptimer_transaction_begin(s->ptimer); >>> + ptimer_stop(s->ptimer); >>> + ptimer_set_count(s->ptimer, irq_delay); >>> + ptimer_run(s->ptimer, 1); >>> + ptimer_transaction_commit(s->ptimer); >>> + } >>> + >>> + s->complete_cnt--; >>> + >>> + if (s->complete_cnt == 0) { >>> + /* Raise the IOC irq. */ >>> + axicdma_set_irq(s, CDMASR_IOC_IRQ, true); >>> + axicdma_reload_complete_cnt(s); >>> + } >>> +} >>> + >>> +static inline bool axicdma_sgmode(XilinxAXICDMA *s) >>> +{ >>> + return !!(s->control & CDMACR_SGMODE); >>> +} >>> + >>> +static void axicdma_set_dma_err(XilinxAXICDMA *s, uint32_t err) >>> +{ >>> + g_assert(err == CDMASR_DMA_INT_ERR || >>> + err == CDMASR_DMA_SLV_ERR || >>> + err == CDMASR_DMA_DEC_ERR); >>> + >>> + s->status |= err; >>> + axicdma_set_irq(s, CDMASR_ERR_IRQ, true); >>> +} >>> + >>> +static void axicdma_set_sg_dma_err(XilinxAXICDMA *s, uint32_t err, >>> hwaddr addr) >>> +{ >>> + g_assert(axicdma_sgmode(s)); >>> + >>> + axicdma_set_dma_err(s, err); >>> + >>> + /* >>> + * There are 24-bit shift between >>> + * SDesc status bit and CDMACR.DMA_[INT|SLV|DEC]_ERR bit. >>> + */ >>> + s->sdesc.status |= (err << 24); >>> + sdesc_store(s, addr); >>> +} >>> + >>> +static void axicdma_set_sg_err(XilinxAXICDMA *s, uint32_t err) >>> +{ >>> + g_assert(err == CDMASR_SG_INT_ERR || >>> + err == CDMASR_SG_SLV_ERR || >>> + err == CDMASR_SG_DEC_ERR); >>> + >>> + s->status |= err; >>> + axicdma_set_irq(s, CDMASR_ERR_IRQ, true); >>> +} >>> + >>> +static bool axicdma_perform_dma(XilinxAXICDMA *s, hwaddr src, hwaddr >>> dest, >>> + uint32_t btt) >>> +{ >>> + uint32_t r_off = 0, w_off = 0; >>> + uint32_t len; >>> + MemTxResult ret; >>> + >>> + while (btt > 0) { >>> + len = MIN(btt, CDMA_BUF_SIZE); >>> + >>> + ret = dma_memory_read(s->as, src + r_off, s->buf + r_off, len, >>> + MEMTXATTRS_UNSPECIFIED); >>> + if (ret != MEMTX_OK) { >>> + return false; >>> + } >>> + >>> + ret = dma_memory_write(s->as, dest + w_off, s->buf + w_off, len, >>> + MEMTXATTRS_UNSPECIFIED); >>> + if (ret != MEMTX_OK) { >>> + return false; >>> + } >>> + >>> + btt -= len; >>> + >>> + if (!(s->control & CDMACR_KEY_HOLE_READ)) { >>> + r_off += len; >>> + } >>> + >>> + if (!(s->control & CDMACR_KEY_HOLE_WRITE)) { >>> + w_off += len; >>> + } >>> + } >>> + >>> + return true; >>> +} >>> + >>> +static void axicdma_run_simple(XilinxAXICDMA *s) >>> +{ >>> + if (!s->btt) { >>> + /* Value of zero BTT is not allowed. */ >>> + axicdma_set_dma_err(s, CDMASR_DMA_INT_ERR); >>> + return; >>> + } >>> + >>> + if (!axicdma_perform_dma(s, s->src, s->dest, s->btt)) { >>> + axicdma_set_dma_err(s, CDMASR_DMA_DEC_ERR); >>> + return; >>> + } >>> + >>> + /* Generate IOC interrupt. */ >>> + axicdma_set_irq(s, CDMASR_IOC_IRQ, true); >>> +} >>> + >>> +static void axicdma_run_sgmode(XilinxAXICDMA *s) >>> +{ >>> + uint64_t pdesc; >>> + uint32_t btt; >>> + >>> + while (1) { >>> + if (!sdesc_load(s, s->curdesc)) { >>> + break; >>> + } >>> + >>> + if (s->sdesc.status & SDEC_STATUS_DMA_CMPLT) { >>> + axicdma_set_sg_err(s, CDMASR_SG_INT_ERR); >>> + break; >>> + } >>> + >>> + btt = s->sdesc.control & BTT_MASK; >>> + >>> + if (btt == 0) { >>> + /* Value of zero BTT is not allowed. */ >>> + axicdma_set_sg_err(s, CDMASR_SG_INT_ERR); >>> + break; >>> + } >>> + >>> + if (!axicdma_perform_dma(s, s->sdesc.src, s->sdesc.dest, btt)) { >>> + axicdma_set_sg_dma_err(s, CDMASR_DMA_DEC_ERR, s->curdesc); >>> + break; >>> + } >>> + >>> + /* Update the descriptor. */ >>> + s->sdesc.status |= SDEC_STATUS_DMA_CMPLT; >>> + sdesc_store(s, s->curdesc); >>> + sdesc_complete(s); >>> + >>> + /* Advance current descriptor pointer. */ >>> + pdesc = s->curdesc; >>> + s->curdesc = s->sdesc.nxtdesc; >>> + >>> + if (!(s->control & CDMACR_CYCLIC_BD_ENABLE) && >>> + pdesc == s->taildesc) { >>> + /* Reach the tail descriptor. */ >>> + break; >>> + } >>> + } >>> + >>> + /* Stop the delayed timer. */ >>> + ptimer_transaction_begin(s->ptimer); >>> + ptimer_stop(s->ptimer); >>> + ptimer_transaction_commit(s->ptimer); >>> +} >>> + >>> +static void axicdma_run(XilinxAXICDMA *s) >>> +{ >>> + bool sgmode = axicdma_sgmode(s); >>> + >>> + /* Not idle. */ >>> + s->status &= ~CDMASR_IDLE; >>> + >>> + if (!sgmode) { >>> + axicdma_run_simple(s); >>> + } else { >>> + axicdma_run_sgmode(s); >>> + } >>> + >>> + /* Idle. */ >>> + s->status |= CDMASR_IDLE; >>> +} >>> + >>> +static void axicdma_reset(XilinxAXICDMA *s) >>> +{ >>> + s->control = CDMACR_DEFAULT; >>> + s->status = CDMASR_DEFAULT; >>> + s->complete_cnt = 1; >>> + qemu_irq_lower(s->irq); >>> +} >>> + >>> +static void axicdma_write_control(XilinxAXICDMA *s, uint32_t value) >>> +{ >>> + if (value & CDMACR_RESET) { >>> + axicdma_reset(s); >>> + return; >>> + } >>> + >>> + /* >>> + * The minimum setting for the threshold is 0x01. >>> + * A write of 0x00 to CDMACR.IRQThreshold has no effect. >>> + */ >>> + if (!extract32(value, CDMASR_IRQ_THRES_SHIFT, >>> CDMASR_IRQ_THRES_WIDTH)) { >>> + value = deposit32(value, CDMASR_IRQ_THRES_SHIFT, >>> CDMASR_IRQ_THRES_WIDTH, >>> + s->control); >>> + } >>> + >>> + /* >>> + * AXI CDMA is built with SG enabled, >>> + * tail pointer mode is always enabled. >>> + */ >>> + s->control = (value & CDMACR_MASK) | CDMACR_TAIL_PNTR_EN; >>> + >>> + if (!axicdma_sgmode(s)) { >>> + /* >>> + * CDMASR.Dly_Irq, CURDESC_PNTR, TAILDESC_PNTR are cleared >>> + * when not in SGMode. >>> + */ >>> + s->status &= ~CDMASR_DLY_IRQ; >>> + s->curdesc = 0; >>> + s->taildesc = 0; >>> + } >>> + >>> + axicdma_reload_complete_cnt(s); >>> +} >>> + >>> +static uint32_t axicdma_read_status(XilinxAXICDMA *s) >>> +{ >>> + uint32_t value = s->status; >>> + >>> + value = deposit32(value, CDMASR_IRQ_THRES_SHIFT, >>> + CDMASR_IRQ_THRES_WIDTH, s->complete_cnt); >>> + value = deposit32(value, CDMASR_IRQ_DELAY_SHIFT, >>> + CDMASR_IRQ_DELAY_WIDTH, >>> ptimer_get_count(s->ptimer)); >>> + >>> + return value; >>> +} >>> + >>> +static void axicdma_write_status(XilinxAXICDMA *s, uint32_t value) >>> +{ >>> + /* Write 1s to clear interrupts. */ >>> + s->status &= ~(value & CDMASR_IRQ_MASK); >>> + axicdma_update_irq(s); >>> +} >>> + >>> +static void axicdma_write_curdesc(XilinxAXICDMA *s, uint64_t value) >>> +{ >>> + /* Should be idle. */ >>> + g_assert(s->status & CDMASR_IDLE); >>> + >>> + if (!axicdma_sgmode(s)) { >>> + /* This register is cleared if SGMode = 0. */ >>> + return; >>> + } >>> + >>> + s->curdesc = value & CURDESC_MASK; >>> +} >>> + >>> +static void axicdma_write_taildesc(XilinxAXICDMA *s, uint64_t value) >>> +{ >>> + /* Should be idle. */ >>> + g_assert(s->status & CDMASR_IDLE); >>> + >>> + if (!axicdma_sgmode(s)) { >>> + /* This register is cleared if SGMode = 0. */ >>> + return; >>> + } >>> + >>> + s->taildesc = value & TAILDESC_MASK; >>> + >>> + /* Kick-off CDMA. */ >>> + axicdma_run(s); >>> +} >>> + >>> +static void axicdma_write_btt(XilinxAXICDMA *s, uint64_t value) >>> +{ >>> + s->btt = value & BTT_MASK; >>> + >>> + if (!axicdma_sgmode(s)) { >>> + /* Kick-off CDMA. */ >>> + axicdma_run(s); >>> + } >>> +} >>> + >>> +static uint32_t axicdma_readl(void *opaque, hwaddr addr, unsigned size) >>> +{ >>> + XilinxAXICDMA *s = opaque; >>> + uint32_t value = 0; >>> + >>> + if (s->addrwidth <= 32) { >>> + switch (addr) { >>> + case R_CURDESC_MSB: >>> + case R_TAILDESC_MSB: >>> + case R_SA_MSB: >>> + case R_DA_MSB: >>> + qemu_log_mask(LOG_GUEST_ERROR, >>> + "%s: Invalid 32-bit read to 0x%" HWADDR_PRIX >>> "\n", >>> + __func__, addr); >>> + return value; >>> + } >>> + } >>> + >>> + switch (addr) { >>> + case R_CDMACR: >>> + value = s->control; >>> + break; >>> + case R_CDMASR: >>> + value = axicdma_read_status(s); >>> + break; >>> + case R_CURDESC: >>> + value = s->curdesc; >>> + break; >>> + case R_CURDESC_MSB: >>> + value = extract64(s->curdesc, 32, 32); >>> + break; >>> + case R_TAILDESC: >>> + value = s->taildesc; >>> + break; >>> + case R_TAILDESC_MSB: >>> + value = extract64(s->taildesc, 32, 32); >>> + break; >>> + case R_SA: >>> + value = s->src; >>> + break; >>> + case R_SA_MSB: >>> + value = extract64(s->src, 32, 32); >>> + break; >>> + case R_DA: >>> + value = s->dest; >>> + break; >>> + case R_DA_MSB: >>> + value = extract64(s->dest, 32, 32); >>> + break; >>> + case R_BTT: >>> + value = s->btt; >>> + break; >>> + default: >>> + qemu_log_mask(LOG_GUEST_ERROR, >>> + "%s: Invalid 32-bit read to 0x%" HWADDR_PRIX "\n", >>> + __func__, addr); >>> + } >>> + >>> + return value; >>> +} >>> + >>> +static uint32_t axicdma_readq(void *opaque, hwaddr addr, unsigned size) >>> +{ >>> + XilinxAXICDMA *s = opaque; >>> + uint64_t value = 0; >>> + >>> + switch (addr) { >>> + case R_CDMACR: >>> + value = s->control; >>> + break; >>> + case R_CDMASR: >>> + value = axicdma_read_status(s); >>> + break; >>> + case R_CURDESC: >>> + value = s->curdesc; >>> + break; >>> + case R_TAILDESC: >>> + value = s->taildesc; >>> + break; >>> + case R_SA: >>> + value = s->src; >>> + break; >>> + case R_DA: >>> + value = s->dest; >>> + break; >>> + case R_BTT: >>> + value = s->btt; >>> + break; >>> + default: >>> + qemu_log_mask(LOG_GUEST_ERROR, >>> + "%s: Invalid 64-bit read to 0x%" HWADDR_PRIX "\n", >>> + __func__, addr); >>> + } >>> + >>> + return value; >>> +} >>> + >>> +static uint64_t axicdma_read(void *opaque, hwaddr addr, unsigned size) >>> +{ >>> + uint64_t value = 0; >>> + >>> + switch (size) { >>> + case 4: >>> + value = axicdma_readl(opaque, addr, size); >>> + break; >>> + case 8: >>> + value = axicdma_readq(opaque, addr, size); >>> + break; >>> + default: >>> + qemu_log_mask(LOG_GUEST_ERROR, "%s: Invalid read size %u to >>> AXI-CDMA\n", >>> + __func__, size); >>> + } >>> + >>> + return value; >>> +} >>> + >>> +static void axicdma_writel(void *opaque, hwaddr addr, uint32_t value, >>> + unsigned size) >>> +{ >>> + XilinxAXICDMA *s = opaque; >>> + >>> + if (s->addrwidth <= 32) { >>> + switch (addr) { >>> + case R_CURDESC_MSB: >>> + case R_TAILDESC_MSB: >>> + case R_SA_MSB: >>> + case R_DA_MSB: >>> + qemu_log_mask(LOG_GUEST_ERROR, >>> + "%s: Invalid 32-bit write to 0x%" HWADDR_PRIX >>> "\n", >>> + __func__, addr); >>> + return; >>> + } >>> + } >>> + >>> + switch (addr) { >>> + case R_CDMACR: >>> + axicdma_write_control(s, value); >>> + break; >>> + case R_CDMASR: >>> + axicdma_write_status(s, value); >>> + break; >>> + case R_CURDESC: >>> + axicdma_write_curdesc(s, deposit64(s->curdesc, 0, 32, value)); >>> + break; >>> + case R_CURDESC_MSB: >>> + axicdma_write_curdesc(s, deposit64(s->curdesc, 32, 32, value)); >>> + break; >>> + case R_TAILDESC: >>> + axicdma_write_taildesc(s, deposit64(s->taildesc, 0, 32, value)); >>> + break; >>> + case R_TAILDESC_MSB: >>> + axicdma_write_taildesc(s, deposit64(s->taildesc, 32, 32, >>> value)); >>> + break; >>> + case R_SA: >>> + s->src = deposit64(s->src, 0, 32, value); >>> + break; >>> + case R_SA_MSB: >>> + s->src = deposit64(s->src, 32, 32, value); >>> + break; >>> + case R_DA: >>> + s->dest = deposit64(s->dest, 0, 32, value); >>> + break; >>> + case R_DA_MSB: >>> + s->dest = deposit64(s->dest, 32, 32, value); >>> + break; >>> + case R_BTT: >>> + axicdma_write_btt(s, value); >>> + break; >>> + default: >>> + qemu_log_mask(LOG_GUEST_ERROR, >>> + "%s: Invalid 32-bit write to 0x%" HWADDR_PRIX >>> "\n", >>> + __func__, addr); >>> + } >>> +} >>> + >>> +static void axicdma_writeq(void *opaque, hwaddr addr, uint64_t value, >>> + unsigned size) >>> +{ >>> + XilinxAXICDMA *s = opaque; >>> + >>> + switch (addr) { >>> + case R_CDMACR: >>> + axicdma_write_control(s, value); >>> + break; >>> + case R_CDMASR: >>> + axicdma_write_status(s, value); >>> + break; >>> + case R_CURDESC: >>> + axicdma_write_curdesc(s, value); >>> + break; >>> + case R_TAILDESC: >>> + axicdma_write_taildesc(s, value); >>> + break; >>> + case R_SA: >>> + s->src = value; >>> + break; >>> + case R_DA: >>> + s->dest = value; >>> + break; >>> + case R_BTT: >>> + axicdma_write_btt(s, value); >>> + break; >>> + default: >>> + qemu_log_mask(LOG_GUEST_ERROR, >>> + "%s: Invalid 64-bit write to 0x%" HWADDR_PRIX >>> "\n", >>> + __func__, addr); >>> + } >>> +} >>> + >>> +static void axicdma_write(void *opaque, hwaddr addr, >>> + uint64_t value, unsigned size) >>> +{ >>> + switch (size) { >>> + case 4: >>> + axicdma_writel(opaque, addr, value, size); >>> + break; >>> + case 8: >>> + axicdma_writeq(opaque, addr, value, size); >>> + break; >>> + default: >>> + qemu_log_mask(LOG_GUEST_ERROR, >>> + "%s: Invalid write size %u to AXI-CDMA\n", >>> + __func__, size); >>> + } >>> +} >>> + >>> +static const MemoryRegionOps axicdma_ops = { >>> + .read = axicdma_read, >>> + .write = axicdma_write, >>> + .endianness = DEVICE_NATIVE_ENDIAN, >>> + .valid = { >>> + .min_access_size = 4, >>> + .max_access_size = 8 >>> + }, >>> + .impl = { >>> + .min_access_size = 4, >>> + .max_access_size = 8, >>> + }, >>> +}; >>> + >>> +static void xilinx_axicdma_realize(DeviceState *dev, Error **errp) >>> +{ >>> + XilinxAXICDMA *s = XILINX_AXI_CDMA(dev); >>> + SysBusDevice *sbd = SYS_BUS_DEVICE(dev); >>> + >>> + memory_region_init_io(&s->mmio, OBJECT(dev), &axicdma_ops, s, >>> + TYPE_XILINX_AXI_CDMA, R_MAX); >>> + sysbus_init_mmio(sbd, &s->mmio); >>> + sysbus_init_irq(sbd, &s->irq); >>> + >>> + if (!s->dma_mr || s->dma_mr == get_system_memory()) { >>> + /* Avoid creating new AddressSpace for system memory. */ >>> + s->as = &address_space_memory; >>> + } else { >>> + s->as = g_new0(AddressSpace, 1); >>> + address_space_init(s->as, s->dma_mr, >>> memory_region_name(s->dma_mr)); >>> + } >>> + >>> + s->ptimer = ptimer_init(timer_hit, s, PTIMER_POLICY_DEFAULT); >>> + ptimer_transaction_begin(s->ptimer); >>> + ptimer_set_freq(s->ptimer, s->freqhz); >>> + ptimer_transaction_commit(s->ptimer); >>> +} >>> + >>> +static void xilinx_axicdma_unrealize(DeviceState *dev) >>> +{ >>> + XilinxAXICDMA *s = XILINX_AXI_CDMA(dev); >>> + >>> + if (s->ptimer) { >>> + ptimer_free(s->ptimer); >>> + } >>> + >>> + if (s->as && s->dma_mr != get_system_memory()) { >>> + g_free(s->as); >>> + } >>> +} >>> + >>> +static Property axicdma_properties[] = { >>> + DEFINE_PROP_UINT32("freqhz", XilinxAXICDMA, freqhz, 50000000), >>> + DEFINE_PROP_INT32("addrwidth", XilinxAXICDMA, addrwidth, 64), >>> + DEFINE_PROP_LINK("dma", XilinxAXICDMA, dma_mr, >>> + TYPE_MEMORY_REGION, MemoryRegion *), >>> + DEFINE_PROP_END_OF_LIST(), >>> +}; >>> + >>> +static void xilinx_axicdma_reset_enter(Object *obj, ResetType type) >>> +{ >>> + axicdma_reset(XILINX_AXI_CDMA(obj)); >>> +} >>> + >>> +static void axicdma_class_init(ObjectClass *klass, void *data) >>> +{ >>> + DeviceClass *dc = DEVICE_CLASS(klass); >>> + ResettableClass *rc = RESETTABLE_CLASS(klass); >>> + >>> + dc->realize = xilinx_axicdma_realize; >>> + dc->unrealize = xilinx_axicdma_unrealize; >>> + device_class_set_props(dc, axicdma_properties); >>> + >>> + rc->phases.enter = xilinx_axicdma_reset_enter; >>> +} >>> + >>> +static const TypeInfo axicdma_info = { >>> + .name = TYPE_XILINX_AXI_CDMA, >>> + .parent = TYPE_SYS_BUS_DEVICE, >>> + .instance_size = sizeof(XilinxAXICDMA), >>> + .class_init = axicdma_class_init, >>> +}; >>> + >>> +static void xilinx_axicdma_register_types(void) >>> +{ >>> + type_register_static(&axicdma_info); >>> +} >>> + >>> +type_init(xilinx_axicdma_register_types) >>> diff --git a/include/hw/dma/xilinx_axicdma.h >>> b/include/hw/dma/xilinx_axicdma.h >>> new file mode 100644 >>> index 0000000000..67b7cfce99 >>> --- /dev/null >>> +++ b/include/hw/dma/xilinx_axicdma.h >>> @@ -0,0 +1,72 @@ >>> +/* >>> + * QEMU model of Xilinx AXI-CDMA block. >>> + * >>> + * Copyright (c) 2022 Frank Chang <frank.ch...@sifive.com>. >>> + * >>> + * Permission is hereby granted, free of charge, to any person >>> obtaining a copy >>> + * of this software and associated documentation files (the >>> "Software"), to deal >>> + * in the Software without restriction, including without limitation >>> the rights >>> + * to use, copy, modify, merge, publish, distribute, sublicense, and/or >>> sell >>> + * copies of the Software, and to permit persons to whom the Software is >>> + * furnished to do so, subject to the following conditions: >>> + * >>> + * The above copyright notice and this permission notice shall be >>> included in >>> + * all copies or substantial portions of the Software. >>> + * >>> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, >>> EXPRESS OR >>> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF >>> MERCHANTABILITY, >>> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT >>> SHALL >>> + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR >>> OTHER >>> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, >>> ARISING FROM, >>> + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER >>> DEALINGS IN >>> + * THE SOFTWARE. >>> + */ >>> + >>> +#include "exec/hwaddr.h" >>> +#include "hw/ptimer.h" >>> +#include "hw/sysbus.h" >>> +#include "qom/object.h" >>> +#include "qemu/units.h" >>> + >>> +#define CDMA_BUF_SIZE (64 * KiB) >>> + >>> +typedef struct XilinxAXICDMA XilinxAXICDMA; >>> + >>> +#define TYPE_XILINX_AXI_CDMA "xlnx.axi-cdma" >>> +OBJECT_DECLARE_SIMPLE_TYPE(XilinxAXICDMA, XILINX_AXI_CDMA) >>> + >>> +/* Scatter Gather Transfer Descriptor */ >>> +typedef struct SDesc { >>> + uint64_t nxtdesc; >>> + hwaddr src; >>> + hwaddr dest; >>> + uint32_t control; >>> + uint32_t status; >>> +} SDesc; >>> + >>> +struct XilinxAXICDMA { >>> + /*< private >*/ >>> + SysBusDevice parent_obj; >>> + >>> + /*< public >*/ >>> + MemoryRegion mmio; >>> + AddressSpace *as; >>> + MemoryRegion *dma_mr; >>> + >>> + /* Properties */ >>> + uint32_t control; >>> + uint32_t status; >>> + uint64_t curdesc; >>> + uint64_t taildesc; >>> + hwaddr src; >>> + hwaddr dest; >>> + uint32_t btt; >>> + >>> + uint32_t freqhz; >>> + int32_t addrwidth; >>> + ptimer_state *ptimer; >>> + SDesc sdesc; >>> + qemu_irq irq; >>> + uint16_t complete_cnt; >>> + char buf[CDMA_BUF_SIZE]; >>> +}; >>> -- >>> 2.35.1 >>> >>>