All of lore.kernel.org
 help / color / mirror / Atom feed
* [Qemu-devel] [PATCH v1 0/2] xlnx-zynqmp: Add emulation of the ZynqMP GDMA and ADMA
@ 2018-04-29  0:43 Francisco Iglesias
  2018-04-29  0:43 ` [Qemu-devel] [PATCH v1 1/2] xlnx-zdma: Add a model of the Xilinx ZynqMP generic DMA Francisco Iglesias
  2018-04-29  0:43 ` [Qemu-devel] [PATCH v1 2/2] xlnx-zynqmp: Connect the ZynqMP GDMA and ADMA Francisco Iglesias
  0 siblings, 2 replies; 7+ messages in thread
From: Francisco Iglesias @ 2018-04-29  0:43 UTC (permalink / raw)
  To: qemu-devel
  Cc: edgari, sai.pavan.boddu, alistair, alistair23,
	francisco.iglesias, peter.maydell

Hi,

The ZynqMP Soc contains two separate instances of a generic DMA, one located in
the FPD (full power domain) called GDMA and a second one located in the LPD
(low power domain) called ADMA. This patch series attempts to add emulation
support for these two DMAs on the ZynqMP board. The first patch in the series
adds a model of the ZynqMP generic DMA (the ZDMA). The second patch in the
series connects the two instances of the ZDMA, the GDMA and the ADMA, to the
ZynqMP board.

Best regards,
Francisco Iglesias

Francisco Iglesias (2):
  xlnx-zdma: Add a model of the Xilinx ZynqMP generic DMA
  xlnx-zynqmp: Connect the ZynqMP GDMA and ADMA

 hw/arm/xlnx-zynqmp.c         |  53 +++
 hw/dma/Makefile.objs         |   1 +
 hw/dma/xlnx-zdma.c           | 833 +++++++++++++++++++++++++++++++++++++++++++
 include/hw/arm/xlnx-zynqmp.h |   5 +
 include/hw/dma/xlnx-zdma.h   |  84 +++++
 5 files changed, 976 insertions(+)
 create mode 100644 hw/dma/xlnx-zdma.c
 create mode 100644 include/hw/dma/xlnx-zdma.h

-- 
2.11.0

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH v1 1/2] xlnx-zdma: Add a model of the Xilinx ZynqMP generic DMA
  2018-04-29  0:43 [Qemu-devel] [PATCH v1 0/2] xlnx-zynqmp: Add emulation of the ZynqMP GDMA and ADMA Francisco Iglesias
@ 2018-04-29  0:43 ` Francisco Iglesias
  2018-04-30 13:27   ` Sai Pavan Boddu
  2018-04-30 13:42   ` Edgar E. Iglesias
  2018-04-29  0:43 ` [Qemu-devel] [PATCH v1 2/2] xlnx-zynqmp: Connect the ZynqMP GDMA and ADMA Francisco Iglesias
  1 sibling, 2 replies; 7+ messages in thread
From: Francisco Iglesias @ 2018-04-29  0:43 UTC (permalink / raw)
  To: qemu-devel
  Cc: edgari, sai.pavan.boddu, alistair, alistair23,
	francisco.iglesias, peter.maydell

Add a model of the generic DMA found on Xilinx ZynqMP.

Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
Signed-off-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
---
 hw/dma/Makefile.objs       |   1 +
 hw/dma/xlnx-zdma.c         | 833 +++++++++++++++++++++++++++++++++++++++++++++
 include/hw/dma/xlnx-zdma.h |  84 +++++
 3 files changed, 918 insertions(+)
 create mode 100644 hw/dma/xlnx-zdma.c
 create mode 100644 include/hw/dma/xlnx-zdma.h

diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs
index c2afecbf73..79affecc39 100644
--- a/hw/dma/Makefile.objs
+++ b/hw/dma/Makefile.objs
@@ -10,6 +10,7 @@ common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o
 common-obj-$(CONFIG_STP2000) += sparc32_dma.o
 obj-$(CONFIG_XLNX_ZYNQMP) += xlnx_dpdma.o
 obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx_dpdma.o
+common-obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zdma.o
 
 obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o
 obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o
diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c
new file mode 100644
index 0000000000..7955a6116d
--- /dev/null
+++ b/hw/dma/xlnx-zdma.c
@@ -0,0 +1,833 @@
+/*
+ * QEMU model of the ZynqMP generic DMA
+ *
+ * Copyright (c) 2014 Xilinx Inc.
+ * Copyright (c) 2018 FEIMTECH AB
+ *
+ * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>,
+ *            Francisco Iglesias <francisco.iglesias@feimtech.se>
+ *
+ * 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/dma/xlnx-zdma.h"
+#include "qemu/bitops.h"
+#include "qemu/log.h"
+#include "qapi/error.h"
+
+#ifndef XLNX_ZDMA_ERR_DEBUG
+#define XLNX_ZDMA_ERR_DEBUG 0
+#endif
+
+REG32(ZDMA_ERR_CTRL, 0x0)
+    FIELD(ZDMA_ERR_CTRL, APB_ERR_RES, 0, 1)
+REG32(ZDMA_CH_ISR, 0x100)
+    FIELD(ZDMA_CH_ISR, DMA_PAUSE, 11, 1)
+    FIELD(ZDMA_CH_ISR, DMA_DONE, 10, 1)
+    FIELD(ZDMA_CH_ISR, AXI_WR_DATA, 9, 1)
+    FIELD(ZDMA_CH_ISR, AXI_RD_DATA, 8, 1)
+    FIELD(ZDMA_CH_ISR, AXI_RD_DST_DSCR, 7, 1)
+    FIELD(ZDMA_CH_ISR, AXI_RD_SRC_DSCR, 6, 1)
+    FIELD(ZDMA_CH_ISR, IRQ_DST_ACCT_ERR, 5, 1)
+    FIELD(ZDMA_CH_ISR, IRQ_SRC_ACCT_ERR, 4, 1)
+    FIELD(ZDMA_CH_ISR, BYTE_CNT_OVRFL, 3, 1)
+    FIELD(ZDMA_CH_ISR, DST_DSCR_DONE, 2, 1)
+    FIELD(ZDMA_CH_ISR, SRC_DSCR_DONE, 1, 1)
+    FIELD(ZDMA_CH_ISR, INV_APB, 0, 1)
+REG32(ZDMA_CH_IMR, 0x104)
+    FIELD(ZDMA_CH_IMR, DMA_PAUSE, 11, 1)
+    FIELD(ZDMA_CH_IMR, DMA_DONE, 10, 1)
+    FIELD(ZDMA_CH_IMR, AXI_WR_DATA, 9, 1)
+    FIELD(ZDMA_CH_IMR, AXI_RD_DATA, 8, 1)
+    FIELD(ZDMA_CH_IMR, AXI_RD_DST_DSCR, 7, 1)
+    FIELD(ZDMA_CH_IMR, AXI_RD_SRC_DSCR, 6, 1)
+    FIELD(ZDMA_CH_IMR, IRQ_DST_ACCT_ERR, 5, 1)
+    FIELD(ZDMA_CH_IMR, IRQ_SRC_ACCT_ERR, 4, 1)
+    FIELD(ZDMA_CH_IMR, BYTE_CNT_OVRFL, 3, 1)
+    FIELD(ZDMA_CH_IMR, DST_DSCR_DONE, 2, 1)
+    FIELD(ZDMA_CH_IMR, SRC_DSCR_DONE, 1, 1)
+    FIELD(ZDMA_CH_IMR, INV_APB, 0, 1)
+REG32(ZDMA_CH_IEN, 0x108)
+    FIELD(ZDMA_CH_IEN, DMA_PAUSE, 11, 1)
+    FIELD(ZDMA_CH_IEN, DMA_DONE, 10, 1)
+    FIELD(ZDMA_CH_IEN, AXI_WR_DATA, 9, 1)
+    FIELD(ZDMA_CH_IEN, AXI_RD_DATA, 8, 1)
+    FIELD(ZDMA_CH_IEN, AXI_RD_DST_DSCR, 7, 1)
+    FIELD(ZDMA_CH_IEN, AXI_RD_SRC_DSCR, 6, 1)
+    FIELD(ZDMA_CH_IEN, IRQ_DST_ACCT_ERR, 5, 1)
+    FIELD(ZDMA_CH_IEN, IRQ_SRC_ACCT_ERR, 4, 1)
+    FIELD(ZDMA_CH_IEN, BYTE_CNT_OVRFL, 3, 1)
+    FIELD(ZDMA_CH_IEN, DST_DSCR_DONE, 2, 1)
+    FIELD(ZDMA_CH_IEN, SRC_DSCR_DONE, 1, 1)
+    FIELD(ZDMA_CH_IEN, INV_APB, 0, 1)
+REG32(ZDMA_CH_IDS, 0x10c)
+    FIELD(ZDMA_CH_IDS, DMA_PAUSE, 11, 1)
+    FIELD(ZDMA_CH_IDS, DMA_DONE, 10, 1)
+    FIELD(ZDMA_CH_IDS, AXI_WR_DATA, 9, 1)
+    FIELD(ZDMA_CH_IDS, AXI_RD_DATA, 8, 1)
+    FIELD(ZDMA_CH_IDS, AXI_RD_DST_DSCR, 7, 1)
+    FIELD(ZDMA_CH_IDS, AXI_RD_SRC_DSCR, 6, 1)
+    FIELD(ZDMA_CH_IDS, IRQ_DST_ACCT_ERR, 5, 1)
+    FIELD(ZDMA_CH_IDS, IRQ_SRC_ACCT_ERR, 4, 1)
+    FIELD(ZDMA_CH_IDS, BYTE_CNT_OVRFL, 3, 1)
+    FIELD(ZDMA_CH_IDS, DST_DSCR_DONE, 2, 1)
+    FIELD(ZDMA_CH_IDS, SRC_DSCR_DONE, 1, 1)
+    FIELD(ZDMA_CH_IDS, INV_APB, 0, 1)
+REG32(ZDMA_CH_CTRL0, 0x110)
+    FIELD(ZDMA_CH_CTRL0, OVR_FETCH, 7, 1)
+    FIELD(ZDMA_CH_CTRL0, POINT_TYPE, 6, 1)
+    FIELD(ZDMA_CH_CTRL0, MODE, 4, 2)
+    FIELD(ZDMA_CH_CTRL0, RATE_CTRL, 3, 1)
+    FIELD(ZDMA_CH_CTRL0, CONT_ADDR, 2, 1)
+    FIELD(ZDMA_CH_CTRL0, CONT, 1, 1)
+REG32(ZDMA_CH_CTRL1, 0x114)
+    FIELD(ZDMA_CH_CTRL1, DST_ISSUE, 5, 5)
+    FIELD(ZDMA_CH_CTRL1, SRC_ISSUE, 0, 5)
+REG32(ZDMA_CH_FCI, 0x118)
+    FIELD(ZDMA_CH_FCI, PROG_CELL_CNT, 2, 2)
+    FIELD(ZDMA_CH_FCI, SIDE, 1, 1)
+    FIELD(ZDMA_CH_FCI, EN, 0, 1)
+REG32(ZDMA_CH_STATUS, 0x11c)
+    FIELD(ZDMA_CH_STATUS, STATE, 0, 2)
+REG32(ZDMA_CH_DATA_ATTR, 0x120)
+    FIELD(ZDMA_CH_DATA_ATTR, ARBURST, 26, 2)
+    FIELD(ZDMA_CH_DATA_ATTR, ARCACHE, 22, 4)
+    FIELD(ZDMA_CH_DATA_ATTR, ARQOS, 18, 4)
+    FIELD(ZDMA_CH_DATA_ATTR, ARLEN, 14, 4)
+    FIELD(ZDMA_CH_DATA_ATTR, AWBURST, 12, 2)
+    FIELD(ZDMA_CH_DATA_ATTR, AWCACHE, 8, 4)
+    FIELD(ZDMA_CH_DATA_ATTR, AWQOS, 4, 4)
+    FIELD(ZDMA_CH_DATA_ATTR, AWLEN, 0, 4)
+REG32(ZDMA_CH_DSCR_ATTR, 0x124)
+    FIELD(ZDMA_CH_DSCR_ATTR, AXCOHRNT, 8, 1)
+    FIELD(ZDMA_CH_DSCR_ATTR, AXCACHE, 4, 4)
+    FIELD(ZDMA_CH_DSCR_ATTR, AXQOS, 0, 4)
+REG32(ZDMA_CH_SRC_DSCR_WORD0, 0x128)
+REG32(ZDMA_CH_SRC_DSCR_WORD1, 0x12c)
+    FIELD(ZDMA_CH_SRC_DSCR_WORD1, MSB, 0, 17)
+REG32(ZDMA_CH_SRC_DSCR_WORD2, 0x130)
+    FIELD(ZDMA_CH_SRC_DSCR_WORD2, SIZE, 0, 30)
+REG32(ZDMA_CH_SRC_DSCR_WORD3, 0x134)
+    FIELD(ZDMA_CH_SRC_DSCR_WORD3, CMD, 3, 2)
+    FIELD(ZDMA_CH_SRC_DSCR_WORD3, INTR, 2, 1)
+    FIELD(ZDMA_CH_SRC_DSCR_WORD3, TYPE, 1, 1)
+    FIELD(ZDMA_CH_SRC_DSCR_WORD3, COHRNT, 0, 1)
+REG32(ZDMA_CH_DST_DSCR_WORD0, 0x138)
+REG32(ZDMA_CH_DST_DSCR_WORD1, 0x13c)
+    FIELD(ZDMA_CH_DST_DSCR_WORD1, MSB, 0, 17)
+REG32(ZDMA_CH_DST_DSCR_WORD2, 0x140)
+    FIELD(ZDMA_CH_DST_DSCR_WORD2, SIZE, 0, 30)
+REG32(ZDMA_CH_DST_DSCR_WORD3, 0x144)
+    FIELD(ZDMA_CH_DST_DSCR_WORD3, INTR, 2, 1)
+    FIELD(ZDMA_CH_DST_DSCR_WORD3, TYPE, 1, 1)
+    FIELD(ZDMA_CH_DST_DSCR_WORD3, COHRNT, 0, 1)
+REG32(ZDMA_CH_WR_ONLY_WORD0, 0x148)
+REG32(ZDMA_CH_WR_ONLY_WORD1, 0x14c)
+REG32(ZDMA_CH_WR_ONLY_WORD2, 0x150)
+REG32(ZDMA_CH_WR_ONLY_WORD3, 0x154)
+REG32(ZDMA_CH_SRC_START_LSB, 0x158)
+REG32(ZDMA_CH_SRC_START_MSB, 0x15c)
+    FIELD(ZDMA_CH_SRC_START_MSB, ADDR, 0, 17)
+REG32(ZDMA_CH_DST_START_LSB, 0x160)
+REG32(ZDMA_CH_DST_START_MSB, 0x164)
+    FIELD(ZDMA_CH_DST_START_MSB, ADDR, 0, 17)
+REG32(ZDMA_CH_RATE_CTRL, 0x18c)
+    FIELD(ZDMA_CH_RATE_CTRL, CNT, 0, 12)
+REG32(ZDMA_CH_SRC_CUR_PYLD_LSB, 0x168)
+REG32(ZDMA_CH_SRC_CUR_PYLD_MSB, 0x16c)
+    FIELD(ZDMA_CH_SRC_CUR_PYLD_MSB, ADDR, 0, 17)
+REG32(ZDMA_CH_DST_CUR_PYLD_LSB, 0x170)
+REG32(ZDMA_CH_DST_CUR_PYLD_MSB, 0x174)
+    FIELD(ZDMA_CH_DST_CUR_PYLD_MSB, ADDR, 0, 17)
+REG32(ZDMA_CH_SRC_CUR_DSCR_LSB, 0x178)
+REG32(ZDMA_CH_SRC_CUR_DSCR_MSB, 0x17c)
+    FIELD(ZDMA_CH_SRC_CUR_DSCR_MSB, ADDR, 0, 17)
+REG32(ZDMA_CH_DST_CUR_DSCR_LSB, 0x180)
+REG32(ZDMA_CH_DST_CUR_DSCR_MSB, 0x184)
+    FIELD(ZDMA_CH_DST_CUR_DSCR_MSB, ADDR, 0, 17)
+REG32(ZDMA_CH_TOTAL_BYTE, 0x188)
+REG32(ZDMA_CH_RATE_CNTL, 0x18c)
+    FIELD(ZDMA_CH_RATE_CNTL, CNT, 0, 12)
+REG32(ZDMA_CH_IRQ_SRC_ACCT, 0x190)
+    FIELD(ZDMA_CH_IRQ_SRC_ACCT, CNT, 0, 8)
+REG32(ZDMA_CH_IRQ_DST_ACCT, 0x194)
+    FIELD(ZDMA_CH_IRQ_DST_ACCT, CNT, 0, 8)
+REG32(ZDMA_CH_DBG0, 0x198)
+    FIELD(ZDMA_CH_DBG0, CMN_BUF_FREE, 0, 9)
+REG32(ZDMA_CH_DBG1, 0x19c)
+    FIELD(ZDMA_CH_DBG1, CMN_BUF_OCC, 0, 9)
+REG32(ZDMA_CH_CTRL2, 0x200)
+    FIELD(ZDMA_CH_CTRL2, EN, 0, 1)
+
+enum {
+    PT_REG = 0,
+    PT_MEM = 1,
+};
+
+enum {
+    CMD_HALT = 1,
+    CMD_STOP = 2,
+};
+
+enum {
+    RW_MODE_RW = 0,
+    RW_MODE_WO = 1,
+    RW_MODE_RO = 2,
+};
+
+enum {
+    DTYPE_LINEAR = 0,
+    DTYPE_LINKED = 1,
+};
+
+enum {
+    AXI_BURST_FIXED = 0,
+    AXI_BURST_INCR  = 1,
+};
+
+static void zdma_ch_imr_update_irq(XlnxZDMA *s)
+{
+    bool pending;
+
+    pending = s->regs[R_ZDMA_CH_ISR] & ~s->regs[R_ZDMA_CH_IMR];
+
+    qemu_set_irq(s->irq_zdma_ch_imr, pending);
+}
+
+static void zdma_ch_isr_postw(RegisterInfo *reg, uint64_t val64)
+{
+    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
+    zdma_ch_imr_update_irq(s);
+}
+
+static uint64_t zdma_ch_ien_prew(RegisterInfo *reg, uint64_t val64)
+{
+    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
+    uint32_t val = val64;
+
+    s->regs[R_ZDMA_CH_IMR] &= ~val;
+    zdma_ch_imr_update_irq(s);
+    return 0;
+}
+
+static uint64_t zdma_ch_ids_prew(RegisterInfo *reg, uint64_t val64)
+{
+    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
+    uint32_t val = val64;
+
+    s->regs[R_ZDMA_CH_IMR] |= val;
+    zdma_ch_imr_update_irq(s);
+    return 0;
+}
+
+static void zdma_set_state(XlnxZDMA *s, XlnxZDMAState state)
+{
+    s->state = state;
+    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_STATUS, STATE, state);
+
+    /* Signal error if we have an error condition.  */
+    if (s->error) {
+        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_STATUS, STATE, 3);
+    }
+}
+
+static void zdma_src_done(XlnxZDMA *s)
+{
+    unsigned int cnt;
+    cnt = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT);
+    cnt++;
+    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT, cnt);
+    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, SRC_DSCR_DONE, true);
+
+    /* Did we overflow?  */
+    if (cnt != ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT)) {
+        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, IRQ_SRC_ACCT_ERR, true);
+    }
+    zdma_ch_imr_update_irq(s);
+}
+
+static void zdma_dst_done(XlnxZDMA *s)
+{
+    unsigned int cnt;
+    cnt = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT);
+    cnt++;
+    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT, cnt);
+    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DST_DSCR_DONE, true);
+
+    /* Did we overflow?  */
+    if (cnt != ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT)) {
+        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, IRQ_DST_ACCT_ERR, true);
+    }
+    zdma_ch_imr_update_irq(s);
+}
+
+static uint64_t zdma_get_regaddr64(XlnxZDMA *s, unsigned int basereg)
+{
+    uint64_t addr;
+
+    addr = s->regs[basereg + 1];
+    addr <<= 32;
+    addr |= s->regs[basereg];
+
+    return addr;
+}
+
+static void zdma_put_regaddr64(XlnxZDMA *s, unsigned int basereg, uint64_t addr)
+{
+    s->regs[basereg] = addr;
+    s->regs[basereg + 1] = addr >> 32;
+}
+
+static bool zdma_load_descriptor(XlnxZDMA *s, uint64_t addr, void *buf)
+{
+    /* ZDMA descriptors must be aligned to their own size.  */
+    if (addr % sizeof(XlnxZDMADescr)) {
+        qemu_log_mask(LOG_GUEST_ERROR,
+                      "zdma: unaligned descriptor at %" PRIx64,
+                      addr);
+        memset(buf, 0xdeadbeef, sizeof(XlnxZDMADescr));
+        s->error = true;
+        return false;
+    }
+
+    address_space_rw(s->dma_as, addr, s->attr,
+                     buf, sizeof(XlnxZDMADescr), false);
+    return true;
+}
+
+static void zdma_load_src_descriptor(XlnxZDMA *s)
+{
+    uint64_t src_addr;
+    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE);
+
+    if (ptype == PT_REG) {
+        memcpy(&s->dsc_src, &s->regs[R_ZDMA_CH_SRC_DSCR_WORD0],
+               sizeof(s->dsc_src));
+        return;
+    }
+
+    src_addr = zdma_get_regaddr64(s, R_ZDMA_CH_SRC_CUR_DSCR_LSB);
+
+    if (!zdma_load_descriptor(s, src_addr, &s->dsc_src)) {
+        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, AXI_RD_SRC_DSCR, true);
+    }
+}
+
+static void zdma_load_dst_descriptor(XlnxZDMA *s)
+{
+    uint64_t dst_addr;
+    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE);
+
+    if (ptype == PT_REG) {
+        memcpy(&s->dsc_dst, &s->regs[R_ZDMA_CH_DST_DSCR_WORD0],
+               sizeof(s->dsc_src));
+        return;
+    }
+
+    dst_addr = zdma_get_regaddr64(s, R_ZDMA_CH_DST_CUR_DSCR_LSB);
+
+    if (!zdma_load_descriptor(s, dst_addr, &s->dsc_dst)) {
+        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, AXI_RD_DST_DSCR, true);
+    }
+}
+
+static uint64_t zdma_update_descr_addr(XlnxZDMA *s, bool type,
+                                       unsigned int basereg)
+{
+    uint64_t addr, next;
+
+    if (type == DTYPE_LINEAR) {
+        next = zdma_get_regaddr64(s, basereg);
+        next += sizeof(s->dsc_dst);
+        zdma_put_regaddr64(s, basereg, next);
+    } else {
+        addr = zdma_get_regaddr64(s, basereg);
+        addr += sizeof(s->dsc_dst);
+        address_space_rw(s->dma_as, addr, s->attr, (void *) &next, 8, false);
+        zdma_put_regaddr64(s, basereg, next);
+    }
+    return next;
+}
+
+static void zdma_write_dst(XlnxZDMA *s, uint8_t *buf, uint32_t len)
+{
+    uint32_t dst_size, dlen;
+    bool dst_intr, dst_type;
+    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE);
+    unsigned int rw_mode = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, MODE);
+    unsigned int burst_type = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_DATA_ATTR,
+                                               AWBURST);
+
+    /* FIXED burst types are only supported in simple dma mode.  */
+    if (ptype != PT_REG) {
+        burst_type = AXI_BURST_INCR;
+    }
+
+    while (len) {
+        dst_size = FIELD_EX32(s->dsc_dst.words[2], ZDMA_CH_DST_DSCR_WORD2,
+                              SIZE);
+        dst_type = FIELD_EX32(s->dsc_dst.words[3], ZDMA_CH_DST_DSCR_WORD3,
+                              TYPE);
+        if (dst_size == 0 && ptype == PT_MEM) {
+            uint64_t next;
+            next = zdma_update_descr_addr(s, dst_type,
+                                          R_ZDMA_CH_DST_CUR_DSCR_LSB);
+            zdma_load_descriptor(s, next, &s->dsc_dst);
+            dst_size = FIELD_EX32(s->dsc_dst.words[2], ZDMA_CH_DST_DSCR_WORD2,
+                                  SIZE);
+            dst_type = FIELD_EX32(s->dsc_dst.words[3], ZDMA_CH_DST_DSCR_WORD3,
+                                  TYPE);
+        }
+
+        /* Match what hardware does by ignoring the dst_size and only using
+         * the src size for Simple register mode.  */
+        if (ptype == PT_REG && rw_mode != RW_MODE_WO) {
+            dst_size = len;
+        }
+
+        if (dst_size == 0) {
+            qemu_log("Can't write to dst\n");
+        }
+        dst_intr = FIELD_EX32(s->dsc_dst.words[3], ZDMA_CH_DST_DSCR_WORD3,
+                              INTR);
+
+        dlen = len > dst_size ? dst_size : len;
+        if (burst_type == AXI_BURST_FIXED) {
+            if (dlen > (s->cfg.bus_width / 8)) {
+                dlen = s->cfg.bus_width / 8;
+            }
+        }
+
+        address_space_rw(s->dma_as, s->dsc_dst.addr, s->attr, buf, dlen,
+                         true);
+        if (burst_type == AXI_BURST_INCR) {
+            s->dsc_dst.addr += dlen;
+        }
+        dst_size -= dlen;
+        buf += dlen;
+        len -= dlen;
+
+        if (dst_size == 0 && dst_intr) {
+            zdma_dst_done(s);
+        }
+
+        /* Write back to buffered descriptor.  */
+        s->dsc_dst.words[2] = FIELD_DP32(s->dsc_dst.words[2],
+                                         ZDMA_CH_DST_DSCR_WORD2,
+                                         SIZE,
+                                         dst_size);
+    }
+}
+
+static void zdma_process_descr(XlnxZDMA *s)
+{
+    uint64_t src_addr;
+    uint32_t src_size, len;
+    unsigned int src_cmd;
+    bool src_intr, src_type;
+    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE);
+    unsigned int rw_mode = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, MODE);
+    unsigned int burst_type = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_DATA_ATTR,
+                                               ARBURST);
+
+    src_addr = s->dsc_src.addr;
+    src_size = FIELD_EX32(s->dsc_src.words[2], ZDMA_CH_SRC_DSCR_WORD2, SIZE);
+    src_cmd = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3, CMD);
+    src_type = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3, TYPE);
+    src_intr = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3, INTR);
+
+    /* FIXED burst types and non-rw modes are only supported in
+     * simple dma mode.
+     */
+    if (ptype != PT_REG) {
+        if (rw_mode != RW_MODE_RW) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "zDMA: rw-mode=%d but not simple DMA mode.\n",
+                          rw_mode);
+        }
+        if (burst_type != AXI_BURST_INCR) {
+            qemu_log_mask(LOG_GUEST_ERROR,
+                          "zDMA: burst_type=%d but not simple DMA mode.\n",
+                          burst_type);
+        }
+        burst_type = AXI_BURST_INCR;
+        rw_mode = RW_MODE_RW;
+    }
+
+    if (rw_mode == RW_MODE_WO) {
+        /* In Simple DMA Write-Only, we need to push DST size bytes
+         * regardless of what SRC size is set to.  */
+        src_size = FIELD_EX32(s->dsc_dst.words[2], ZDMA_CH_DST_DSCR_WORD2,
+                              SIZE);
+        memcpy(s->buf, &s->regs[R_ZDMA_CH_WR_ONLY_WORD0], s->cfg.bus_width / 8);
+    }
+
+    while (src_size) {
+        len = src_size > ARRAY_SIZE(s->buf) ? ARRAY_SIZE(s->buf) : src_size;
+        if (burst_type == AXI_BURST_FIXED) {
+            if (len > (s->cfg.bus_width / 8)) {
+                len = s->cfg.bus_width / 8;
+            }
+        }
+
+        if (rw_mode == RW_MODE_WO) {
+            if (len > s->cfg.bus_width / 8) {
+                len = s->cfg.bus_width / 8;
+            }
+        } else {
+            address_space_rw(s->dma_as, src_addr, s->attr, s->buf, len,
+                             false);
+            if (burst_type == AXI_BURST_INCR) {
+                src_addr += len;
+            }
+        }
+
+        if (rw_mode != RW_MODE_RO) {
+            zdma_write_dst(s, s->buf, len);
+        }
+
+        s->regs[R_ZDMA_CH_TOTAL_BYTE] += len;
+        src_size -= len;
+
+        if (src_size == 0) {
+            ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_DONE, true);
+        }
+
+    }
+
+    if (src_intr) {
+        zdma_src_done(s);
+    }
+
+    /* Load next descriptor.  */
+    if (ptype == PT_REG || src_cmd == CMD_STOP) {
+        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_CTRL2, EN, 0);
+        zdma_set_state(s, DISABLED);
+        return;
+    }
+
+    if (src_cmd == CMD_HALT) {
+        zdma_set_state(s, PAUSED);
+        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_PAUSE, 1);
+        zdma_ch_imr_update_irq(s);
+        return;
+    }
+
+    zdma_update_descr_addr(s, src_type, R_ZDMA_CH_SRC_CUR_DSCR_LSB);
+}
+
+static void zdma_run(XlnxZDMA *s)
+{
+    while (s->state == ENABLED && !s->error) {
+        zdma_load_src_descriptor(s);
+
+        if (s->error) {
+            zdma_set_state(s, DISABLED);
+        } else {
+            zdma_process_descr(s);
+        }
+    }
+
+    zdma_ch_imr_update_irq(s);
+}
+
+static void zdma_update_descr_addr_from_start(XlnxZDMA *s)
+{
+    uint64_t src_addr, dst_addr;
+
+    src_addr = zdma_get_regaddr64(s, R_ZDMA_CH_SRC_START_LSB);
+    zdma_put_regaddr64(s, R_ZDMA_CH_SRC_CUR_DSCR_LSB, src_addr);
+    dst_addr = zdma_get_regaddr64(s, R_ZDMA_CH_DST_START_LSB);
+    zdma_put_regaddr64(s, R_ZDMA_CH_DST_CUR_DSCR_LSB, dst_addr);
+    zdma_load_dst_descriptor(s);
+}
+
+static void zdma_ch_ctrlx_postw(RegisterInfo *reg, uint64_t val64)
+{
+    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
+
+    if (ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL2, EN)) {
+        s->error = false;
+
+        if (s->state == PAUSED &&
+            ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT)) {
+            if (ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT_ADDR) == 1) {
+                zdma_update_descr_addr_from_start(s);
+            } else {
+                bool src_type = FIELD_EX32(s->dsc_src.words[3],
+                                       ZDMA_CH_SRC_DSCR_WORD3, TYPE);
+                zdma_update_descr_addr(s, src_type,
+                                          R_ZDMA_CH_SRC_CUR_DSCR_LSB);
+            }
+        } else {
+            zdma_update_descr_addr_from_start(s);
+        }
+        zdma_set_state(s, ENABLED);
+    } else {
+        /* Leave Paused state?  */
+        if (s->state == PAUSED &&
+            ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT)) {
+            zdma_set_state(s, DISABLED);
+        }
+    }
+
+    zdma_run(s);
+}
+
+static RegisterAccessInfo zdma_regs_info[] = {
+    {   .name = "ZDMA_ERR_CTRL",  .addr = A_ZDMA_ERR_CTRL,
+        .rsvd = 0xfffffffe,
+    },{ .name = "ZDMA_CH_ISR",  .addr = A_ZDMA_CH_ISR,
+        .rsvd = 0xfffff000,
+        .w1c = 0xfff,
+        .post_write = zdma_ch_isr_postw,
+    },{ .name = "ZDMA_CH_IMR",  .addr = A_ZDMA_CH_IMR,
+        .reset = 0xfff,
+        .rsvd = 0xfffff000,
+        .ro = 0xfff,
+    },{ .name = "ZDMA_CH_IEN",  .addr = A_ZDMA_CH_IEN,
+        .rsvd = 0xfffff000,
+        .pre_write = zdma_ch_ien_prew,
+    },{ .name = "ZDMA_CH_IDS",  .addr = A_ZDMA_CH_IDS,
+        .rsvd = 0xfffff000,
+        .pre_write = zdma_ch_ids_prew,
+    },{ .name = "ZDMA_CH_CTRL0",  .addr = A_ZDMA_CH_CTRL0,
+        .reset = 0x80,
+        .rsvd = 0xffffff01,
+        .post_write = zdma_ch_ctrlx_postw,
+    },{ .name = "ZDMA_CH_CTRL1",  .addr = A_ZDMA_CH_CTRL1,
+        .reset = 0x3ff,
+        .rsvd = 0xfffffc00,
+    },{ .name = "ZDMA_CH_FCI",  .addr = A_ZDMA_CH_FCI,
+        .rsvd = 0xffffffc0,
+    },{ .name = "ZDMA_CH_STATUS",  .addr = A_ZDMA_CH_STATUS,
+        .rsvd = 0xfffffffc,
+        .ro = 0x3,
+    },{ .name = "ZDMA_CH_DATA_ATTR",  .addr = A_ZDMA_CH_DATA_ATTR,
+        .reset = 0x483d20f,
+        .rsvd = 0xf0000000,
+    },{ .name = "ZDMA_CH_DSCR_ATTR",  .addr = A_ZDMA_CH_DSCR_ATTR,
+        .rsvd = 0xfffffe00,
+    },{ .name = "ZDMA_CH_SRC_DSCR_WORD0",  .addr = A_ZDMA_CH_SRC_DSCR_WORD0,
+    },{ .name = "ZDMA_CH_SRC_DSCR_WORD1",  .addr = A_ZDMA_CH_SRC_DSCR_WORD1,
+        .rsvd = 0xfffe0000,
+    },{ .name = "ZDMA_CH_SRC_DSCR_WORD2",  .addr = A_ZDMA_CH_SRC_DSCR_WORD2,
+        .rsvd = 0xc0000000,
+    },{ .name = "ZDMA_CH_SRC_DSCR_WORD3",  .addr = A_ZDMA_CH_SRC_DSCR_WORD3,
+        .rsvd = 0xffffffe0,
+    },{ .name = "ZDMA_CH_DST_DSCR_WORD0",  .addr = A_ZDMA_CH_DST_DSCR_WORD0,
+    },{ .name = "ZDMA_CH_DST_DSCR_WORD1",  .addr = A_ZDMA_CH_DST_DSCR_WORD1,
+        .rsvd = 0xfffe0000,
+    },{ .name = "ZDMA_CH_DST_DSCR_WORD2",  .addr = A_ZDMA_CH_DST_DSCR_WORD2,
+        .rsvd = 0xc0000000,
+    },{ .name = "ZDMA_CH_DST_DSCR_WORD3",  .addr = A_ZDMA_CH_DST_DSCR_WORD3,
+        .rsvd = 0xfffffffa,
+    },{ .name = "ZDMA_CH_WR_ONLY_WORD0",  .addr = A_ZDMA_CH_WR_ONLY_WORD0,
+    },{ .name = "ZDMA_CH_WR_ONLY_WORD1",  .addr = A_ZDMA_CH_WR_ONLY_WORD1,
+    },{ .name = "ZDMA_CH_WR_ONLY_WORD2",  .addr = A_ZDMA_CH_WR_ONLY_WORD2,
+    },{ .name = "ZDMA_CH_WR_ONLY_WORD3",  .addr = A_ZDMA_CH_WR_ONLY_WORD3,
+    },{ .name = "ZDMA_CH_SRC_START_LSB",  .addr = A_ZDMA_CH_SRC_START_LSB,
+    },{ .name = "ZDMA_CH_SRC_START_MSB",  .addr = A_ZDMA_CH_SRC_START_MSB,
+        .rsvd = 0xfffe0000,
+    },{ .name = "ZDMA_CH_DST_START_LSB",  .addr = A_ZDMA_CH_DST_START_LSB,
+    },{ .name = "ZDMA_CH_DST_START_MSB",  .addr = A_ZDMA_CH_DST_START_MSB,
+        .rsvd = 0xfffe0000,
+    },{ .name = "ZDMA_CH_SRC_CUR_PYLD_LSB",  .addr = A_ZDMA_CH_SRC_CUR_PYLD_LSB,
+        .ro = 0xffffffff,
+    },{ .name = "ZDMA_CH_SRC_CUR_PYLD_MSB",  .addr = A_ZDMA_CH_SRC_CUR_PYLD_MSB,
+        .rsvd = 0xfffe0000,
+        .ro = 0x1ffff,
+    },{ .name = "ZDMA_CH_DST_CUR_PYLD_LSB",  .addr = A_ZDMA_CH_DST_CUR_PYLD_LSB,
+        .ro = 0xffffffff,
+    },{ .name = "ZDMA_CH_DST_CUR_PYLD_MSB",  .addr = A_ZDMA_CH_DST_CUR_PYLD_MSB,
+        .rsvd = 0xfffe0000,
+        .ro = 0x1ffff,
+    },{ .name = "ZDMA_CH_SRC_CUR_DSCR_LSB",  .addr = A_ZDMA_CH_SRC_CUR_DSCR_LSB,
+        .ro = 0xffffffff,
+    },{ .name = "ZDMA_CH_SRC_CUR_DSCR_MSB",  .addr = A_ZDMA_CH_SRC_CUR_DSCR_MSB,
+        .rsvd = 0xfffe0000,
+        .ro = 0x1ffff,
+    },{ .name = "ZDMA_CH_DST_CUR_DSCR_LSB",  .addr = A_ZDMA_CH_DST_CUR_DSCR_LSB,
+        .ro = 0xffffffff,
+    },{ .name = "ZDMA_CH_DST_CUR_DSCR_MSB",  .addr = A_ZDMA_CH_DST_CUR_DSCR_MSB,
+        .rsvd = 0xfffe0000,
+        .ro = 0x1ffff,
+    },{ .name = "ZDMA_CH_TOTAL_BYTE",  .addr = A_ZDMA_CH_TOTAL_BYTE,
+        .w1c = 0xffffffff,
+    },{ .name = "ZDMA_CH_RATE_CNTL",  .addr = A_ZDMA_CH_RATE_CNTL,
+        .rsvd = 0xfffff000,
+    },{ .name = "ZDMA_CH_IRQ_SRC_ACCT",  .addr = A_ZDMA_CH_IRQ_SRC_ACCT,
+        .rsvd = 0xffffff00,
+        .ro = 0xff,
+        .cor = 0xff,
+    },{ .name = "ZDMA_CH_IRQ_DST_ACCT",  .addr = A_ZDMA_CH_IRQ_DST_ACCT,
+        .rsvd = 0xffffff00,
+        .ro = 0xff,
+        .cor = 0xff,
+    },{ .name = "ZDMA_CH_DBG0",  .addr = A_ZDMA_CH_DBG0,
+        .rsvd = 0xfffffe00,
+        .ro = 0x1ff,
+    },{ .name = "ZDMA_CH_DBG1",  .addr = A_ZDMA_CH_DBG1,
+        .rsvd = 0xfffffe00,
+        .ro = 0x1ff,
+    },{ .name = "ZDMA_CH_CTRL2",  .addr = A_ZDMA_CH_CTRL2,
+        .rsvd = 0xfffffffe,
+        .post_write = zdma_ch_ctrlx_postw,
+    }
+};
+
+static void zdma_reset(DeviceState *dev)
+{
+    XlnxZDMA *s = XLNX_ZDMA(dev);
+    unsigned int i;
+
+    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
+        register_reset(&s->regs_info[i]);
+    }
+
+    zdma_ch_imr_update_irq(s);
+}
+
+static uint64_t zdma_read(void *opaque, hwaddr addr, unsigned size)
+{
+    XlnxZDMA *s = XLNX_ZDMA(opaque);
+    RegisterInfo *r = &s->regs_info[addr / 4];
+
+    if (!r->data) {
+        qemu_log("%s: Decode error: read from %" HWADDR_PRIx "\n",
+                 object_get_canonical_path(OBJECT(s)),
+                 addr);
+        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true);
+        zdma_ch_imr_update_irq(s);
+        return 0;
+    }
+    return register_read(r, ~0, NULL, false);
+}
+
+static void zdma_write(void *opaque, hwaddr addr, uint64_t value,
+                      unsigned size)
+{
+    XlnxZDMA *s = XLNX_ZDMA(opaque);
+    RegisterInfo *r = &s->regs_info[addr / 4];
+
+    if (!r->data) {
+        qemu_log("%s: Decode error: write to %" HWADDR_PRIx "=%" PRIx64 "\n",
+                 object_get_canonical_path(OBJECT(s)),
+                 addr, value);
+        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true);
+        zdma_ch_imr_update_irq(s);
+        return;
+    }
+    register_write(r, value, ~0, NULL, false);
+}
+
+static const MemoryRegionOps zdma_ops = {
+    .read = zdma_read,
+    .write = zdma_write,
+    .endianness = DEVICE_LITTLE_ENDIAN,
+    .valid = {
+        .min_access_size = 4,
+        .max_access_size = 4,
+    },
+};
+
+static void zdma_realize(DeviceState *dev, Error **errp)
+{
+    XlnxZDMA *s = XLNX_ZDMA(dev);
+    unsigned int i;
+
+    for (i = 0; i < ARRAY_SIZE(zdma_regs_info); ++i) {
+        RegisterInfo *r = &s->regs_info[zdma_regs_info[i].addr / 4];
+
+        *r = (RegisterInfo) {
+            .data = (uint8_t *)&s->regs[
+                    zdma_regs_info[i].addr / 4],
+            .data_size = sizeof(uint32_t),
+            .access = &zdma_regs_info[i],
+            .opaque = s,
+        };
+    }
+
+    if (s->dma_mr) {
+        s->dma_as = g_malloc0(sizeof(AddressSpace));
+        address_space_init(s->dma_as, s->dma_mr, NULL);
+    } else {
+        s->dma_as = &address_space_memory;
+    }
+    s->attr = MEMTXATTRS_UNSPECIFIED;
+}
+
+static void zdma_init(Object *obj)
+{
+    XlnxZDMA *s = XLNX_ZDMA(obj);
+    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
+
+    memory_region_init_io(&s->iomem, obj, &zdma_ops, s,
+                          TYPE_XLNX_ZDMA, ZDMA_R_MAX * 4);
+    sysbus_init_mmio(sbd, &s->iomem);
+    sysbus_init_irq(sbd, &s->irq_zdma_ch_imr);
+
+    object_property_add_link(obj, "dma", TYPE_MEMORY_REGION,
+                             (Object **)&s->dma_mr,
+                             qdev_prop_allow_set_link_before_realize,
+                             OBJ_PROP_LINK_UNREF_ON_RELEASE,
+                             &error_abort);
+}
+
+static const VMStateDescription vmstate_zdma = {
+    .name = TYPE_XLNX_ZDMA,
+    .version_id = 1,
+    .minimum_version_id = 1,
+    .minimum_version_id_old = 1,
+    .fields = (VMStateField[]) {
+        VMSTATE_UINT32_ARRAY(regs, XlnxZDMA, ZDMA_R_MAX),
+        VMSTATE_END_OF_LIST(),
+    }
+};
+
+static Property zdma_props[] = {
+    DEFINE_PROP_UINT32("bus-width", XlnxZDMA, cfg.bus_width, 64),
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void zdma_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    dc->reset = zdma_reset;
+    dc->realize = zdma_realize;
+    dc->props = zdma_props;
+    dc->vmsd = &vmstate_zdma;
+}
+
+static const TypeInfo zdma_info = {
+    .name          = TYPE_XLNX_ZDMA,
+    .parent        = TYPE_SYS_BUS_DEVICE,
+    .instance_size = sizeof(XlnxZDMA),
+    .class_init    = zdma_class_init,
+    .instance_init = zdma_init,
+};
+
+static void zdma_register_types(void)
+{
+    type_register_static(&zdma_info);
+}
+
+type_init(zdma_register_types)
diff --git a/include/hw/dma/xlnx-zdma.h b/include/hw/dma/xlnx-zdma.h
new file mode 100644
index 0000000000..0b240b4c3c
--- /dev/null
+++ b/include/hw/dma/xlnx-zdma.h
@@ -0,0 +1,84 @@
+/*
+ * QEMU model of the ZynqMP generic DMA
+ *
+ * Copyright (c) 2014 Xilinx Inc.
+ * Copyright (c) 2018 FEIMTECH AB
+ *
+ * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>,
+ *            Francisco Iglesias <francisco.iglesias@feimtech.se>
+ *
+ * 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.
+ */
+
+#ifndef XLNX_ZDMA_H
+#define XLNX_ZDMA_H
+
+#include "hw/sysbus.h"
+#include "hw/register.h"
+#include "sysemu/dma.h"
+
+#define ZDMA_R_MAX (0x204 / 4)
+
+typedef enum {
+    DISABLED = 0,
+    ENABLED = 1,
+    PAUSED = 2,
+} XlnxZDMAState;
+
+typedef union {
+    struct {
+        uint64_t addr;
+        uint32_t size;
+        uint32_t attr;
+    };
+    uint32_t words[4];
+} XlnxZDMADescr;
+
+typedef struct XlnxZDMA {
+    SysBusDevice parent_obj;
+    MemoryRegion iomem;
+    MemTxAttrs attr;
+    MemoryRegion *dma_mr;
+    AddressSpace *dma_as;
+    qemu_irq irq_zdma_ch_imr;
+
+    struct {
+        uint32_t bus_width;
+    } cfg;
+
+    XlnxZDMAState state;
+    bool error;
+
+    XlnxZDMADescr dsc_src;
+    XlnxZDMADescr dsc_dst;
+
+    uint32_t regs[ZDMA_R_MAX];
+    RegisterInfo regs_info[ZDMA_R_MAX];
+
+    /* We don't model the common bufs. Must be at least 16 bytes
+       to model write only mode.  */
+    uint8_t buf[2048];
+} XlnxZDMA;
+
+#define TYPE_XLNX_ZDMA "xlnx.zdma"
+
+#define XLNX_ZDMA(obj) \
+     OBJECT_CHECK(XlnxZDMA, (obj), TYPE_XLNX_ZDMA)
+
+#endif /* XLNX_ZDMA_H */
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [Qemu-devel] [PATCH v1 2/2] xlnx-zynqmp: Connect the ZynqMP GDMA and ADMA
  2018-04-29  0:43 [Qemu-devel] [PATCH v1 0/2] xlnx-zynqmp: Add emulation of the ZynqMP GDMA and ADMA Francisco Iglesias
  2018-04-29  0:43 ` [Qemu-devel] [PATCH v1 1/2] xlnx-zdma: Add a model of the Xilinx ZynqMP generic DMA Francisco Iglesias
@ 2018-04-29  0:43 ` Francisco Iglesias
  1 sibling, 0 replies; 7+ messages in thread
From: Francisco Iglesias @ 2018-04-29  0:43 UTC (permalink / raw)
  To: qemu-devel
  Cc: edgari, sai.pavan.boddu, alistair, alistair23,
	francisco.iglesias, peter.maydell

The ZynqMP contains two instances of a generic DMA, the GDMA, located in the
FPD (full power domain), and the ADMA, located in LPD (low power domain).  This
patch adds these two DMAs to the ZynqMP board.

Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
---
 hw/arm/xlnx-zynqmp.c         | 53 ++++++++++++++++++++++++++++++++++++++++++++
 include/hw/arm/xlnx-zynqmp.h |  5 +++++
 2 files changed, 58 insertions(+)

diff --git a/hw/arm/xlnx-zynqmp.c b/hw/arm/xlnx-zynqmp.c
index 505253e0d2..d8c3c9e8d9 100644
--- a/hw/arm/xlnx-zynqmp.c
+++ b/hw/arm/xlnx-zynqmp.c
@@ -90,6 +90,24 @@ static const int spi_intr[XLNX_ZYNQMP_NUM_SPIS] = {
     19, 20,
 };
 
+static const uint64_t gdma_addr[XLNX_ZYNQMP_NUM_GDMA] = {
+    0xFD500000, 0xFD510000, 0xFD520000, 0xFD530000,
+    0xFD540000, 0xFD550000, 0xFD560000, 0xFD570000
+};
+
+static const int gdma_intr[XLNX_ZYNQMP_NUM_GDMA] = {
+    124, 125, 126, 127, 128, 129, 130, 131
+};
+
+static const uint64_t adma_addr[XLNX_ZYNQMP_NUM_ADMA] = {
+    0xFFA80000, 0xFFA90000, 0xFFAA0000, 0xFFAB0000,
+    0xFFAC0000, 0xFFAD0000, 0xFFAE0000, 0xFFAF0000
+};
+
+static const int adma_intr[XLNX_ZYNQMP_NUM_ADMA] = {
+    77, 78, 79, 80, 81, 82, 83, 84
+};
+
 typedef struct XlnxZynqMPGICRegion {
     int region_index;
     uint32_t address;
@@ -197,6 +215,16 @@ static void xlnx_zynqmp_init(Object *obj)
 
     object_initialize(&s->rtc, sizeof(s->rtc), TYPE_XLNX_ZYNQMP_RTC);
     qdev_set_parent_bus(DEVICE(&s->rtc), sysbus_get_default());
+
+    for (i = 0; i < XLNX_ZYNQMP_NUM_GDMA; i++) {
+        object_initialize(&s->gdma[i], sizeof(s->gdma[i]), TYPE_XLNX_ZDMA);
+        qdev_set_parent_bus(DEVICE(&s->gdma[i]), sysbus_get_default());
+    }
+
+    for (i = 0; i < XLNX_ZYNQMP_NUM_ADMA; i++) {
+        object_initialize(&s->adma[i], sizeof(s->adma[i]), TYPE_XLNX_ZDMA);
+        qdev_set_parent_bus(DEVICE(&s->adma[i]), sysbus_get_default());
+    }
 }
 
 static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
@@ -492,6 +520,31 @@ static void xlnx_zynqmp_realize(DeviceState *dev, Error **errp)
     }
     sysbus_mmio_map(SYS_BUS_DEVICE(&s->rtc), 0, RTC_ADDR);
     sysbus_connect_irq(SYS_BUS_DEVICE(&s->rtc), 0, gic_spi[RTC_IRQ]);
+
+    for (i = 0; i < XLNX_ZYNQMP_NUM_GDMA; i++) {
+        object_property_set_uint(OBJECT(&s->gdma[i]), 128, "bus-width", &err);
+        object_property_set_bool(OBJECT(&s->gdma[i]), true, "realized", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->gdma[i]), 0, gdma_addr[i]);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->gdma[i]), 0,
+                           gic_spi[gdma_intr[i]]);
+    }
+
+    for (i = 0; i < XLNX_ZYNQMP_NUM_ADMA; i++) {
+        object_property_set_bool(OBJECT(&s->adma[i]), true, "realized", &err);
+        if (err) {
+            error_propagate(errp, err);
+            return;
+        }
+
+        sysbus_mmio_map(SYS_BUS_DEVICE(&s->adma[i]), 0, adma_addr[i]);
+        sysbus_connect_irq(SYS_BUS_DEVICE(&s->adma[i]), 0,
+                           gic_spi[adma_intr[i]]);
+    }
 }
 
 static Property xlnx_zynqmp_props[] = {
diff --git a/include/hw/arm/xlnx-zynqmp.h b/include/hw/arm/xlnx-zynqmp.h
index 3b613e364d..9824a0fa17 100644
--- a/include/hw/arm/xlnx-zynqmp.h
+++ b/include/hw/arm/xlnx-zynqmp.h
@@ -27,6 +27,7 @@
 #include "hw/sd/sdhci.h"
 #include "hw/ssi/xilinx_spips.h"
 #include "hw/dma/xlnx_dpdma.h"
+#include "hw/dma/xlnx-zdma.h"
 #include "hw/display/xlnx_dp.h"
 #include "hw/intc/xlnx-zynqmp-ipi.h"
 #include "hw/timer/xlnx-zynqmp-rtc.h"
@@ -41,6 +42,8 @@
 #define XLNX_ZYNQMP_NUM_UARTS 2
 #define XLNX_ZYNQMP_NUM_SDHCI 2
 #define XLNX_ZYNQMP_NUM_SPIS 2
+#define XLNX_ZYNQMP_NUM_GDMA 8
+#define XLNX_ZYNQMP_NUM_ADMA 8
 
 #define XLNX_ZYNQMP_NUM_QSPI_BUS 2
 #define XLNX_ZYNQMP_NUM_QSPI_BUS_CS 2
@@ -94,6 +97,8 @@ typedef struct XlnxZynqMPState {
     XlnxDPDMAState dpdma;
     XlnxZynqMPIPI ipi;
     XlnxZynqMPRTC rtc;
+    XlnxZDMA gdma[XLNX_ZYNQMP_NUM_GDMA];
+    XlnxZDMA adma[XLNX_ZYNQMP_NUM_ADMA];
 
     char *boot_cpu;
     ARMCPU *boot_cpu_ptr;
-- 
2.11.0

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [Qemu-devel] [PATCH v1 1/2] xlnx-zdma: Add a model of the Xilinx ZynqMP generic DMA
  2018-04-29  0:43 ` [Qemu-devel] [PATCH v1 1/2] xlnx-zdma: Add a model of the Xilinx ZynqMP generic DMA Francisco Iglesias
@ 2018-04-30 13:27   ` Sai Pavan Boddu
  2018-05-02 19:58     ` francisco iglesias
  2018-04-30 13:42   ` Edgar E. Iglesias
  1 sibling, 1 reply; 7+ messages in thread
From: Sai Pavan Boddu @ 2018-04-30 13:27 UTC (permalink / raw)
  To: Francisco Iglesias
  Cc: qemu-devel, edgari, alistair, alistair23, francisco.iglesias,
	peter.maydell

Hi Francisco,

On Sun, Apr 29, 2018 at 02:43:28AM +0200, Francisco Iglesias wrote:
> Add a model of the generic DMA found on Xilinx ZynqMP.
> 
> Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
> Signed-off-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
> ---
>  hw/dma/Makefile.objs       |   1 +
>  hw/dma/xlnx-zdma.c         | 833 +++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/dma/xlnx-zdma.h |  84 +++++
>  3 files changed, 918 insertions(+)
>  create mode 100644 hw/dma/xlnx-zdma.c
>  create mode 100644 include/hw/dma/xlnx-zdma.h
> 
> diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs
> index c2afecbf73..79affecc39 100644
> --- a/hw/dma/Makefile.objs
> +++ b/hw/dma/Makefile.objs
> @@ -10,6 +10,7 @@ common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o
>  common-obj-$(CONFIG_STP2000) += sparc32_dma.o
>  obj-$(CONFIG_XLNX_ZYNQMP) += xlnx_dpdma.o
>  obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx_dpdma.o
> +common-obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zdma.o
>  
>  obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o
>  obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o
> diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c
> new file mode 100644
> index 0000000000..7955a6116d
> --- /dev/null
> +++ b/hw/dma/xlnx-zdma.c
> @@ -0,0 +1,833 @@
> +/*
> + * QEMU model of the ZynqMP generic DMA
> + *
> + * Copyright (c) 2014 Xilinx Inc.
> + * Copyright (c) 2018 FEIMTECH AB
> + *
> + * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>,
> + *            Francisco Iglesias <francisco.iglesias@feimtech.se>
> + *
> + * 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/dma/xlnx-zdma.h"
> +#include "qemu/bitops.h"
> +#include "qemu/log.h"
> +#include "qapi/error.h"
> +
> +#ifndef XLNX_ZDMA_ERR_DEBUG
> +#define XLNX_ZDMA_ERR_DEBUG 0
> +#endif
> +
> +REG32(ZDMA_ERR_CTRL, 0x0)
> +    FIELD(ZDMA_ERR_CTRL, APB_ERR_RES, 0, 1)
> +REG32(ZDMA_CH_ISR, 0x100)
> +    FIELD(ZDMA_CH_ISR, DMA_PAUSE, 11, 1)
> +    FIELD(ZDMA_CH_ISR, DMA_DONE, 10, 1)
> +    FIELD(ZDMA_CH_ISR, AXI_WR_DATA, 9, 1)
> +    FIELD(ZDMA_CH_ISR, AXI_RD_DATA, 8, 1)
> +    FIELD(ZDMA_CH_ISR, AXI_RD_DST_DSCR, 7, 1)
> +    FIELD(ZDMA_CH_ISR, AXI_RD_SRC_DSCR, 6, 1)
> +    FIELD(ZDMA_CH_ISR, IRQ_DST_ACCT_ERR, 5, 1)
> +    FIELD(ZDMA_CH_ISR, IRQ_SRC_ACCT_ERR, 4, 1)
> +    FIELD(ZDMA_CH_ISR, BYTE_CNT_OVRFL, 3, 1)
> +    FIELD(ZDMA_CH_ISR, DST_DSCR_DONE, 2, 1)
> +    FIELD(ZDMA_CH_ISR, SRC_DSCR_DONE, 1, 1)
> +    FIELD(ZDMA_CH_ISR, INV_APB, 0, 1)
> +REG32(ZDMA_CH_IMR, 0x104)
> +    FIELD(ZDMA_CH_IMR, DMA_PAUSE, 11, 1)
> +    FIELD(ZDMA_CH_IMR, DMA_DONE, 10, 1)
> +    FIELD(ZDMA_CH_IMR, AXI_WR_DATA, 9, 1)
> +    FIELD(ZDMA_CH_IMR, AXI_RD_DATA, 8, 1)
> +    FIELD(ZDMA_CH_IMR, AXI_RD_DST_DSCR, 7, 1)
> +    FIELD(ZDMA_CH_IMR, AXI_RD_SRC_DSCR, 6, 1)
> +    FIELD(ZDMA_CH_IMR, IRQ_DST_ACCT_ERR, 5, 1)
> +    FIELD(ZDMA_CH_IMR, IRQ_SRC_ACCT_ERR, 4, 1)
> +    FIELD(ZDMA_CH_IMR, BYTE_CNT_OVRFL, 3, 1)
> +    FIELD(ZDMA_CH_IMR, DST_DSCR_DONE, 2, 1)
> +    FIELD(ZDMA_CH_IMR, SRC_DSCR_DONE, 1, 1)
> +    FIELD(ZDMA_CH_IMR, INV_APB, 0, 1)
> +REG32(ZDMA_CH_IEN, 0x108)
> +    FIELD(ZDMA_CH_IEN, DMA_PAUSE, 11, 1)
> +    FIELD(ZDMA_CH_IEN, DMA_DONE, 10, 1)
> +    FIELD(ZDMA_CH_IEN, AXI_WR_DATA, 9, 1)
> +    FIELD(ZDMA_CH_IEN, AXI_RD_DATA, 8, 1)
> +    FIELD(ZDMA_CH_IEN, AXI_RD_DST_DSCR, 7, 1)
> +    FIELD(ZDMA_CH_IEN, AXI_RD_SRC_DSCR, 6, 1)
> +    FIELD(ZDMA_CH_IEN, IRQ_DST_ACCT_ERR, 5, 1)
> +    FIELD(ZDMA_CH_IEN, IRQ_SRC_ACCT_ERR, 4, 1)
> +    FIELD(ZDMA_CH_IEN, BYTE_CNT_OVRFL, 3, 1)
> +    FIELD(ZDMA_CH_IEN, DST_DSCR_DONE, 2, 1)
> +    FIELD(ZDMA_CH_IEN, SRC_DSCR_DONE, 1, 1)
> +    FIELD(ZDMA_CH_IEN, INV_APB, 0, 1)
> +REG32(ZDMA_CH_IDS, 0x10c)
> +    FIELD(ZDMA_CH_IDS, DMA_PAUSE, 11, 1)
> +    FIELD(ZDMA_CH_IDS, DMA_DONE, 10, 1)
> +    FIELD(ZDMA_CH_IDS, AXI_WR_DATA, 9, 1)
> +    FIELD(ZDMA_CH_IDS, AXI_RD_DATA, 8, 1)
> +    FIELD(ZDMA_CH_IDS, AXI_RD_DST_DSCR, 7, 1)
> +    FIELD(ZDMA_CH_IDS, AXI_RD_SRC_DSCR, 6, 1)
> +    FIELD(ZDMA_CH_IDS, IRQ_DST_ACCT_ERR, 5, 1)
> +    FIELD(ZDMA_CH_IDS, IRQ_SRC_ACCT_ERR, 4, 1)
> +    FIELD(ZDMA_CH_IDS, BYTE_CNT_OVRFL, 3, 1)
> +    FIELD(ZDMA_CH_IDS, DST_DSCR_DONE, 2, 1)
> +    FIELD(ZDMA_CH_IDS, SRC_DSCR_DONE, 1, 1)
> +    FIELD(ZDMA_CH_IDS, INV_APB, 0, 1)
> +REG32(ZDMA_CH_CTRL0, 0x110)
> +    FIELD(ZDMA_CH_CTRL0, OVR_FETCH, 7, 1)
> +    FIELD(ZDMA_CH_CTRL0, POINT_TYPE, 6, 1)
> +    FIELD(ZDMA_CH_CTRL0, MODE, 4, 2)
> +    FIELD(ZDMA_CH_CTRL0, RATE_CTRL, 3, 1)
> +    FIELD(ZDMA_CH_CTRL0, CONT_ADDR, 2, 1)
> +    FIELD(ZDMA_CH_CTRL0, CONT, 1, 1)
> +REG32(ZDMA_CH_CTRL1, 0x114)
> +    FIELD(ZDMA_CH_CTRL1, DST_ISSUE, 5, 5)
> +    FIELD(ZDMA_CH_CTRL1, SRC_ISSUE, 0, 5)
> +REG32(ZDMA_CH_FCI, 0x118)
> +    FIELD(ZDMA_CH_FCI, PROG_CELL_CNT, 2, 2)
> +    FIELD(ZDMA_CH_FCI, SIDE, 1, 1)
> +    FIELD(ZDMA_CH_FCI, EN, 0, 1)
> +REG32(ZDMA_CH_STATUS, 0x11c)
> +    FIELD(ZDMA_CH_STATUS, STATE, 0, 2)
> +REG32(ZDMA_CH_DATA_ATTR, 0x120)
> +    FIELD(ZDMA_CH_DATA_ATTR, ARBURST, 26, 2)
> +    FIELD(ZDMA_CH_DATA_ATTR, ARCACHE, 22, 4)
> +    FIELD(ZDMA_CH_DATA_ATTR, ARQOS, 18, 4)
> +    FIELD(ZDMA_CH_DATA_ATTR, ARLEN, 14, 4)
> +    FIELD(ZDMA_CH_DATA_ATTR, AWBURST, 12, 2)
> +    FIELD(ZDMA_CH_DATA_ATTR, AWCACHE, 8, 4)
> +    FIELD(ZDMA_CH_DATA_ATTR, AWQOS, 4, 4)
> +    FIELD(ZDMA_CH_DATA_ATTR, AWLEN, 0, 4)
> +REG32(ZDMA_CH_DSCR_ATTR, 0x124)
> +    FIELD(ZDMA_CH_DSCR_ATTR, AXCOHRNT, 8, 1)
> +    FIELD(ZDMA_CH_DSCR_ATTR, AXCACHE, 4, 4)
> +    FIELD(ZDMA_CH_DSCR_ATTR, AXQOS, 0, 4)
> +REG32(ZDMA_CH_SRC_DSCR_WORD0, 0x128)
> +REG32(ZDMA_CH_SRC_DSCR_WORD1, 0x12c)
> +    FIELD(ZDMA_CH_SRC_DSCR_WORD1, MSB, 0, 17)
> +REG32(ZDMA_CH_SRC_DSCR_WORD2, 0x130)
> +    FIELD(ZDMA_CH_SRC_DSCR_WORD2, SIZE, 0, 30)
> +REG32(ZDMA_CH_SRC_DSCR_WORD3, 0x134)
> +    FIELD(ZDMA_CH_SRC_DSCR_WORD3, CMD, 3, 2)
> +    FIELD(ZDMA_CH_SRC_DSCR_WORD3, INTR, 2, 1)
> +    FIELD(ZDMA_CH_SRC_DSCR_WORD3, TYPE, 1, 1)
> +    FIELD(ZDMA_CH_SRC_DSCR_WORD3, COHRNT, 0, 1)
> +REG32(ZDMA_CH_DST_DSCR_WORD0, 0x138)
> +REG32(ZDMA_CH_DST_DSCR_WORD1, 0x13c)
> +    FIELD(ZDMA_CH_DST_DSCR_WORD1, MSB, 0, 17)
> +REG32(ZDMA_CH_DST_DSCR_WORD2, 0x140)
> +    FIELD(ZDMA_CH_DST_DSCR_WORD2, SIZE, 0, 30)
> +REG32(ZDMA_CH_DST_DSCR_WORD3, 0x144)
> +    FIELD(ZDMA_CH_DST_DSCR_WORD3, INTR, 2, 1)
> +    FIELD(ZDMA_CH_DST_DSCR_WORD3, TYPE, 1, 1)
> +    FIELD(ZDMA_CH_DST_DSCR_WORD3, COHRNT, 0, 1)
> +REG32(ZDMA_CH_WR_ONLY_WORD0, 0x148)
> +REG32(ZDMA_CH_WR_ONLY_WORD1, 0x14c)
> +REG32(ZDMA_CH_WR_ONLY_WORD2, 0x150)
> +REG32(ZDMA_CH_WR_ONLY_WORD3, 0x154)
> +REG32(ZDMA_CH_SRC_START_LSB, 0x158)
> +REG32(ZDMA_CH_SRC_START_MSB, 0x15c)
> +    FIELD(ZDMA_CH_SRC_START_MSB, ADDR, 0, 17)
> +REG32(ZDMA_CH_DST_START_LSB, 0x160)
> +REG32(ZDMA_CH_DST_START_MSB, 0x164)
> +    FIELD(ZDMA_CH_DST_START_MSB, ADDR, 0, 17)
> +REG32(ZDMA_CH_RATE_CTRL, 0x18c)
> +    FIELD(ZDMA_CH_RATE_CTRL, CNT, 0, 12)
> +REG32(ZDMA_CH_SRC_CUR_PYLD_LSB, 0x168)
> +REG32(ZDMA_CH_SRC_CUR_PYLD_MSB, 0x16c)
> +    FIELD(ZDMA_CH_SRC_CUR_PYLD_MSB, ADDR, 0, 17)
> +REG32(ZDMA_CH_DST_CUR_PYLD_LSB, 0x170)
> +REG32(ZDMA_CH_DST_CUR_PYLD_MSB, 0x174)
> +    FIELD(ZDMA_CH_DST_CUR_PYLD_MSB, ADDR, 0, 17)
> +REG32(ZDMA_CH_SRC_CUR_DSCR_LSB, 0x178)
> +REG32(ZDMA_CH_SRC_CUR_DSCR_MSB, 0x17c)
> +    FIELD(ZDMA_CH_SRC_CUR_DSCR_MSB, ADDR, 0, 17)
> +REG32(ZDMA_CH_DST_CUR_DSCR_LSB, 0x180)
> +REG32(ZDMA_CH_DST_CUR_DSCR_MSB, 0x184)
> +    FIELD(ZDMA_CH_DST_CUR_DSCR_MSB, ADDR, 0, 17)
> +REG32(ZDMA_CH_TOTAL_BYTE, 0x188)
> +REG32(ZDMA_CH_RATE_CNTL, 0x18c)
> +    FIELD(ZDMA_CH_RATE_CNTL, CNT, 0, 12)
> +REG32(ZDMA_CH_IRQ_SRC_ACCT, 0x190)
> +    FIELD(ZDMA_CH_IRQ_SRC_ACCT, CNT, 0, 8)
> +REG32(ZDMA_CH_IRQ_DST_ACCT, 0x194)
> +    FIELD(ZDMA_CH_IRQ_DST_ACCT, CNT, 0, 8)
> +REG32(ZDMA_CH_DBG0, 0x198)
> +    FIELD(ZDMA_CH_DBG0, CMN_BUF_FREE, 0, 9)
> +REG32(ZDMA_CH_DBG1, 0x19c)
> +    FIELD(ZDMA_CH_DBG1, CMN_BUF_OCC, 0, 9)
> +REG32(ZDMA_CH_CTRL2, 0x200)
> +    FIELD(ZDMA_CH_CTRL2, EN, 0, 1)
> +
> +enum {
> +    PT_REG = 0,
> +    PT_MEM = 1,
> +};
> +
> +enum {
> +    CMD_HALT = 1,
> +    CMD_STOP = 2,
> +};
> +
> +enum {
> +    RW_MODE_RW = 0,
> +    RW_MODE_WO = 1,
> +    RW_MODE_RO = 2,
> +};
> +
> +enum {
> +    DTYPE_LINEAR = 0,
> +    DTYPE_LINKED = 1,
> +};
> +
> +enum {
> +    AXI_BURST_FIXED = 0,
> +    AXI_BURST_INCR  = 1,
> +};
> +
> +static void zdma_ch_imr_update_irq(XlnxZDMA *s)
> +{
> +    bool pending;
> +
> +    pending = s->regs[R_ZDMA_CH_ISR] & ~s->regs[R_ZDMA_CH_IMR];
> +
> +    qemu_set_irq(s->irq_zdma_ch_imr, pending);
> +}
> +
> +static void zdma_ch_isr_postw(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
> +    zdma_ch_imr_update_irq(s);
> +}
> +
> +static uint64_t zdma_ch_ien_prew(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
> +    uint32_t val = val64;
> +
> +    s->regs[R_ZDMA_CH_IMR] &= ~val;
> +    zdma_ch_imr_update_irq(s);
> +    return 0;
> +}
> +
> +static uint64_t zdma_ch_ids_prew(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
> +    uint32_t val = val64;
> +
> +    s->regs[R_ZDMA_CH_IMR] |= val;
> +    zdma_ch_imr_update_irq(s);
> +    return 0;
> +}
> +
> +static void zdma_set_state(XlnxZDMA *s, XlnxZDMAState state)
> +{
> +    s->state = state;
> +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_STATUS, STATE, state);
> +
> +    /* Signal error if we have an error condition.  */
> +    if (s->error) {
> +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_STATUS, STATE, 3);
> +    }
> +}
> +
> +static void zdma_src_done(XlnxZDMA *s)
> +{
> +    unsigned int cnt;
> +    cnt = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT);
> +    cnt++;
> +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT, cnt);
> +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, SRC_DSCR_DONE, true);
> +
> +    /* Did we overflow?  */
> +    if (cnt != ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT)) {
> +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, IRQ_SRC_ACCT_ERR, true);
> +    }
> +    zdma_ch_imr_update_irq(s);
> +}
> +
> +static void zdma_dst_done(XlnxZDMA *s)
> +{
> +    unsigned int cnt;
> +    cnt = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT);
> +    cnt++;
> +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT, cnt);
> +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DST_DSCR_DONE, true);
> +
> +    /* Did we overflow?  */
> +    if (cnt != ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT)) {
> +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, IRQ_DST_ACCT_ERR, true);
> +    }
> +    zdma_ch_imr_update_irq(s);
> +}
> +
> +static uint64_t zdma_get_regaddr64(XlnxZDMA *s, unsigned int basereg)
> +{
> +    uint64_t addr;
> +
> +    addr = s->regs[basereg + 1];
> +    addr <<= 32;
> +    addr |= s->regs[basereg];
> +
> +    return addr;
> +}
> +
> +static void zdma_put_regaddr64(XlnxZDMA *s, unsigned int basereg, uint64_t addr)
> +{
> +    s->regs[basereg] = addr;
> +    s->regs[basereg + 1] = addr >> 32;
> +}
> +
> +static bool zdma_load_descriptor(XlnxZDMA *s, uint64_t addr, void *buf)
> +{
> +    /* ZDMA descriptors must be aligned to their own size.  */
> +    if (addr % sizeof(XlnxZDMADescr)) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "zdma: unaligned descriptor at %" PRIx64,
> +                      addr);
> +        memset(buf, 0xdeadbeef, sizeof(XlnxZDMADescr));
> +        s->error = true;
> +        return false;
> +    }
> +
> +    address_space_rw(s->dma_as, addr, s->attr,
> +                     buf, sizeof(XlnxZDMADescr), false);
> +    return true;
> +}
> +
> +static void zdma_load_src_descriptor(XlnxZDMA *s)
> +{
> +    uint64_t src_addr;
> +    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE);
> +
> +    if (ptype == PT_REG) {
> +        memcpy(&s->dsc_src, &s->regs[R_ZDMA_CH_SRC_DSCR_WORD0],
> +               sizeof(s->dsc_src));
> +        return;
> +    }
> +
> +    src_addr = zdma_get_regaddr64(s, R_ZDMA_CH_SRC_CUR_DSCR_LSB);
> +
> +    if (!zdma_load_descriptor(s, src_addr, &s->dsc_src)) {
> +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, AXI_RD_SRC_DSCR, true);
> +    }
> +}
> +
> +static void zdma_load_dst_descriptor(XlnxZDMA *s)
> +{
> +    uint64_t dst_addr;
> +    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE);
> +
> +    if (ptype == PT_REG) {
> +        memcpy(&s->dsc_dst, &s->regs[R_ZDMA_CH_DST_DSCR_WORD0],
> +               sizeof(s->dsc_src));
> +        return;
> +    }
> +
> +    dst_addr = zdma_get_regaddr64(s, R_ZDMA_CH_DST_CUR_DSCR_LSB);
> +
> +    if (!zdma_load_descriptor(s, dst_addr, &s->dsc_dst)) {
> +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, AXI_RD_DST_DSCR, true);
> +    }
> +}
> +
> +static uint64_t zdma_update_descr_addr(XlnxZDMA *s, bool type,
> +                                       unsigned int basereg)
> +{
> +    uint64_t addr, next;
> +
> +    if (type == DTYPE_LINEAR) {
> +        next = zdma_get_regaddr64(s, basereg);
> +        next += sizeof(s->dsc_dst);
> +        zdma_put_regaddr64(s, basereg, next);
> +    } else {
> +        addr = zdma_get_regaddr64(s, basereg);
> +        addr += sizeof(s->dsc_dst);
> +        address_space_rw(s->dma_as, addr, s->attr, (void *) &next, 8, false);
> +        zdma_put_regaddr64(s, basereg, next);
> +    }
> +    return next;
> +}
> +
> +static void zdma_write_dst(XlnxZDMA *s, uint8_t *buf, uint32_t len)
> +{
> +    uint32_t dst_size, dlen;
> +    bool dst_intr, dst_type;
> +    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE);
> +    unsigned int rw_mode = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, MODE);
> +    unsigned int burst_type = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_DATA_ATTR,
> +                                               AWBURST);
> +
> +    /* FIXED burst types are only supported in simple dma mode.  */
> +    if (ptype != PT_REG) {
> +        burst_type = AXI_BURST_INCR;
> +    }
> +
> +    while (len) {
> +        dst_size = FIELD_EX32(s->dsc_dst.words[2], ZDMA_CH_DST_DSCR_WORD2,
> +                              SIZE);
> +        dst_type = FIELD_EX32(s->dsc_dst.words[3], ZDMA_CH_DST_DSCR_WORD3,
> +                              TYPE);
> +        if (dst_size == 0 && ptype == PT_MEM) {
> +            uint64_t next;
> +            next = zdma_update_descr_addr(s, dst_type,
> +                                          R_ZDMA_CH_DST_CUR_DSCR_LSB);
> +            zdma_load_descriptor(s, next, &s->dsc_dst);
> +            dst_size = FIELD_EX32(s->dsc_dst.words[2], ZDMA_CH_DST_DSCR_WORD2,
> +                                  SIZE);
> +            dst_type = FIELD_EX32(s->dsc_dst.words[3], ZDMA_CH_DST_DSCR_WORD3,
> +                                  TYPE);
> +        }
> +
> +        /* Match what hardware does by ignoring the dst_size and only using
> +         * the src size for Simple register mode.  */
> +        if (ptype == PT_REG && rw_mode != RW_MODE_WO) {
> +            dst_size = len;
> +        }
> +
> +        if (dst_size == 0) {
> +            qemu_log("Can't write to dst\n");
> +        }
> +        dst_intr = FIELD_EX32(s->dsc_dst.words[3], ZDMA_CH_DST_DSCR_WORD3,
> +                              INTR);
> +
> +        dlen = len > dst_size ? dst_size : len;
> +        if (burst_type == AXI_BURST_FIXED) {
> +            if (dlen > (s->cfg.bus_width / 8)) {
> +                dlen = s->cfg.bus_width / 8;
> +            }
> +        }
> +
> +        address_space_rw(s->dma_as, s->dsc_dst.addr, s->attr, buf, dlen,
> +                         true);
> +        if (burst_type == AXI_BURST_INCR) {
> +            s->dsc_dst.addr += dlen;
> +        }
> +        dst_size -= dlen;
> +        buf += dlen;
> +        len -= dlen;
> +
> +        if (dst_size == 0 && dst_intr) {
> +            zdma_dst_done(s);
> +        }
> +
> +        /* Write back to buffered descriptor.  */
> +        s->dsc_dst.words[2] = FIELD_DP32(s->dsc_dst.words[2],
> +                                         ZDMA_CH_DST_DSCR_WORD2,
> +                                         SIZE,
> +                                         dst_size);
> +    }
> +}
> +
> +static void zdma_process_descr(XlnxZDMA *s)
> +{
> +    uint64_t src_addr;
> +    uint32_t src_size, len;
> +    unsigned int src_cmd;
> +    bool src_intr, src_type;
> +    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE);
> +    unsigned int rw_mode = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, MODE);
> +    unsigned int burst_type = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_DATA_ATTR,
> +                                               ARBURST);
> +
> +    src_addr = s->dsc_src.addr;
> +    src_size = FIELD_EX32(s->dsc_src.words[2], ZDMA_CH_SRC_DSCR_WORD2, SIZE);
> +    src_cmd = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3, CMD);
> +    src_type = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3, TYPE);
> +    src_intr = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3, INTR);
> +
> +    /* FIXED burst types and non-rw modes are only supported in
> +     * simple dma mode.
> +     */
> +    if (ptype != PT_REG) {
> +        if (rw_mode != RW_MODE_RW) {
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                          "zDMA: rw-mode=%d but not simple DMA mode.\n",
> +                          rw_mode);
> +        }
> +        if (burst_type != AXI_BURST_INCR) {
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                          "zDMA: burst_type=%d but not simple DMA mode.\n",
> +                          burst_type);
> +        }
> +        burst_type = AXI_BURST_INCR;
> +        rw_mode = RW_MODE_RW;
> +    }
> +
> +    if (rw_mode == RW_MODE_WO) {
> +        /* In Simple DMA Write-Only, we need to push DST size bytes
> +         * regardless of what SRC size is set to.  */
> +        src_size = FIELD_EX32(s->dsc_dst.words[2], ZDMA_CH_DST_DSCR_WORD2,
> +                              SIZE);
> +        memcpy(s->buf, &s->regs[R_ZDMA_CH_WR_ONLY_WORD0], s->cfg.bus_width / 8);
> +    }
> +
> +    while (src_size) {
> +        len = src_size > ARRAY_SIZE(s->buf) ? ARRAY_SIZE(s->buf) : src_size;
> +        if (burst_type == AXI_BURST_FIXED) {
> +            if (len > (s->cfg.bus_width / 8)) {
> +                len = s->cfg.bus_width / 8;
> +            }
> +        }
> +
> +        if (rw_mode == RW_MODE_WO) {
> +            if (len > s->cfg.bus_width / 8) {
> +                len = s->cfg.bus_width / 8;
> +            }
> +        } else {
> +            address_space_rw(s->dma_as, src_addr, s->attr, s->buf, len,
> +                             false);
> +            if (burst_type == AXI_BURST_INCR) {
> +                src_addr += len;
> +            }
> +        }
> +
> +        if (rw_mode != RW_MODE_RO) {
> +            zdma_write_dst(s, s->buf, len);
> +        }
> +
> +        s->regs[R_ZDMA_CH_TOTAL_BYTE] += len;
> +        src_size -= len;
> +
> +        if (src_size == 0) {
> +            ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_DONE, true);
> +        }
> +
> +    }
> +
> +    if (src_intr) {
> +        zdma_src_done(s);
> +    }
> +
> +    /* Load next descriptor.  */
> +    if (ptype == PT_REG || src_cmd == CMD_STOP) {
> +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_CTRL2, EN, 0);
> +        zdma_set_state(s, DISABLED);
> +        return;
> +    }
> +
> +    if (src_cmd == CMD_HALT) {
> +        zdma_set_state(s, PAUSED);
> +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_PAUSE, 1);
> +        zdma_ch_imr_update_irq(s);
> +        return;
> +    }
> +
> +    zdma_update_descr_addr(s, src_type, R_ZDMA_CH_SRC_CUR_DSCR_LSB);
> +}
> +
> +static void zdma_run(XlnxZDMA *s)
> +{
> +    while (s->state == ENABLED && !s->error) {
> +        zdma_load_src_descriptor(s);
> +
> +        if (s->error) {
> +            zdma_set_state(s, DISABLED);
> +        } else {
> +            zdma_process_descr(s);
> +        }
> +    }
> +
> +    zdma_ch_imr_update_irq(s);
> +}
> +
> +static void zdma_update_descr_addr_from_start(XlnxZDMA *s)
> +{
> +    uint64_t src_addr, dst_addr;
> +
> +    src_addr = zdma_get_regaddr64(s, R_ZDMA_CH_SRC_START_LSB);
> +    zdma_put_regaddr64(s, R_ZDMA_CH_SRC_CUR_DSCR_LSB, src_addr);
> +    dst_addr = zdma_get_regaddr64(s, R_ZDMA_CH_DST_START_LSB);
> +    zdma_put_regaddr64(s, R_ZDMA_CH_DST_CUR_DSCR_LSB, dst_addr);
> +    zdma_load_dst_descriptor(s);
> +}
> +
> +static void zdma_ch_ctrlx_postw(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
> +
> +    if (ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL2, EN)) {
> +        s->error = false;
> +
> +        if (s->state == PAUSED &&
> +            ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT)) {
We can clear the CONT, if dma is beeing unpaused.

> +            if (ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT_ADDR) == 1) {
> +                zdma_update_descr_addr_from_start(s);
> +            } else {
> +                bool src_type = FIELD_EX32(s->dsc_src.words[3],
> +                                       ZDMA_CH_SRC_DSCR_WORD3, TYPE);
> +                zdma_update_descr_addr(s, src_type,
> +                                          R_ZDMA_CH_SRC_CUR_DSCR_LSB);
> +            }
> +        } else {
> +            zdma_update_descr_addr_from_start(s);
Need to look more here, looks like we are running down and processing
descriptors when channel is paused.

Thanks
Sai Pavan

> +        }
> +        zdma_set_state(s, ENABLED);
> +    } else {
> +        /* Leave Paused state?  */
> +        if (s->state == PAUSED &&
> +            ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT)) {
> +            zdma_set_state(s, DISABLED);
> +        }
> +    }
> +
> +    zdma_run(s);
> +}
> +
> +static RegisterAccessInfo zdma_regs_info[] = {
> +    {   .name = "ZDMA_ERR_CTRL",  .addr = A_ZDMA_ERR_CTRL,
> +        .rsvd = 0xfffffffe,
> +    },{ .name = "ZDMA_CH_ISR",  .addr = A_ZDMA_CH_ISR,
> +        .rsvd = 0xfffff000,
> +        .w1c = 0xfff,
> +        .post_write = zdma_ch_isr_postw,
> +    },{ .name = "ZDMA_CH_IMR",  .addr = A_ZDMA_CH_IMR,
> +        .reset = 0xfff,
> +        .rsvd = 0xfffff000,
> +        .ro = 0xfff,
> +    },{ .name = "ZDMA_CH_IEN",  .addr = A_ZDMA_CH_IEN,
> +        .rsvd = 0xfffff000,
> +        .pre_write = zdma_ch_ien_prew,
> +    },{ .name = "ZDMA_CH_IDS",  .addr = A_ZDMA_CH_IDS,
> +        .rsvd = 0xfffff000,
> +        .pre_write = zdma_ch_ids_prew,
> +    },{ .name = "ZDMA_CH_CTRL0",  .addr = A_ZDMA_CH_CTRL0,
> +        .reset = 0x80,
> +        .rsvd = 0xffffff01,
> +        .post_write = zdma_ch_ctrlx_postw,
> +    },{ .name = "ZDMA_CH_CTRL1",  .addr = A_ZDMA_CH_CTRL1,
> +        .reset = 0x3ff,
> +        .rsvd = 0xfffffc00,
> +    },{ .name = "ZDMA_CH_FCI",  .addr = A_ZDMA_CH_FCI,
> +        .rsvd = 0xffffffc0,
> +    },{ .name = "ZDMA_CH_STATUS",  .addr = A_ZDMA_CH_STATUS,
> +        .rsvd = 0xfffffffc,
> +        .ro = 0x3,
> +    },{ .name = "ZDMA_CH_DATA_ATTR",  .addr = A_ZDMA_CH_DATA_ATTR,
> +        .reset = 0x483d20f,
> +        .rsvd = 0xf0000000,
> +    },{ .name = "ZDMA_CH_DSCR_ATTR",  .addr = A_ZDMA_CH_DSCR_ATTR,
> +        .rsvd = 0xfffffe00,
> +    },{ .name = "ZDMA_CH_SRC_DSCR_WORD0",  .addr = A_ZDMA_CH_SRC_DSCR_WORD0,
> +    },{ .name = "ZDMA_CH_SRC_DSCR_WORD1",  .addr = A_ZDMA_CH_SRC_DSCR_WORD1,
> +        .rsvd = 0xfffe0000,
> +    },{ .name = "ZDMA_CH_SRC_DSCR_WORD2",  .addr = A_ZDMA_CH_SRC_DSCR_WORD2,
> +        .rsvd = 0xc0000000,
> +    },{ .name = "ZDMA_CH_SRC_DSCR_WORD3",  .addr = A_ZDMA_CH_SRC_DSCR_WORD3,
> +        .rsvd = 0xffffffe0,
> +    },{ .name = "ZDMA_CH_DST_DSCR_WORD0",  .addr = A_ZDMA_CH_DST_DSCR_WORD0,
> +    },{ .name = "ZDMA_CH_DST_DSCR_WORD1",  .addr = A_ZDMA_CH_DST_DSCR_WORD1,
> +        .rsvd = 0xfffe0000,
> +    },{ .name = "ZDMA_CH_DST_DSCR_WORD2",  .addr = A_ZDMA_CH_DST_DSCR_WORD2,
> +        .rsvd = 0xc0000000,
> +    },{ .name = "ZDMA_CH_DST_DSCR_WORD3",  .addr = A_ZDMA_CH_DST_DSCR_WORD3,
> +        .rsvd = 0xfffffffa,
> +    },{ .name = "ZDMA_CH_WR_ONLY_WORD0",  .addr = A_ZDMA_CH_WR_ONLY_WORD0,
> +    },{ .name = "ZDMA_CH_WR_ONLY_WORD1",  .addr = A_ZDMA_CH_WR_ONLY_WORD1,
> +    },{ .name = "ZDMA_CH_WR_ONLY_WORD2",  .addr = A_ZDMA_CH_WR_ONLY_WORD2,
> +    },{ .name = "ZDMA_CH_WR_ONLY_WORD3",  .addr = A_ZDMA_CH_WR_ONLY_WORD3,
> +    },{ .name = "ZDMA_CH_SRC_START_LSB",  .addr = A_ZDMA_CH_SRC_START_LSB,
> +    },{ .name = "ZDMA_CH_SRC_START_MSB",  .addr = A_ZDMA_CH_SRC_START_MSB,
> +        .rsvd = 0xfffe0000,
> +    },{ .name = "ZDMA_CH_DST_START_LSB",  .addr = A_ZDMA_CH_DST_START_LSB,
> +    },{ .name = "ZDMA_CH_DST_START_MSB",  .addr = A_ZDMA_CH_DST_START_MSB,
> +        .rsvd = 0xfffe0000,
> +    },{ .name = "ZDMA_CH_SRC_CUR_PYLD_LSB",  .addr = A_ZDMA_CH_SRC_CUR_PYLD_LSB,
> +        .ro = 0xffffffff,
> +    },{ .name = "ZDMA_CH_SRC_CUR_PYLD_MSB",  .addr = A_ZDMA_CH_SRC_CUR_PYLD_MSB,
> +        .rsvd = 0xfffe0000,
> +        .ro = 0x1ffff,
> +    },{ .name = "ZDMA_CH_DST_CUR_PYLD_LSB",  .addr = A_ZDMA_CH_DST_CUR_PYLD_LSB,
> +        .ro = 0xffffffff,
> +    },{ .name = "ZDMA_CH_DST_CUR_PYLD_MSB",  .addr = A_ZDMA_CH_DST_CUR_PYLD_MSB,
> +        .rsvd = 0xfffe0000,
> +        .ro = 0x1ffff,
> +    },{ .name = "ZDMA_CH_SRC_CUR_DSCR_LSB",  .addr = A_ZDMA_CH_SRC_CUR_DSCR_LSB,
> +        .ro = 0xffffffff,
> +    },{ .name = "ZDMA_CH_SRC_CUR_DSCR_MSB",  .addr = A_ZDMA_CH_SRC_CUR_DSCR_MSB,
> +        .rsvd = 0xfffe0000,
> +        .ro = 0x1ffff,
> +    },{ .name = "ZDMA_CH_DST_CUR_DSCR_LSB",  .addr = A_ZDMA_CH_DST_CUR_DSCR_LSB,
> +        .ro = 0xffffffff,
> +    },{ .name = "ZDMA_CH_DST_CUR_DSCR_MSB",  .addr = A_ZDMA_CH_DST_CUR_DSCR_MSB,
> +        .rsvd = 0xfffe0000,
> +        .ro = 0x1ffff,
> +    },{ .name = "ZDMA_CH_TOTAL_BYTE",  .addr = A_ZDMA_CH_TOTAL_BYTE,
> +        .w1c = 0xffffffff,
> +    },{ .name = "ZDMA_CH_RATE_CNTL",  .addr = A_ZDMA_CH_RATE_CNTL,
> +        .rsvd = 0xfffff000,
> +    },{ .name = "ZDMA_CH_IRQ_SRC_ACCT",  .addr = A_ZDMA_CH_IRQ_SRC_ACCT,
> +        .rsvd = 0xffffff00,
> +        .ro = 0xff,
> +        .cor = 0xff,
> +    },{ .name = "ZDMA_CH_IRQ_DST_ACCT",  .addr = A_ZDMA_CH_IRQ_DST_ACCT,
> +        .rsvd = 0xffffff00,
> +        .ro = 0xff,
> +        .cor = 0xff,
> +    },{ .name = "ZDMA_CH_DBG0",  .addr = A_ZDMA_CH_DBG0,
> +        .rsvd = 0xfffffe00,
> +        .ro = 0x1ff,
> +    },{ .name = "ZDMA_CH_DBG1",  .addr = A_ZDMA_CH_DBG1,
> +        .rsvd = 0xfffffe00,
> +        .ro = 0x1ff,
> +    },{ .name = "ZDMA_CH_CTRL2",  .addr = A_ZDMA_CH_CTRL2,
> +        .rsvd = 0xfffffffe,
> +        .post_write = zdma_ch_ctrlx_postw,
> +    }
> +};
> +
> +static void zdma_reset(DeviceState *dev)
> +{
> +    XlnxZDMA *s = XLNX_ZDMA(dev);
> +    unsigned int i;
> +
> +    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
> +        register_reset(&s->regs_info[i]);
> +    }
> +
> +    zdma_ch_imr_update_irq(s);
> +}
> +
> +static uint64_t zdma_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    XlnxZDMA *s = XLNX_ZDMA(opaque);
> +    RegisterInfo *r = &s->regs_info[addr / 4];
> +
> +    if (!r->data) {
> +        qemu_log("%s: Decode error: read from %" HWADDR_PRIx "\n",
> +                 object_get_canonical_path(OBJECT(s)),
> +                 addr);
> +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true);
> +        zdma_ch_imr_update_irq(s);
> +        return 0;
> +    }
> +    return register_read(r, ~0, NULL, false);
> +}
> +
> +static void zdma_write(void *opaque, hwaddr addr, uint64_t value,
> +                      unsigned size)
> +{
> +    XlnxZDMA *s = XLNX_ZDMA(opaque);
> +    RegisterInfo *r = &s->regs_info[addr / 4];
> +
> +    if (!r->data) {
> +        qemu_log("%s: Decode error: write to %" HWADDR_PRIx "=%" PRIx64 "\n",
> +                 object_get_canonical_path(OBJECT(s)),
> +                 addr, value);
> +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true);
> +        zdma_ch_imr_update_irq(s);
> +        return;
> +    }
> +    register_write(r, value, ~0, NULL, false);
> +}
> +
> +static const MemoryRegionOps zdma_ops = {
> +    .read = zdma_read,
> +    .write = zdma_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static void zdma_realize(DeviceState *dev, Error **errp)
> +{
> +    XlnxZDMA *s = XLNX_ZDMA(dev);
> +    unsigned int i;
> +
> +    for (i = 0; i < ARRAY_SIZE(zdma_regs_info); ++i) {
> +        RegisterInfo *r = &s->regs_info[zdma_regs_info[i].addr / 4];
> +
> +        *r = (RegisterInfo) {
> +            .data = (uint8_t *)&s->regs[
> +                    zdma_regs_info[i].addr / 4],
> +            .data_size = sizeof(uint32_t),
> +            .access = &zdma_regs_info[i],
> +            .opaque = s,
> +        };
> +    }
> +
> +    if (s->dma_mr) {
> +        s->dma_as = g_malloc0(sizeof(AddressSpace));
> +        address_space_init(s->dma_as, s->dma_mr, NULL);
> +    } else {
> +        s->dma_as = &address_space_memory;
> +    }
> +    s->attr = MEMTXATTRS_UNSPECIFIED;
> +}
> +
> +static void zdma_init(Object *obj)
> +{
> +    XlnxZDMA *s = XLNX_ZDMA(obj);
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> +
> +    memory_region_init_io(&s->iomem, obj, &zdma_ops, s,
> +                          TYPE_XLNX_ZDMA, ZDMA_R_MAX * 4);
> +    sysbus_init_mmio(sbd, &s->iomem);
> +    sysbus_init_irq(sbd, &s->irq_zdma_ch_imr);
> +
> +    object_property_add_link(obj, "dma", TYPE_MEMORY_REGION,
> +                             (Object **)&s->dma_mr,
> +                             qdev_prop_allow_set_link_before_realize,
> +                             OBJ_PROP_LINK_UNREF_ON_RELEASE,
> +                             &error_abort);
> +}
> +
> +static const VMStateDescription vmstate_zdma = {
> +    .name = TYPE_XLNX_ZDMA,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32_ARRAY(regs, XlnxZDMA, ZDMA_R_MAX),
> +        VMSTATE_END_OF_LIST(),
> +    }
> +};
> +
> +static Property zdma_props[] = {
> +    DEFINE_PROP_UINT32("bus-width", XlnxZDMA, cfg.bus_width, 64),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void zdma_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->reset = zdma_reset;
> +    dc->realize = zdma_realize;
> +    dc->props = zdma_props;
> +    dc->vmsd = &vmstate_zdma;
> +}
> +
> +static const TypeInfo zdma_info = {
> +    .name          = TYPE_XLNX_ZDMA,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(XlnxZDMA),
> +    .class_init    = zdma_class_init,
> +    .instance_init = zdma_init,
> +};
> +
> +static void zdma_register_types(void)
> +{
> +    type_register_static(&zdma_info);
> +}
> +
> +type_init(zdma_register_types)
> diff --git a/include/hw/dma/xlnx-zdma.h b/include/hw/dma/xlnx-zdma.h
> new file mode 100644
> index 0000000000..0b240b4c3c
> --- /dev/null
> +++ b/include/hw/dma/xlnx-zdma.h
> @@ -0,0 +1,84 @@
> +/*
> + * QEMU model of the ZynqMP generic DMA
> + *
> + * Copyright (c) 2014 Xilinx Inc.
> + * Copyright (c) 2018 FEIMTECH AB
> + *
> + * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>,
> + *            Francisco Iglesias <francisco.iglesias@feimtech.se>
> + *
> + * 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.
> + */
> +
> +#ifndef XLNX_ZDMA_H
> +#define XLNX_ZDMA_H
> +
> +#include "hw/sysbus.h"
> +#include "hw/register.h"
> +#include "sysemu/dma.h"
> +
> +#define ZDMA_R_MAX (0x204 / 4)
> +
> +typedef enum {
> +    DISABLED = 0,
> +    ENABLED = 1,
> +    PAUSED = 2,
> +} XlnxZDMAState;
> +
> +typedef union {
> +    struct {
> +        uint64_t addr;
> +        uint32_t size;
> +        uint32_t attr;
> +    };
> +    uint32_t words[4];
> +} XlnxZDMADescr;
> +
> +typedef struct XlnxZDMA {
> +    SysBusDevice parent_obj;
> +    MemoryRegion iomem;
> +    MemTxAttrs attr;
> +    MemoryRegion *dma_mr;
> +    AddressSpace *dma_as;
> +    qemu_irq irq_zdma_ch_imr;
> +
> +    struct {
> +        uint32_t bus_width;
> +    } cfg;
> +
> +    XlnxZDMAState state;
> +    bool error;
> +
> +    XlnxZDMADescr dsc_src;
> +    XlnxZDMADescr dsc_dst;
> +
> +    uint32_t regs[ZDMA_R_MAX];
> +    RegisterInfo regs_info[ZDMA_R_MAX];
> +
> +    /* We don't model the common bufs. Must be at least 16 bytes
> +       to model write only mode.  */
> +    uint8_t buf[2048];
> +} XlnxZDMA;
> +
> +#define TYPE_XLNX_ZDMA "xlnx.zdma"
> +
> +#define XLNX_ZDMA(obj) \
> +     OBJECT_CHECK(XlnxZDMA, (obj), TYPE_XLNX_ZDMA)
> +
> +#endif /* XLNX_ZDMA_H */
> -- 
> 2.11.0
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [Qemu-devel] [PATCH v1 1/2] xlnx-zdma: Add a model of the Xilinx ZynqMP generic DMA
  2018-04-29  0:43 ` [Qemu-devel] [PATCH v1 1/2] xlnx-zdma: Add a model of the Xilinx ZynqMP generic DMA Francisco Iglesias
  2018-04-30 13:27   ` Sai Pavan Boddu
@ 2018-04-30 13:42   ` Edgar E. Iglesias
  2018-05-02 19:58     ` francisco iglesias
  1 sibling, 1 reply; 7+ messages in thread
From: Edgar E. Iglesias @ 2018-04-30 13:42 UTC (permalink / raw)
  To: Francisco Iglesias
  Cc: qemu-devel, edgari, sai.pavan.boddu, alistair, alistair23,
	francisco.iglesias, peter.maydell

On Sun, Apr 29, 2018 at 02:43:28AM +0200, Francisco Iglesias wrote:
> Add a model of the generic DMA found on Xilinx ZynqMP.


Hi Francisco,

I think we need to look at the VMStateDescription, some state
seems to be missing...

Cheers,
Edgar


> 
> Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
> Signed-off-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
> ---
>  hw/dma/Makefile.objs       |   1 +
>  hw/dma/xlnx-zdma.c         | 833 +++++++++++++++++++++++++++++++++++++++++++++
>  include/hw/dma/xlnx-zdma.h |  84 +++++
>  3 files changed, 918 insertions(+)
>  create mode 100644 hw/dma/xlnx-zdma.c
>  create mode 100644 include/hw/dma/xlnx-zdma.h
> 
> diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs
> index c2afecbf73..79affecc39 100644
> --- a/hw/dma/Makefile.objs
> +++ b/hw/dma/Makefile.objs
> @@ -10,6 +10,7 @@ common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o
>  common-obj-$(CONFIG_STP2000) += sparc32_dma.o
>  obj-$(CONFIG_XLNX_ZYNQMP) += xlnx_dpdma.o
>  obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx_dpdma.o
> +common-obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zdma.o
>  
>  obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o
>  obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o
> diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c
> new file mode 100644
> index 0000000000..7955a6116d
> --- /dev/null
> +++ b/hw/dma/xlnx-zdma.c
> @@ -0,0 +1,833 @@
> +/*
> + * QEMU model of the ZynqMP generic DMA
> + *
> + * Copyright (c) 2014 Xilinx Inc.
> + * Copyright (c) 2018 FEIMTECH AB
> + *
> + * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>,
> + *            Francisco Iglesias <francisco.iglesias@feimtech.se>
> + *
> + * 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/dma/xlnx-zdma.h"
> +#include "qemu/bitops.h"
> +#include "qemu/log.h"
> +#include "qapi/error.h"
> +
> +#ifndef XLNX_ZDMA_ERR_DEBUG
> +#define XLNX_ZDMA_ERR_DEBUG 0
> +#endif
> +
> +REG32(ZDMA_ERR_CTRL, 0x0)
> +    FIELD(ZDMA_ERR_CTRL, APB_ERR_RES, 0, 1)
> +REG32(ZDMA_CH_ISR, 0x100)
> +    FIELD(ZDMA_CH_ISR, DMA_PAUSE, 11, 1)
> +    FIELD(ZDMA_CH_ISR, DMA_DONE, 10, 1)
> +    FIELD(ZDMA_CH_ISR, AXI_WR_DATA, 9, 1)
> +    FIELD(ZDMA_CH_ISR, AXI_RD_DATA, 8, 1)
> +    FIELD(ZDMA_CH_ISR, AXI_RD_DST_DSCR, 7, 1)
> +    FIELD(ZDMA_CH_ISR, AXI_RD_SRC_DSCR, 6, 1)
> +    FIELD(ZDMA_CH_ISR, IRQ_DST_ACCT_ERR, 5, 1)
> +    FIELD(ZDMA_CH_ISR, IRQ_SRC_ACCT_ERR, 4, 1)
> +    FIELD(ZDMA_CH_ISR, BYTE_CNT_OVRFL, 3, 1)
> +    FIELD(ZDMA_CH_ISR, DST_DSCR_DONE, 2, 1)
> +    FIELD(ZDMA_CH_ISR, SRC_DSCR_DONE, 1, 1)
> +    FIELD(ZDMA_CH_ISR, INV_APB, 0, 1)
> +REG32(ZDMA_CH_IMR, 0x104)
> +    FIELD(ZDMA_CH_IMR, DMA_PAUSE, 11, 1)
> +    FIELD(ZDMA_CH_IMR, DMA_DONE, 10, 1)
> +    FIELD(ZDMA_CH_IMR, AXI_WR_DATA, 9, 1)
> +    FIELD(ZDMA_CH_IMR, AXI_RD_DATA, 8, 1)
> +    FIELD(ZDMA_CH_IMR, AXI_RD_DST_DSCR, 7, 1)
> +    FIELD(ZDMA_CH_IMR, AXI_RD_SRC_DSCR, 6, 1)
> +    FIELD(ZDMA_CH_IMR, IRQ_DST_ACCT_ERR, 5, 1)
> +    FIELD(ZDMA_CH_IMR, IRQ_SRC_ACCT_ERR, 4, 1)
> +    FIELD(ZDMA_CH_IMR, BYTE_CNT_OVRFL, 3, 1)
> +    FIELD(ZDMA_CH_IMR, DST_DSCR_DONE, 2, 1)
> +    FIELD(ZDMA_CH_IMR, SRC_DSCR_DONE, 1, 1)
> +    FIELD(ZDMA_CH_IMR, INV_APB, 0, 1)
> +REG32(ZDMA_CH_IEN, 0x108)
> +    FIELD(ZDMA_CH_IEN, DMA_PAUSE, 11, 1)
> +    FIELD(ZDMA_CH_IEN, DMA_DONE, 10, 1)
> +    FIELD(ZDMA_CH_IEN, AXI_WR_DATA, 9, 1)
> +    FIELD(ZDMA_CH_IEN, AXI_RD_DATA, 8, 1)
> +    FIELD(ZDMA_CH_IEN, AXI_RD_DST_DSCR, 7, 1)
> +    FIELD(ZDMA_CH_IEN, AXI_RD_SRC_DSCR, 6, 1)
> +    FIELD(ZDMA_CH_IEN, IRQ_DST_ACCT_ERR, 5, 1)
> +    FIELD(ZDMA_CH_IEN, IRQ_SRC_ACCT_ERR, 4, 1)
> +    FIELD(ZDMA_CH_IEN, BYTE_CNT_OVRFL, 3, 1)
> +    FIELD(ZDMA_CH_IEN, DST_DSCR_DONE, 2, 1)
> +    FIELD(ZDMA_CH_IEN, SRC_DSCR_DONE, 1, 1)
> +    FIELD(ZDMA_CH_IEN, INV_APB, 0, 1)
> +REG32(ZDMA_CH_IDS, 0x10c)
> +    FIELD(ZDMA_CH_IDS, DMA_PAUSE, 11, 1)
> +    FIELD(ZDMA_CH_IDS, DMA_DONE, 10, 1)
> +    FIELD(ZDMA_CH_IDS, AXI_WR_DATA, 9, 1)
> +    FIELD(ZDMA_CH_IDS, AXI_RD_DATA, 8, 1)
> +    FIELD(ZDMA_CH_IDS, AXI_RD_DST_DSCR, 7, 1)
> +    FIELD(ZDMA_CH_IDS, AXI_RD_SRC_DSCR, 6, 1)
> +    FIELD(ZDMA_CH_IDS, IRQ_DST_ACCT_ERR, 5, 1)
> +    FIELD(ZDMA_CH_IDS, IRQ_SRC_ACCT_ERR, 4, 1)
> +    FIELD(ZDMA_CH_IDS, BYTE_CNT_OVRFL, 3, 1)
> +    FIELD(ZDMA_CH_IDS, DST_DSCR_DONE, 2, 1)
> +    FIELD(ZDMA_CH_IDS, SRC_DSCR_DONE, 1, 1)
> +    FIELD(ZDMA_CH_IDS, INV_APB, 0, 1)
> +REG32(ZDMA_CH_CTRL0, 0x110)
> +    FIELD(ZDMA_CH_CTRL0, OVR_FETCH, 7, 1)
> +    FIELD(ZDMA_CH_CTRL0, POINT_TYPE, 6, 1)
> +    FIELD(ZDMA_CH_CTRL0, MODE, 4, 2)
> +    FIELD(ZDMA_CH_CTRL0, RATE_CTRL, 3, 1)
> +    FIELD(ZDMA_CH_CTRL0, CONT_ADDR, 2, 1)
> +    FIELD(ZDMA_CH_CTRL0, CONT, 1, 1)
> +REG32(ZDMA_CH_CTRL1, 0x114)
> +    FIELD(ZDMA_CH_CTRL1, DST_ISSUE, 5, 5)
> +    FIELD(ZDMA_CH_CTRL1, SRC_ISSUE, 0, 5)
> +REG32(ZDMA_CH_FCI, 0x118)
> +    FIELD(ZDMA_CH_FCI, PROG_CELL_CNT, 2, 2)
> +    FIELD(ZDMA_CH_FCI, SIDE, 1, 1)
> +    FIELD(ZDMA_CH_FCI, EN, 0, 1)
> +REG32(ZDMA_CH_STATUS, 0x11c)
> +    FIELD(ZDMA_CH_STATUS, STATE, 0, 2)
> +REG32(ZDMA_CH_DATA_ATTR, 0x120)
> +    FIELD(ZDMA_CH_DATA_ATTR, ARBURST, 26, 2)
> +    FIELD(ZDMA_CH_DATA_ATTR, ARCACHE, 22, 4)
> +    FIELD(ZDMA_CH_DATA_ATTR, ARQOS, 18, 4)
> +    FIELD(ZDMA_CH_DATA_ATTR, ARLEN, 14, 4)
> +    FIELD(ZDMA_CH_DATA_ATTR, AWBURST, 12, 2)
> +    FIELD(ZDMA_CH_DATA_ATTR, AWCACHE, 8, 4)
> +    FIELD(ZDMA_CH_DATA_ATTR, AWQOS, 4, 4)
> +    FIELD(ZDMA_CH_DATA_ATTR, AWLEN, 0, 4)
> +REG32(ZDMA_CH_DSCR_ATTR, 0x124)
> +    FIELD(ZDMA_CH_DSCR_ATTR, AXCOHRNT, 8, 1)
> +    FIELD(ZDMA_CH_DSCR_ATTR, AXCACHE, 4, 4)
> +    FIELD(ZDMA_CH_DSCR_ATTR, AXQOS, 0, 4)
> +REG32(ZDMA_CH_SRC_DSCR_WORD0, 0x128)
> +REG32(ZDMA_CH_SRC_DSCR_WORD1, 0x12c)
> +    FIELD(ZDMA_CH_SRC_DSCR_WORD1, MSB, 0, 17)
> +REG32(ZDMA_CH_SRC_DSCR_WORD2, 0x130)
> +    FIELD(ZDMA_CH_SRC_DSCR_WORD2, SIZE, 0, 30)
> +REG32(ZDMA_CH_SRC_DSCR_WORD3, 0x134)
> +    FIELD(ZDMA_CH_SRC_DSCR_WORD3, CMD, 3, 2)
> +    FIELD(ZDMA_CH_SRC_DSCR_WORD3, INTR, 2, 1)
> +    FIELD(ZDMA_CH_SRC_DSCR_WORD3, TYPE, 1, 1)
> +    FIELD(ZDMA_CH_SRC_DSCR_WORD3, COHRNT, 0, 1)
> +REG32(ZDMA_CH_DST_DSCR_WORD0, 0x138)
> +REG32(ZDMA_CH_DST_DSCR_WORD1, 0x13c)
> +    FIELD(ZDMA_CH_DST_DSCR_WORD1, MSB, 0, 17)
> +REG32(ZDMA_CH_DST_DSCR_WORD2, 0x140)
> +    FIELD(ZDMA_CH_DST_DSCR_WORD2, SIZE, 0, 30)
> +REG32(ZDMA_CH_DST_DSCR_WORD3, 0x144)
> +    FIELD(ZDMA_CH_DST_DSCR_WORD3, INTR, 2, 1)
> +    FIELD(ZDMA_CH_DST_DSCR_WORD3, TYPE, 1, 1)
> +    FIELD(ZDMA_CH_DST_DSCR_WORD3, COHRNT, 0, 1)
> +REG32(ZDMA_CH_WR_ONLY_WORD0, 0x148)
> +REG32(ZDMA_CH_WR_ONLY_WORD1, 0x14c)
> +REG32(ZDMA_CH_WR_ONLY_WORD2, 0x150)
> +REG32(ZDMA_CH_WR_ONLY_WORD3, 0x154)
> +REG32(ZDMA_CH_SRC_START_LSB, 0x158)
> +REG32(ZDMA_CH_SRC_START_MSB, 0x15c)
> +    FIELD(ZDMA_CH_SRC_START_MSB, ADDR, 0, 17)
> +REG32(ZDMA_CH_DST_START_LSB, 0x160)
> +REG32(ZDMA_CH_DST_START_MSB, 0x164)
> +    FIELD(ZDMA_CH_DST_START_MSB, ADDR, 0, 17)
> +REG32(ZDMA_CH_RATE_CTRL, 0x18c)
> +    FIELD(ZDMA_CH_RATE_CTRL, CNT, 0, 12)
> +REG32(ZDMA_CH_SRC_CUR_PYLD_LSB, 0x168)
> +REG32(ZDMA_CH_SRC_CUR_PYLD_MSB, 0x16c)
> +    FIELD(ZDMA_CH_SRC_CUR_PYLD_MSB, ADDR, 0, 17)
> +REG32(ZDMA_CH_DST_CUR_PYLD_LSB, 0x170)
> +REG32(ZDMA_CH_DST_CUR_PYLD_MSB, 0x174)
> +    FIELD(ZDMA_CH_DST_CUR_PYLD_MSB, ADDR, 0, 17)
> +REG32(ZDMA_CH_SRC_CUR_DSCR_LSB, 0x178)
> +REG32(ZDMA_CH_SRC_CUR_DSCR_MSB, 0x17c)
> +    FIELD(ZDMA_CH_SRC_CUR_DSCR_MSB, ADDR, 0, 17)
> +REG32(ZDMA_CH_DST_CUR_DSCR_LSB, 0x180)
> +REG32(ZDMA_CH_DST_CUR_DSCR_MSB, 0x184)
> +    FIELD(ZDMA_CH_DST_CUR_DSCR_MSB, ADDR, 0, 17)
> +REG32(ZDMA_CH_TOTAL_BYTE, 0x188)
> +REG32(ZDMA_CH_RATE_CNTL, 0x18c)
> +    FIELD(ZDMA_CH_RATE_CNTL, CNT, 0, 12)
> +REG32(ZDMA_CH_IRQ_SRC_ACCT, 0x190)
> +    FIELD(ZDMA_CH_IRQ_SRC_ACCT, CNT, 0, 8)
> +REG32(ZDMA_CH_IRQ_DST_ACCT, 0x194)
> +    FIELD(ZDMA_CH_IRQ_DST_ACCT, CNT, 0, 8)
> +REG32(ZDMA_CH_DBG0, 0x198)
> +    FIELD(ZDMA_CH_DBG0, CMN_BUF_FREE, 0, 9)
> +REG32(ZDMA_CH_DBG1, 0x19c)
> +    FIELD(ZDMA_CH_DBG1, CMN_BUF_OCC, 0, 9)
> +REG32(ZDMA_CH_CTRL2, 0x200)
> +    FIELD(ZDMA_CH_CTRL2, EN, 0, 1)
> +
> +enum {
> +    PT_REG = 0,
> +    PT_MEM = 1,
> +};
> +
> +enum {
> +    CMD_HALT = 1,
> +    CMD_STOP = 2,
> +};
> +
> +enum {
> +    RW_MODE_RW = 0,
> +    RW_MODE_WO = 1,
> +    RW_MODE_RO = 2,
> +};
> +
> +enum {
> +    DTYPE_LINEAR = 0,
> +    DTYPE_LINKED = 1,
> +};
> +
> +enum {
> +    AXI_BURST_FIXED = 0,
> +    AXI_BURST_INCR  = 1,
> +};
> +
> +static void zdma_ch_imr_update_irq(XlnxZDMA *s)
> +{
> +    bool pending;
> +
> +    pending = s->regs[R_ZDMA_CH_ISR] & ~s->regs[R_ZDMA_CH_IMR];
> +
> +    qemu_set_irq(s->irq_zdma_ch_imr, pending);
> +}
> +
> +static void zdma_ch_isr_postw(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
> +    zdma_ch_imr_update_irq(s);
> +}
> +
> +static uint64_t zdma_ch_ien_prew(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
> +    uint32_t val = val64;
> +
> +    s->regs[R_ZDMA_CH_IMR] &= ~val;
> +    zdma_ch_imr_update_irq(s);
> +    return 0;
> +}
> +
> +static uint64_t zdma_ch_ids_prew(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
> +    uint32_t val = val64;
> +
> +    s->regs[R_ZDMA_CH_IMR] |= val;
> +    zdma_ch_imr_update_irq(s);
> +    return 0;
> +}
> +
> +static void zdma_set_state(XlnxZDMA *s, XlnxZDMAState state)
> +{
> +    s->state = state;
> +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_STATUS, STATE, state);
> +
> +    /* Signal error if we have an error condition.  */
> +    if (s->error) {
> +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_STATUS, STATE, 3);
> +    }
> +}
> +
> +static void zdma_src_done(XlnxZDMA *s)
> +{
> +    unsigned int cnt;
> +    cnt = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT);
> +    cnt++;
> +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT, cnt);
> +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, SRC_DSCR_DONE, true);
> +
> +    /* Did we overflow?  */
> +    if (cnt != ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT)) {
> +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, IRQ_SRC_ACCT_ERR, true);
> +    }
> +    zdma_ch_imr_update_irq(s);
> +}
> +
> +static void zdma_dst_done(XlnxZDMA *s)
> +{
> +    unsigned int cnt;
> +    cnt = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT);
> +    cnt++;
> +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT, cnt);
> +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DST_DSCR_DONE, true);
> +
> +    /* Did we overflow?  */
> +    if (cnt != ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT)) {
> +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, IRQ_DST_ACCT_ERR, true);
> +    }
> +    zdma_ch_imr_update_irq(s);
> +}
> +
> +static uint64_t zdma_get_regaddr64(XlnxZDMA *s, unsigned int basereg)
> +{
> +    uint64_t addr;
> +
> +    addr = s->regs[basereg + 1];
> +    addr <<= 32;
> +    addr |= s->regs[basereg];
> +
> +    return addr;
> +}
> +
> +static void zdma_put_regaddr64(XlnxZDMA *s, unsigned int basereg, uint64_t addr)
> +{
> +    s->regs[basereg] = addr;
> +    s->regs[basereg + 1] = addr >> 32;
> +}
> +
> +static bool zdma_load_descriptor(XlnxZDMA *s, uint64_t addr, void *buf)
> +{
> +    /* ZDMA descriptors must be aligned to their own size.  */
> +    if (addr % sizeof(XlnxZDMADescr)) {
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +                      "zdma: unaligned descriptor at %" PRIx64,
> +                      addr);
> +        memset(buf, 0xdeadbeef, sizeof(XlnxZDMADescr));
> +        s->error = true;
> +        return false;
> +    }
> +
> +    address_space_rw(s->dma_as, addr, s->attr,
> +                     buf, sizeof(XlnxZDMADescr), false);
> +    return true;
> +}
> +
> +static void zdma_load_src_descriptor(XlnxZDMA *s)
> +{
> +    uint64_t src_addr;
> +    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE);
> +
> +    if (ptype == PT_REG) {
> +        memcpy(&s->dsc_src, &s->regs[R_ZDMA_CH_SRC_DSCR_WORD0],
> +               sizeof(s->dsc_src));
> +        return;
> +    }
> +
> +    src_addr = zdma_get_regaddr64(s, R_ZDMA_CH_SRC_CUR_DSCR_LSB);
> +
> +    if (!zdma_load_descriptor(s, src_addr, &s->dsc_src)) {
> +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, AXI_RD_SRC_DSCR, true);
> +    }
> +}
> +
> +static void zdma_load_dst_descriptor(XlnxZDMA *s)
> +{
> +    uint64_t dst_addr;
> +    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE);
> +
> +    if (ptype == PT_REG) {
> +        memcpy(&s->dsc_dst, &s->regs[R_ZDMA_CH_DST_DSCR_WORD0],
> +               sizeof(s->dsc_src));
> +        return;
> +    }
> +
> +    dst_addr = zdma_get_regaddr64(s, R_ZDMA_CH_DST_CUR_DSCR_LSB);
> +
> +    if (!zdma_load_descriptor(s, dst_addr, &s->dsc_dst)) {
> +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, AXI_RD_DST_DSCR, true);
> +    }
> +}
> +
> +static uint64_t zdma_update_descr_addr(XlnxZDMA *s, bool type,
> +                                       unsigned int basereg)
> +{
> +    uint64_t addr, next;
> +
> +    if (type == DTYPE_LINEAR) {
> +        next = zdma_get_regaddr64(s, basereg);
> +        next += sizeof(s->dsc_dst);
> +        zdma_put_regaddr64(s, basereg, next);
> +    } else {
> +        addr = zdma_get_regaddr64(s, basereg);
> +        addr += sizeof(s->dsc_dst);
> +        address_space_rw(s->dma_as, addr, s->attr, (void *) &next, 8, false);
> +        zdma_put_regaddr64(s, basereg, next);
> +    }
> +    return next;
> +}
> +
> +static void zdma_write_dst(XlnxZDMA *s, uint8_t *buf, uint32_t len)
> +{
> +    uint32_t dst_size, dlen;
> +    bool dst_intr, dst_type;
> +    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE);
> +    unsigned int rw_mode = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, MODE);
> +    unsigned int burst_type = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_DATA_ATTR,
> +                                               AWBURST);
> +
> +    /* FIXED burst types are only supported in simple dma mode.  */
> +    if (ptype != PT_REG) {
> +        burst_type = AXI_BURST_INCR;
> +    }
> +
> +    while (len) {
> +        dst_size = FIELD_EX32(s->dsc_dst.words[2], ZDMA_CH_DST_DSCR_WORD2,
> +                              SIZE);
> +        dst_type = FIELD_EX32(s->dsc_dst.words[3], ZDMA_CH_DST_DSCR_WORD3,
> +                              TYPE);
> +        if (dst_size == 0 && ptype == PT_MEM) {
> +            uint64_t next;
> +            next = zdma_update_descr_addr(s, dst_type,
> +                                          R_ZDMA_CH_DST_CUR_DSCR_LSB);
> +            zdma_load_descriptor(s, next, &s->dsc_dst);
> +            dst_size = FIELD_EX32(s->dsc_dst.words[2], ZDMA_CH_DST_DSCR_WORD2,
> +                                  SIZE);
> +            dst_type = FIELD_EX32(s->dsc_dst.words[3], ZDMA_CH_DST_DSCR_WORD3,
> +                                  TYPE);
> +        }
> +
> +        /* Match what hardware does by ignoring the dst_size and only using
> +         * the src size for Simple register mode.  */
> +        if (ptype == PT_REG && rw_mode != RW_MODE_WO) {
> +            dst_size = len;
> +        }
> +
> +        if (dst_size == 0) {
> +            qemu_log("Can't write to dst\n");
> +        }
> +        dst_intr = FIELD_EX32(s->dsc_dst.words[3], ZDMA_CH_DST_DSCR_WORD3,
> +                              INTR);
> +
> +        dlen = len > dst_size ? dst_size : len;
> +        if (burst_type == AXI_BURST_FIXED) {
> +            if (dlen > (s->cfg.bus_width / 8)) {
> +                dlen = s->cfg.bus_width / 8;
> +            }
> +        }
> +
> +        address_space_rw(s->dma_as, s->dsc_dst.addr, s->attr, buf, dlen,
> +                         true);
> +        if (burst_type == AXI_BURST_INCR) {
> +            s->dsc_dst.addr += dlen;
> +        }
> +        dst_size -= dlen;
> +        buf += dlen;
> +        len -= dlen;
> +
> +        if (dst_size == 0 && dst_intr) {
> +            zdma_dst_done(s);
> +        }
> +
> +        /* Write back to buffered descriptor.  */
> +        s->dsc_dst.words[2] = FIELD_DP32(s->dsc_dst.words[2],
> +                                         ZDMA_CH_DST_DSCR_WORD2,
> +                                         SIZE,
> +                                         dst_size);
> +    }
> +}
> +
> +static void zdma_process_descr(XlnxZDMA *s)
> +{
> +    uint64_t src_addr;
> +    uint32_t src_size, len;
> +    unsigned int src_cmd;
> +    bool src_intr, src_type;
> +    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, POINT_TYPE);
> +    unsigned int rw_mode = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, MODE);
> +    unsigned int burst_type = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_DATA_ATTR,
> +                                               ARBURST);
> +
> +    src_addr = s->dsc_src.addr;
> +    src_size = FIELD_EX32(s->dsc_src.words[2], ZDMA_CH_SRC_DSCR_WORD2, SIZE);
> +    src_cmd = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3, CMD);
> +    src_type = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3, TYPE);
> +    src_intr = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3, INTR);
> +
> +    /* FIXED burst types and non-rw modes are only supported in
> +     * simple dma mode.
> +     */
> +    if (ptype != PT_REG) {
> +        if (rw_mode != RW_MODE_RW) {
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                          "zDMA: rw-mode=%d but not simple DMA mode.\n",
> +                          rw_mode);
> +        }
> +        if (burst_type != AXI_BURST_INCR) {
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                          "zDMA: burst_type=%d but not simple DMA mode.\n",
> +                          burst_type);
> +        }
> +        burst_type = AXI_BURST_INCR;
> +        rw_mode = RW_MODE_RW;
> +    }
> +
> +    if (rw_mode == RW_MODE_WO) {
> +        /* In Simple DMA Write-Only, we need to push DST size bytes
> +         * regardless of what SRC size is set to.  */
> +        src_size = FIELD_EX32(s->dsc_dst.words[2], ZDMA_CH_DST_DSCR_WORD2,
> +                              SIZE);
> +        memcpy(s->buf, &s->regs[R_ZDMA_CH_WR_ONLY_WORD0], s->cfg.bus_width / 8);
> +    }
> +
> +    while (src_size) {
> +        len = src_size > ARRAY_SIZE(s->buf) ? ARRAY_SIZE(s->buf) : src_size;
> +        if (burst_type == AXI_BURST_FIXED) {
> +            if (len > (s->cfg.bus_width / 8)) {
> +                len = s->cfg.bus_width / 8;
> +            }
> +        }
> +
> +        if (rw_mode == RW_MODE_WO) {
> +            if (len > s->cfg.bus_width / 8) {
> +                len = s->cfg.bus_width / 8;
> +            }
> +        } else {
> +            address_space_rw(s->dma_as, src_addr, s->attr, s->buf, len,
> +                             false);
> +            if (burst_type == AXI_BURST_INCR) {
> +                src_addr += len;
> +            }
> +        }
> +
> +        if (rw_mode != RW_MODE_RO) {
> +            zdma_write_dst(s, s->buf, len);
> +        }
> +
> +        s->regs[R_ZDMA_CH_TOTAL_BYTE] += len;
> +        src_size -= len;
> +
> +        if (src_size == 0) {
> +            ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_DONE, true);
> +        }
> +
> +    }
> +
> +    if (src_intr) {
> +        zdma_src_done(s);
> +    }
> +
> +    /* Load next descriptor.  */
> +    if (ptype == PT_REG || src_cmd == CMD_STOP) {
> +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_CTRL2, EN, 0);
> +        zdma_set_state(s, DISABLED);
> +        return;
> +    }
> +
> +    if (src_cmd == CMD_HALT) {
> +        zdma_set_state(s, PAUSED);
> +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_PAUSE, 1);
> +        zdma_ch_imr_update_irq(s);
> +        return;
> +    }
> +
> +    zdma_update_descr_addr(s, src_type, R_ZDMA_CH_SRC_CUR_DSCR_LSB);
> +}
> +
> +static void zdma_run(XlnxZDMA *s)
> +{
> +    while (s->state == ENABLED && !s->error) {
> +        zdma_load_src_descriptor(s);
> +
> +        if (s->error) {
> +            zdma_set_state(s, DISABLED);
> +        } else {
> +            zdma_process_descr(s);
> +        }
> +    }
> +
> +    zdma_ch_imr_update_irq(s);
> +}
> +
> +static void zdma_update_descr_addr_from_start(XlnxZDMA *s)
> +{
> +    uint64_t src_addr, dst_addr;
> +
> +    src_addr = zdma_get_regaddr64(s, R_ZDMA_CH_SRC_START_LSB);
> +    zdma_put_regaddr64(s, R_ZDMA_CH_SRC_CUR_DSCR_LSB, src_addr);
> +    dst_addr = zdma_get_regaddr64(s, R_ZDMA_CH_DST_START_LSB);
> +    zdma_put_regaddr64(s, R_ZDMA_CH_DST_CUR_DSCR_LSB, dst_addr);
> +    zdma_load_dst_descriptor(s);
> +}
> +
> +static void zdma_ch_ctrlx_postw(RegisterInfo *reg, uint64_t val64)
> +{
> +    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
> +
> +    if (ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL2, EN)) {
> +        s->error = false;
> +
> +        if (s->state == PAUSED &&
> +            ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT)) {
> +            if (ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT_ADDR) == 1) {
> +                zdma_update_descr_addr_from_start(s);
> +            } else {
> +                bool src_type = FIELD_EX32(s->dsc_src.words[3],
> +                                       ZDMA_CH_SRC_DSCR_WORD3, TYPE);
> +                zdma_update_descr_addr(s, src_type,
> +                                          R_ZDMA_CH_SRC_CUR_DSCR_LSB);
> +            }
> +        } else {
> +            zdma_update_descr_addr_from_start(s);
> +        }
> +        zdma_set_state(s, ENABLED);
> +    } else {
> +        /* Leave Paused state?  */
> +        if (s->state == PAUSED &&
> +            ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT)) {
> +            zdma_set_state(s, DISABLED);
> +        }
> +    }
> +
> +    zdma_run(s);
> +}
> +
> +static RegisterAccessInfo zdma_regs_info[] = {
> +    {   .name = "ZDMA_ERR_CTRL",  .addr = A_ZDMA_ERR_CTRL,
> +        .rsvd = 0xfffffffe,
> +    },{ .name = "ZDMA_CH_ISR",  .addr = A_ZDMA_CH_ISR,
> +        .rsvd = 0xfffff000,
> +        .w1c = 0xfff,
> +        .post_write = zdma_ch_isr_postw,
> +    },{ .name = "ZDMA_CH_IMR",  .addr = A_ZDMA_CH_IMR,
> +        .reset = 0xfff,
> +        .rsvd = 0xfffff000,
> +        .ro = 0xfff,
> +    },{ .name = "ZDMA_CH_IEN",  .addr = A_ZDMA_CH_IEN,
> +        .rsvd = 0xfffff000,
> +        .pre_write = zdma_ch_ien_prew,
> +    },{ .name = "ZDMA_CH_IDS",  .addr = A_ZDMA_CH_IDS,
> +        .rsvd = 0xfffff000,
> +        .pre_write = zdma_ch_ids_prew,
> +    },{ .name = "ZDMA_CH_CTRL0",  .addr = A_ZDMA_CH_CTRL0,
> +        .reset = 0x80,
> +        .rsvd = 0xffffff01,
> +        .post_write = zdma_ch_ctrlx_postw,
> +    },{ .name = "ZDMA_CH_CTRL1",  .addr = A_ZDMA_CH_CTRL1,
> +        .reset = 0x3ff,
> +        .rsvd = 0xfffffc00,
> +    },{ .name = "ZDMA_CH_FCI",  .addr = A_ZDMA_CH_FCI,
> +        .rsvd = 0xffffffc0,
> +    },{ .name = "ZDMA_CH_STATUS",  .addr = A_ZDMA_CH_STATUS,
> +        .rsvd = 0xfffffffc,
> +        .ro = 0x3,
> +    },{ .name = "ZDMA_CH_DATA_ATTR",  .addr = A_ZDMA_CH_DATA_ATTR,
> +        .reset = 0x483d20f,
> +        .rsvd = 0xf0000000,
> +    },{ .name = "ZDMA_CH_DSCR_ATTR",  .addr = A_ZDMA_CH_DSCR_ATTR,
> +        .rsvd = 0xfffffe00,
> +    },{ .name = "ZDMA_CH_SRC_DSCR_WORD0",  .addr = A_ZDMA_CH_SRC_DSCR_WORD0,
> +    },{ .name = "ZDMA_CH_SRC_DSCR_WORD1",  .addr = A_ZDMA_CH_SRC_DSCR_WORD1,
> +        .rsvd = 0xfffe0000,
> +    },{ .name = "ZDMA_CH_SRC_DSCR_WORD2",  .addr = A_ZDMA_CH_SRC_DSCR_WORD2,
> +        .rsvd = 0xc0000000,
> +    },{ .name = "ZDMA_CH_SRC_DSCR_WORD3",  .addr = A_ZDMA_CH_SRC_DSCR_WORD3,
> +        .rsvd = 0xffffffe0,
> +    },{ .name = "ZDMA_CH_DST_DSCR_WORD0",  .addr = A_ZDMA_CH_DST_DSCR_WORD0,
> +    },{ .name = "ZDMA_CH_DST_DSCR_WORD1",  .addr = A_ZDMA_CH_DST_DSCR_WORD1,
> +        .rsvd = 0xfffe0000,
> +    },{ .name = "ZDMA_CH_DST_DSCR_WORD2",  .addr = A_ZDMA_CH_DST_DSCR_WORD2,
> +        .rsvd = 0xc0000000,
> +    },{ .name = "ZDMA_CH_DST_DSCR_WORD3",  .addr = A_ZDMA_CH_DST_DSCR_WORD3,
> +        .rsvd = 0xfffffffa,
> +    },{ .name = "ZDMA_CH_WR_ONLY_WORD0",  .addr = A_ZDMA_CH_WR_ONLY_WORD0,
> +    },{ .name = "ZDMA_CH_WR_ONLY_WORD1",  .addr = A_ZDMA_CH_WR_ONLY_WORD1,
> +    },{ .name = "ZDMA_CH_WR_ONLY_WORD2",  .addr = A_ZDMA_CH_WR_ONLY_WORD2,
> +    },{ .name = "ZDMA_CH_WR_ONLY_WORD3",  .addr = A_ZDMA_CH_WR_ONLY_WORD3,
> +    },{ .name = "ZDMA_CH_SRC_START_LSB",  .addr = A_ZDMA_CH_SRC_START_LSB,
> +    },{ .name = "ZDMA_CH_SRC_START_MSB",  .addr = A_ZDMA_CH_SRC_START_MSB,
> +        .rsvd = 0xfffe0000,
> +    },{ .name = "ZDMA_CH_DST_START_LSB",  .addr = A_ZDMA_CH_DST_START_LSB,
> +    },{ .name = "ZDMA_CH_DST_START_MSB",  .addr = A_ZDMA_CH_DST_START_MSB,
> +        .rsvd = 0xfffe0000,
> +    },{ .name = "ZDMA_CH_SRC_CUR_PYLD_LSB",  .addr = A_ZDMA_CH_SRC_CUR_PYLD_LSB,
> +        .ro = 0xffffffff,
> +    },{ .name = "ZDMA_CH_SRC_CUR_PYLD_MSB",  .addr = A_ZDMA_CH_SRC_CUR_PYLD_MSB,
> +        .rsvd = 0xfffe0000,
> +        .ro = 0x1ffff,
> +    },{ .name = "ZDMA_CH_DST_CUR_PYLD_LSB",  .addr = A_ZDMA_CH_DST_CUR_PYLD_LSB,
> +        .ro = 0xffffffff,
> +    },{ .name = "ZDMA_CH_DST_CUR_PYLD_MSB",  .addr = A_ZDMA_CH_DST_CUR_PYLD_MSB,
> +        .rsvd = 0xfffe0000,
> +        .ro = 0x1ffff,
> +    },{ .name = "ZDMA_CH_SRC_CUR_DSCR_LSB",  .addr = A_ZDMA_CH_SRC_CUR_DSCR_LSB,
> +        .ro = 0xffffffff,
> +    },{ .name = "ZDMA_CH_SRC_CUR_DSCR_MSB",  .addr = A_ZDMA_CH_SRC_CUR_DSCR_MSB,
> +        .rsvd = 0xfffe0000,
> +        .ro = 0x1ffff,
> +    },{ .name = "ZDMA_CH_DST_CUR_DSCR_LSB",  .addr = A_ZDMA_CH_DST_CUR_DSCR_LSB,
> +        .ro = 0xffffffff,
> +    },{ .name = "ZDMA_CH_DST_CUR_DSCR_MSB",  .addr = A_ZDMA_CH_DST_CUR_DSCR_MSB,
> +        .rsvd = 0xfffe0000,
> +        .ro = 0x1ffff,
> +    },{ .name = "ZDMA_CH_TOTAL_BYTE",  .addr = A_ZDMA_CH_TOTAL_BYTE,
> +        .w1c = 0xffffffff,
> +    },{ .name = "ZDMA_CH_RATE_CNTL",  .addr = A_ZDMA_CH_RATE_CNTL,
> +        .rsvd = 0xfffff000,
> +    },{ .name = "ZDMA_CH_IRQ_SRC_ACCT",  .addr = A_ZDMA_CH_IRQ_SRC_ACCT,
> +        .rsvd = 0xffffff00,
> +        .ro = 0xff,
> +        .cor = 0xff,
> +    },{ .name = "ZDMA_CH_IRQ_DST_ACCT",  .addr = A_ZDMA_CH_IRQ_DST_ACCT,
> +        .rsvd = 0xffffff00,
> +        .ro = 0xff,
> +        .cor = 0xff,
> +    },{ .name = "ZDMA_CH_DBG0",  .addr = A_ZDMA_CH_DBG0,
> +        .rsvd = 0xfffffe00,
> +        .ro = 0x1ff,
> +    },{ .name = "ZDMA_CH_DBG1",  .addr = A_ZDMA_CH_DBG1,
> +        .rsvd = 0xfffffe00,
> +        .ro = 0x1ff,
> +    },{ .name = "ZDMA_CH_CTRL2",  .addr = A_ZDMA_CH_CTRL2,
> +        .rsvd = 0xfffffffe,
> +        .post_write = zdma_ch_ctrlx_postw,
> +    }
> +};
> +
> +static void zdma_reset(DeviceState *dev)
> +{
> +    XlnxZDMA *s = XLNX_ZDMA(dev);
> +    unsigned int i;
> +
> +    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
> +        register_reset(&s->regs_info[i]);
> +    }
> +
> +    zdma_ch_imr_update_irq(s);
> +}
> +
> +static uint64_t zdma_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    XlnxZDMA *s = XLNX_ZDMA(opaque);
> +    RegisterInfo *r = &s->regs_info[addr / 4];
> +
> +    if (!r->data) {
> +        qemu_log("%s: Decode error: read from %" HWADDR_PRIx "\n",
> +                 object_get_canonical_path(OBJECT(s)),
> +                 addr);
> +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true);
> +        zdma_ch_imr_update_irq(s);
> +        return 0;
> +    }
> +    return register_read(r, ~0, NULL, false);
> +}
> +
> +static void zdma_write(void *opaque, hwaddr addr, uint64_t value,
> +                      unsigned size)
> +{
> +    XlnxZDMA *s = XLNX_ZDMA(opaque);
> +    RegisterInfo *r = &s->regs_info[addr / 4];
> +
> +    if (!r->data) {
> +        qemu_log("%s: Decode error: write to %" HWADDR_PRIx "=%" PRIx64 "\n",
> +                 object_get_canonical_path(OBJECT(s)),
> +                 addr, value);
> +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true);
> +        zdma_ch_imr_update_irq(s);
> +        return;
> +    }
> +    register_write(r, value, ~0, NULL, false);
> +}
> +
> +static const MemoryRegionOps zdma_ops = {
> +    .read = zdma_read,
> +    .write = zdma_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4,
> +    },
> +};
> +
> +static void zdma_realize(DeviceState *dev, Error **errp)
> +{
> +    XlnxZDMA *s = XLNX_ZDMA(dev);
> +    unsigned int i;
> +
> +    for (i = 0; i < ARRAY_SIZE(zdma_regs_info); ++i) {
> +        RegisterInfo *r = &s->regs_info[zdma_regs_info[i].addr / 4];
> +
> +        *r = (RegisterInfo) {
> +            .data = (uint8_t *)&s->regs[
> +                    zdma_regs_info[i].addr / 4],
> +            .data_size = sizeof(uint32_t),
> +            .access = &zdma_regs_info[i],
> +            .opaque = s,
> +        };
> +    }
> +
> +    if (s->dma_mr) {
> +        s->dma_as = g_malloc0(sizeof(AddressSpace));
> +        address_space_init(s->dma_as, s->dma_mr, NULL);
> +    } else {
> +        s->dma_as = &address_space_memory;
> +    }
> +    s->attr = MEMTXATTRS_UNSPECIFIED;
> +}
> +
> +static void zdma_init(Object *obj)
> +{
> +    XlnxZDMA *s = XLNX_ZDMA(obj);
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> +
> +    memory_region_init_io(&s->iomem, obj, &zdma_ops, s,
> +                          TYPE_XLNX_ZDMA, ZDMA_R_MAX * 4);
> +    sysbus_init_mmio(sbd, &s->iomem);
> +    sysbus_init_irq(sbd, &s->irq_zdma_ch_imr);
> +
> +    object_property_add_link(obj, "dma", TYPE_MEMORY_REGION,
> +                             (Object **)&s->dma_mr,
> +                             qdev_prop_allow_set_link_before_realize,
> +                             OBJ_PROP_LINK_UNREF_ON_RELEASE,
> +                             &error_abort);
> +}
> +
> +static const VMStateDescription vmstate_zdma = {
> +    .name = TYPE_XLNX_ZDMA,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32_ARRAY(regs, XlnxZDMA, ZDMA_R_MAX),
> +        VMSTATE_END_OF_LIST(),
> +    }
> +};
> +
> +static Property zdma_props[] = {
> +    DEFINE_PROP_UINT32("bus-width", XlnxZDMA, cfg.bus_width, 64),
> +    DEFINE_PROP_END_OF_LIST(),
> +};
> +
> +static void zdma_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->reset = zdma_reset;
> +    dc->realize = zdma_realize;
> +    dc->props = zdma_props;
> +    dc->vmsd = &vmstate_zdma;
> +}
> +
> +static const TypeInfo zdma_info = {
> +    .name          = TYPE_XLNX_ZDMA,
> +    .parent        = TYPE_SYS_BUS_DEVICE,
> +    .instance_size = sizeof(XlnxZDMA),
> +    .class_init    = zdma_class_init,
> +    .instance_init = zdma_init,
> +};
> +
> +static void zdma_register_types(void)
> +{
> +    type_register_static(&zdma_info);
> +}
> +
> +type_init(zdma_register_types)
> diff --git a/include/hw/dma/xlnx-zdma.h b/include/hw/dma/xlnx-zdma.h
> new file mode 100644
> index 0000000000..0b240b4c3c
> --- /dev/null
> +++ b/include/hw/dma/xlnx-zdma.h
> @@ -0,0 +1,84 @@
> +/*
> + * QEMU model of the ZynqMP generic DMA
> + *
> + * Copyright (c) 2014 Xilinx Inc.
> + * Copyright (c) 2018 FEIMTECH AB
> + *
> + * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>,
> + *            Francisco Iglesias <francisco.iglesias@feimtech.se>
> + *
> + * 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.
> + */
> +
> +#ifndef XLNX_ZDMA_H
> +#define XLNX_ZDMA_H
> +
> +#include "hw/sysbus.h"
> +#include "hw/register.h"
> +#include "sysemu/dma.h"
> +
> +#define ZDMA_R_MAX (0x204 / 4)
> +
> +typedef enum {
> +    DISABLED = 0,
> +    ENABLED = 1,
> +    PAUSED = 2,
> +} XlnxZDMAState;
> +
> +typedef union {
> +    struct {
> +        uint64_t addr;
> +        uint32_t size;
> +        uint32_t attr;
> +    };
> +    uint32_t words[4];
> +} XlnxZDMADescr;
> +
> +typedef struct XlnxZDMA {
> +    SysBusDevice parent_obj;
> +    MemoryRegion iomem;
> +    MemTxAttrs attr;
> +    MemoryRegion *dma_mr;
> +    AddressSpace *dma_as;
> +    qemu_irq irq_zdma_ch_imr;
> +
> +    struct {
> +        uint32_t bus_width;
> +    } cfg;
> +
> +    XlnxZDMAState state;
> +    bool error;
> +
> +    XlnxZDMADescr dsc_src;
> +    XlnxZDMADescr dsc_dst;
> +
> +    uint32_t regs[ZDMA_R_MAX];
> +    RegisterInfo regs_info[ZDMA_R_MAX];
> +
> +    /* We don't model the common bufs. Must be at least 16 bytes
> +       to model write only mode.  */
> +    uint8_t buf[2048];
> +} XlnxZDMA;
> +
> +#define TYPE_XLNX_ZDMA "xlnx.zdma"
> +
> +#define XLNX_ZDMA(obj) \
> +     OBJECT_CHECK(XlnxZDMA, (obj), TYPE_XLNX_ZDMA)
> +
> +#endif /* XLNX_ZDMA_H */
> -- 
> 2.11.0
> 

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [Qemu-devel] [PATCH v1 1/2] xlnx-zdma: Add a model of the Xilinx ZynqMP generic DMA
  2018-04-30 13:27   ` Sai Pavan Boddu
@ 2018-05-02 19:58     ` francisco iglesias
  0 siblings, 0 replies; 7+ messages in thread
From: francisco iglesias @ 2018-05-02 19:58 UTC (permalink / raw)
  To: Sai Pavan Boddu
  Cc: qemu-devel@nongnu.org Developers, Edgar Iglesias, alistair,
	Alistair Francis, Francisco Iglesias, Peter Maydell

On 30 April 2018 at 15:27, Sai Pavan Boddu <sai.pavan.boddu@xilinx.com>
wrote:

> Hi Francisco,
>
> On Sun, Apr 29, 2018 at 02:43:28AM +0200, Francisco Iglesias wrote:
> > Add a model of the generic DMA found on Xilinx ZynqMP.
> >
> > Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
> > Signed-off-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
> > ---
> >  hw/dma/Makefile.objs       |   1 +
> >  hw/dma/xlnx-zdma.c         | 833 ++++++++++++++++++++++++++++++
> +++++++++++++++
> >  include/hw/dma/xlnx-zdma.h |  84 +++++
> >  3 files changed, 918 insertions(+)
> >  create mode 100644 hw/dma/xlnx-zdma.c
> >  create mode 100644 include/hw/dma/xlnx-zdma.h
> >
> > diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs
> > index c2afecbf73..79affecc39 100644
> > --- a/hw/dma/Makefile.objs
> > +++ b/hw/dma/Makefile.objs
> > @@ -10,6 +10,7 @@ common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o
> >  common-obj-$(CONFIG_STP2000) += sparc32_dma.o
> >  obj-$(CONFIG_XLNX_ZYNQMP) += xlnx_dpdma.o
> >  obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx_dpdma.o
> > +common-obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zdma.o
> >
> >  obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o
> >  obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o
> > diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c
> > new file mode 100644
> > index 0000000000..7955a6116d
> > --- /dev/null
> > +++ b/hw/dma/xlnx-zdma.c
> > @@ -0,0 +1,833 @@
> > +/*
> > + * QEMU model of the ZynqMP generic DMA
> > + *
> > + * Copyright (c) 2014 Xilinx Inc.
> > + * Copyright (c) 2018 FEIMTECH AB
> > + *
> > + * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>,
> > + *            Francisco Iglesias <francisco.iglesias@feimtech.se>
> > + *
> > + * 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/dma/xlnx-zdma.h"
> > +#include "qemu/bitops.h"
> > +#include "qemu/log.h"
> > +#include "qapi/error.h"
> > +
> > +#ifndef XLNX_ZDMA_ERR_DEBUG
> > +#define XLNX_ZDMA_ERR_DEBUG 0
> > +#endif
> > +
> > +REG32(ZDMA_ERR_CTRL, 0x0)
> > +    FIELD(ZDMA_ERR_CTRL, APB_ERR_RES, 0, 1)
> > +REG32(ZDMA_CH_ISR, 0x100)
> > +    FIELD(ZDMA_CH_ISR, DMA_PAUSE, 11, 1)
> > +    FIELD(ZDMA_CH_ISR, DMA_DONE, 10, 1)
> > +    FIELD(ZDMA_CH_ISR, AXI_WR_DATA, 9, 1)
> > +    FIELD(ZDMA_CH_ISR, AXI_RD_DATA, 8, 1)
> > +    FIELD(ZDMA_CH_ISR, AXI_RD_DST_DSCR, 7, 1)
> > +    FIELD(ZDMA_CH_ISR, AXI_RD_SRC_DSCR, 6, 1)
> > +    FIELD(ZDMA_CH_ISR, IRQ_DST_ACCT_ERR, 5, 1)
> > +    FIELD(ZDMA_CH_ISR, IRQ_SRC_ACCT_ERR, 4, 1)
> > +    FIELD(ZDMA_CH_ISR, BYTE_CNT_OVRFL, 3, 1)
> > +    FIELD(ZDMA_CH_ISR, DST_DSCR_DONE, 2, 1)
> > +    FIELD(ZDMA_CH_ISR, SRC_DSCR_DONE, 1, 1)
> > +    FIELD(ZDMA_CH_ISR, INV_APB, 0, 1)
> > +REG32(ZDMA_CH_IMR, 0x104)
> > +    FIELD(ZDMA_CH_IMR, DMA_PAUSE, 11, 1)
> > +    FIELD(ZDMA_CH_IMR, DMA_DONE, 10, 1)
> > +    FIELD(ZDMA_CH_IMR, AXI_WR_DATA, 9, 1)
> > +    FIELD(ZDMA_CH_IMR, AXI_RD_DATA, 8, 1)
> > +    FIELD(ZDMA_CH_IMR, AXI_RD_DST_DSCR, 7, 1)
> > +    FIELD(ZDMA_CH_IMR, AXI_RD_SRC_DSCR, 6, 1)
> > +    FIELD(ZDMA_CH_IMR, IRQ_DST_ACCT_ERR, 5, 1)
> > +    FIELD(ZDMA_CH_IMR, IRQ_SRC_ACCT_ERR, 4, 1)
> > +    FIELD(ZDMA_CH_IMR, BYTE_CNT_OVRFL, 3, 1)
> > +    FIELD(ZDMA_CH_IMR, DST_DSCR_DONE, 2, 1)
> > +    FIELD(ZDMA_CH_IMR, SRC_DSCR_DONE, 1, 1)
> > +    FIELD(ZDMA_CH_IMR, INV_APB, 0, 1)
> > +REG32(ZDMA_CH_IEN, 0x108)
> > +    FIELD(ZDMA_CH_IEN, DMA_PAUSE, 11, 1)
> > +    FIELD(ZDMA_CH_IEN, DMA_DONE, 10, 1)
> > +    FIELD(ZDMA_CH_IEN, AXI_WR_DATA, 9, 1)
> > +    FIELD(ZDMA_CH_IEN, AXI_RD_DATA, 8, 1)
> > +    FIELD(ZDMA_CH_IEN, AXI_RD_DST_DSCR, 7, 1)
> > +    FIELD(ZDMA_CH_IEN, AXI_RD_SRC_DSCR, 6, 1)
> > +    FIELD(ZDMA_CH_IEN, IRQ_DST_ACCT_ERR, 5, 1)
> > +    FIELD(ZDMA_CH_IEN, IRQ_SRC_ACCT_ERR, 4, 1)
> > +    FIELD(ZDMA_CH_IEN, BYTE_CNT_OVRFL, 3, 1)
> > +    FIELD(ZDMA_CH_IEN, DST_DSCR_DONE, 2, 1)
> > +    FIELD(ZDMA_CH_IEN, SRC_DSCR_DONE, 1, 1)
> > +    FIELD(ZDMA_CH_IEN, INV_APB, 0, 1)
> > +REG32(ZDMA_CH_IDS, 0x10c)
> > +    FIELD(ZDMA_CH_IDS, DMA_PAUSE, 11, 1)
> > +    FIELD(ZDMA_CH_IDS, DMA_DONE, 10, 1)
> > +    FIELD(ZDMA_CH_IDS, AXI_WR_DATA, 9, 1)
> > +    FIELD(ZDMA_CH_IDS, AXI_RD_DATA, 8, 1)
> > +    FIELD(ZDMA_CH_IDS, AXI_RD_DST_DSCR, 7, 1)
> > +    FIELD(ZDMA_CH_IDS, AXI_RD_SRC_DSCR, 6, 1)
> > +    FIELD(ZDMA_CH_IDS, IRQ_DST_ACCT_ERR, 5, 1)
> > +    FIELD(ZDMA_CH_IDS, IRQ_SRC_ACCT_ERR, 4, 1)
> > +    FIELD(ZDMA_CH_IDS, BYTE_CNT_OVRFL, 3, 1)
> > +    FIELD(ZDMA_CH_IDS, DST_DSCR_DONE, 2, 1)
> > +    FIELD(ZDMA_CH_IDS, SRC_DSCR_DONE, 1, 1)
> > +    FIELD(ZDMA_CH_IDS, INV_APB, 0, 1)
> > +REG32(ZDMA_CH_CTRL0, 0x110)
> > +    FIELD(ZDMA_CH_CTRL0, OVR_FETCH, 7, 1)
> > +    FIELD(ZDMA_CH_CTRL0, POINT_TYPE, 6, 1)
> > +    FIELD(ZDMA_CH_CTRL0, MODE, 4, 2)
> > +    FIELD(ZDMA_CH_CTRL0, RATE_CTRL, 3, 1)
> > +    FIELD(ZDMA_CH_CTRL0, CONT_ADDR, 2, 1)
> > +    FIELD(ZDMA_CH_CTRL0, CONT, 1, 1)
> > +REG32(ZDMA_CH_CTRL1, 0x114)
> > +    FIELD(ZDMA_CH_CTRL1, DST_ISSUE, 5, 5)
> > +    FIELD(ZDMA_CH_CTRL1, SRC_ISSUE, 0, 5)
> > +REG32(ZDMA_CH_FCI, 0x118)
> > +    FIELD(ZDMA_CH_FCI, PROG_CELL_CNT, 2, 2)
> > +    FIELD(ZDMA_CH_FCI, SIDE, 1, 1)
> > +    FIELD(ZDMA_CH_FCI, EN, 0, 1)
> > +REG32(ZDMA_CH_STATUS, 0x11c)
> > +    FIELD(ZDMA_CH_STATUS, STATE, 0, 2)
> > +REG32(ZDMA_CH_DATA_ATTR, 0x120)
> > +    FIELD(ZDMA_CH_DATA_ATTR, ARBURST, 26, 2)
> > +    FIELD(ZDMA_CH_DATA_ATTR, ARCACHE, 22, 4)
> > +    FIELD(ZDMA_CH_DATA_ATTR, ARQOS, 18, 4)
> > +    FIELD(ZDMA_CH_DATA_ATTR, ARLEN, 14, 4)
> > +    FIELD(ZDMA_CH_DATA_ATTR, AWBURST, 12, 2)
> > +    FIELD(ZDMA_CH_DATA_ATTR, AWCACHE, 8, 4)
> > +    FIELD(ZDMA_CH_DATA_ATTR, AWQOS, 4, 4)
> > +    FIELD(ZDMA_CH_DATA_ATTR, AWLEN, 0, 4)
> > +REG32(ZDMA_CH_DSCR_ATTR, 0x124)
> > +    FIELD(ZDMA_CH_DSCR_ATTR, AXCOHRNT, 8, 1)
> > +    FIELD(ZDMA_CH_DSCR_ATTR, AXCACHE, 4, 4)
> > +    FIELD(ZDMA_CH_DSCR_ATTR, AXQOS, 0, 4)
> > +REG32(ZDMA_CH_SRC_DSCR_WORD0, 0x128)
> > +REG32(ZDMA_CH_SRC_DSCR_WORD1, 0x12c)
> > +    FIELD(ZDMA_CH_SRC_DSCR_WORD1, MSB, 0, 17)
> > +REG32(ZDMA_CH_SRC_DSCR_WORD2, 0x130)
> > +    FIELD(ZDMA_CH_SRC_DSCR_WORD2, SIZE, 0, 30)
> > +REG32(ZDMA_CH_SRC_DSCR_WORD3, 0x134)
> > +    FIELD(ZDMA_CH_SRC_DSCR_WORD3, CMD, 3, 2)
> > +    FIELD(ZDMA_CH_SRC_DSCR_WORD3, INTR, 2, 1)
> > +    FIELD(ZDMA_CH_SRC_DSCR_WORD3, TYPE, 1, 1)
> > +    FIELD(ZDMA_CH_SRC_DSCR_WORD3, COHRNT, 0, 1)
> > +REG32(ZDMA_CH_DST_DSCR_WORD0, 0x138)
> > +REG32(ZDMA_CH_DST_DSCR_WORD1, 0x13c)
> > +    FIELD(ZDMA_CH_DST_DSCR_WORD1, MSB, 0, 17)
> > +REG32(ZDMA_CH_DST_DSCR_WORD2, 0x140)
> > +    FIELD(ZDMA_CH_DST_DSCR_WORD2, SIZE, 0, 30)
> > +REG32(ZDMA_CH_DST_DSCR_WORD3, 0x144)
> > +    FIELD(ZDMA_CH_DST_DSCR_WORD3, INTR, 2, 1)
> > +    FIELD(ZDMA_CH_DST_DSCR_WORD3, TYPE, 1, 1)
> > +    FIELD(ZDMA_CH_DST_DSCR_WORD3, COHRNT, 0, 1)
> > +REG32(ZDMA_CH_WR_ONLY_WORD0, 0x148)
> > +REG32(ZDMA_CH_WR_ONLY_WORD1, 0x14c)
> > +REG32(ZDMA_CH_WR_ONLY_WORD2, 0x150)
> > +REG32(ZDMA_CH_WR_ONLY_WORD3, 0x154)
> > +REG32(ZDMA_CH_SRC_START_LSB, 0x158)
> > +REG32(ZDMA_CH_SRC_START_MSB, 0x15c)
> > +    FIELD(ZDMA_CH_SRC_START_MSB, ADDR, 0, 17)
> > +REG32(ZDMA_CH_DST_START_LSB, 0x160)
> > +REG32(ZDMA_CH_DST_START_MSB, 0x164)
> > +    FIELD(ZDMA_CH_DST_START_MSB, ADDR, 0, 17)
> > +REG32(ZDMA_CH_RATE_CTRL, 0x18c)
> > +    FIELD(ZDMA_CH_RATE_CTRL, CNT, 0, 12)
> > +REG32(ZDMA_CH_SRC_CUR_PYLD_LSB, 0x168)
> > +REG32(ZDMA_CH_SRC_CUR_PYLD_MSB, 0x16c)
> > +    FIELD(ZDMA_CH_SRC_CUR_PYLD_MSB, ADDR, 0, 17)
> > +REG32(ZDMA_CH_DST_CUR_PYLD_LSB, 0x170)
> > +REG32(ZDMA_CH_DST_CUR_PYLD_MSB, 0x174)
> > +    FIELD(ZDMA_CH_DST_CUR_PYLD_MSB, ADDR, 0, 17)
> > +REG32(ZDMA_CH_SRC_CUR_DSCR_LSB, 0x178)
> > +REG32(ZDMA_CH_SRC_CUR_DSCR_MSB, 0x17c)
> > +    FIELD(ZDMA_CH_SRC_CUR_DSCR_MSB, ADDR, 0, 17)
> > +REG32(ZDMA_CH_DST_CUR_DSCR_LSB, 0x180)
> > +REG32(ZDMA_CH_DST_CUR_DSCR_MSB, 0x184)
> > +    FIELD(ZDMA_CH_DST_CUR_DSCR_MSB, ADDR, 0, 17)
> > +REG32(ZDMA_CH_TOTAL_BYTE, 0x188)
> > +REG32(ZDMA_CH_RATE_CNTL, 0x18c)
> > +    FIELD(ZDMA_CH_RATE_CNTL, CNT, 0, 12)
> > +REG32(ZDMA_CH_IRQ_SRC_ACCT, 0x190)
> > +    FIELD(ZDMA_CH_IRQ_SRC_ACCT, CNT, 0, 8)
> > +REG32(ZDMA_CH_IRQ_DST_ACCT, 0x194)
> > +    FIELD(ZDMA_CH_IRQ_DST_ACCT, CNT, 0, 8)
> > +REG32(ZDMA_CH_DBG0, 0x198)
> > +    FIELD(ZDMA_CH_DBG0, CMN_BUF_FREE, 0, 9)
> > +REG32(ZDMA_CH_DBG1, 0x19c)
> > +    FIELD(ZDMA_CH_DBG1, CMN_BUF_OCC, 0, 9)
> > +REG32(ZDMA_CH_CTRL2, 0x200)
> > +    FIELD(ZDMA_CH_CTRL2, EN, 0, 1)
> > +
> > +enum {
> > +    PT_REG = 0,
> > +    PT_MEM = 1,
> > +};
> > +
> > +enum {
> > +    CMD_HALT = 1,
> > +    CMD_STOP = 2,
> > +};
> > +
> > +enum {
> > +    RW_MODE_RW = 0,
> > +    RW_MODE_WO = 1,
> > +    RW_MODE_RO = 2,
> > +};
> > +
> > +enum {
> > +    DTYPE_LINEAR = 0,
> > +    DTYPE_LINKED = 1,
> > +};
> > +
> > +enum {
> > +    AXI_BURST_FIXED = 0,
> > +    AXI_BURST_INCR  = 1,
> > +};
> > +
> > +static void zdma_ch_imr_update_irq(XlnxZDMA *s)
> > +{
> > +    bool pending;
> > +
> > +    pending = s->regs[R_ZDMA_CH_ISR] & ~s->regs[R_ZDMA_CH_IMR];
> > +
> > +    qemu_set_irq(s->irq_zdma_ch_imr, pending);
> > +}
> > +
> > +static void zdma_ch_isr_postw(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
> > +    zdma_ch_imr_update_irq(s);
> > +}
> > +
> > +static uint64_t zdma_ch_ien_prew(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
> > +    uint32_t val = val64;
> > +
> > +    s->regs[R_ZDMA_CH_IMR] &= ~val;
> > +    zdma_ch_imr_update_irq(s);
> > +    return 0;
> > +}
> > +
> > +static uint64_t zdma_ch_ids_prew(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
> > +    uint32_t val = val64;
> > +
> > +    s->regs[R_ZDMA_CH_IMR] |= val;
> > +    zdma_ch_imr_update_irq(s);
> > +    return 0;
> > +}
> > +
> > +static void zdma_set_state(XlnxZDMA *s, XlnxZDMAState state)
> > +{
> > +    s->state = state;
> > +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_STATUS, STATE, state);
> > +
> > +    /* Signal error if we have an error condition.  */
> > +    if (s->error) {
> > +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_STATUS, STATE, 3);
> > +    }
> > +}
> > +
> > +static void zdma_src_done(XlnxZDMA *s)
> > +{
> > +    unsigned int cnt;
> > +    cnt = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT);
> > +    cnt++;
> > +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT, cnt);
> > +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, SRC_DSCR_DONE, true);
> > +
> > +    /* Did we overflow?  */
> > +    if (cnt != ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT)) {
> > +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, IRQ_SRC_ACCT_ERR, true);
> > +    }
> > +    zdma_ch_imr_update_irq(s);
> > +}
> > +
> > +static void zdma_dst_done(XlnxZDMA *s)
> > +{
> > +    unsigned int cnt;
> > +    cnt = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT);
> > +    cnt++;
> > +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT, cnt);
> > +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DST_DSCR_DONE, true);
> > +
> > +    /* Did we overflow?  */
> > +    if (cnt != ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT)) {
> > +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, IRQ_DST_ACCT_ERR, true);
> > +    }
> > +    zdma_ch_imr_update_irq(s);
> > +}
> > +
> > +static uint64_t zdma_get_regaddr64(XlnxZDMA *s, unsigned int basereg)
> > +{
> > +    uint64_t addr;
> > +
> > +    addr = s->regs[basereg + 1];
> > +    addr <<= 32;
> > +    addr |= s->regs[basereg];
> > +
> > +    return addr;
> > +}
> > +
> > +static void zdma_put_regaddr64(XlnxZDMA *s, unsigned int basereg,
> uint64_t addr)
> > +{
> > +    s->regs[basereg] = addr;
> > +    s->regs[basereg + 1] = addr >> 32;
> > +}
> > +
> > +static bool zdma_load_descriptor(XlnxZDMA *s, uint64_t addr, void *buf)
> > +{
> > +    /* ZDMA descriptors must be aligned to their own size.  */
> > +    if (addr % sizeof(XlnxZDMADescr)) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "zdma: unaligned descriptor at %" PRIx64,
> > +                      addr);
> > +        memset(buf, 0xdeadbeef, sizeof(XlnxZDMADescr));
> > +        s->error = true;
> > +        return false;
> > +    }
> > +
> > +    address_space_rw(s->dma_as, addr, s->attr,
> > +                     buf, sizeof(XlnxZDMADescr), false);
> > +    return true;
> > +}
> > +
> > +static void zdma_load_src_descriptor(XlnxZDMA *s)
> > +{
> > +    uint64_t src_addr;
> > +    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0,
> POINT_TYPE);
> > +
> > +    if (ptype == PT_REG) {
> > +        memcpy(&s->dsc_src, &s->regs[R_ZDMA_CH_SRC_DSCR_WORD0],
> > +               sizeof(s->dsc_src));
> > +        return;
> > +    }
> > +
> > +    src_addr = zdma_get_regaddr64(s, R_ZDMA_CH_SRC_CUR_DSCR_LSB);
> > +
> > +    if (!zdma_load_descriptor(s, src_addr, &s->dsc_src)) {
> > +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, AXI_RD_SRC_DSCR, true);
> > +    }
> > +}
> > +
> > +static void zdma_load_dst_descriptor(XlnxZDMA *s)
> > +{
> > +    uint64_t dst_addr;
> > +    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0,
> POINT_TYPE);
> > +
> > +    if (ptype == PT_REG) {
> > +        memcpy(&s->dsc_dst, &s->regs[R_ZDMA_CH_DST_DSCR_WORD0],
> > +               sizeof(s->dsc_src));
> > +        return;
> > +    }
> > +
> > +    dst_addr = zdma_get_regaddr64(s, R_ZDMA_CH_DST_CUR_DSCR_LSB);
> > +
> > +    if (!zdma_load_descriptor(s, dst_addr, &s->dsc_dst)) {
> > +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, AXI_RD_DST_DSCR, true);
> > +    }
> > +}
> > +
> > +static uint64_t zdma_update_descr_addr(XlnxZDMA *s, bool type,
> > +                                       unsigned int basereg)
> > +{
> > +    uint64_t addr, next;
> > +
> > +    if (type == DTYPE_LINEAR) {
> > +        next = zdma_get_regaddr64(s, basereg);
> > +        next += sizeof(s->dsc_dst);
> > +        zdma_put_regaddr64(s, basereg, next);
> > +    } else {
> > +        addr = zdma_get_regaddr64(s, basereg);
> > +        addr += sizeof(s->dsc_dst);
> > +        address_space_rw(s->dma_as, addr, s->attr, (void *) &next, 8,
> false);
> > +        zdma_put_regaddr64(s, basereg, next);
> > +    }
> > +    return next;
> > +}
> > +
> > +static void zdma_write_dst(XlnxZDMA *s, uint8_t *buf, uint32_t len)
> > +{
> > +    uint32_t dst_size, dlen;
> > +    bool dst_intr, dst_type;
> > +    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0,
> POINT_TYPE);
> > +    unsigned int rw_mode = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0,
> MODE);
> > +    unsigned int burst_type = ARRAY_FIELD_EX32(s->regs,
> ZDMA_CH_DATA_ATTR,
> > +                                               AWBURST);
> > +
> > +    /* FIXED burst types are only supported in simple dma mode.  */
> > +    if (ptype != PT_REG) {
> > +        burst_type = AXI_BURST_INCR;
> > +    }
> > +
> > +    while (len) {
> > +        dst_size = FIELD_EX32(s->dsc_dst.words[2],
> ZDMA_CH_DST_DSCR_WORD2,
> > +                              SIZE);
> > +        dst_type = FIELD_EX32(s->dsc_dst.words[3],
> ZDMA_CH_DST_DSCR_WORD3,
> > +                              TYPE);
> > +        if (dst_size == 0 && ptype == PT_MEM) {
> > +            uint64_t next;
> > +            next = zdma_update_descr_addr(s, dst_type,
> > +                                          R_ZDMA_CH_DST_CUR_DSCR_LSB);
> > +            zdma_load_descriptor(s, next, &s->dsc_dst);
> > +            dst_size = FIELD_EX32(s->dsc_dst.words[2],
> ZDMA_CH_DST_DSCR_WORD2,
> > +                                  SIZE);
> > +            dst_type = FIELD_EX32(s->dsc_dst.words[3],
> ZDMA_CH_DST_DSCR_WORD3,
> > +                                  TYPE);
> > +        }
> > +
> > +        /* Match what hardware does by ignoring the dst_size and only
> using
> > +         * the src size for Simple register mode.  */
> > +        if (ptype == PT_REG && rw_mode != RW_MODE_WO) {
> > +            dst_size = len;
> > +        }
> > +
> > +        if (dst_size == 0) {
> > +            qemu_log("Can't write to dst\n");
> > +        }
> > +        dst_intr = FIELD_EX32(s->dsc_dst.words[3],
> ZDMA_CH_DST_DSCR_WORD3,
> > +                              INTR);
> > +
> > +        dlen = len > dst_size ? dst_size : len;
> > +        if (burst_type == AXI_BURST_FIXED) {
> > +            if (dlen > (s->cfg.bus_width / 8)) {
> > +                dlen = s->cfg.bus_width / 8;
> > +            }
> > +        }
> > +
> > +        address_space_rw(s->dma_as, s->dsc_dst.addr, s->attr, buf, dlen,
> > +                         true);
> > +        if (burst_type == AXI_BURST_INCR) {
> > +            s->dsc_dst.addr += dlen;
> > +        }
> > +        dst_size -= dlen;
> > +        buf += dlen;
> > +        len -= dlen;
> > +
> > +        if (dst_size == 0 && dst_intr) {
> > +            zdma_dst_done(s);
> > +        }
> > +
> > +        /* Write back to buffered descriptor.  */
> > +        s->dsc_dst.words[2] = FIELD_DP32(s->dsc_dst.words[2],
> > +                                         ZDMA_CH_DST_DSCR_WORD2,
> > +                                         SIZE,
> > +                                         dst_size);
> > +    }
> > +}
> > +
> > +static void zdma_process_descr(XlnxZDMA *s)
> > +{
> > +    uint64_t src_addr;
> > +    uint32_t src_size, len;
> > +    unsigned int src_cmd;
> > +    bool src_intr, src_type;
> > +    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0,
> POINT_TYPE);
> > +    unsigned int rw_mode = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0,
> MODE);
> > +    unsigned int burst_type = ARRAY_FIELD_EX32(s->regs,
> ZDMA_CH_DATA_ATTR,
> > +                                               ARBURST);
> > +
> > +    src_addr = s->dsc_src.addr;
> > +    src_size = FIELD_EX32(s->dsc_src.words[2], ZDMA_CH_SRC_DSCR_WORD2,
> SIZE);
> > +    src_cmd = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3,
> CMD);
> > +    src_type = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3,
> TYPE);
> > +    src_intr = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3,
> INTR);
> > +
> > +    /* FIXED burst types and non-rw modes are only supported in
> > +     * simple dma mode.
> > +     */
> > +    if (ptype != PT_REG) {
> > +        if (rw_mode != RW_MODE_RW) {
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                          "zDMA: rw-mode=%d but not simple DMA mode.\n",
> > +                          rw_mode);
> > +        }
> > +        if (burst_type != AXI_BURST_INCR) {
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                          "zDMA: burst_type=%d but not simple DMA
> mode.\n",
> > +                          burst_type);
> > +        }
> > +        burst_type = AXI_BURST_INCR;
> > +        rw_mode = RW_MODE_RW;
> > +    }
> > +
> > +    if (rw_mode == RW_MODE_WO) {
> > +        /* In Simple DMA Write-Only, we need to push DST size bytes
> > +         * regardless of what SRC size is set to.  */
> > +        src_size = FIELD_EX32(s->dsc_dst.words[2],
> ZDMA_CH_DST_DSCR_WORD2,
> > +                              SIZE);
> > +        memcpy(s->buf, &s->regs[R_ZDMA_CH_WR_ONLY_WORD0],
> s->cfg.bus_width / 8);
> > +    }
> > +
> > +    while (src_size) {
> > +        len = src_size > ARRAY_SIZE(s->buf) ? ARRAY_SIZE(s->buf) :
> src_size;
> > +        if (burst_type == AXI_BURST_FIXED) {
> > +            if (len > (s->cfg.bus_width / 8)) {
> > +                len = s->cfg.bus_width / 8;
> > +            }
> > +        }
> > +
> > +        if (rw_mode == RW_MODE_WO) {
> > +            if (len > s->cfg.bus_width / 8) {
> > +                len = s->cfg.bus_width / 8;
> > +            }
> > +        } else {
> > +            address_space_rw(s->dma_as, src_addr, s->attr, s->buf, len,
> > +                             false);
> > +            if (burst_type == AXI_BURST_INCR) {
> > +                src_addr += len;
> > +            }
> > +        }
> > +
> > +        if (rw_mode != RW_MODE_RO) {
> > +            zdma_write_dst(s, s->buf, len);
> > +        }
> > +
> > +        s->regs[R_ZDMA_CH_TOTAL_BYTE] += len;
> > +        src_size -= len;
> > +
> > +        if (src_size == 0) {
> > +            ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_DONE, true);
> > +        }
> > +
> > +    }
> > +
> > +    if (src_intr) {
> > +        zdma_src_done(s);
> > +    }
> > +
> > +    /* Load next descriptor.  */
> > +    if (ptype == PT_REG || src_cmd == CMD_STOP) {
> > +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_CTRL2, EN, 0);
> > +        zdma_set_state(s, DISABLED);
> > +        return;
> > +    }
> > +
> > +    if (src_cmd == CMD_HALT) {
> > +        zdma_set_state(s, PAUSED);
> > +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_PAUSE, 1);
> > +        zdma_ch_imr_update_irq(s);
> > +        return;
> > +    }
> > +
> > +    zdma_update_descr_addr(s, src_type, R_ZDMA_CH_SRC_CUR_DSCR_LSB);
> > +}
> > +
> > +static void zdma_run(XlnxZDMA *s)
> > +{
> > +    while (s->state == ENABLED && !s->error) {
> > +        zdma_load_src_descriptor(s);
> > +
> > +        if (s->error) {
> > +            zdma_set_state(s, DISABLED);
> > +        } else {
> > +            zdma_process_descr(s);
> > +        }
> > +    }
> > +
> > +    zdma_ch_imr_update_irq(s);
> > +}
> > +
> > +static void zdma_update_descr_addr_from_start(XlnxZDMA *s)
> > +{
> > +    uint64_t src_addr, dst_addr;
> > +
> > +    src_addr = zdma_get_regaddr64(s, R_ZDMA_CH_SRC_START_LSB);
> > +    zdma_put_regaddr64(s, R_ZDMA_CH_SRC_CUR_DSCR_LSB, src_addr);
> > +    dst_addr = zdma_get_regaddr64(s, R_ZDMA_CH_DST_START_LSB);
> > +    zdma_put_regaddr64(s, R_ZDMA_CH_DST_CUR_DSCR_LSB, dst_addr);
> > +    zdma_load_dst_descriptor(s);
> > +}
> > +
> > +static void zdma_ch_ctrlx_postw(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL2, EN)) {
> > +        s->error = false;
> > +
> > +        if (s->state == PAUSED &&
> > +            ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT)) {
> We can clear the CONT, if dma is beeing unpaused.
>
> > +            if (ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT_ADDR) ==
> 1) {
> > +                zdma_update_descr_addr_from_start(s);
> > +            } else {
> > +                bool src_type = FIELD_EX32(s->dsc_src.words[3],
> > +                                       ZDMA_CH_SRC_DSCR_WORD3, TYPE);
> > +                zdma_update_descr_addr(s, src_type,
> > +                                          R_ZDMA_CH_SRC_CUR_DSCR_LSB);
> > +            }
> > +        } else {
> > +            zdma_update_descr_addr_from_start(s);
> Need to look more here, looks like we are running down and processing
> descriptors when channel is paused.
>
> Thanks
> Sai Pavan
>

Hi Sai,

I see what you mean and will attempt to correct this in the next version
(v2)!

Thank you,
Best regards,
Francisco Iglesias



>
> > +        }
> > +        zdma_set_state(s, ENABLED);
> > +    } else {
> > +        /* Leave Paused state?  */
> > +        if (s->state == PAUSED &&
> > +            ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT)) {
> > +            zdma_set_state(s, DISABLED);
> > +        }
> > +    }
> > +
> > +    zdma_run(s);
> > +}
> > +
> > +static RegisterAccessInfo zdma_regs_info[] = {
> > +    {   .name = "ZDMA_ERR_CTRL",  .addr = A_ZDMA_ERR_CTRL,
> > +        .rsvd = 0xfffffffe,
> > +    },{ .name = "ZDMA_CH_ISR",  .addr = A_ZDMA_CH_ISR,
> > +        .rsvd = 0xfffff000,
> > +        .w1c = 0xfff,
> > +        .post_write = zdma_ch_isr_postw,
> > +    },{ .name = "ZDMA_CH_IMR",  .addr = A_ZDMA_CH_IMR,
> > +        .reset = 0xfff,
> > +        .rsvd = 0xfffff000,
> > +        .ro = 0xfff,
> > +    },{ .name = "ZDMA_CH_IEN",  .addr = A_ZDMA_CH_IEN,
> > +        .rsvd = 0xfffff000,
> > +        .pre_write = zdma_ch_ien_prew,
> > +    },{ .name = "ZDMA_CH_IDS",  .addr = A_ZDMA_CH_IDS,
> > +        .rsvd = 0xfffff000,
> > +        .pre_write = zdma_ch_ids_prew,
> > +    },{ .name = "ZDMA_CH_CTRL0",  .addr = A_ZDMA_CH_CTRL0,
> > +        .reset = 0x80,
> > +        .rsvd = 0xffffff01,
> > +        .post_write = zdma_ch_ctrlx_postw,
> > +    },{ .name = "ZDMA_CH_CTRL1",  .addr = A_ZDMA_CH_CTRL1,
> > +        .reset = 0x3ff,
> > +        .rsvd = 0xfffffc00,
> > +    },{ .name = "ZDMA_CH_FCI",  .addr = A_ZDMA_CH_FCI,
> > +        .rsvd = 0xffffffc0,
> > +    },{ .name = "ZDMA_CH_STATUS",  .addr = A_ZDMA_CH_STATUS,
> > +        .rsvd = 0xfffffffc,
> > +        .ro = 0x3,
> > +    },{ .name = "ZDMA_CH_DATA_ATTR",  .addr = A_ZDMA_CH_DATA_ATTR,
> > +        .reset = 0x483d20f,
> > +        .rsvd = 0xf0000000,
> > +    },{ .name = "ZDMA_CH_DSCR_ATTR",  .addr = A_ZDMA_CH_DSCR_ATTR,
> > +        .rsvd = 0xfffffe00,
> > +    },{ .name = "ZDMA_CH_SRC_DSCR_WORD0",  .addr =
> A_ZDMA_CH_SRC_DSCR_WORD0,
> > +    },{ .name = "ZDMA_CH_SRC_DSCR_WORD1",  .addr =
> A_ZDMA_CH_SRC_DSCR_WORD1,
> > +        .rsvd = 0xfffe0000,
> > +    },{ .name = "ZDMA_CH_SRC_DSCR_WORD2",  .addr =
> A_ZDMA_CH_SRC_DSCR_WORD2,
> > +        .rsvd = 0xc0000000,
> > +    },{ .name = "ZDMA_CH_SRC_DSCR_WORD3",  .addr =
> A_ZDMA_CH_SRC_DSCR_WORD3,
> > +        .rsvd = 0xffffffe0,
> > +    },{ .name = "ZDMA_CH_DST_DSCR_WORD0",  .addr =
> A_ZDMA_CH_DST_DSCR_WORD0,
> > +    },{ .name = "ZDMA_CH_DST_DSCR_WORD1",  .addr =
> A_ZDMA_CH_DST_DSCR_WORD1,
> > +        .rsvd = 0xfffe0000,
> > +    },{ .name = "ZDMA_CH_DST_DSCR_WORD2",  .addr =
> A_ZDMA_CH_DST_DSCR_WORD2,
> > +        .rsvd = 0xc0000000,
> > +    },{ .name = "ZDMA_CH_DST_DSCR_WORD3",  .addr =
> A_ZDMA_CH_DST_DSCR_WORD3,
> > +        .rsvd = 0xfffffffa,
> > +    },{ .name = "ZDMA_CH_WR_ONLY_WORD0",  .addr =
> A_ZDMA_CH_WR_ONLY_WORD0,
> > +    },{ .name = "ZDMA_CH_WR_ONLY_WORD1",  .addr =
> A_ZDMA_CH_WR_ONLY_WORD1,
> > +    },{ .name = "ZDMA_CH_WR_ONLY_WORD2",  .addr =
> A_ZDMA_CH_WR_ONLY_WORD2,
> > +    },{ .name = "ZDMA_CH_WR_ONLY_WORD3",  .addr =
> A_ZDMA_CH_WR_ONLY_WORD3,
> > +    },{ .name = "ZDMA_CH_SRC_START_LSB",  .addr =
> A_ZDMA_CH_SRC_START_LSB,
> > +    },{ .name = "ZDMA_CH_SRC_START_MSB",  .addr =
> A_ZDMA_CH_SRC_START_MSB,
> > +        .rsvd = 0xfffe0000,
> > +    },{ .name = "ZDMA_CH_DST_START_LSB",  .addr =
> A_ZDMA_CH_DST_START_LSB,
> > +    },{ .name = "ZDMA_CH_DST_START_MSB",  .addr =
> A_ZDMA_CH_DST_START_MSB,
> > +        .rsvd = 0xfffe0000,
> > +    },{ .name = "ZDMA_CH_SRC_CUR_PYLD_LSB",  .addr =
> A_ZDMA_CH_SRC_CUR_PYLD_LSB,
> > +        .ro = 0xffffffff,
> > +    },{ .name = "ZDMA_CH_SRC_CUR_PYLD_MSB",  .addr =
> A_ZDMA_CH_SRC_CUR_PYLD_MSB,
> > +        .rsvd = 0xfffe0000,
> > +        .ro = 0x1ffff,
> > +    },{ .name = "ZDMA_CH_DST_CUR_PYLD_LSB",  .addr =
> A_ZDMA_CH_DST_CUR_PYLD_LSB,
> > +        .ro = 0xffffffff,
> > +    },{ .name = "ZDMA_CH_DST_CUR_PYLD_MSB",  .addr =
> A_ZDMA_CH_DST_CUR_PYLD_MSB,
> > +        .rsvd = 0xfffe0000,
> > +        .ro = 0x1ffff,
> > +    },{ .name = "ZDMA_CH_SRC_CUR_DSCR_LSB",  .addr =
> A_ZDMA_CH_SRC_CUR_DSCR_LSB,
> > +        .ro = 0xffffffff,
> > +    },{ .name = "ZDMA_CH_SRC_CUR_DSCR_MSB",  .addr =
> A_ZDMA_CH_SRC_CUR_DSCR_MSB,
> > +        .rsvd = 0xfffe0000,
> > +        .ro = 0x1ffff,
> > +    },{ .name = "ZDMA_CH_DST_CUR_DSCR_LSB",  .addr =
> A_ZDMA_CH_DST_CUR_DSCR_LSB,
> > +        .ro = 0xffffffff,
> > +    },{ .name = "ZDMA_CH_DST_CUR_DSCR_MSB",  .addr =
> A_ZDMA_CH_DST_CUR_DSCR_MSB,
> > +        .rsvd = 0xfffe0000,
> > +        .ro = 0x1ffff,
> > +    },{ .name = "ZDMA_CH_TOTAL_BYTE",  .addr = A_ZDMA_CH_TOTAL_BYTE,
> > +        .w1c = 0xffffffff,
> > +    },{ .name = "ZDMA_CH_RATE_CNTL",  .addr = A_ZDMA_CH_RATE_CNTL,
> > +        .rsvd = 0xfffff000,
> > +    },{ .name = "ZDMA_CH_IRQ_SRC_ACCT",  .addr = A_ZDMA_CH_IRQ_SRC_ACCT,
> > +        .rsvd = 0xffffff00,
> > +        .ro = 0xff,
> > +        .cor = 0xff,
> > +    },{ .name = "ZDMA_CH_IRQ_DST_ACCT",  .addr = A_ZDMA_CH_IRQ_DST_ACCT,
> > +        .rsvd = 0xffffff00,
> > +        .ro = 0xff,
> > +        .cor = 0xff,
> > +    },{ .name = "ZDMA_CH_DBG0",  .addr = A_ZDMA_CH_DBG0,
> > +        .rsvd = 0xfffffe00,
> > +        .ro = 0x1ff,
> > +    },{ .name = "ZDMA_CH_DBG1",  .addr = A_ZDMA_CH_DBG1,
> > +        .rsvd = 0xfffffe00,
> > +        .ro = 0x1ff,
> > +    },{ .name = "ZDMA_CH_CTRL2",  .addr = A_ZDMA_CH_CTRL2,
> > +        .rsvd = 0xfffffffe,
> > +        .post_write = zdma_ch_ctrlx_postw,
> > +    }
> > +};
> > +
> > +static void zdma_reset(DeviceState *dev)
> > +{
> > +    XlnxZDMA *s = XLNX_ZDMA(dev);
> > +    unsigned int i;
> > +
> > +    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
> > +        register_reset(&s->regs_info[i]);
> > +    }
> > +
> > +    zdma_ch_imr_update_irq(s);
> > +}
> > +
> > +static uint64_t zdma_read(void *opaque, hwaddr addr, unsigned size)
> > +{
> > +    XlnxZDMA *s = XLNX_ZDMA(opaque);
> > +    RegisterInfo *r = &s->regs_info[addr / 4];
> > +
> > +    if (!r->data) {
> > +        qemu_log("%s: Decode error: read from %" HWADDR_PRIx "\n",
> > +                 object_get_canonical_path(OBJECT(s)),
> > +                 addr);
> > +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true);
> > +        zdma_ch_imr_update_irq(s);
> > +        return 0;
> > +    }
> > +    return register_read(r, ~0, NULL, false);
> > +}
> > +
> > +static void zdma_write(void *opaque, hwaddr addr, uint64_t value,
> > +                      unsigned size)
> > +{
> > +    XlnxZDMA *s = XLNX_ZDMA(opaque);
> > +    RegisterInfo *r = &s->regs_info[addr / 4];
> > +
> > +    if (!r->data) {
> > +        qemu_log("%s: Decode error: write to %" HWADDR_PRIx "=%" PRIx64
> "\n",
> > +                 object_get_canonical_path(OBJECT(s)),
> > +                 addr, value);
> > +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true);
> > +        zdma_ch_imr_update_irq(s);
> > +        return;
> > +    }
> > +    register_write(r, value, ~0, NULL, false);
> > +}
> > +
> > +static const MemoryRegionOps zdma_ops = {
> > +    .read = zdma_read,
> > +    .write = zdma_write,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +    .valid = {
> > +        .min_access_size = 4,
> > +        .max_access_size = 4,
> > +    },
> > +};
> > +
> > +static void zdma_realize(DeviceState *dev, Error **errp)
> > +{
> > +    XlnxZDMA *s = XLNX_ZDMA(dev);
> > +    unsigned int i;
> > +
> > +    for (i = 0; i < ARRAY_SIZE(zdma_regs_info); ++i) {
> > +        RegisterInfo *r = &s->regs_info[zdma_regs_info[i].addr / 4];
> > +
> > +        *r = (RegisterInfo) {
> > +            .data = (uint8_t *)&s->regs[
> > +                    zdma_regs_info[i].addr / 4],
> > +            .data_size = sizeof(uint32_t),
> > +            .access = &zdma_regs_info[i],
> > +            .opaque = s,
> > +        };
> > +    }
> > +
> > +    if (s->dma_mr) {
> > +        s->dma_as = g_malloc0(sizeof(AddressSpace));
> > +        address_space_init(s->dma_as, s->dma_mr, NULL);
> > +    } else {
> > +        s->dma_as = &address_space_memory;
> > +    }
> > +    s->attr = MEMTXATTRS_UNSPECIFIED;
> > +}
> > +
> > +static void zdma_init(Object *obj)
> > +{
> > +    XlnxZDMA *s = XLNX_ZDMA(obj);
> > +    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> > +
> > +    memory_region_init_io(&s->iomem, obj, &zdma_ops, s,
> > +                          TYPE_XLNX_ZDMA, ZDMA_R_MAX * 4);
> > +    sysbus_init_mmio(sbd, &s->iomem);
> > +    sysbus_init_irq(sbd, &s->irq_zdma_ch_imr);
> > +
> > +    object_property_add_link(obj, "dma", TYPE_MEMORY_REGION,
> > +                             (Object **)&s->dma_mr,
> > +                             qdev_prop_allow_set_link_before_realize,
> > +                             OBJ_PROP_LINK_UNREF_ON_RELEASE,
> > +                             &error_abort);
> > +}
> > +
> > +static const VMStateDescription vmstate_zdma = {
> > +    .name = TYPE_XLNX_ZDMA,
> > +    .version_id = 1,
> > +    .minimum_version_id = 1,
> > +    .minimum_version_id_old = 1,
> > +    .fields = (VMStateField[]) {
> > +        VMSTATE_UINT32_ARRAY(regs, XlnxZDMA, ZDMA_R_MAX),
> > +        VMSTATE_END_OF_LIST(),
> > +    }
> > +};
> > +
> > +static Property zdma_props[] = {
> > +    DEFINE_PROP_UINT32("bus-width", XlnxZDMA, cfg.bus_width, 64),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static void zdma_class_init(ObjectClass *klass, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(klass);
> > +
> > +    dc->reset = zdma_reset;
> > +    dc->realize = zdma_realize;
> > +    dc->props = zdma_props;
> > +    dc->vmsd = &vmstate_zdma;
> > +}
> > +
> > +static const TypeInfo zdma_info = {
> > +    .name          = TYPE_XLNX_ZDMA,
> > +    .parent        = TYPE_SYS_BUS_DEVICE,
> > +    .instance_size = sizeof(XlnxZDMA),
> > +    .class_init    = zdma_class_init,
> > +    .instance_init = zdma_init,
> > +};
> > +
> > +static void zdma_register_types(void)
> > +{
> > +    type_register_static(&zdma_info);
> > +}
> > +
> > +type_init(zdma_register_types)
> > diff --git a/include/hw/dma/xlnx-zdma.h b/include/hw/dma/xlnx-zdma.h
> > new file mode 100644
> > index 0000000000..0b240b4c3c
> > --- /dev/null
> > +++ b/include/hw/dma/xlnx-zdma.h
> > @@ -0,0 +1,84 @@
> > +/*
> > + * QEMU model of the ZynqMP generic DMA
> > + *
> > + * Copyright (c) 2014 Xilinx Inc.
> > + * Copyright (c) 2018 FEIMTECH AB
> > + *
> > + * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>,
> > + *            Francisco Iglesias <francisco.iglesias@feimtech.se>
> > + *
> > + * 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.
> > + */
> > +
> > +#ifndef XLNX_ZDMA_H
> > +#define XLNX_ZDMA_H
> > +
> > +#include "hw/sysbus.h"
> > +#include "hw/register.h"
> > +#include "sysemu/dma.h"
> > +
> > +#define ZDMA_R_MAX (0x204 / 4)
> > +
> > +typedef enum {
> > +    DISABLED = 0,
> > +    ENABLED = 1,
> > +    PAUSED = 2,
> > +} XlnxZDMAState;
> > +
> > +typedef union {
> > +    struct {
> > +        uint64_t addr;
> > +        uint32_t size;
> > +        uint32_t attr;
> > +    };
> > +    uint32_t words[4];
> > +} XlnxZDMADescr;
> > +
> > +typedef struct XlnxZDMA {
> > +    SysBusDevice parent_obj;
> > +    MemoryRegion iomem;
> > +    MemTxAttrs attr;
> > +    MemoryRegion *dma_mr;
> > +    AddressSpace *dma_as;
> > +    qemu_irq irq_zdma_ch_imr;
> > +
> > +    struct {
> > +        uint32_t bus_width;
> > +    } cfg;
> > +
> > +    XlnxZDMAState state;
> > +    bool error;
> > +
> > +    XlnxZDMADescr dsc_src;
> > +    XlnxZDMADescr dsc_dst;
> > +
> > +    uint32_t regs[ZDMA_R_MAX];
> > +    RegisterInfo regs_info[ZDMA_R_MAX];
> > +
> > +    /* We don't model the common bufs. Must be at least 16 bytes
> > +       to model write only mode.  */
> > +    uint8_t buf[2048];
> > +} XlnxZDMA;
> > +
> > +#define TYPE_XLNX_ZDMA "xlnx.zdma"
> > +
> > +#define XLNX_ZDMA(obj) \
> > +     OBJECT_CHECK(XlnxZDMA, (obj), TYPE_XLNX_ZDMA)
> > +
> > +#endif /* XLNX_ZDMA_H */
> > --
> > 2.11.0
> >
>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [Qemu-devel] [PATCH v1 1/2] xlnx-zdma: Add a model of the Xilinx ZynqMP generic DMA
  2018-04-30 13:42   ` Edgar E. Iglesias
@ 2018-05-02 19:58     ` francisco iglesias
  0 siblings, 0 replies; 7+ messages in thread
From: francisco iglesias @ 2018-05-02 19:58 UTC (permalink / raw)
  To: Edgar E. Iglesias
  Cc: qemu-devel@nongnu.org Developers, Edgar Iglesias,
	Sai Pavan Boddu, alistair, Alistair Francis, Francisco Iglesias,
	Peter Maydell

On 30 April 2018 at 15:42, Edgar E. Iglesias <edgar.iglesias@xilinx.com>
wrote:

> On Sun, Apr 29, 2018 at 02:43:28AM +0200, Francisco Iglesias wrote:
> > Add a model of the generic DMA found on Xilinx ZynqMP.
>
>
> Hi Francisco,
>
> I think we need to look at the VMStateDescription, some state
> seems to be missing...
>
> Cheers,
> Edgar
>

Hi Edgar,

I'll look into this an attempt to correct it in v2!

Thank you,
Best regards,
Francisco Iglesias


>
>
> >
> > Signed-off-by: Francisco Iglesias <frasse.iglesias@gmail.com>
> > Signed-off-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com>
> > ---
> >  hw/dma/Makefile.objs       |   1 +
> >  hw/dma/xlnx-zdma.c         | 833 ++++++++++++++++++++++++++++++
> +++++++++++++++
> >  include/hw/dma/xlnx-zdma.h |  84 +++++
> >  3 files changed, 918 insertions(+)
> >  create mode 100644 hw/dma/xlnx-zdma.c
> >  create mode 100644 include/hw/dma/xlnx-zdma.h
> >
> > diff --git a/hw/dma/Makefile.objs b/hw/dma/Makefile.objs
> > index c2afecbf73..79affecc39 100644
> > --- a/hw/dma/Makefile.objs
> > +++ b/hw/dma/Makefile.objs
> > @@ -10,6 +10,7 @@ common-obj-$(CONFIG_ETRAXFS) += etraxfs_dma.o
> >  common-obj-$(CONFIG_STP2000) += sparc32_dma.o
> >  obj-$(CONFIG_XLNX_ZYNQMP) += xlnx_dpdma.o
> >  obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx_dpdma.o
> > +common-obj-$(CONFIG_XLNX_ZYNQMP_ARM) += xlnx-zdma.o
> >
> >  obj-$(CONFIG_OMAP) += omap_dma.o soc_dma.o
> >  obj-$(CONFIG_PXA2XX) += pxa2xx_dma.o
> > diff --git a/hw/dma/xlnx-zdma.c b/hw/dma/xlnx-zdma.c
> > new file mode 100644
> > index 0000000000..7955a6116d
> > --- /dev/null
> > +++ b/hw/dma/xlnx-zdma.c
> > @@ -0,0 +1,833 @@
> > +/*
> > + * QEMU model of the ZynqMP generic DMA
> > + *
> > + * Copyright (c) 2014 Xilinx Inc.
> > + * Copyright (c) 2018 FEIMTECH AB
> > + *
> > + * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>,
> > + *            Francisco Iglesias <francisco.iglesias@feimtech.se>
> > + *
> > + * 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/dma/xlnx-zdma.h"
> > +#include "qemu/bitops.h"
> > +#include "qemu/log.h"
> > +#include "qapi/error.h"
> > +
> > +#ifndef XLNX_ZDMA_ERR_DEBUG
> > +#define XLNX_ZDMA_ERR_DEBUG 0
> > +#endif
> > +
> > +REG32(ZDMA_ERR_CTRL, 0x0)
> > +    FIELD(ZDMA_ERR_CTRL, APB_ERR_RES, 0, 1)
> > +REG32(ZDMA_CH_ISR, 0x100)
> > +    FIELD(ZDMA_CH_ISR, DMA_PAUSE, 11, 1)
> > +    FIELD(ZDMA_CH_ISR, DMA_DONE, 10, 1)
> > +    FIELD(ZDMA_CH_ISR, AXI_WR_DATA, 9, 1)
> > +    FIELD(ZDMA_CH_ISR, AXI_RD_DATA, 8, 1)
> > +    FIELD(ZDMA_CH_ISR, AXI_RD_DST_DSCR, 7, 1)
> > +    FIELD(ZDMA_CH_ISR, AXI_RD_SRC_DSCR, 6, 1)
> > +    FIELD(ZDMA_CH_ISR, IRQ_DST_ACCT_ERR, 5, 1)
> > +    FIELD(ZDMA_CH_ISR, IRQ_SRC_ACCT_ERR, 4, 1)
> > +    FIELD(ZDMA_CH_ISR, BYTE_CNT_OVRFL, 3, 1)
> > +    FIELD(ZDMA_CH_ISR, DST_DSCR_DONE, 2, 1)
> > +    FIELD(ZDMA_CH_ISR, SRC_DSCR_DONE, 1, 1)
> > +    FIELD(ZDMA_CH_ISR, INV_APB, 0, 1)
> > +REG32(ZDMA_CH_IMR, 0x104)
> > +    FIELD(ZDMA_CH_IMR, DMA_PAUSE, 11, 1)
> > +    FIELD(ZDMA_CH_IMR, DMA_DONE, 10, 1)
> > +    FIELD(ZDMA_CH_IMR, AXI_WR_DATA, 9, 1)
> > +    FIELD(ZDMA_CH_IMR, AXI_RD_DATA, 8, 1)
> > +    FIELD(ZDMA_CH_IMR, AXI_RD_DST_DSCR, 7, 1)
> > +    FIELD(ZDMA_CH_IMR, AXI_RD_SRC_DSCR, 6, 1)
> > +    FIELD(ZDMA_CH_IMR, IRQ_DST_ACCT_ERR, 5, 1)
> > +    FIELD(ZDMA_CH_IMR, IRQ_SRC_ACCT_ERR, 4, 1)
> > +    FIELD(ZDMA_CH_IMR, BYTE_CNT_OVRFL, 3, 1)
> > +    FIELD(ZDMA_CH_IMR, DST_DSCR_DONE, 2, 1)
> > +    FIELD(ZDMA_CH_IMR, SRC_DSCR_DONE, 1, 1)
> > +    FIELD(ZDMA_CH_IMR, INV_APB, 0, 1)
> > +REG32(ZDMA_CH_IEN, 0x108)
> > +    FIELD(ZDMA_CH_IEN, DMA_PAUSE, 11, 1)
> > +    FIELD(ZDMA_CH_IEN, DMA_DONE, 10, 1)
> > +    FIELD(ZDMA_CH_IEN, AXI_WR_DATA, 9, 1)
> > +    FIELD(ZDMA_CH_IEN, AXI_RD_DATA, 8, 1)
> > +    FIELD(ZDMA_CH_IEN, AXI_RD_DST_DSCR, 7, 1)
> > +    FIELD(ZDMA_CH_IEN, AXI_RD_SRC_DSCR, 6, 1)
> > +    FIELD(ZDMA_CH_IEN, IRQ_DST_ACCT_ERR, 5, 1)
> > +    FIELD(ZDMA_CH_IEN, IRQ_SRC_ACCT_ERR, 4, 1)
> > +    FIELD(ZDMA_CH_IEN, BYTE_CNT_OVRFL, 3, 1)
> > +    FIELD(ZDMA_CH_IEN, DST_DSCR_DONE, 2, 1)
> > +    FIELD(ZDMA_CH_IEN, SRC_DSCR_DONE, 1, 1)
> > +    FIELD(ZDMA_CH_IEN, INV_APB, 0, 1)
> > +REG32(ZDMA_CH_IDS, 0x10c)
> > +    FIELD(ZDMA_CH_IDS, DMA_PAUSE, 11, 1)
> > +    FIELD(ZDMA_CH_IDS, DMA_DONE, 10, 1)
> > +    FIELD(ZDMA_CH_IDS, AXI_WR_DATA, 9, 1)
> > +    FIELD(ZDMA_CH_IDS, AXI_RD_DATA, 8, 1)
> > +    FIELD(ZDMA_CH_IDS, AXI_RD_DST_DSCR, 7, 1)
> > +    FIELD(ZDMA_CH_IDS, AXI_RD_SRC_DSCR, 6, 1)
> > +    FIELD(ZDMA_CH_IDS, IRQ_DST_ACCT_ERR, 5, 1)
> > +    FIELD(ZDMA_CH_IDS, IRQ_SRC_ACCT_ERR, 4, 1)
> > +    FIELD(ZDMA_CH_IDS, BYTE_CNT_OVRFL, 3, 1)
> > +    FIELD(ZDMA_CH_IDS, DST_DSCR_DONE, 2, 1)
> > +    FIELD(ZDMA_CH_IDS, SRC_DSCR_DONE, 1, 1)
> > +    FIELD(ZDMA_CH_IDS, INV_APB, 0, 1)
> > +REG32(ZDMA_CH_CTRL0, 0x110)
> > +    FIELD(ZDMA_CH_CTRL0, OVR_FETCH, 7, 1)
> > +    FIELD(ZDMA_CH_CTRL0, POINT_TYPE, 6, 1)
> > +    FIELD(ZDMA_CH_CTRL0, MODE, 4, 2)
> > +    FIELD(ZDMA_CH_CTRL0, RATE_CTRL, 3, 1)
> > +    FIELD(ZDMA_CH_CTRL0, CONT_ADDR, 2, 1)
> > +    FIELD(ZDMA_CH_CTRL0, CONT, 1, 1)
> > +REG32(ZDMA_CH_CTRL1, 0x114)
> > +    FIELD(ZDMA_CH_CTRL1, DST_ISSUE, 5, 5)
> > +    FIELD(ZDMA_CH_CTRL1, SRC_ISSUE, 0, 5)
> > +REG32(ZDMA_CH_FCI, 0x118)
> > +    FIELD(ZDMA_CH_FCI, PROG_CELL_CNT, 2, 2)
> > +    FIELD(ZDMA_CH_FCI, SIDE, 1, 1)
> > +    FIELD(ZDMA_CH_FCI, EN, 0, 1)
> > +REG32(ZDMA_CH_STATUS, 0x11c)
> > +    FIELD(ZDMA_CH_STATUS, STATE, 0, 2)
> > +REG32(ZDMA_CH_DATA_ATTR, 0x120)
> > +    FIELD(ZDMA_CH_DATA_ATTR, ARBURST, 26, 2)
> > +    FIELD(ZDMA_CH_DATA_ATTR, ARCACHE, 22, 4)
> > +    FIELD(ZDMA_CH_DATA_ATTR, ARQOS, 18, 4)
> > +    FIELD(ZDMA_CH_DATA_ATTR, ARLEN, 14, 4)
> > +    FIELD(ZDMA_CH_DATA_ATTR, AWBURST, 12, 2)
> > +    FIELD(ZDMA_CH_DATA_ATTR, AWCACHE, 8, 4)
> > +    FIELD(ZDMA_CH_DATA_ATTR, AWQOS, 4, 4)
> > +    FIELD(ZDMA_CH_DATA_ATTR, AWLEN, 0, 4)
> > +REG32(ZDMA_CH_DSCR_ATTR, 0x124)
> > +    FIELD(ZDMA_CH_DSCR_ATTR, AXCOHRNT, 8, 1)
> > +    FIELD(ZDMA_CH_DSCR_ATTR, AXCACHE, 4, 4)
> > +    FIELD(ZDMA_CH_DSCR_ATTR, AXQOS, 0, 4)
> > +REG32(ZDMA_CH_SRC_DSCR_WORD0, 0x128)
> > +REG32(ZDMA_CH_SRC_DSCR_WORD1, 0x12c)
> > +    FIELD(ZDMA_CH_SRC_DSCR_WORD1, MSB, 0, 17)
> > +REG32(ZDMA_CH_SRC_DSCR_WORD2, 0x130)
> > +    FIELD(ZDMA_CH_SRC_DSCR_WORD2, SIZE, 0, 30)
> > +REG32(ZDMA_CH_SRC_DSCR_WORD3, 0x134)
> > +    FIELD(ZDMA_CH_SRC_DSCR_WORD3, CMD, 3, 2)
> > +    FIELD(ZDMA_CH_SRC_DSCR_WORD3, INTR, 2, 1)
> > +    FIELD(ZDMA_CH_SRC_DSCR_WORD3, TYPE, 1, 1)
> > +    FIELD(ZDMA_CH_SRC_DSCR_WORD3, COHRNT, 0, 1)
> > +REG32(ZDMA_CH_DST_DSCR_WORD0, 0x138)
> > +REG32(ZDMA_CH_DST_DSCR_WORD1, 0x13c)
> > +    FIELD(ZDMA_CH_DST_DSCR_WORD1, MSB, 0, 17)
> > +REG32(ZDMA_CH_DST_DSCR_WORD2, 0x140)
> > +    FIELD(ZDMA_CH_DST_DSCR_WORD2, SIZE, 0, 30)
> > +REG32(ZDMA_CH_DST_DSCR_WORD3, 0x144)
> > +    FIELD(ZDMA_CH_DST_DSCR_WORD3, INTR, 2, 1)
> > +    FIELD(ZDMA_CH_DST_DSCR_WORD3, TYPE, 1, 1)
> > +    FIELD(ZDMA_CH_DST_DSCR_WORD3, COHRNT, 0, 1)
> > +REG32(ZDMA_CH_WR_ONLY_WORD0, 0x148)
> > +REG32(ZDMA_CH_WR_ONLY_WORD1, 0x14c)
> > +REG32(ZDMA_CH_WR_ONLY_WORD2, 0x150)
> > +REG32(ZDMA_CH_WR_ONLY_WORD3, 0x154)
> > +REG32(ZDMA_CH_SRC_START_LSB, 0x158)
> > +REG32(ZDMA_CH_SRC_START_MSB, 0x15c)
> > +    FIELD(ZDMA_CH_SRC_START_MSB, ADDR, 0, 17)
> > +REG32(ZDMA_CH_DST_START_LSB, 0x160)
> > +REG32(ZDMA_CH_DST_START_MSB, 0x164)
> > +    FIELD(ZDMA_CH_DST_START_MSB, ADDR, 0, 17)
> > +REG32(ZDMA_CH_RATE_CTRL, 0x18c)
> > +    FIELD(ZDMA_CH_RATE_CTRL, CNT, 0, 12)
> > +REG32(ZDMA_CH_SRC_CUR_PYLD_LSB, 0x168)
> > +REG32(ZDMA_CH_SRC_CUR_PYLD_MSB, 0x16c)
> > +    FIELD(ZDMA_CH_SRC_CUR_PYLD_MSB, ADDR, 0, 17)
> > +REG32(ZDMA_CH_DST_CUR_PYLD_LSB, 0x170)
> > +REG32(ZDMA_CH_DST_CUR_PYLD_MSB, 0x174)
> > +    FIELD(ZDMA_CH_DST_CUR_PYLD_MSB, ADDR, 0, 17)
> > +REG32(ZDMA_CH_SRC_CUR_DSCR_LSB, 0x178)
> > +REG32(ZDMA_CH_SRC_CUR_DSCR_MSB, 0x17c)
> > +    FIELD(ZDMA_CH_SRC_CUR_DSCR_MSB, ADDR, 0, 17)
> > +REG32(ZDMA_CH_DST_CUR_DSCR_LSB, 0x180)
> > +REG32(ZDMA_CH_DST_CUR_DSCR_MSB, 0x184)
> > +    FIELD(ZDMA_CH_DST_CUR_DSCR_MSB, ADDR, 0, 17)
> > +REG32(ZDMA_CH_TOTAL_BYTE, 0x188)
> > +REG32(ZDMA_CH_RATE_CNTL, 0x18c)
> > +    FIELD(ZDMA_CH_RATE_CNTL, CNT, 0, 12)
> > +REG32(ZDMA_CH_IRQ_SRC_ACCT, 0x190)
> > +    FIELD(ZDMA_CH_IRQ_SRC_ACCT, CNT, 0, 8)
> > +REG32(ZDMA_CH_IRQ_DST_ACCT, 0x194)
> > +    FIELD(ZDMA_CH_IRQ_DST_ACCT, CNT, 0, 8)
> > +REG32(ZDMA_CH_DBG0, 0x198)
> > +    FIELD(ZDMA_CH_DBG0, CMN_BUF_FREE, 0, 9)
> > +REG32(ZDMA_CH_DBG1, 0x19c)
> > +    FIELD(ZDMA_CH_DBG1, CMN_BUF_OCC, 0, 9)
> > +REG32(ZDMA_CH_CTRL2, 0x200)
> > +    FIELD(ZDMA_CH_CTRL2, EN, 0, 1)
> > +
> > +enum {
> > +    PT_REG = 0,
> > +    PT_MEM = 1,
> > +};
> > +
> > +enum {
> > +    CMD_HALT = 1,
> > +    CMD_STOP = 2,
> > +};
> > +
> > +enum {
> > +    RW_MODE_RW = 0,
> > +    RW_MODE_WO = 1,
> > +    RW_MODE_RO = 2,
> > +};
> > +
> > +enum {
> > +    DTYPE_LINEAR = 0,
> > +    DTYPE_LINKED = 1,
> > +};
> > +
> > +enum {
> > +    AXI_BURST_FIXED = 0,
> > +    AXI_BURST_INCR  = 1,
> > +};
> > +
> > +static void zdma_ch_imr_update_irq(XlnxZDMA *s)
> > +{
> > +    bool pending;
> > +
> > +    pending = s->regs[R_ZDMA_CH_ISR] & ~s->regs[R_ZDMA_CH_IMR];
> > +
> > +    qemu_set_irq(s->irq_zdma_ch_imr, pending);
> > +}
> > +
> > +static void zdma_ch_isr_postw(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
> > +    zdma_ch_imr_update_irq(s);
> > +}
> > +
> > +static uint64_t zdma_ch_ien_prew(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
> > +    uint32_t val = val64;
> > +
> > +    s->regs[R_ZDMA_CH_IMR] &= ~val;
> > +    zdma_ch_imr_update_irq(s);
> > +    return 0;
> > +}
> > +
> > +static uint64_t zdma_ch_ids_prew(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
> > +    uint32_t val = val64;
> > +
> > +    s->regs[R_ZDMA_CH_IMR] |= val;
> > +    zdma_ch_imr_update_irq(s);
> > +    return 0;
> > +}
> > +
> > +static void zdma_set_state(XlnxZDMA *s, XlnxZDMAState state)
> > +{
> > +    s->state = state;
> > +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_STATUS, STATE, state);
> > +
> > +    /* Signal error if we have an error condition.  */
> > +    if (s->error) {
> > +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_STATUS, STATE, 3);
> > +    }
> > +}
> > +
> > +static void zdma_src_done(XlnxZDMA *s)
> > +{
> > +    unsigned int cnt;
> > +    cnt = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT);
> > +    cnt++;
> > +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT, cnt);
> > +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, SRC_DSCR_DONE, true);
> > +
> > +    /* Did we overflow?  */
> > +    if (cnt != ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_SRC_ACCT, CNT)) {
> > +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, IRQ_SRC_ACCT_ERR, true);
> > +    }
> > +    zdma_ch_imr_update_irq(s);
> > +}
> > +
> > +static void zdma_dst_done(XlnxZDMA *s)
> > +{
> > +    unsigned int cnt;
> > +    cnt = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT);
> > +    cnt++;
> > +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT, cnt);
> > +    ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DST_DSCR_DONE, true);
> > +
> > +    /* Did we overflow?  */
> > +    if (cnt != ARRAY_FIELD_EX32(s->regs, ZDMA_CH_IRQ_DST_ACCT, CNT)) {
> > +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, IRQ_DST_ACCT_ERR, true);
> > +    }
> > +    zdma_ch_imr_update_irq(s);
> > +}
> > +
> > +static uint64_t zdma_get_regaddr64(XlnxZDMA *s, unsigned int basereg)
> > +{
> > +    uint64_t addr;
> > +
> > +    addr = s->regs[basereg + 1];
> > +    addr <<= 32;
> > +    addr |= s->regs[basereg];
> > +
> > +    return addr;
> > +}
> > +
> > +static void zdma_put_regaddr64(XlnxZDMA *s, unsigned int basereg,
> uint64_t addr)
> > +{
> > +    s->regs[basereg] = addr;
> > +    s->regs[basereg + 1] = addr >> 32;
> > +}
> > +
> > +static bool zdma_load_descriptor(XlnxZDMA *s, uint64_t addr, void *buf)
> > +{
> > +    /* ZDMA descriptors must be aligned to their own size.  */
> > +    if (addr % sizeof(XlnxZDMADescr)) {
> > +        qemu_log_mask(LOG_GUEST_ERROR,
> > +                      "zdma: unaligned descriptor at %" PRIx64,
> > +                      addr);
> > +        memset(buf, 0xdeadbeef, sizeof(XlnxZDMADescr));
> > +        s->error = true;
> > +        return false;
> > +    }
> > +
> > +    address_space_rw(s->dma_as, addr, s->attr,
> > +                     buf, sizeof(XlnxZDMADescr), false);
> > +    return true;
> > +}
> > +
> > +static void zdma_load_src_descriptor(XlnxZDMA *s)
> > +{
> > +    uint64_t src_addr;
> > +    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0,
> POINT_TYPE);
> > +
> > +    if (ptype == PT_REG) {
> > +        memcpy(&s->dsc_src, &s->regs[R_ZDMA_CH_SRC_DSCR_WORD0],
> > +               sizeof(s->dsc_src));
> > +        return;
> > +    }
> > +
> > +    src_addr = zdma_get_regaddr64(s, R_ZDMA_CH_SRC_CUR_DSCR_LSB);
> > +
> > +    if (!zdma_load_descriptor(s, src_addr, &s->dsc_src)) {
> > +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, AXI_RD_SRC_DSCR, true);
> > +    }
> > +}
> > +
> > +static void zdma_load_dst_descriptor(XlnxZDMA *s)
> > +{
> > +    uint64_t dst_addr;
> > +    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0,
> POINT_TYPE);
> > +
> > +    if (ptype == PT_REG) {
> > +        memcpy(&s->dsc_dst, &s->regs[R_ZDMA_CH_DST_DSCR_WORD0],
> > +               sizeof(s->dsc_src));
> > +        return;
> > +    }
> > +
> > +    dst_addr = zdma_get_regaddr64(s, R_ZDMA_CH_DST_CUR_DSCR_LSB);
> > +
> > +    if (!zdma_load_descriptor(s, dst_addr, &s->dsc_dst)) {
> > +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, AXI_RD_DST_DSCR, true);
> > +    }
> > +}
> > +
> > +static uint64_t zdma_update_descr_addr(XlnxZDMA *s, bool type,
> > +                                       unsigned int basereg)
> > +{
> > +    uint64_t addr, next;
> > +
> > +    if (type == DTYPE_LINEAR) {
> > +        next = zdma_get_regaddr64(s, basereg);
> > +        next += sizeof(s->dsc_dst);
> > +        zdma_put_regaddr64(s, basereg, next);
> > +    } else {
> > +        addr = zdma_get_regaddr64(s, basereg);
> > +        addr += sizeof(s->dsc_dst);
> > +        address_space_rw(s->dma_as, addr, s->attr, (void *) &next, 8,
> false);
> > +        zdma_put_regaddr64(s, basereg, next);
> > +    }
> > +    return next;
> > +}
> > +
> > +static void zdma_write_dst(XlnxZDMA *s, uint8_t *buf, uint32_t len)
> > +{
> > +    uint32_t dst_size, dlen;
> > +    bool dst_intr, dst_type;
> > +    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0,
> POINT_TYPE);
> > +    unsigned int rw_mode = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0,
> MODE);
> > +    unsigned int burst_type = ARRAY_FIELD_EX32(s->regs,
> ZDMA_CH_DATA_ATTR,
> > +                                               AWBURST);
> > +
> > +    /* FIXED burst types are only supported in simple dma mode.  */
> > +    if (ptype != PT_REG) {
> > +        burst_type = AXI_BURST_INCR;
> > +    }
> > +
> > +    while (len) {
> > +        dst_size = FIELD_EX32(s->dsc_dst.words[2],
> ZDMA_CH_DST_DSCR_WORD2,
> > +                              SIZE);
> > +        dst_type = FIELD_EX32(s->dsc_dst.words[3],
> ZDMA_CH_DST_DSCR_WORD3,
> > +                              TYPE);
> > +        if (dst_size == 0 && ptype == PT_MEM) {
> > +            uint64_t next;
> > +            next = zdma_update_descr_addr(s, dst_type,
> > +                                          R_ZDMA_CH_DST_CUR_DSCR_LSB);
> > +            zdma_load_descriptor(s, next, &s->dsc_dst);
> > +            dst_size = FIELD_EX32(s->dsc_dst.words[2],
> ZDMA_CH_DST_DSCR_WORD2,
> > +                                  SIZE);
> > +            dst_type = FIELD_EX32(s->dsc_dst.words[3],
> ZDMA_CH_DST_DSCR_WORD3,
> > +                                  TYPE);
> > +        }
> > +
> > +        /* Match what hardware does by ignoring the dst_size and only
> using
> > +         * the src size for Simple register mode.  */
> > +        if (ptype == PT_REG && rw_mode != RW_MODE_WO) {
> > +            dst_size = len;
> > +        }
> > +
> > +        if (dst_size == 0) {
> > +            qemu_log("Can't write to dst\n");
> > +        }
> > +        dst_intr = FIELD_EX32(s->dsc_dst.words[3],
> ZDMA_CH_DST_DSCR_WORD3,
> > +                              INTR);
> > +
> > +        dlen = len > dst_size ? dst_size : len;
> > +        if (burst_type == AXI_BURST_FIXED) {
> > +            if (dlen > (s->cfg.bus_width / 8)) {
> > +                dlen = s->cfg.bus_width / 8;
> > +            }
> > +        }
> > +
> > +        address_space_rw(s->dma_as, s->dsc_dst.addr, s->attr, buf, dlen,
> > +                         true);
> > +        if (burst_type == AXI_BURST_INCR) {
> > +            s->dsc_dst.addr += dlen;
> > +        }
> > +        dst_size -= dlen;
> > +        buf += dlen;
> > +        len -= dlen;
> > +
> > +        if (dst_size == 0 && dst_intr) {
> > +            zdma_dst_done(s);
> > +        }
> > +
> > +        /* Write back to buffered descriptor.  */
> > +        s->dsc_dst.words[2] = FIELD_DP32(s->dsc_dst.words[2],
> > +                                         ZDMA_CH_DST_DSCR_WORD2,
> > +                                         SIZE,
> > +                                         dst_size);
> > +    }
> > +}
> > +
> > +static void zdma_process_descr(XlnxZDMA *s)
> > +{
> > +    uint64_t src_addr;
> > +    uint32_t src_size, len;
> > +    unsigned int src_cmd;
> > +    bool src_intr, src_type;
> > +    unsigned int ptype = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0,
> POINT_TYPE);
> > +    unsigned int rw_mode = ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0,
> MODE);
> > +    unsigned int burst_type = ARRAY_FIELD_EX32(s->regs,
> ZDMA_CH_DATA_ATTR,
> > +                                               ARBURST);
> > +
> > +    src_addr = s->dsc_src.addr;
> > +    src_size = FIELD_EX32(s->dsc_src.words[2], ZDMA_CH_SRC_DSCR_WORD2,
> SIZE);
> > +    src_cmd = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3,
> CMD);
> > +    src_type = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3,
> TYPE);
> > +    src_intr = FIELD_EX32(s->dsc_src.words[3], ZDMA_CH_SRC_DSCR_WORD3,
> INTR);
> > +
> > +    /* FIXED burst types and non-rw modes are only supported in
> > +     * simple dma mode.
> > +     */
> > +    if (ptype != PT_REG) {
> > +        if (rw_mode != RW_MODE_RW) {
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                          "zDMA: rw-mode=%d but not simple DMA mode.\n",
> > +                          rw_mode);
> > +        }
> > +        if (burst_type != AXI_BURST_INCR) {
> > +            qemu_log_mask(LOG_GUEST_ERROR,
> > +                          "zDMA: burst_type=%d but not simple DMA
> mode.\n",
> > +                          burst_type);
> > +        }
> > +        burst_type = AXI_BURST_INCR;
> > +        rw_mode = RW_MODE_RW;
> > +    }
> > +
> > +    if (rw_mode == RW_MODE_WO) {
> > +        /* In Simple DMA Write-Only, we need to push DST size bytes
> > +         * regardless of what SRC size is set to.  */
> > +        src_size = FIELD_EX32(s->dsc_dst.words[2],
> ZDMA_CH_DST_DSCR_WORD2,
> > +                              SIZE);
> > +        memcpy(s->buf, &s->regs[R_ZDMA_CH_WR_ONLY_WORD0],
> s->cfg.bus_width / 8);
> > +    }
> > +
> > +    while (src_size) {
> > +        len = src_size > ARRAY_SIZE(s->buf) ? ARRAY_SIZE(s->buf) :
> src_size;
> > +        if (burst_type == AXI_BURST_FIXED) {
> > +            if (len > (s->cfg.bus_width / 8)) {
> > +                len = s->cfg.bus_width / 8;
> > +            }
> > +        }
> > +
> > +        if (rw_mode == RW_MODE_WO) {
> > +            if (len > s->cfg.bus_width / 8) {
> > +                len = s->cfg.bus_width / 8;
> > +            }
> > +        } else {
> > +            address_space_rw(s->dma_as, src_addr, s->attr, s->buf, len,
> > +                             false);
> > +            if (burst_type == AXI_BURST_INCR) {
> > +                src_addr += len;
> > +            }
> > +        }
> > +
> > +        if (rw_mode != RW_MODE_RO) {
> > +            zdma_write_dst(s, s->buf, len);
> > +        }
> > +
> > +        s->regs[R_ZDMA_CH_TOTAL_BYTE] += len;
> > +        src_size -= len;
> > +
> > +        if (src_size == 0) {
> > +            ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_DONE, true);
> > +        }
> > +
> > +    }
> > +
> > +    if (src_intr) {
> > +        zdma_src_done(s);
> > +    }
> > +
> > +    /* Load next descriptor.  */
> > +    if (ptype == PT_REG || src_cmd == CMD_STOP) {
> > +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_CTRL2, EN, 0);
> > +        zdma_set_state(s, DISABLED);
> > +        return;
> > +    }
> > +
> > +    if (src_cmd == CMD_HALT) {
> > +        zdma_set_state(s, PAUSED);
> > +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, DMA_PAUSE, 1);
> > +        zdma_ch_imr_update_irq(s);
> > +        return;
> > +    }
> > +
> > +    zdma_update_descr_addr(s, src_type, R_ZDMA_CH_SRC_CUR_DSCR_LSB);
> > +}
> > +
> > +static void zdma_run(XlnxZDMA *s)
> > +{
> > +    while (s->state == ENABLED && !s->error) {
> > +        zdma_load_src_descriptor(s);
> > +
> > +        if (s->error) {
> > +            zdma_set_state(s, DISABLED);
> > +        } else {
> > +            zdma_process_descr(s);
> > +        }
> > +    }
> > +
> > +    zdma_ch_imr_update_irq(s);
> > +}
> > +
> > +static void zdma_update_descr_addr_from_start(XlnxZDMA *s)
> > +{
> > +    uint64_t src_addr, dst_addr;
> > +
> > +    src_addr = zdma_get_regaddr64(s, R_ZDMA_CH_SRC_START_LSB);
> > +    zdma_put_regaddr64(s, R_ZDMA_CH_SRC_CUR_DSCR_LSB, src_addr);
> > +    dst_addr = zdma_get_regaddr64(s, R_ZDMA_CH_DST_START_LSB);
> > +    zdma_put_regaddr64(s, R_ZDMA_CH_DST_CUR_DSCR_LSB, dst_addr);
> > +    zdma_load_dst_descriptor(s);
> > +}
> > +
> > +static void zdma_ch_ctrlx_postw(RegisterInfo *reg, uint64_t val64)
> > +{
> > +    XlnxZDMA *s = XLNX_ZDMA(reg->opaque);
> > +
> > +    if (ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL2, EN)) {
> > +        s->error = false;
> > +
> > +        if (s->state == PAUSED &&
> > +            ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT)) {
> > +            if (ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT_ADDR) ==
> 1) {
> > +                zdma_update_descr_addr_from_start(s);
> > +            } else {
> > +                bool src_type = FIELD_EX32(s->dsc_src.words[3],
> > +                                       ZDMA_CH_SRC_DSCR_WORD3, TYPE);
> > +                zdma_update_descr_addr(s, src_type,
> > +                                          R_ZDMA_CH_SRC_CUR_DSCR_LSB);
> > +            }
> > +        } else {
> > +            zdma_update_descr_addr_from_start(s);
> > +        }
> > +        zdma_set_state(s, ENABLED);
> > +    } else {
> > +        /* Leave Paused state?  */
> > +        if (s->state == PAUSED &&
> > +            ARRAY_FIELD_EX32(s->regs, ZDMA_CH_CTRL0, CONT)) {
> > +            zdma_set_state(s, DISABLED);
> > +        }
> > +    }
> > +
> > +    zdma_run(s);
> > +}
> > +
> > +static RegisterAccessInfo zdma_regs_info[] = {
> > +    {   .name = "ZDMA_ERR_CTRL",  .addr = A_ZDMA_ERR_CTRL,
> > +        .rsvd = 0xfffffffe,
> > +    },{ .name = "ZDMA_CH_ISR",  .addr = A_ZDMA_CH_ISR,
> > +        .rsvd = 0xfffff000,
> > +        .w1c = 0xfff,
> > +        .post_write = zdma_ch_isr_postw,
> > +    },{ .name = "ZDMA_CH_IMR",  .addr = A_ZDMA_CH_IMR,
> > +        .reset = 0xfff,
> > +        .rsvd = 0xfffff000,
> > +        .ro = 0xfff,
> > +    },{ .name = "ZDMA_CH_IEN",  .addr = A_ZDMA_CH_IEN,
> > +        .rsvd = 0xfffff000,
> > +        .pre_write = zdma_ch_ien_prew,
> > +    },{ .name = "ZDMA_CH_IDS",  .addr = A_ZDMA_CH_IDS,
> > +        .rsvd = 0xfffff000,
> > +        .pre_write = zdma_ch_ids_prew,
> > +    },{ .name = "ZDMA_CH_CTRL0",  .addr = A_ZDMA_CH_CTRL0,
> > +        .reset = 0x80,
> > +        .rsvd = 0xffffff01,
> > +        .post_write = zdma_ch_ctrlx_postw,
> > +    },{ .name = "ZDMA_CH_CTRL1",  .addr = A_ZDMA_CH_CTRL1,
> > +        .reset = 0x3ff,
> > +        .rsvd = 0xfffffc00,
> > +    },{ .name = "ZDMA_CH_FCI",  .addr = A_ZDMA_CH_FCI,
> > +        .rsvd = 0xffffffc0,
> > +    },{ .name = "ZDMA_CH_STATUS",  .addr = A_ZDMA_CH_STATUS,
> > +        .rsvd = 0xfffffffc,
> > +        .ro = 0x3,
> > +    },{ .name = "ZDMA_CH_DATA_ATTR",  .addr = A_ZDMA_CH_DATA_ATTR,
> > +        .reset = 0x483d20f,
> > +        .rsvd = 0xf0000000,
> > +    },{ .name = "ZDMA_CH_DSCR_ATTR",  .addr = A_ZDMA_CH_DSCR_ATTR,
> > +        .rsvd = 0xfffffe00,
> > +    },{ .name = "ZDMA_CH_SRC_DSCR_WORD0",  .addr =
> A_ZDMA_CH_SRC_DSCR_WORD0,
> > +    },{ .name = "ZDMA_CH_SRC_DSCR_WORD1",  .addr =
> A_ZDMA_CH_SRC_DSCR_WORD1,
> > +        .rsvd = 0xfffe0000,
> > +    },{ .name = "ZDMA_CH_SRC_DSCR_WORD2",  .addr =
> A_ZDMA_CH_SRC_DSCR_WORD2,
> > +        .rsvd = 0xc0000000,
> > +    },{ .name = "ZDMA_CH_SRC_DSCR_WORD3",  .addr =
> A_ZDMA_CH_SRC_DSCR_WORD3,
> > +        .rsvd = 0xffffffe0,
> > +    },{ .name = "ZDMA_CH_DST_DSCR_WORD0",  .addr =
> A_ZDMA_CH_DST_DSCR_WORD0,
> > +    },{ .name = "ZDMA_CH_DST_DSCR_WORD1",  .addr =
> A_ZDMA_CH_DST_DSCR_WORD1,
> > +        .rsvd = 0xfffe0000,
> > +    },{ .name = "ZDMA_CH_DST_DSCR_WORD2",  .addr =
> A_ZDMA_CH_DST_DSCR_WORD2,
> > +        .rsvd = 0xc0000000,
> > +    },{ .name = "ZDMA_CH_DST_DSCR_WORD3",  .addr =
> A_ZDMA_CH_DST_DSCR_WORD3,
> > +        .rsvd = 0xfffffffa,
> > +    },{ .name = "ZDMA_CH_WR_ONLY_WORD0",  .addr =
> A_ZDMA_CH_WR_ONLY_WORD0,
> > +    },{ .name = "ZDMA_CH_WR_ONLY_WORD1",  .addr =
> A_ZDMA_CH_WR_ONLY_WORD1,
> > +    },{ .name = "ZDMA_CH_WR_ONLY_WORD2",  .addr =
> A_ZDMA_CH_WR_ONLY_WORD2,
> > +    },{ .name = "ZDMA_CH_WR_ONLY_WORD3",  .addr =
> A_ZDMA_CH_WR_ONLY_WORD3,
> > +    },{ .name = "ZDMA_CH_SRC_START_LSB",  .addr =
> A_ZDMA_CH_SRC_START_LSB,
> > +    },{ .name = "ZDMA_CH_SRC_START_MSB",  .addr =
> A_ZDMA_CH_SRC_START_MSB,
> > +        .rsvd = 0xfffe0000,
> > +    },{ .name = "ZDMA_CH_DST_START_LSB",  .addr =
> A_ZDMA_CH_DST_START_LSB,
> > +    },{ .name = "ZDMA_CH_DST_START_MSB",  .addr =
> A_ZDMA_CH_DST_START_MSB,
> > +        .rsvd = 0xfffe0000,
> > +    },{ .name = "ZDMA_CH_SRC_CUR_PYLD_LSB",  .addr =
> A_ZDMA_CH_SRC_CUR_PYLD_LSB,
> > +        .ro = 0xffffffff,
> > +    },{ .name = "ZDMA_CH_SRC_CUR_PYLD_MSB",  .addr =
> A_ZDMA_CH_SRC_CUR_PYLD_MSB,
> > +        .rsvd = 0xfffe0000,
> > +        .ro = 0x1ffff,
> > +    },{ .name = "ZDMA_CH_DST_CUR_PYLD_LSB",  .addr =
> A_ZDMA_CH_DST_CUR_PYLD_LSB,
> > +        .ro = 0xffffffff,
> > +    },{ .name = "ZDMA_CH_DST_CUR_PYLD_MSB",  .addr =
> A_ZDMA_CH_DST_CUR_PYLD_MSB,
> > +        .rsvd = 0xfffe0000,
> > +        .ro = 0x1ffff,
> > +    },{ .name = "ZDMA_CH_SRC_CUR_DSCR_LSB",  .addr =
> A_ZDMA_CH_SRC_CUR_DSCR_LSB,
> > +        .ro = 0xffffffff,
> > +    },{ .name = "ZDMA_CH_SRC_CUR_DSCR_MSB",  .addr =
> A_ZDMA_CH_SRC_CUR_DSCR_MSB,
> > +        .rsvd = 0xfffe0000,
> > +        .ro = 0x1ffff,
> > +    },{ .name = "ZDMA_CH_DST_CUR_DSCR_LSB",  .addr =
> A_ZDMA_CH_DST_CUR_DSCR_LSB,
> > +        .ro = 0xffffffff,
> > +    },{ .name = "ZDMA_CH_DST_CUR_DSCR_MSB",  .addr =
> A_ZDMA_CH_DST_CUR_DSCR_MSB,
> > +        .rsvd = 0xfffe0000,
> > +        .ro = 0x1ffff,
> > +    },{ .name = "ZDMA_CH_TOTAL_BYTE",  .addr = A_ZDMA_CH_TOTAL_BYTE,
> > +        .w1c = 0xffffffff,
> > +    },{ .name = "ZDMA_CH_RATE_CNTL",  .addr = A_ZDMA_CH_RATE_CNTL,
> > +        .rsvd = 0xfffff000,
> > +    },{ .name = "ZDMA_CH_IRQ_SRC_ACCT",  .addr = A_ZDMA_CH_IRQ_SRC_ACCT,
> > +        .rsvd = 0xffffff00,
> > +        .ro = 0xff,
> > +        .cor = 0xff,
> > +    },{ .name = "ZDMA_CH_IRQ_DST_ACCT",  .addr = A_ZDMA_CH_IRQ_DST_ACCT,
> > +        .rsvd = 0xffffff00,
> > +        .ro = 0xff,
> > +        .cor = 0xff,
> > +    },{ .name = "ZDMA_CH_DBG0",  .addr = A_ZDMA_CH_DBG0,
> > +        .rsvd = 0xfffffe00,
> > +        .ro = 0x1ff,
> > +    },{ .name = "ZDMA_CH_DBG1",  .addr = A_ZDMA_CH_DBG1,
> > +        .rsvd = 0xfffffe00,
> > +        .ro = 0x1ff,
> > +    },{ .name = "ZDMA_CH_CTRL2",  .addr = A_ZDMA_CH_CTRL2,
> > +        .rsvd = 0xfffffffe,
> > +        .post_write = zdma_ch_ctrlx_postw,
> > +    }
> > +};
> > +
> > +static void zdma_reset(DeviceState *dev)
> > +{
> > +    XlnxZDMA *s = XLNX_ZDMA(dev);
> > +    unsigned int i;
> > +
> > +    for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
> > +        register_reset(&s->regs_info[i]);
> > +    }
> > +
> > +    zdma_ch_imr_update_irq(s);
> > +}
> > +
> > +static uint64_t zdma_read(void *opaque, hwaddr addr, unsigned size)
> > +{
> > +    XlnxZDMA *s = XLNX_ZDMA(opaque);
> > +    RegisterInfo *r = &s->regs_info[addr / 4];
> > +
> > +    if (!r->data) {
> > +        qemu_log("%s: Decode error: read from %" HWADDR_PRIx "\n",
> > +                 object_get_canonical_path(OBJECT(s)),
> > +                 addr);
> > +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true);
> > +        zdma_ch_imr_update_irq(s);
> > +        return 0;
> > +    }
> > +    return register_read(r, ~0, NULL, false);
> > +}
> > +
> > +static void zdma_write(void *opaque, hwaddr addr, uint64_t value,
> > +                      unsigned size)
> > +{
> > +    XlnxZDMA *s = XLNX_ZDMA(opaque);
> > +    RegisterInfo *r = &s->regs_info[addr / 4];
> > +
> > +    if (!r->data) {
> > +        qemu_log("%s: Decode error: write to %" HWADDR_PRIx "=%" PRIx64
> "\n",
> > +                 object_get_canonical_path(OBJECT(s)),
> > +                 addr, value);
> > +        ARRAY_FIELD_DP32(s->regs, ZDMA_CH_ISR, INV_APB, true);
> > +        zdma_ch_imr_update_irq(s);
> > +        return;
> > +    }
> > +    register_write(r, value, ~0, NULL, false);
> > +}
> > +
> > +static const MemoryRegionOps zdma_ops = {
> > +    .read = zdma_read,
> > +    .write = zdma_write,
> > +    .endianness = DEVICE_LITTLE_ENDIAN,
> > +    .valid = {
> > +        .min_access_size = 4,
> > +        .max_access_size = 4,
> > +    },
> > +};
> > +
> > +static void zdma_realize(DeviceState *dev, Error **errp)
> > +{
> > +    XlnxZDMA *s = XLNX_ZDMA(dev);
> > +    unsigned int i;
> > +
> > +    for (i = 0; i < ARRAY_SIZE(zdma_regs_info); ++i) {
> > +        RegisterInfo *r = &s->regs_info[zdma_regs_info[i].addr / 4];
> > +
> > +        *r = (RegisterInfo) {
> > +            .data = (uint8_t *)&s->regs[
> > +                    zdma_regs_info[i].addr / 4],
> > +            .data_size = sizeof(uint32_t),
> > +            .access = &zdma_regs_info[i],
> > +            .opaque = s,
> > +        };
> > +    }
> > +
> > +    if (s->dma_mr) {
> > +        s->dma_as = g_malloc0(sizeof(AddressSpace));
> > +        address_space_init(s->dma_as, s->dma_mr, NULL);
> > +    } else {
> > +        s->dma_as = &address_space_memory;
> > +    }
> > +    s->attr = MEMTXATTRS_UNSPECIFIED;
> > +}
> > +
> > +static void zdma_init(Object *obj)
> > +{
> > +    XlnxZDMA *s = XLNX_ZDMA(obj);
> > +    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
> > +
> > +    memory_region_init_io(&s->iomem, obj, &zdma_ops, s,
> > +                          TYPE_XLNX_ZDMA, ZDMA_R_MAX * 4);
> > +    sysbus_init_mmio(sbd, &s->iomem);
> > +    sysbus_init_irq(sbd, &s->irq_zdma_ch_imr);
> > +
> > +    object_property_add_link(obj, "dma", TYPE_MEMORY_REGION,
> > +                             (Object **)&s->dma_mr,
> > +                             qdev_prop_allow_set_link_before_realize,
> > +                             OBJ_PROP_LINK_UNREF_ON_RELEASE,
> > +                             &error_abort);
> > +}
> > +
> > +static const VMStateDescription vmstate_zdma = {
> > +    .name = TYPE_XLNX_ZDMA,
> > +    .version_id = 1,
> > +    .minimum_version_id = 1,
> > +    .minimum_version_id_old = 1,
> > +    .fields = (VMStateField[]) {
> > +        VMSTATE_UINT32_ARRAY(regs, XlnxZDMA, ZDMA_R_MAX),
> > +        VMSTATE_END_OF_LIST(),
> > +    }
> > +};
> > +
> > +static Property zdma_props[] = {
> > +    DEFINE_PROP_UINT32("bus-width", XlnxZDMA, cfg.bus_width, 64),
> > +    DEFINE_PROP_END_OF_LIST(),
> > +};
> > +
> > +static void zdma_class_init(ObjectClass *klass, void *data)
> > +{
> > +    DeviceClass *dc = DEVICE_CLASS(klass);
> > +
> > +    dc->reset = zdma_reset;
> > +    dc->realize = zdma_realize;
> > +    dc->props = zdma_props;
> > +    dc->vmsd = &vmstate_zdma;
> > +}
> > +
> > +static const TypeInfo zdma_info = {
> > +    .name          = TYPE_XLNX_ZDMA,
> > +    .parent        = TYPE_SYS_BUS_DEVICE,
> > +    .instance_size = sizeof(XlnxZDMA),
> > +    .class_init    = zdma_class_init,
> > +    .instance_init = zdma_init,
> > +};
> > +
> > +static void zdma_register_types(void)
> > +{
> > +    type_register_static(&zdma_info);
> > +}
> > +
> > +type_init(zdma_register_types)
> > diff --git a/include/hw/dma/xlnx-zdma.h b/include/hw/dma/xlnx-zdma.h
> > new file mode 100644
> > index 0000000000..0b240b4c3c
> > --- /dev/null
> > +++ b/include/hw/dma/xlnx-zdma.h
> > @@ -0,0 +1,84 @@
> > +/*
> > + * QEMU model of the ZynqMP generic DMA
> > + *
> > + * Copyright (c) 2014 Xilinx Inc.
> > + * Copyright (c) 2018 FEIMTECH AB
> > + *
> > + * Written by Edgar E. Iglesias <edgar.iglesias@xilinx.com>,
> > + *            Francisco Iglesias <francisco.iglesias@feimtech.se>
> > + *
> > + * 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.
> > + */
> > +
> > +#ifndef XLNX_ZDMA_H
> > +#define XLNX_ZDMA_H
> > +
> > +#include "hw/sysbus.h"
> > +#include "hw/register.h"
> > +#include "sysemu/dma.h"
> > +
> > +#define ZDMA_R_MAX (0x204 / 4)
> > +
> > +typedef enum {
> > +    DISABLED = 0,
> > +    ENABLED = 1,
> > +    PAUSED = 2,
> > +} XlnxZDMAState;
> > +
> > +typedef union {
> > +    struct {
> > +        uint64_t addr;
> > +        uint32_t size;
> > +        uint32_t attr;
> > +    };
> > +    uint32_t words[4];
> > +} XlnxZDMADescr;
> > +
> > +typedef struct XlnxZDMA {
> > +    SysBusDevice parent_obj;
> > +    MemoryRegion iomem;
> > +    MemTxAttrs attr;
> > +    MemoryRegion *dma_mr;
> > +    AddressSpace *dma_as;
> > +    qemu_irq irq_zdma_ch_imr;
> > +
> > +    struct {
> > +        uint32_t bus_width;
> > +    } cfg;
> > +
> > +    XlnxZDMAState state;
> > +    bool error;
> > +
> > +    XlnxZDMADescr dsc_src;
> > +    XlnxZDMADescr dsc_dst;
> > +
> > +    uint32_t regs[ZDMA_R_MAX];
> > +    RegisterInfo regs_info[ZDMA_R_MAX];
> > +
> > +    /* We don't model the common bufs. Must be at least 16 bytes
> > +       to model write only mode.  */
> > +    uint8_t buf[2048];
> > +} XlnxZDMA;
> > +
> > +#define TYPE_XLNX_ZDMA "xlnx.zdma"
> > +
> > +#define XLNX_ZDMA(obj) \
> > +     OBJECT_CHECK(XlnxZDMA, (obj), TYPE_XLNX_ZDMA)
> > +
> > +#endif /* XLNX_ZDMA_H */
> > --
> > 2.11.0
> >
>

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2018-05-02 19:59 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-04-29  0:43 [Qemu-devel] [PATCH v1 0/2] xlnx-zynqmp: Add emulation of the ZynqMP GDMA and ADMA Francisco Iglesias
2018-04-29  0:43 ` [Qemu-devel] [PATCH v1 1/2] xlnx-zdma: Add a model of the Xilinx ZynqMP generic DMA Francisco Iglesias
2018-04-30 13:27   ` Sai Pavan Boddu
2018-05-02 19:58     ` francisco iglesias
2018-04-30 13:42   ` Edgar E. Iglesias
2018-05-02 19:58     ` francisco iglesias
2018-04-29  0:43 ` [Qemu-devel] [PATCH v1 2/2] xlnx-zynqmp: Connect the ZynqMP GDMA and ADMA Francisco Iglesias

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.