xref: /qemu/hw/dma/xlnx_csu_dma.c (revision 63e6b564)
135593573SXuzhou Cheng /*
235593573SXuzhou Cheng  * Xilinx Platform CSU Stream DMA emulation
335593573SXuzhou Cheng  *
435593573SXuzhou Cheng  * This implementation is based on
535593573SXuzhou Cheng  * https://github.com/Xilinx/qemu/blob/master/hw/dma/csu_stream_dma.c
635593573SXuzhou Cheng  *
735593573SXuzhou Cheng  * This program is free software; you can redistribute it and/or
835593573SXuzhou Cheng  * modify it under the terms of the GNU General Public License as
935593573SXuzhou Cheng  * published by the Free Software Foundation; either version 2 or
1035593573SXuzhou Cheng  * (at your option) version 3 of the License.
1135593573SXuzhou Cheng  *
1235593573SXuzhou Cheng  * This program is distributed in the hope that it will be useful,
1335593573SXuzhou Cheng  * but WITHOUT ANY WARRANTY; without even the implied warranty of
1435593573SXuzhou Cheng  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1535593573SXuzhou Cheng  * GNU General Public License for more details.
1635593573SXuzhou Cheng  *
1735593573SXuzhou Cheng  * You should have received a copy of the GNU General Public License along
1835593573SXuzhou Cheng  * with this program; if not, see <http://www.gnu.org/licenses/>.
1935593573SXuzhou Cheng  */
2035593573SXuzhou Cheng 
2135593573SXuzhou Cheng #include "qemu/osdep.h"
2235593573SXuzhou Cheng #include "qemu/log.h"
2335593573SXuzhou Cheng #include "qapi/error.h"
2435593573SXuzhou Cheng #include "hw/irq.h"
2535593573SXuzhou Cheng #include "hw/qdev-properties.h"
2635593573SXuzhou Cheng #include "hw/sysbus.h"
2735593573SXuzhou Cheng #include "migration/vmstate.h"
2835593573SXuzhou Cheng #include "sysemu/dma.h"
2935593573SXuzhou Cheng #include "hw/ptimer.h"
3035593573SXuzhou Cheng #include "hw/stream.h"
3135593573SXuzhou Cheng #include "hw/register.h"
3235593573SXuzhou Cheng #include "hw/dma/xlnx_csu_dma.h"
3335593573SXuzhou Cheng 
3435593573SXuzhou Cheng /*
3535593573SXuzhou Cheng  * Ref: UG1087 (v1.7) February 8, 2019
36a9bc470eSFrederic Konrad  * https://www.xilinx.com/html_docs/registers/ug1087/ug1087-zynq-ultrascale-registers
3735593573SXuzhou Cheng  * CSUDMA Module section
3835593573SXuzhou Cheng  */
3935593573SXuzhou Cheng REG32(ADDR, 0x0)
4035593573SXuzhou Cheng     FIELD(ADDR, ADDR, 2, 30) /* wo */
4135593573SXuzhou Cheng REG32(SIZE, 0x4)
421ee80592SFrederic Konrad     FIELD(SIZE, SIZE, 2, 27)
4335593573SXuzhou Cheng     FIELD(SIZE, LAST_WORD, 0, 1) /* rw, only exists in SRC */
4435593573SXuzhou Cheng REG32(STATUS, 0x8)
4535593573SXuzhou Cheng     FIELD(STATUS, DONE_CNT, 13, 3) /* wtc */
4635593573SXuzhou Cheng     FIELD(STATUS, FIFO_LEVEL, 5, 8) /* ro */
4735593573SXuzhou Cheng     FIELD(STATUS, OUTSTANDING, 1, 4) /* ro */
4835593573SXuzhou Cheng     FIELD(STATUS, BUSY, 0, 1) /* ro */
4935593573SXuzhou Cheng REG32(CTRL, 0xc)
5035593573SXuzhou Cheng     FIELD(CTRL, FIFOTHRESH, 25, 7) /* rw, only exists in DST, reset 0x40 */
5135593573SXuzhou Cheng     FIELD(CTRL, APB_ERR_RESP, 24, 1) /* rw */
5235593573SXuzhou Cheng     FIELD(CTRL, ENDIANNESS, 23, 1) /* rw */
5335593573SXuzhou Cheng     FIELD(CTRL, AXI_BRST_TYPE, 22, 1) /* rw */
5435593573SXuzhou Cheng     FIELD(CTRL, TIMEOUT_VAL, 10, 12) /* rw, reset: 0xFFE */
5535593573SXuzhou Cheng     FIELD(CTRL, FIFO_THRESH, 2, 8) /* rw, reset: 0x80 */
5635593573SXuzhou Cheng     FIELD(CTRL, PAUSE_STRM, 1, 1) /* rw */
5735593573SXuzhou Cheng     FIELD(CTRL, PAUSE_MEM, 0, 1) /* rw */
5835593573SXuzhou Cheng REG32(CRC, 0x10)
5935593573SXuzhou Cheng REG32(INT_STATUS, 0x14)
6035593573SXuzhou Cheng     FIELD(INT_STATUS, FIFO_OVERFLOW, 7, 1) /* wtc */
6135593573SXuzhou Cheng     FIELD(INT_STATUS, INVALID_APB, 6, 1) /* wtc */
6235593573SXuzhou Cheng     FIELD(INT_STATUS, THRESH_HIT, 5, 1) /* wtc */
6335593573SXuzhou Cheng     FIELD(INT_STATUS, TIMEOUT_MEM, 4, 1) /* wtc */
6435593573SXuzhou Cheng     FIELD(INT_STATUS, TIMEOUT_STRM, 3, 1) /* wtc */
6535593573SXuzhou Cheng     FIELD(INT_STATUS, AXI_BRESP_ERR, 2, 1) /* wtc, SRC: AXI_RDERR */
6635593573SXuzhou Cheng     FIELD(INT_STATUS, DONE, 1, 1) /* wtc */
6735593573SXuzhou Cheng     FIELD(INT_STATUS, MEM_DONE, 0, 1) /* wtc */
6835593573SXuzhou Cheng REG32(INT_ENABLE, 0x18)
6935593573SXuzhou Cheng     FIELD(INT_ENABLE, FIFO_OVERFLOW, 7, 1) /* wtc */
7035593573SXuzhou Cheng     FIELD(INT_ENABLE, INVALID_APB, 6, 1) /* wtc */
7135593573SXuzhou Cheng     FIELD(INT_ENABLE, THRESH_HIT, 5, 1) /* wtc */
7235593573SXuzhou Cheng     FIELD(INT_ENABLE, TIMEOUT_MEM, 4, 1) /* wtc */
7335593573SXuzhou Cheng     FIELD(INT_ENABLE, TIMEOUT_STRM, 3, 1) /* wtc */
7435593573SXuzhou Cheng     FIELD(INT_ENABLE, AXI_BRESP_ERR, 2, 1) /* wtc, SRC: AXI_RDERR */
7535593573SXuzhou Cheng     FIELD(INT_ENABLE, DONE, 1, 1) /* wtc */
7635593573SXuzhou Cheng     FIELD(INT_ENABLE, MEM_DONE, 0, 1) /* wtc */
7735593573SXuzhou Cheng REG32(INT_DISABLE, 0x1c)
7835593573SXuzhou Cheng     FIELD(INT_DISABLE, FIFO_OVERFLOW, 7, 1) /* wtc */
7935593573SXuzhou Cheng     FIELD(INT_DISABLE, INVALID_APB, 6, 1) /* wtc */
8035593573SXuzhou Cheng     FIELD(INT_DISABLE, THRESH_HIT, 5, 1) /* wtc */
8135593573SXuzhou Cheng     FIELD(INT_DISABLE, TIMEOUT_MEM, 4, 1) /* wtc */
8235593573SXuzhou Cheng     FIELD(INT_DISABLE, TIMEOUT_STRM, 3, 1) /* wtc */
8335593573SXuzhou Cheng     FIELD(INT_DISABLE, AXI_BRESP_ERR, 2, 1) /* wtc, SRC: AXI_RDERR */
8435593573SXuzhou Cheng     FIELD(INT_DISABLE, DONE, 1, 1) /* wtc */
8535593573SXuzhou Cheng     FIELD(INT_DISABLE, MEM_DONE, 0, 1) /* wtc */
8635593573SXuzhou Cheng REG32(INT_MASK, 0x20)
8735593573SXuzhou Cheng     FIELD(INT_MASK, FIFO_OVERFLOW, 7, 1) /* ro, reset: 0x1 */
8835593573SXuzhou Cheng     FIELD(INT_MASK, INVALID_APB, 6, 1) /* ro, reset: 0x1 */
8935593573SXuzhou Cheng     FIELD(INT_MASK, THRESH_HIT, 5, 1) /* ro, reset: 0x1 */
9035593573SXuzhou Cheng     FIELD(INT_MASK, TIMEOUT_MEM, 4, 1) /* ro, reset: 0x1 */
9135593573SXuzhou Cheng     FIELD(INT_MASK, TIMEOUT_STRM, 3, 1) /* ro, reset: 0x1 */
9235593573SXuzhou Cheng     FIELD(INT_MASK, AXI_BRESP_ERR, 2, 1) /* ro, reset: 0x1, SRC: AXI_RDERR */
9335593573SXuzhou Cheng     FIELD(INT_MASK, DONE, 1, 1) /* ro, reset: 0x1 */
9435593573SXuzhou Cheng     FIELD(INT_MASK, MEM_DONE, 0, 1) /* ro, reset: 0x1 */
9535593573SXuzhou Cheng REG32(CTRL2, 0x24)
9635593573SXuzhou Cheng     FIELD(CTRL2, ARCACHE, 24, 3) /* rw */
9735593573SXuzhou Cheng     FIELD(CTRL2, ROUTE_BIT, 23, 1) /* rw */
9835593573SXuzhou Cheng     FIELD(CTRL2, TIMEOUT_EN, 22, 1) /* rw */
9935593573SXuzhou Cheng     FIELD(CTRL2, TIMEOUT_PRE, 4, 12) /* rw, reset: 0xFFF */
10035593573SXuzhou Cheng     FIELD(CTRL2, MAX_OUTS_CMDS, 0, 4) /* rw, reset: 0x8 */
10135593573SXuzhou Cheng REG32(ADDR_MSB, 0x28)
10235593573SXuzhou Cheng     FIELD(ADDR_MSB, ADDR_MSB, 0, 17) /* wo */
10335593573SXuzhou Cheng 
10435593573SXuzhou Cheng #define R_CTRL_TIMEOUT_VAL_RESET    (0xFFE)
10535593573SXuzhou Cheng #define R_CTRL_FIFO_THRESH_RESET    (0x80)
10635593573SXuzhou Cheng #define R_CTRL_FIFOTHRESH_RESET     (0x40)
10735593573SXuzhou Cheng 
10835593573SXuzhou Cheng #define R_CTRL2_TIMEOUT_PRE_RESET   (0xFFF)
10935593573SXuzhou Cheng #define R_CTRL2_MAX_OUTS_CMDS_RESET (0x8)
11035593573SXuzhou Cheng 
11135593573SXuzhou Cheng #define XLNX_CSU_DMA_ERR_DEBUG      (0)
11235593573SXuzhou Cheng #define XLNX_CSU_DMA_INT_R_MASK     (0xff)
11335593573SXuzhou Cheng 
11435593573SXuzhou Cheng /* UG1807: Set the prescaler value for the timeout in clk (~2.5ns) cycles */
11535593573SXuzhou Cheng #define XLNX_CSU_DMA_TIMER_FREQ     (400 * 1000 * 1000)
11635593573SXuzhou Cheng 
xlnx_csu_dma_is_paused(XlnxCSUDMA * s)11735593573SXuzhou Cheng static bool xlnx_csu_dma_is_paused(XlnxCSUDMA *s)
11835593573SXuzhou Cheng {
11935593573SXuzhou Cheng     bool paused;
12035593573SXuzhou Cheng 
12135593573SXuzhou Cheng     paused = !!(s->regs[R_CTRL] & R_CTRL_PAUSE_STRM_MASK);
12235593573SXuzhou Cheng     paused |= !!(s->regs[R_CTRL] & R_CTRL_PAUSE_MEM_MASK);
12335593573SXuzhou Cheng 
12435593573SXuzhou Cheng     return paused;
12535593573SXuzhou Cheng }
12635593573SXuzhou Cheng 
xlnx_csu_dma_get_eop(XlnxCSUDMA * s)12735593573SXuzhou Cheng static bool xlnx_csu_dma_get_eop(XlnxCSUDMA *s)
12835593573SXuzhou Cheng {
12935593573SXuzhou Cheng     return s->r_size_last_word;
13035593573SXuzhou Cheng }
13135593573SXuzhou Cheng 
xlnx_csu_dma_burst_is_fixed(XlnxCSUDMA * s)13235593573SXuzhou Cheng static bool xlnx_csu_dma_burst_is_fixed(XlnxCSUDMA *s)
13335593573SXuzhou Cheng {
13435593573SXuzhou Cheng     return !!(s->regs[R_CTRL] & R_CTRL_AXI_BRST_TYPE_MASK);
13535593573SXuzhou Cheng }
13635593573SXuzhou Cheng 
xlnx_csu_dma_timeout_enabled(XlnxCSUDMA * s)13735593573SXuzhou Cheng static bool xlnx_csu_dma_timeout_enabled(XlnxCSUDMA *s)
13835593573SXuzhou Cheng {
13935593573SXuzhou Cheng     return !!(s->regs[R_CTRL2] & R_CTRL2_TIMEOUT_EN_MASK);
14035593573SXuzhou Cheng }
14135593573SXuzhou Cheng 
xlnx_csu_dma_update_done_cnt(XlnxCSUDMA * s,int a)14235593573SXuzhou Cheng static void xlnx_csu_dma_update_done_cnt(XlnxCSUDMA *s, int a)
14335593573SXuzhou Cheng {
14435593573SXuzhou Cheng     int cnt;
14535593573SXuzhou Cheng 
14635593573SXuzhou Cheng     /* Increase DONE_CNT */
14735593573SXuzhou Cheng     cnt = ARRAY_FIELD_EX32(s->regs, STATUS, DONE_CNT) + a;
14835593573SXuzhou Cheng     ARRAY_FIELD_DP32(s->regs, STATUS, DONE_CNT, cnt);
14935593573SXuzhou Cheng }
15035593573SXuzhou Cheng 
xlnx_csu_dma_data_process(XlnxCSUDMA * s,uint8_t * buf,uint32_t len)15135593573SXuzhou Cheng static void xlnx_csu_dma_data_process(XlnxCSUDMA *s, uint8_t *buf, uint32_t len)
15235593573SXuzhou Cheng {
15335593573SXuzhou Cheng     uint32_t bswap;
15435593573SXuzhou Cheng     uint32_t i;
15535593573SXuzhou Cheng 
15635593573SXuzhou Cheng     bswap = s->regs[R_CTRL] & R_CTRL_ENDIANNESS_MASK;
15735593573SXuzhou Cheng     if (s->is_dst && !bswap) {
15835593573SXuzhou Cheng         /* Fast when ENDIANNESS cleared */
15935593573SXuzhou Cheng         return;
16035593573SXuzhou Cheng     }
16135593573SXuzhou Cheng 
16235593573SXuzhou Cheng     for (i = 0; i < len; i += 4) {
16335593573SXuzhou Cheng         uint8_t *b = &buf[i];
16435593573SXuzhou Cheng         union {
16535593573SXuzhou Cheng             uint8_t u8[4];
16635593573SXuzhou Cheng             uint32_t u32;
16735593573SXuzhou Cheng         } v = {
16835593573SXuzhou Cheng             .u8 = { b[0], b[1], b[2], b[3] }
16935593573SXuzhou Cheng         };
17035593573SXuzhou Cheng 
17135593573SXuzhou Cheng         if (!s->is_dst) {
17235593573SXuzhou Cheng             s->regs[R_CRC] += v.u32;
17335593573SXuzhou Cheng         }
17435593573SXuzhou Cheng         if (bswap) {
17535593573SXuzhou Cheng             /*
17635593573SXuzhou Cheng              * No point using bswap, we need to writeback
17735593573SXuzhou Cheng              * into a potentially unaligned pointer.
17835593573SXuzhou Cheng              */
17935593573SXuzhou Cheng             b[0] = v.u8[3];
18035593573SXuzhou Cheng             b[1] = v.u8[2];
18135593573SXuzhou Cheng             b[2] = v.u8[1];
18235593573SXuzhou Cheng             b[3] = v.u8[0];
18335593573SXuzhou Cheng         }
18435593573SXuzhou Cheng     }
18535593573SXuzhou Cheng }
18635593573SXuzhou Cheng 
xlnx_csu_dma_update_irq(XlnxCSUDMA * s)18735593573SXuzhou Cheng static void xlnx_csu_dma_update_irq(XlnxCSUDMA *s)
18835593573SXuzhou Cheng {
18935593573SXuzhou Cheng     qemu_set_irq(s->irq, !!(s->regs[R_INT_STATUS] & ~s->regs[R_INT_MASK]));
19035593573SXuzhou Cheng }
19135593573SXuzhou Cheng 
19235593573SXuzhou Cheng /* len is in bytes */
xlnx_csu_dma_read(XlnxCSUDMA * s,uint8_t * buf,uint32_t len)19335593573SXuzhou Cheng static uint32_t xlnx_csu_dma_read(XlnxCSUDMA *s, uint8_t *buf, uint32_t len)
19435593573SXuzhou Cheng {
19535593573SXuzhou Cheng     hwaddr addr = (hwaddr)s->regs[R_ADDR_MSB] << 32 | s->regs[R_ADDR];
19635593573SXuzhou Cheng     MemTxResult result = MEMTX_OK;
19735593573SXuzhou Cheng 
19835593573SXuzhou Cheng     if (xlnx_csu_dma_burst_is_fixed(s)) {
19935593573SXuzhou Cheng         uint32_t i;
20035593573SXuzhou Cheng 
20135593573SXuzhou Cheng         for (i = 0; i < len && (result == MEMTX_OK); i += s->width) {
20235593573SXuzhou Cheng             uint32_t mlen = MIN(len - i, s->width);
20335593573SXuzhou Cheng 
204c31b7f59SPhilippe Mathieu-Daudé             result = address_space_rw(&s->dma_as, addr, s->attr,
20535593573SXuzhou Cheng                                       buf + i, mlen, false);
20635593573SXuzhou Cheng         }
20735593573SXuzhou Cheng     } else {
208c31b7f59SPhilippe Mathieu-Daudé         result = address_space_rw(&s->dma_as, addr, s->attr, buf, len, false);
20935593573SXuzhou Cheng     }
21035593573SXuzhou Cheng 
21135593573SXuzhou Cheng     if (result == MEMTX_OK) {
21235593573SXuzhou Cheng         xlnx_csu_dma_data_process(s, buf, len);
21335593573SXuzhou Cheng     } else {
214883f2c59SPhilippe Mathieu-Daudé         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address " HWADDR_FMT_plx
21535593573SXuzhou Cheng                       " for mem read", __func__, addr);
21635593573SXuzhou Cheng         s->regs[R_INT_STATUS] |= R_INT_STATUS_AXI_BRESP_ERR_MASK;
21735593573SXuzhou Cheng         xlnx_csu_dma_update_irq(s);
21835593573SXuzhou Cheng     }
21935593573SXuzhou Cheng     return len;
22035593573SXuzhou Cheng }
22135593573SXuzhou Cheng 
22235593573SXuzhou Cheng /* len is in bytes */
xlnx_csu_dma_write(XlnxCSUDMA * s,uint8_t * buf,uint32_t len)22335593573SXuzhou Cheng static uint32_t xlnx_csu_dma_write(XlnxCSUDMA *s, uint8_t *buf, uint32_t len)
22435593573SXuzhou Cheng {
22535593573SXuzhou Cheng     hwaddr addr = (hwaddr)s->regs[R_ADDR_MSB] << 32 | s->regs[R_ADDR];
22635593573SXuzhou Cheng     MemTxResult result = MEMTX_OK;
22735593573SXuzhou Cheng 
22835593573SXuzhou Cheng     xlnx_csu_dma_data_process(s, buf, len);
22935593573SXuzhou Cheng     if (xlnx_csu_dma_burst_is_fixed(s)) {
23035593573SXuzhou Cheng         uint32_t i;
23135593573SXuzhou Cheng 
23235593573SXuzhou Cheng         for (i = 0; i < len && (result == MEMTX_OK); i += s->width) {
23335593573SXuzhou Cheng             uint32_t mlen = MIN(len - i, s->width);
23435593573SXuzhou Cheng 
235c31b7f59SPhilippe Mathieu-Daudé             result = address_space_rw(&s->dma_as, addr, s->attr,
23635593573SXuzhou Cheng                                       buf, mlen, true);
23735593573SXuzhou Cheng             buf += mlen;
23835593573SXuzhou Cheng         }
23935593573SXuzhou Cheng     } else {
240c31b7f59SPhilippe Mathieu-Daudé         result = address_space_rw(&s->dma_as, addr, s->attr, buf, len, true);
24135593573SXuzhou Cheng     }
24235593573SXuzhou Cheng 
24335593573SXuzhou Cheng     if (result != MEMTX_OK) {
244883f2c59SPhilippe Mathieu-Daudé         qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address " HWADDR_FMT_plx
24535593573SXuzhou Cheng                       " for mem write", __func__, addr);
24635593573SXuzhou Cheng         s->regs[R_INT_STATUS] |= R_INT_STATUS_AXI_BRESP_ERR_MASK;
24735593573SXuzhou Cheng         xlnx_csu_dma_update_irq(s);
24835593573SXuzhou Cheng     }
24935593573SXuzhou Cheng     return len;
25035593573SXuzhou Cheng }
25135593573SXuzhou Cheng 
xlnx_csu_dma_done(XlnxCSUDMA * s)25235593573SXuzhou Cheng static void xlnx_csu_dma_done(XlnxCSUDMA *s)
25335593573SXuzhou Cheng {
25435593573SXuzhou Cheng     s->regs[R_STATUS] &= ~R_STATUS_BUSY_MASK;
25535593573SXuzhou Cheng     s->regs[R_INT_STATUS] |= R_INT_STATUS_DONE_MASK;
25635593573SXuzhou Cheng 
25735593573SXuzhou Cheng     if (!s->is_dst) {
25835593573SXuzhou Cheng         s->regs[R_INT_STATUS] |= R_INT_STATUS_MEM_DONE_MASK;
25935593573SXuzhou Cheng     }
26035593573SXuzhou Cheng 
26135593573SXuzhou Cheng     xlnx_csu_dma_update_done_cnt(s, 1);
26235593573SXuzhou Cheng }
26335593573SXuzhou Cheng 
xlnx_csu_dma_advance(XlnxCSUDMA * s,uint32_t len)26435593573SXuzhou Cheng static uint32_t xlnx_csu_dma_advance(XlnxCSUDMA *s, uint32_t len)
26535593573SXuzhou Cheng {
26635593573SXuzhou Cheng     uint32_t size = s->regs[R_SIZE];
26735593573SXuzhou Cheng     hwaddr dst = (hwaddr)s->regs[R_ADDR_MSB] << 32 | s->regs[R_ADDR];
26835593573SXuzhou Cheng 
26935593573SXuzhou Cheng     assert(len <= size);
27035593573SXuzhou Cheng 
27135593573SXuzhou Cheng     size -= len;
27235593573SXuzhou Cheng     s->regs[R_SIZE] = size;
27335593573SXuzhou Cheng 
27435593573SXuzhou Cheng     if (!xlnx_csu_dma_burst_is_fixed(s)) {
27535593573SXuzhou Cheng         dst += len;
27635593573SXuzhou Cheng         s->regs[R_ADDR] = (uint32_t) dst;
27735593573SXuzhou Cheng         s->regs[R_ADDR_MSB] = dst >> 32;
27835593573SXuzhou Cheng     }
27935593573SXuzhou Cheng 
28035593573SXuzhou Cheng     if (size == 0) {
28135593573SXuzhou Cheng         xlnx_csu_dma_done(s);
28235593573SXuzhou Cheng     }
28335593573SXuzhou Cheng 
28435593573SXuzhou Cheng     return size;
28535593573SXuzhou Cheng }
28635593573SXuzhou Cheng 
xlnx_csu_dma_src_notify(void * opaque)28735593573SXuzhou Cheng static void xlnx_csu_dma_src_notify(void *opaque)
28835593573SXuzhou Cheng {
28935593573SXuzhou Cheng     XlnxCSUDMA *s = XLNX_CSU_DMA(opaque);
29035593573SXuzhou Cheng     unsigned char buf[4 * 1024];
29135593573SXuzhou Cheng     size_t rlen = 0;
29235593573SXuzhou Cheng 
29335593573SXuzhou Cheng     ptimer_transaction_begin(s->src_timer);
29435593573SXuzhou Cheng     /* Stop the backpreassure timer */
29535593573SXuzhou Cheng     ptimer_stop(s->src_timer);
29635593573SXuzhou Cheng 
29735593573SXuzhou Cheng     while (s->regs[R_SIZE] && !xlnx_csu_dma_is_paused(s) &&
29835593573SXuzhou Cheng            stream_can_push(s->tx_dev, xlnx_csu_dma_src_notify, s)) {
29935593573SXuzhou Cheng         uint32_t plen = MIN(s->regs[R_SIZE], sizeof buf);
30035593573SXuzhou Cheng         bool eop = false;
30135593573SXuzhou Cheng 
30235593573SXuzhou Cheng         /* Did we fit it all? */
30335593573SXuzhou Cheng         if (s->regs[R_SIZE] == plen && xlnx_csu_dma_get_eop(s)) {
30435593573SXuzhou Cheng             eop = true;
30535593573SXuzhou Cheng         }
30635593573SXuzhou Cheng 
30735593573SXuzhou Cheng         /* DMA transfer */
30835593573SXuzhou Cheng         xlnx_csu_dma_read(s, buf, plen);
30935593573SXuzhou Cheng         rlen = stream_push(s->tx_dev, buf, plen, eop);
31035593573SXuzhou Cheng         xlnx_csu_dma_advance(s, rlen);
31135593573SXuzhou Cheng     }
31235593573SXuzhou Cheng 
31335593573SXuzhou Cheng     if (xlnx_csu_dma_timeout_enabled(s) && s->regs[R_SIZE] &&
31435593573SXuzhou Cheng         !stream_can_push(s->tx_dev, xlnx_csu_dma_src_notify, s)) {
31535593573SXuzhou Cheng         uint32_t timeout = ARRAY_FIELD_EX32(s->regs, CTRL, TIMEOUT_VAL);
31635593573SXuzhou Cheng         uint32_t div = ARRAY_FIELD_EX32(s->regs, CTRL2, TIMEOUT_PRE) + 1;
31735593573SXuzhou Cheng         uint32_t freq = XLNX_CSU_DMA_TIMER_FREQ;
31835593573SXuzhou Cheng 
31935593573SXuzhou Cheng         freq /= div;
32035593573SXuzhou Cheng         ptimer_set_freq(s->src_timer, freq);
32135593573SXuzhou Cheng         ptimer_set_count(s->src_timer, timeout);
32235593573SXuzhou Cheng         ptimer_run(s->src_timer, 1);
32335593573SXuzhou Cheng     }
32435593573SXuzhou Cheng 
32535593573SXuzhou Cheng     ptimer_transaction_commit(s->src_timer);
32635593573SXuzhou Cheng     xlnx_csu_dma_update_irq(s);
32735593573SXuzhou Cheng }
32835593573SXuzhou Cheng 
addr_pre_write(RegisterInfo * reg,uint64_t val)32935593573SXuzhou Cheng static uint64_t addr_pre_write(RegisterInfo *reg, uint64_t val)
33035593573SXuzhou Cheng {
33135593573SXuzhou Cheng     /* Address is word aligned */
33235593573SXuzhou Cheng     return val & R_ADDR_ADDR_MASK;
33335593573SXuzhou Cheng }
33435593573SXuzhou Cheng 
size_pre_write(RegisterInfo * reg,uint64_t val)33535593573SXuzhou Cheng static uint64_t size_pre_write(RegisterInfo *reg, uint64_t val)
33635593573SXuzhou Cheng {
33735593573SXuzhou Cheng     XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
3381ee80592SFrederic Konrad     uint64_t size = val & R_SIZE_SIZE_MASK;
33935593573SXuzhou Cheng 
34035593573SXuzhou Cheng     if (s->regs[R_SIZE] != 0) {
3411ee80592SFrederic Konrad         if (size || s->is_dst) {
34235593573SXuzhou Cheng             qemu_log_mask(LOG_GUEST_ERROR,
3431ee80592SFrederic Konrad                           "%s: Starting DMA while already running.\n",
3441ee80592SFrederic Konrad                           __func__);
3451ee80592SFrederic Konrad         }
34635593573SXuzhou Cheng     }
34735593573SXuzhou Cheng 
34835593573SXuzhou Cheng     if (!s->is_dst) {
34935593573SXuzhou Cheng         s->r_size_last_word = !!(val & R_SIZE_LAST_WORD_MASK);
35035593573SXuzhou Cheng     }
35135593573SXuzhou Cheng 
35235593573SXuzhou Cheng     /* Size is word aligned */
3531ee80592SFrederic Konrad     return size;
35435593573SXuzhou Cheng }
35535593573SXuzhou Cheng 
size_post_read(RegisterInfo * reg,uint64_t val)35635593573SXuzhou Cheng static uint64_t size_post_read(RegisterInfo *reg, uint64_t val)
35735593573SXuzhou Cheng {
35835593573SXuzhou Cheng     XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
35935593573SXuzhou Cheng 
36035593573SXuzhou Cheng     return val | s->r_size_last_word;
36135593573SXuzhou Cheng }
36235593573SXuzhou Cheng 
size_post_write(RegisterInfo * reg,uint64_t val)36335593573SXuzhou Cheng static void size_post_write(RegisterInfo *reg, uint64_t val)
36435593573SXuzhou Cheng {
36535593573SXuzhou Cheng     XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
36635593573SXuzhou Cheng 
36735593573SXuzhou Cheng     s->regs[R_STATUS] |= R_STATUS_BUSY_MASK;
36835593573SXuzhou Cheng 
36935593573SXuzhou Cheng     /*
37035593573SXuzhou Cheng      * Note that if SIZE is programmed to 0, and the DMA is started,
37135593573SXuzhou Cheng      * the interrupts DONE and MEM_DONE will be asserted.
37235593573SXuzhou Cheng      */
37335593573SXuzhou Cheng     if (s->regs[R_SIZE] == 0) {
37435593573SXuzhou Cheng         xlnx_csu_dma_done(s);
37535593573SXuzhou Cheng         xlnx_csu_dma_update_irq(s);
37635593573SXuzhou Cheng         return;
37735593573SXuzhou Cheng     }
37835593573SXuzhou Cheng 
37935593573SXuzhou Cheng     /* Set SIZE is considered the last step in transfer configuration */
38035593573SXuzhou Cheng     if (!s->is_dst) {
38135593573SXuzhou Cheng         xlnx_csu_dma_src_notify(s);
38235593573SXuzhou Cheng     } else {
38335593573SXuzhou Cheng         if (s->notify) {
38435593573SXuzhou Cheng             s->notify(s->notify_opaque);
38535593573SXuzhou Cheng         }
38635593573SXuzhou Cheng     }
38735593573SXuzhou Cheng }
38835593573SXuzhou Cheng 
status_pre_write(RegisterInfo * reg,uint64_t val)38935593573SXuzhou Cheng static uint64_t status_pre_write(RegisterInfo *reg, uint64_t val)
39035593573SXuzhou Cheng {
39135593573SXuzhou Cheng     return val & (R_STATUS_DONE_CNT_MASK | R_STATUS_BUSY_MASK);
39235593573SXuzhou Cheng }
39335593573SXuzhou Cheng 
ctrl_post_write(RegisterInfo * reg,uint64_t val)39435593573SXuzhou Cheng static void ctrl_post_write(RegisterInfo *reg, uint64_t val)
39535593573SXuzhou Cheng {
39635593573SXuzhou Cheng     XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
39735593573SXuzhou Cheng 
39835593573SXuzhou Cheng     if (!s->is_dst) {
39935593573SXuzhou Cheng         if (!xlnx_csu_dma_is_paused(s)) {
40035593573SXuzhou Cheng             xlnx_csu_dma_src_notify(s);
40135593573SXuzhou Cheng         }
40235593573SXuzhou Cheng     } else {
40335593573SXuzhou Cheng         if (!xlnx_csu_dma_is_paused(s) && s->notify) {
40435593573SXuzhou Cheng             s->notify(s->notify_opaque);
40535593573SXuzhou Cheng         }
40635593573SXuzhou Cheng     }
40735593573SXuzhou Cheng }
40835593573SXuzhou Cheng 
int_status_pre_write(RegisterInfo * reg,uint64_t val)40935593573SXuzhou Cheng static uint64_t int_status_pre_write(RegisterInfo *reg, uint64_t val)
41035593573SXuzhou Cheng {
41135593573SXuzhou Cheng     XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
41235593573SXuzhou Cheng 
41335593573SXuzhou Cheng     /* DMA counter decrements when flag 'DONE' is cleared */
41435593573SXuzhou Cheng     if ((val & s->regs[R_INT_STATUS] & R_INT_STATUS_DONE_MASK)) {
41535593573SXuzhou Cheng         xlnx_csu_dma_update_done_cnt(s, -1);
41635593573SXuzhou Cheng     }
41735593573SXuzhou Cheng 
41835593573SXuzhou Cheng     return s->regs[R_INT_STATUS] & ~val;
41935593573SXuzhou Cheng }
42035593573SXuzhou Cheng 
int_status_post_write(RegisterInfo * reg,uint64_t val)42135593573SXuzhou Cheng static void int_status_post_write(RegisterInfo *reg, uint64_t val)
42235593573SXuzhou Cheng {
42335593573SXuzhou Cheng     XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
42435593573SXuzhou Cheng 
42535593573SXuzhou Cheng     xlnx_csu_dma_update_irq(s);
42635593573SXuzhou Cheng }
42735593573SXuzhou Cheng 
int_enable_pre_write(RegisterInfo * reg,uint64_t val)42835593573SXuzhou Cheng static uint64_t int_enable_pre_write(RegisterInfo *reg, uint64_t val)
42935593573SXuzhou Cheng {
43035593573SXuzhou Cheng     XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
43135593573SXuzhou Cheng     uint32_t v32 = val;
43235593573SXuzhou Cheng 
43335593573SXuzhou Cheng     /*
43435593573SXuzhou Cheng      * R_INT_ENABLE doesn't have its own state.
43535593573SXuzhou Cheng      * It is used to indirectly modify R_INT_MASK.
43635593573SXuzhou Cheng      *
43735593573SXuzhou Cheng      * 1: Enable this interrupt field (the mask bit will be cleared to 0)
43835593573SXuzhou Cheng      * 0: No effect
43935593573SXuzhou Cheng      */
44035593573SXuzhou Cheng     s->regs[R_INT_MASK] &= ~v32;
44135593573SXuzhou Cheng     return 0;
44235593573SXuzhou Cheng }
44335593573SXuzhou Cheng 
int_enable_post_write(RegisterInfo * reg,uint64_t val)44435593573SXuzhou Cheng static void int_enable_post_write(RegisterInfo *reg, uint64_t val)
44535593573SXuzhou Cheng {
44635593573SXuzhou Cheng     XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
44735593573SXuzhou Cheng 
44835593573SXuzhou Cheng     xlnx_csu_dma_update_irq(s);
44935593573SXuzhou Cheng }
45035593573SXuzhou Cheng 
int_disable_pre_write(RegisterInfo * reg,uint64_t val)45135593573SXuzhou Cheng static uint64_t int_disable_pre_write(RegisterInfo *reg, uint64_t val)
45235593573SXuzhou Cheng {
45335593573SXuzhou Cheng     XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
45435593573SXuzhou Cheng     uint32_t v32 = val;
45535593573SXuzhou Cheng 
45635593573SXuzhou Cheng     /*
45735593573SXuzhou Cheng      * R_INT_DISABLE doesn't have its own state.
45835593573SXuzhou Cheng      * It is used to indirectly modify R_INT_MASK.
45935593573SXuzhou Cheng      *
46035593573SXuzhou Cheng      * 1: Disable this interrupt field (the mask bit will be set to 1)
46135593573SXuzhou Cheng      * 0: No effect
46235593573SXuzhou Cheng      */
46335593573SXuzhou Cheng     s->regs[R_INT_MASK] |= v32;
46435593573SXuzhou Cheng     return 0;
46535593573SXuzhou Cheng }
46635593573SXuzhou Cheng 
int_disable_post_write(RegisterInfo * reg,uint64_t val)46735593573SXuzhou Cheng static void int_disable_post_write(RegisterInfo *reg, uint64_t val)
46835593573SXuzhou Cheng {
46935593573SXuzhou Cheng     XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque);
47035593573SXuzhou Cheng 
47135593573SXuzhou Cheng     xlnx_csu_dma_update_irq(s);
47235593573SXuzhou Cheng }
47335593573SXuzhou Cheng 
addr_msb_pre_write(RegisterInfo * reg,uint64_t val)47435593573SXuzhou Cheng static uint64_t addr_msb_pre_write(RegisterInfo *reg, uint64_t val)
47535593573SXuzhou Cheng {
47635593573SXuzhou Cheng     return val & R_ADDR_MSB_ADDR_MSB_MASK;
47735593573SXuzhou Cheng }
47835593573SXuzhou Cheng 
xlnx_csu_dma_class_read(XlnxCSUDMA * s,hwaddr addr,uint32_t len)47900f05c02SFrancisco Iglesias static MemTxResult xlnx_csu_dma_class_read(XlnxCSUDMA *s, hwaddr addr,
48000f05c02SFrancisco Iglesias                                            uint32_t len)
48100f05c02SFrancisco Iglesias {
48200f05c02SFrancisco Iglesias     RegisterInfo *reg = &s->regs_info[R_SIZE];
48300f05c02SFrancisco Iglesias     uint64_t we = MAKE_64BIT_MASK(0, 4 * 8);
48400f05c02SFrancisco Iglesias 
48500f05c02SFrancisco Iglesias     s->regs[R_ADDR] = addr;
48600f05c02SFrancisco Iglesias     s->regs[R_ADDR_MSB] = (uint64_t)addr >> 32;
48700f05c02SFrancisco Iglesias 
48800f05c02SFrancisco Iglesias     register_write(reg, len, we, object_get_typename(OBJECT(s)), false);
48900f05c02SFrancisco Iglesias 
49000f05c02SFrancisco Iglesias     return (s->regs[R_SIZE] == 0) ? MEMTX_OK : MEMTX_ERROR;
49100f05c02SFrancisco Iglesias }
49200f05c02SFrancisco Iglesias 
49335593573SXuzhou Cheng static const RegisterAccessInfo *xlnx_csu_dma_regs_info[] = {
49435593573SXuzhou Cheng #define DMACH_REGINFO(NAME, snd)                                              \
49535593573SXuzhou Cheng     (const RegisterAccessInfo []) {                                           \
49635593573SXuzhou Cheng         {                                                                     \
49735593573SXuzhou Cheng             .name = #NAME "_ADDR",                                            \
49835593573SXuzhou Cheng             .addr = A_ADDR,                                                   \
49935593573SXuzhou Cheng             .pre_write = addr_pre_write                                       \
50035593573SXuzhou Cheng         }, {                                                                  \
50135593573SXuzhou Cheng             .name = #NAME "_SIZE",                                            \
50235593573SXuzhou Cheng             .addr = A_SIZE,                                                   \
50335593573SXuzhou Cheng             .pre_write = size_pre_write,                                      \
50435593573SXuzhou Cheng             .post_write = size_post_write,                                    \
50535593573SXuzhou Cheng             .post_read = size_post_read                                       \
50635593573SXuzhou Cheng         }, {                                                                  \
50735593573SXuzhou Cheng             .name = #NAME "_STATUS",                                          \
50835593573SXuzhou Cheng             .addr = A_STATUS,                                                 \
50935593573SXuzhou Cheng             .pre_write = status_pre_write,                                    \
51035593573SXuzhou Cheng             .w1c = R_STATUS_DONE_CNT_MASK,                                    \
51135593573SXuzhou Cheng             .ro = (R_STATUS_BUSY_MASK                                         \
51235593573SXuzhou Cheng                    | R_STATUS_FIFO_LEVEL_MASK                                 \
51335593573SXuzhou Cheng                    | R_STATUS_OUTSTANDING_MASK)                               \
51435593573SXuzhou Cheng         }, {                                                                  \
51535593573SXuzhou Cheng             .name = #NAME "_CTRL",                                            \
51635593573SXuzhou Cheng             .addr = A_CTRL,                                                   \
51735593573SXuzhou Cheng             .post_write = ctrl_post_write,                                    \
51835593573SXuzhou Cheng             .reset = ((R_CTRL_TIMEOUT_VAL_RESET << R_CTRL_TIMEOUT_VAL_SHIFT)  \
51935593573SXuzhou Cheng                       | (R_CTRL_FIFO_THRESH_RESET << R_CTRL_FIFO_THRESH_SHIFT)\
52035593573SXuzhou Cheng                       | (snd ? 0 : R_CTRL_FIFOTHRESH_RESET                    \
52135593573SXuzhou Cheng                          << R_CTRL_FIFOTHRESH_SHIFT))                         \
52235593573SXuzhou Cheng         }, {                                                                  \
52335593573SXuzhou Cheng             .name = #NAME "_CRC",                                             \
52435593573SXuzhou Cheng             .addr = A_CRC,                                                    \
52535593573SXuzhou Cheng         }, {                                                                  \
52635593573SXuzhou Cheng             .name =  #NAME "_INT_STATUS",                                     \
52735593573SXuzhou Cheng             .addr = A_INT_STATUS,                                             \
52835593573SXuzhou Cheng             .pre_write = int_status_pre_write,                                \
52935593573SXuzhou Cheng             .post_write = int_status_post_write                               \
53035593573SXuzhou Cheng         }, {                                                                  \
53135593573SXuzhou Cheng             .name = #NAME "_INT_ENABLE",                                      \
53235593573SXuzhou Cheng             .addr = A_INT_ENABLE,                                             \
53335593573SXuzhou Cheng             .pre_write = int_enable_pre_write,                                \
53435593573SXuzhou Cheng             .post_write = int_enable_post_write                               \
53535593573SXuzhou Cheng         }, {                                                                  \
53635593573SXuzhou Cheng             .name = #NAME "_INT_DISABLE",                                     \
53735593573SXuzhou Cheng             .addr = A_INT_DISABLE,                                            \
53835593573SXuzhou Cheng             .pre_write = int_disable_pre_write,                               \
53935593573SXuzhou Cheng             .post_write = int_disable_post_write                              \
54035593573SXuzhou Cheng         }, {                                                                  \
54135593573SXuzhou Cheng             .name = #NAME "_INT_MASK",                                        \
54235593573SXuzhou Cheng             .addr = A_INT_MASK,                                               \
54335593573SXuzhou Cheng             .ro = ~0,                                                         \
54435593573SXuzhou Cheng             .reset = XLNX_CSU_DMA_INT_R_MASK                                  \
54535593573SXuzhou Cheng         }, {                                                                  \
54635593573SXuzhou Cheng             .name = #NAME "_CTRL2",                                           \
54735593573SXuzhou Cheng             .addr = A_CTRL2,                                                  \
54835593573SXuzhou Cheng             .reset = ((R_CTRL2_TIMEOUT_PRE_RESET                              \
54935593573SXuzhou Cheng                        << R_CTRL2_TIMEOUT_PRE_SHIFT)                          \
55035593573SXuzhou Cheng                       | (R_CTRL2_MAX_OUTS_CMDS_RESET                          \
55135593573SXuzhou Cheng                          << R_CTRL2_MAX_OUTS_CMDS_SHIFT))                     \
55235593573SXuzhou Cheng         }, {                                                                  \
55335593573SXuzhou Cheng             .name = #NAME "_ADDR_MSB",                                        \
55435593573SXuzhou Cheng             .addr = A_ADDR_MSB,                                               \
55535593573SXuzhou Cheng             .pre_write = addr_msb_pre_write                                   \
55635593573SXuzhou Cheng         }                                                                     \
55735593573SXuzhou Cheng     }
55835593573SXuzhou Cheng 
55935593573SXuzhou Cheng     DMACH_REGINFO(DMA_SRC, true),
56035593573SXuzhou Cheng     DMACH_REGINFO(DMA_DST, false)
56135593573SXuzhou Cheng };
56235593573SXuzhou Cheng 
56335593573SXuzhou Cheng static const MemoryRegionOps xlnx_csu_dma_ops = {
56435593573SXuzhou Cheng     .read = register_read_memory,
56535593573SXuzhou Cheng     .write = register_write_memory,
56635593573SXuzhou Cheng     .endianness = DEVICE_LITTLE_ENDIAN,
56735593573SXuzhou Cheng     .valid = {
56835593573SXuzhou Cheng         .min_access_size = 4,
56935593573SXuzhou Cheng         .max_access_size = 4,
57035593573SXuzhou Cheng     }
57135593573SXuzhou Cheng };
57235593573SXuzhou Cheng 
xlnx_csu_dma_src_timeout_hit(void * opaque)57335593573SXuzhou Cheng static void xlnx_csu_dma_src_timeout_hit(void *opaque)
57435593573SXuzhou Cheng {
57535593573SXuzhou Cheng     XlnxCSUDMA *s = XLNX_CSU_DMA(opaque);
57635593573SXuzhou Cheng 
57735593573SXuzhou Cheng     /* Ignore if the timeout is masked */
57835593573SXuzhou Cheng     if (!xlnx_csu_dma_timeout_enabled(s)) {
57935593573SXuzhou Cheng         return;
58035593573SXuzhou Cheng     }
58135593573SXuzhou Cheng 
58235593573SXuzhou Cheng     s->regs[R_INT_STATUS] |= R_INT_STATUS_TIMEOUT_STRM_MASK;
58335593573SXuzhou Cheng     xlnx_csu_dma_update_irq(s);
58435593573SXuzhou Cheng }
58535593573SXuzhou Cheng 
xlnx_csu_dma_stream_push(StreamSink * obj,uint8_t * buf,size_t len,bool eop)58635593573SXuzhou Cheng static size_t xlnx_csu_dma_stream_push(StreamSink *obj, uint8_t *buf,
58735593573SXuzhou Cheng                                        size_t len, bool eop)
58835593573SXuzhou Cheng {
58935593573SXuzhou Cheng     XlnxCSUDMA *s = XLNX_CSU_DMA(obj);
59035593573SXuzhou Cheng     uint32_t size = s->regs[R_SIZE];
59135593573SXuzhou Cheng     uint32_t mlen = MIN(size, len) & (~3); /* Size is word aligned */
59235593573SXuzhou Cheng 
59335593573SXuzhou Cheng     /* Be called when it's DST */
59435593573SXuzhou Cheng     assert(s->is_dst);
59535593573SXuzhou Cheng 
59635593573SXuzhou Cheng     if (size == 0 || len <= 0) {
59735593573SXuzhou Cheng         return 0;
59835593573SXuzhou Cheng     }
59935593573SXuzhou Cheng 
60035593573SXuzhou Cheng     if (len && (xlnx_csu_dma_is_paused(s) || mlen == 0)) {
60135593573SXuzhou Cheng         qemu_log_mask(LOG_GUEST_ERROR,
60235593573SXuzhou Cheng                       "csu-dma: DST channel dropping %zd b of data.\n", len);
60335593573SXuzhou Cheng         s->regs[R_INT_STATUS] |= R_INT_STATUS_FIFO_OVERFLOW_MASK;
60435593573SXuzhou Cheng         return len;
60535593573SXuzhou Cheng     }
60635593573SXuzhou Cheng 
60735593573SXuzhou Cheng     if (xlnx_csu_dma_write(s, buf, mlen) != mlen) {
60835593573SXuzhou Cheng         return 0;
60935593573SXuzhou Cheng     }
61035593573SXuzhou Cheng 
61135593573SXuzhou Cheng     xlnx_csu_dma_advance(s, mlen);
61235593573SXuzhou Cheng     xlnx_csu_dma_update_irq(s);
61335593573SXuzhou Cheng 
61435593573SXuzhou Cheng     return mlen;
61535593573SXuzhou Cheng }
61635593573SXuzhou Cheng 
xlnx_csu_dma_stream_can_push(StreamSink * obj,StreamCanPushNotifyFn notify,void * notify_opaque)61735593573SXuzhou Cheng static bool xlnx_csu_dma_stream_can_push(StreamSink *obj,
61835593573SXuzhou Cheng                                          StreamCanPushNotifyFn notify,
61935593573SXuzhou Cheng                                          void *notify_opaque)
62035593573SXuzhou Cheng {
62135593573SXuzhou Cheng     XlnxCSUDMA *s = XLNX_CSU_DMA(obj);
62235593573SXuzhou Cheng 
62335593573SXuzhou Cheng     if (s->regs[R_SIZE] != 0) {
62435593573SXuzhou Cheng         return true;
62535593573SXuzhou Cheng     } else {
62635593573SXuzhou Cheng         s->notify = notify;
62735593573SXuzhou Cheng         s->notify_opaque = notify_opaque;
62835593573SXuzhou Cheng         return false;
62935593573SXuzhou Cheng     }
63035593573SXuzhou Cheng }
63135593573SXuzhou Cheng 
xlnx_csu_dma_reset(DeviceState * dev)63235593573SXuzhou Cheng static void xlnx_csu_dma_reset(DeviceState *dev)
63335593573SXuzhou Cheng {
63435593573SXuzhou Cheng     XlnxCSUDMA *s = XLNX_CSU_DMA(dev);
63535593573SXuzhou Cheng     unsigned int i;
63635593573SXuzhou Cheng 
63735593573SXuzhou Cheng     for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
63835593573SXuzhou Cheng         register_reset(&s->regs_info[i]);
63935593573SXuzhou Cheng     }
64035593573SXuzhou Cheng }
64135593573SXuzhou Cheng 
xlnx_csu_dma_realize(DeviceState * dev,Error ** errp)64235593573SXuzhou Cheng static void xlnx_csu_dma_realize(DeviceState *dev, Error **errp)
64335593573SXuzhou Cheng {
64435593573SXuzhou Cheng     XlnxCSUDMA *s = XLNX_CSU_DMA(dev);
64535593573SXuzhou Cheng     RegisterInfoArray *reg_array;
64635593573SXuzhou Cheng 
647348ba7beSPhilippe Mathieu-Daudé     if (!s->is_dst && !s->tx_dev) {
648348ba7beSPhilippe Mathieu-Daudé         error_setg(errp, "zynqmp.csu-dma: Stream not connected");
649348ba7beSPhilippe Mathieu-Daudé         return;
650348ba7beSPhilippe Mathieu-Daudé     }
651348ba7beSPhilippe Mathieu-Daudé 
652c31b7f59SPhilippe Mathieu-Daudé     if (!s->dma_mr) {
653c31b7f59SPhilippe Mathieu-Daudé         error_setg(errp, TYPE_XLNX_CSU_DMA " 'dma' link not set");
654c31b7f59SPhilippe Mathieu-Daudé         return;
655c31b7f59SPhilippe Mathieu-Daudé     }
656c31b7f59SPhilippe Mathieu-Daudé     address_space_init(&s->dma_as, s->dma_mr, "csu-dma");
657c31b7f59SPhilippe Mathieu-Daudé 
65835593573SXuzhou Cheng     reg_array =
65935593573SXuzhou Cheng         register_init_block32(dev, xlnx_csu_dma_regs_info[!!s->is_dst],
66035593573SXuzhou Cheng                               XLNX_CSU_DMA_R_MAX,
66135593573SXuzhou Cheng                               s->regs_info, s->regs,
66235593573SXuzhou Cheng                               &xlnx_csu_dma_ops,
66335593573SXuzhou Cheng                               XLNX_CSU_DMA_ERR_DEBUG,
66435593573SXuzhou Cheng                               XLNX_CSU_DMA_R_MAX * 4);
66535593573SXuzhou Cheng     memory_region_add_subregion(&s->iomem,
66635593573SXuzhou Cheng                                 0x0,
66735593573SXuzhou Cheng                                 &reg_array->mem);
66835593573SXuzhou Cheng 
66935593573SXuzhou Cheng     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
67035593573SXuzhou Cheng     sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
67135593573SXuzhou Cheng 
67235593573SXuzhou Cheng     s->src_timer = ptimer_init(xlnx_csu_dma_src_timeout_hit,
6739598c1bbSPeter Maydell                                s, PTIMER_POLICY_LEGACY);
67435593573SXuzhou Cheng 
67535593573SXuzhou Cheng     s->attr = MEMTXATTRS_UNSPECIFIED;
67635593573SXuzhou Cheng 
67735593573SXuzhou Cheng     s->r_size_last_word = 0;
67835593573SXuzhou Cheng }
67935593573SXuzhou Cheng 
68035593573SXuzhou Cheng static const VMStateDescription vmstate_xlnx_csu_dma = {
68135593573SXuzhou Cheng     .name = TYPE_XLNX_CSU_DMA,
68235593573SXuzhou Cheng     .version_id = 0,
68335593573SXuzhou Cheng     .minimum_version_id = 0,
684*63e6b564SRichard Henderson     .fields = (const VMStateField[]) {
68535593573SXuzhou Cheng         VMSTATE_PTIMER(src_timer, XlnxCSUDMA),
68635593573SXuzhou Cheng         VMSTATE_UINT16(width, XlnxCSUDMA),
68735593573SXuzhou Cheng         VMSTATE_BOOL(is_dst, XlnxCSUDMA),
68835593573SXuzhou Cheng         VMSTATE_BOOL(r_size_last_word, XlnxCSUDMA),
68935593573SXuzhou Cheng         VMSTATE_UINT32_ARRAY(regs, XlnxCSUDMA, XLNX_CSU_DMA_R_MAX),
69035593573SXuzhou Cheng         VMSTATE_END_OF_LIST(),
69135593573SXuzhou Cheng     }
69235593573SXuzhou Cheng };
69335593573SXuzhou Cheng 
69435593573SXuzhou Cheng static Property xlnx_csu_dma_properties[] = {
69535593573SXuzhou Cheng     /*
69635593573SXuzhou Cheng      * Ref PG021, Stream Data Width:
69735593573SXuzhou Cheng      * Data width in bits of the AXI S2MM AXI4-Stream Data bus.
69835593573SXuzhou Cheng      * This value must be equal or less than the Memory Map Data Width.
69935593573SXuzhou Cheng      * Valid values are 8, 16, 32, 64, 128, 512 and 1024.
70035593573SXuzhou Cheng      * "dma-width" is the byte value of the "Stream Data Width".
70135593573SXuzhou Cheng      */
70235593573SXuzhou Cheng     DEFINE_PROP_UINT16("dma-width", XlnxCSUDMA, width, 4),
70335593573SXuzhou Cheng     /*
70435593573SXuzhou Cheng      * The CSU DMA is a two-channel, simple DMA, allowing separate control of
70535593573SXuzhou Cheng      * the SRC (read) channel and DST (write) channel. "is-dst" is used to mark
70635593573SXuzhou Cheng      * which channel the device is connected to.
70735593573SXuzhou Cheng      */
70835593573SXuzhou Cheng     DEFINE_PROP_BOOL("is-dst", XlnxCSUDMA, is_dst, true),
7091f9d714eSPhilippe Mathieu-Daudé     DEFINE_PROP_LINK("stream-connected-dma", XlnxCSUDMA, tx_dev,
7101f9d714eSPhilippe Mathieu-Daudé                      TYPE_STREAM_SINK, StreamSink *),
7111f9d714eSPhilippe Mathieu-Daudé     DEFINE_PROP_LINK("dma", XlnxCSUDMA, dma_mr,
7121f9d714eSPhilippe Mathieu-Daudé                      TYPE_MEMORY_REGION, MemoryRegion *),
71335593573SXuzhou Cheng     DEFINE_PROP_END_OF_LIST(),
71435593573SXuzhou Cheng };
71535593573SXuzhou Cheng 
xlnx_csu_dma_class_init(ObjectClass * klass,void * data)71635593573SXuzhou Cheng static void xlnx_csu_dma_class_init(ObjectClass *klass, void *data)
71735593573SXuzhou Cheng {
71835593573SXuzhou Cheng     DeviceClass *dc = DEVICE_CLASS(klass);
71935593573SXuzhou Cheng     StreamSinkClass *ssc = STREAM_SINK_CLASS(klass);
72000f05c02SFrancisco Iglesias     XlnxCSUDMAClass *xcdc = XLNX_CSU_DMA_CLASS(klass);
72135593573SXuzhou Cheng 
72235593573SXuzhou Cheng     dc->reset = xlnx_csu_dma_reset;
72335593573SXuzhou Cheng     dc->realize = xlnx_csu_dma_realize;
72435593573SXuzhou Cheng     dc->vmsd = &vmstate_xlnx_csu_dma;
72535593573SXuzhou Cheng     device_class_set_props(dc, xlnx_csu_dma_properties);
72635593573SXuzhou Cheng 
72735593573SXuzhou Cheng     ssc->push = xlnx_csu_dma_stream_push;
72835593573SXuzhou Cheng     ssc->can_push = xlnx_csu_dma_stream_can_push;
72900f05c02SFrancisco Iglesias 
73000f05c02SFrancisco Iglesias     xcdc->read = xlnx_csu_dma_class_read;
73135593573SXuzhou Cheng }
73235593573SXuzhou Cheng 
xlnx_csu_dma_init(Object * obj)73335593573SXuzhou Cheng static void xlnx_csu_dma_init(Object *obj)
73435593573SXuzhou Cheng {
73535593573SXuzhou Cheng     XlnxCSUDMA *s = XLNX_CSU_DMA(obj);
73635593573SXuzhou Cheng 
73735593573SXuzhou Cheng     memory_region_init(&s->iomem, obj, TYPE_XLNX_CSU_DMA,
73835593573SXuzhou Cheng                        XLNX_CSU_DMA_R_MAX * 4);
73935593573SXuzhou Cheng }
74035593573SXuzhou Cheng 
74135593573SXuzhou Cheng static const TypeInfo xlnx_csu_dma_info = {
74235593573SXuzhou Cheng     .name          = TYPE_XLNX_CSU_DMA,
74335593573SXuzhou Cheng     .parent        = TYPE_SYS_BUS_DEVICE,
74435593573SXuzhou Cheng     .instance_size = sizeof(XlnxCSUDMA),
74535593573SXuzhou Cheng     .class_init    = xlnx_csu_dma_class_init,
7469a09273fSPeter Maydell     .class_size    = sizeof(XlnxCSUDMAClass),
74735593573SXuzhou Cheng     .instance_init = xlnx_csu_dma_init,
74835593573SXuzhou Cheng     .interfaces = (InterfaceInfo[]) {
74935593573SXuzhou Cheng         { TYPE_STREAM_SINK },
75035593573SXuzhou Cheng         { }
75135593573SXuzhou Cheng     }
75235593573SXuzhou Cheng };
75335593573SXuzhou Cheng 
xlnx_csu_dma_register_types(void)75435593573SXuzhou Cheng static void xlnx_csu_dma_register_types(void)
75535593573SXuzhou Cheng {
75635593573SXuzhou Cheng     type_register_static(&xlnx_csu_dma_info);
75735593573SXuzhou Cheng }
75835593573SXuzhou Cheng 
75935593573SXuzhou Cheng type_init(xlnx_csu_dma_register_types)
760