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 3635593573SXuzhou Cheng * https://www.xilinx.com/html_docs/registers/ug1087/ug1087-zynq-ultrascale-registers.html 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) 4235593573SXuzhou Cheng FIELD(SIZE, SIZE, 2, 27) /* wo */ 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 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 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 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 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 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 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 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 */ 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 */ 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 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 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 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 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 33535593573SXuzhou Cheng static uint64_t size_pre_write(RegisterInfo *reg, uint64_t val) 33635593573SXuzhou Cheng { 33735593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 33835593573SXuzhou Cheng 33935593573SXuzhou Cheng if (s->regs[R_SIZE] != 0) { 34035593573SXuzhou Cheng qemu_log_mask(LOG_GUEST_ERROR, 34135593573SXuzhou Cheng "%s: Starting DMA while already running.\n", __func__); 34235593573SXuzhou Cheng } 34335593573SXuzhou Cheng 34435593573SXuzhou Cheng if (!s->is_dst) { 34535593573SXuzhou Cheng s->r_size_last_word = !!(val & R_SIZE_LAST_WORD_MASK); 34635593573SXuzhou Cheng } 34735593573SXuzhou Cheng 34835593573SXuzhou Cheng /* Size is word aligned */ 34935593573SXuzhou Cheng return val & R_SIZE_SIZE_MASK; 35035593573SXuzhou Cheng } 35135593573SXuzhou Cheng 35235593573SXuzhou Cheng static uint64_t size_post_read(RegisterInfo *reg, uint64_t val) 35335593573SXuzhou Cheng { 35435593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 35535593573SXuzhou Cheng 35635593573SXuzhou Cheng return val | s->r_size_last_word; 35735593573SXuzhou Cheng } 35835593573SXuzhou Cheng 35935593573SXuzhou Cheng static void size_post_write(RegisterInfo *reg, uint64_t val) 36035593573SXuzhou Cheng { 36135593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 36235593573SXuzhou Cheng 36335593573SXuzhou Cheng s->regs[R_STATUS] |= R_STATUS_BUSY_MASK; 36435593573SXuzhou Cheng 36535593573SXuzhou Cheng /* 36635593573SXuzhou Cheng * Note that if SIZE is programmed to 0, and the DMA is started, 36735593573SXuzhou Cheng * the interrupts DONE and MEM_DONE will be asserted. 36835593573SXuzhou Cheng */ 36935593573SXuzhou Cheng if (s->regs[R_SIZE] == 0) { 37035593573SXuzhou Cheng xlnx_csu_dma_done(s); 37135593573SXuzhou Cheng xlnx_csu_dma_update_irq(s); 37235593573SXuzhou Cheng return; 37335593573SXuzhou Cheng } 37435593573SXuzhou Cheng 37535593573SXuzhou Cheng /* Set SIZE is considered the last step in transfer configuration */ 37635593573SXuzhou Cheng if (!s->is_dst) { 37735593573SXuzhou Cheng xlnx_csu_dma_src_notify(s); 37835593573SXuzhou Cheng } else { 37935593573SXuzhou Cheng if (s->notify) { 38035593573SXuzhou Cheng s->notify(s->notify_opaque); 38135593573SXuzhou Cheng } 38235593573SXuzhou Cheng } 38335593573SXuzhou Cheng } 38435593573SXuzhou Cheng 38535593573SXuzhou Cheng static uint64_t status_pre_write(RegisterInfo *reg, uint64_t val) 38635593573SXuzhou Cheng { 38735593573SXuzhou Cheng return val & (R_STATUS_DONE_CNT_MASK | R_STATUS_BUSY_MASK); 38835593573SXuzhou Cheng } 38935593573SXuzhou Cheng 39035593573SXuzhou Cheng static void ctrl_post_write(RegisterInfo *reg, uint64_t val) 39135593573SXuzhou Cheng { 39235593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 39335593573SXuzhou Cheng 39435593573SXuzhou Cheng if (!s->is_dst) { 39535593573SXuzhou Cheng if (!xlnx_csu_dma_is_paused(s)) { 39635593573SXuzhou Cheng xlnx_csu_dma_src_notify(s); 39735593573SXuzhou Cheng } 39835593573SXuzhou Cheng } else { 39935593573SXuzhou Cheng if (!xlnx_csu_dma_is_paused(s) && s->notify) { 40035593573SXuzhou Cheng s->notify(s->notify_opaque); 40135593573SXuzhou Cheng } 40235593573SXuzhou Cheng } 40335593573SXuzhou Cheng } 40435593573SXuzhou Cheng 40535593573SXuzhou Cheng static uint64_t int_status_pre_write(RegisterInfo *reg, uint64_t val) 40635593573SXuzhou Cheng { 40735593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 40835593573SXuzhou Cheng 40935593573SXuzhou Cheng /* DMA counter decrements when flag 'DONE' is cleared */ 41035593573SXuzhou Cheng if ((val & s->regs[R_INT_STATUS] & R_INT_STATUS_DONE_MASK)) { 41135593573SXuzhou Cheng xlnx_csu_dma_update_done_cnt(s, -1); 41235593573SXuzhou Cheng } 41335593573SXuzhou Cheng 41435593573SXuzhou Cheng return s->regs[R_INT_STATUS] & ~val; 41535593573SXuzhou Cheng } 41635593573SXuzhou Cheng 41735593573SXuzhou Cheng static void int_status_post_write(RegisterInfo *reg, uint64_t val) 41835593573SXuzhou Cheng { 41935593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 42035593573SXuzhou Cheng 42135593573SXuzhou Cheng xlnx_csu_dma_update_irq(s); 42235593573SXuzhou Cheng } 42335593573SXuzhou Cheng 42435593573SXuzhou Cheng static uint64_t int_enable_pre_write(RegisterInfo *reg, uint64_t val) 42535593573SXuzhou Cheng { 42635593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 42735593573SXuzhou Cheng uint32_t v32 = val; 42835593573SXuzhou Cheng 42935593573SXuzhou Cheng /* 43035593573SXuzhou Cheng * R_INT_ENABLE doesn't have its own state. 43135593573SXuzhou Cheng * It is used to indirectly modify R_INT_MASK. 43235593573SXuzhou Cheng * 43335593573SXuzhou Cheng * 1: Enable this interrupt field (the mask bit will be cleared to 0) 43435593573SXuzhou Cheng * 0: No effect 43535593573SXuzhou Cheng */ 43635593573SXuzhou Cheng s->regs[R_INT_MASK] &= ~v32; 43735593573SXuzhou Cheng return 0; 43835593573SXuzhou Cheng } 43935593573SXuzhou Cheng 44035593573SXuzhou Cheng static void int_enable_post_write(RegisterInfo *reg, uint64_t val) 44135593573SXuzhou Cheng { 44235593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 44335593573SXuzhou Cheng 44435593573SXuzhou Cheng xlnx_csu_dma_update_irq(s); 44535593573SXuzhou Cheng } 44635593573SXuzhou Cheng 44735593573SXuzhou Cheng static uint64_t int_disable_pre_write(RegisterInfo *reg, uint64_t val) 44835593573SXuzhou Cheng { 44935593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 45035593573SXuzhou Cheng uint32_t v32 = val; 45135593573SXuzhou Cheng 45235593573SXuzhou Cheng /* 45335593573SXuzhou Cheng * R_INT_DISABLE doesn't have its own state. 45435593573SXuzhou Cheng * It is used to indirectly modify R_INT_MASK. 45535593573SXuzhou Cheng * 45635593573SXuzhou Cheng * 1: Disable this interrupt field (the mask bit will be set to 1) 45735593573SXuzhou Cheng * 0: No effect 45835593573SXuzhou Cheng */ 45935593573SXuzhou Cheng s->regs[R_INT_MASK] |= v32; 46035593573SXuzhou Cheng return 0; 46135593573SXuzhou Cheng } 46235593573SXuzhou Cheng 46335593573SXuzhou Cheng static void int_disable_post_write(RegisterInfo *reg, uint64_t val) 46435593573SXuzhou Cheng { 46535593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 46635593573SXuzhou Cheng 46735593573SXuzhou Cheng xlnx_csu_dma_update_irq(s); 46835593573SXuzhou Cheng } 46935593573SXuzhou Cheng 47035593573SXuzhou Cheng static uint64_t addr_msb_pre_write(RegisterInfo *reg, uint64_t val) 47135593573SXuzhou Cheng { 47235593573SXuzhou Cheng return val & R_ADDR_MSB_ADDR_MSB_MASK; 47335593573SXuzhou Cheng } 47435593573SXuzhou Cheng 47500f05c02SFrancisco Iglesias static MemTxResult xlnx_csu_dma_class_read(XlnxCSUDMA *s, hwaddr addr, 47600f05c02SFrancisco Iglesias uint32_t len) 47700f05c02SFrancisco Iglesias { 47800f05c02SFrancisco Iglesias RegisterInfo *reg = &s->regs_info[R_SIZE]; 47900f05c02SFrancisco Iglesias uint64_t we = MAKE_64BIT_MASK(0, 4 * 8); 48000f05c02SFrancisco Iglesias 48100f05c02SFrancisco Iglesias s->regs[R_ADDR] = addr; 48200f05c02SFrancisco Iglesias s->regs[R_ADDR_MSB] = (uint64_t)addr >> 32; 48300f05c02SFrancisco Iglesias 48400f05c02SFrancisco Iglesias register_write(reg, len, we, object_get_typename(OBJECT(s)), false); 48500f05c02SFrancisco Iglesias 48600f05c02SFrancisco Iglesias return (s->regs[R_SIZE] == 0) ? MEMTX_OK : MEMTX_ERROR; 48700f05c02SFrancisco Iglesias } 48800f05c02SFrancisco Iglesias 48935593573SXuzhou Cheng static const RegisterAccessInfo *xlnx_csu_dma_regs_info[] = { 49035593573SXuzhou Cheng #define DMACH_REGINFO(NAME, snd) \ 49135593573SXuzhou Cheng (const RegisterAccessInfo []) { \ 49235593573SXuzhou Cheng { \ 49335593573SXuzhou Cheng .name = #NAME "_ADDR", \ 49435593573SXuzhou Cheng .addr = A_ADDR, \ 49535593573SXuzhou Cheng .pre_write = addr_pre_write \ 49635593573SXuzhou Cheng }, { \ 49735593573SXuzhou Cheng .name = #NAME "_SIZE", \ 49835593573SXuzhou Cheng .addr = A_SIZE, \ 49935593573SXuzhou Cheng .pre_write = size_pre_write, \ 50035593573SXuzhou Cheng .post_write = size_post_write, \ 50135593573SXuzhou Cheng .post_read = size_post_read \ 50235593573SXuzhou Cheng }, { \ 50335593573SXuzhou Cheng .name = #NAME "_STATUS", \ 50435593573SXuzhou Cheng .addr = A_STATUS, \ 50535593573SXuzhou Cheng .pre_write = status_pre_write, \ 50635593573SXuzhou Cheng .w1c = R_STATUS_DONE_CNT_MASK, \ 50735593573SXuzhou Cheng .ro = (R_STATUS_BUSY_MASK \ 50835593573SXuzhou Cheng | R_STATUS_FIFO_LEVEL_MASK \ 50935593573SXuzhou Cheng | R_STATUS_OUTSTANDING_MASK) \ 51035593573SXuzhou Cheng }, { \ 51135593573SXuzhou Cheng .name = #NAME "_CTRL", \ 51235593573SXuzhou Cheng .addr = A_CTRL, \ 51335593573SXuzhou Cheng .post_write = ctrl_post_write, \ 51435593573SXuzhou Cheng .reset = ((R_CTRL_TIMEOUT_VAL_RESET << R_CTRL_TIMEOUT_VAL_SHIFT) \ 51535593573SXuzhou Cheng | (R_CTRL_FIFO_THRESH_RESET << R_CTRL_FIFO_THRESH_SHIFT)\ 51635593573SXuzhou Cheng | (snd ? 0 : R_CTRL_FIFOTHRESH_RESET \ 51735593573SXuzhou Cheng << R_CTRL_FIFOTHRESH_SHIFT)) \ 51835593573SXuzhou Cheng }, { \ 51935593573SXuzhou Cheng .name = #NAME "_CRC", \ 52035593573SXuzhou Cheng .addr = A_CRC, \ 52135593573SXuzhou Cheng }, { \ 52235593573SXuzhou Cheng .name = #NAME "_INT_STATUS", \ 52335593573SXuzhou Cheng .addr = A_INT_STATUS, \ 52435593573SXuzhou Cheng .pre_write = int_status_pre_write, \ 52535593573SXuzhou Cheng .post_write = int_status_post_write \ 52635593573SXuzhou Cheng }, { \ 52735593573SXuzhou Cheng .name = #NAME "_INT_ENABLE", \ 52835593573SXuzhou Cheng .addr = A_INT_ENABLE, \ 52935593573SXuzhou Cheng .pre_write = int_enable_pre_write, \ 53035593573SXuzhou Cheng .post_write = int_enable_post_write \ 53135593573SXuzhou Cheng }, { \ 53235593573SXuzhou Cheng .name = #NAME "_INT_DISABLE", \ 53335593573SXuzhou Cheng .addr = A_INT_DISABLE, \ 53435593573SXuzhou Cheng .pre_write = int_disable_pre_write, \ 53535593573SXuzhou Cheng .post_write = int_disable_post_write \ 53635593573SXuzhou Cheng }, { \ 53735593573SXuzhou Cheng .name = #NAME "_INT_MASK", \ 53835593573SXuzhou Cheng .addr = A_INT_MASK, \ 53935593573SXuzhou Cheng .ro = ~0, \ 54035593573SXuzhou Cheng .reset = XLNX_CSU_DMA_INT_R_MASK \ 54135593573SXuzhou Cheng }, { \ 54235593573SXuzhou Cheng .name = #NAME "_CTRL2", \ 54335593573SXuzhou Cheng .addr = A_CTRL2, \ 54435593573SXuzhou Cheng .reset = ((R_CTRL2_TIMEOUT_PRE_RESET \ 54535593573SXuzhou Cheng << R_CTRL2_TIMEOUT_PRE_SHIFT) \ 54635593573SXuzhou Cheng | (R_CTRL2_MAX_OUTS_CMDS_RESET \ 54735593573SXuzhou Cheng << R_CTRL2_MAX_OUTS_CMDS_SHIFT)) \ 54835593573SXuzhou Cheng }, { \ 54935593573SXuzhou Cheng .name = #NAME "_ADDR_MSB", \ 55035593573SXuzhou Cheng .addr = A_ADDR_MSB, \ 55135593573SXuzhou Cheng .pre_write = addr_msb_pre_write \ 55235593573SXuzhou Cheng } \ 55335593573SXuzhou Cheng } 55435593573SXuzhou Cheng 55535593573SXuzhou Cheng DMACH_REGINFO(DMA_SRC, true), 55635593573SXuzhou Cheng DMACH_REGINFO(DMA_DST, false) 55735593573SXuzhou Cheng }; 55835593573SXuzhou Cheng 55935593573SXuzhou Cheng static const MemoryRegionOps xlnx_csu_dma_ops = { 56035593573SXuzhou Cheng .read = register_read_memory, 56135593573SXuzhou Cheng .write = register_write_memory, 56235593573SXuzhou Cheng .endianness = DEVICE_LITTLE_ENDIAN, 56335593573SXuzhou Cheng .valid = { 56435593573SXuzhou Cheng .min_access_size = 4, 56535593573SXuzhou Cheng .max_access_size = 4, 56635593573SXuzhou Cheng } 56735593573SXuzhou Cheng }; 56835593573SXuzhou Cheng 56935593573SXuzhou Cheng static void xlnx_csu_dma_src_timeout_hit(void *opaque) 57035593573SXuzhou Cheng { 57135593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(opaque); 57235593573SXuzhou Cheng 57335593573SXuzhou Cheng /* Ignore if the timeout is masked */ 57435593573SXuzhou Cheng if (!xlnx_csu_dma_timeout_enabled(s)) { 57535593573SXuzhou Cheng return; 57635593573SXuzhou Cheng } 57735593573SXuzhou Cheng 57835593573SXuzhou Cheng s->regs[R_INT_STATUS] |= R_INT_STATUS_TIMEOUT_STRM_MASK; 57935593573SXuzhou Cheng xlnx_csu_dma_update_irq(s); 58035593573SXuzhou Cheng } 58135593573SXuzhou Cheng 58235593573SXuzhou Cheng static size_t xlnx_csu_dma_stream_push(StreamSink *obj, uint8_t *buf, 58335593573SXuzhou Cheng size_t len, bool eop) 58435593573SXuzhou Cheng { 58535593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(obj); 58635593573SXuzhou Cheng uint32_t size = s->regs[R_SIZE]; 58735593573SXuzhou Cheng uint32_t mlen = MIN(size, len) & (~3); /* Size is word aligned */ 58835593573SXuzhou Cheng 58935593573SXuzhou Cheng /* Be called when it's DST */ 59035593573SXuzhou Cheng assert(s->is_dst); 59135593573SXuzhou Cheng 59235593573SXuzhou Cheng if (size == 0 || len <= 0) { 59335593573SXuzhou Cheng return 0; 59435593573SXuzhou Cheng } 59535593573SXuzhou Cheng 59635593573SXuzhou Cheng if (len && (xlnx_csu_dma_is_paused(s) || mlen == 0)) { 59735593573SXuzhou Cheng qemu_log_mask(LOG_GUEST_ERROR, 59835593573SXuzhou Cheng "csu-dma: DST channel dropping %zd b of data.\n", len); 59935593573SXuzhou Cheng s->regs[R_INT_STATUS] |= R_INT_STATUS_FIFO_OVERFLOW_MASK; 60035593573SXuzhou Cheng return len; 60135593573SXuzhou Cheng } 60235593573SXuzhou Cheng 60335593573SXuzhou Cheng if (xlnx_csu_dma_write(s, buf, mlen) != mlen) { 60435593573SXuzhou Cheng return 0; 60535593573SXuzhou Cheng } 60635593573SXuzhou Cheng 60735593573SXuzhou Cheng xlnx_csu_dma_advance(s, mlen); 60835593573SXuzhou Cheng xlnx_csu_dma_update_irq(s); 60935593573SXuzhou Cheng 61035593573SXuzhou Cheng return mlen; 61135593573SXuzhou Cheng } 61235593573SXuzhou Cheng 61335593573SXuzhou Cheng static bool xlnx_csu_dma_stream_can_push(StreamSink *obj, 61435593573SXuzhou Cheng StreamCanPushNotifyFn notify, 61535593573SXuzhou Cheng void *notify_opaque) 61635593573SXuzhou Cheng { 61735593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(obj); 61835593573SXuzhou Cheng 61935593573SXuzhou Cheng if (s->regs[R_SIZE] != 0) { 62035593573SXuzhou Cheng return true; 62135593573SXuzhou Cheng } else { 62235593573SXuzhou Cheng s->notify = notify; 62335593573SXuzhou Cheng s->notify_opaque = notify_opaque; 62435593573SXuzhou Cheng return false; 62535593573SXuzhou Cheng } 62635593573SXuzhou Cheng } 62735593573SXuzhou Cheng 62835593573SXuzhou Cheng static void xlnx_csu_dma_reset(DeviceState *dev) 62935593573SXuzhou Cheng { 63035593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(dev); 63135593573SXuzhou Cheng unsigned int i; 63235593573SXuzhou Cheng 63335593573SXuzhou Cheng for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { 63435593573SXuzhou Cheng register_reset(&s->regs_info[i]); 63535593573SXuzhou Cheng } 63635593573SXuzhou Cheng } 63735593573SXuzhou Cheng 63835593573SXuzhou Cheng static void xlnx_csu_dma_realize(DeviceState *dev, Error **errp) 63935593573SXuzhou Cheng { 64035593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(dev); 64135593573SXuzhou Cheng RegisterInfoArray *reg_array; 64235593573SXuzhou Cheng 643348ba7beSPhilippe Mathieu-Daudé if (!s->is_dst && !s->tx_dev) { 644348ba7beSPhilippe Mathieu-Daudé error_setg(errp, "zynqmp.csu-dma: Stream not connected"); 645348ba7beSPhilippe Mathieu-Daudé return; 646348ba7beSPhilippe Mathieu-Daudé } 647348ba7beSPhilippe Mathieu-Daudé 648c31b7f59SPhilippe Mathieu-Daudé if (!s->dma_mr) { 649c31b7f59SPhilippe Mathieu-Daudé error_setg(errp, TYPE_XLNX_CSU_DMA " 'dma' link not set"); 650c31b7f59SPhilippe Mathieu-Daudé return; 651c31b7f59SPhilippe Mathieu-Daudé } 652c31b7f59SPhilippe Mathieu-Daudé address_space_init(&s->dma_as, s->dma_mr, "csu-dma"); 653c31b7f59SPhilippe Mathieu-Daudé 65435593573SXuzhou Cheng reg_array = 65535593573SXuzhou Cheng register_init_block32(dev, xlnx_csu_dma_regs_info[!!s->is_dst], 65635593573SXuzhou Cheng XLNX_CSU_DMA_R_MAX, 65735593573SXuzhou Cheng s->regs_info, s->regs, 65835593573SXuzhou Cheng &xlnx_csu_dma_ops, 65935593573SXuzhou Cheng XLNX_CSU_DMA_ERR_DEBUG, 66035593573SXuzhou Cheng XLNX_CSU_DMA_R_MAX * 4); 66135593573SXuzhou Cheng memory_region_add_subregion(&s->iomem, 66235593573SXuzhou Cheng 0x0, 66335593573SXuzhou Cheng ®_array->mem); 66435593573SXuzhou Cheng 66535593573SXuzhou Cheng sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); 66635593573SXuzhou Cheng sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); 66735593573SXuzhou Cheng 66835593573SXuzhou Cheng s->src_timer = ptimer_init(xlnx_csu_dma_src_timeout_hit, 6699598c1bbSPeter Maydell s, PTIMER_POLICY_LEGACY); 67035593573SXuzhou Cheng 67135593573SXuzhou Cheng s->attr = MEMTXATTRS_UNSPECIFIED; 67235593573SXuzhou Cheng 67335593573SXuzhou Cheng s->r_size_last_word = 0; 67435593573SXuzhou Cheng } 67535593573SXuzhou Cheng 67635593573SXuzhou Cheng static const VMStateDescription vmstate_xlnx_csu_dma = { 67735593573SXuzhou Cheng .name = TYPE_XLNX_CSU_DMA, 67835593573SXuzhou Cheng .version_id = 0, 67935593573SXuzhou Cheng .minimum_version_id = 0, 68035593573SXuzhou Cheng .fields = (VMStateField[]) { 68135593573SXuzhou Cheng VMSTATE_PTIMER(src_timer, XlnxCSUDMA), 68235593573SXuzhou Cheng VMSTATE_UINT16(width, XlnxCSUDMA), 68335593573SXuzhou Cheng VMSTATE_BOOL(is_dst, XlnxCSUDMA), 68435593573SXuzhou Cheng VMSTATE_BOOL(r_size_last_word, XlnxCSUDMA), 68535593573SXuzhou Cheng VMSTATE_UINT32_ARRAY(regs, XlnxCSUDMA, XLNX_CSU_DMA_R_MAX), 68635593573SXuzhou Cheng VMSTATE_END_OF_LIST(), 68735593573SXuzhou Cheng } 68835593573SXuzhou Cheng }; 68935593573SXuzhou Cheng 69035593573SXuzhou Cheng static Property xlnx_csu_dma_properties[] = { 69135593573SXuzhou Cheng /* 69235593573SXuzhou Cheng * Ref PG021, Stream Data Width: 69335593573SXuzhou Cheng * Data width in bits of the AXI S2MM AXI4-Stream Data bus. 69435593573SXuzhou Cheng * This value must be equal or less than the Memory Map Data Width. 69535593573SXuzhou Cheng * Valid values are 8, 16, 32, 64, 128, 512 and 1024. 69635593573SXuzhou Cheng * "dma-width" is the byte value of the "Stream Data Width". 69735593573SXuzhou Cheng */ 69835593573SXuzhou Cheng DEFINE_PROP_UINT16("dma-width", XlnxCSUDMA, width, 4), 69935593573SXuzhou Cheng /* 70035593573SXuzhou Cheng * The CSU DMA is a two-channel, simple DMA, allowing separate control of 70135593573SXuzhou Cheng * the SRC (read) channel and DST (write) channel. "is-dst" is used to mark 70235593573SXuzhou Cheng * which channel the device is connected to. 70335593573SXuzhou Cheng */ 70435593573SXuzhou Cheng DEFINE_PROP_BOOL("is-dst", XlnxCSUDMA, is_dst, true), 7051f9d714eSPhilippe Mathieu-Daudé DEFINE_PROP_LINK("stream-connected-dma", XlnxCSUDMA, tx_dev, 7061f9d714eSPhilippe Mathieu-Daudé TYPE_STREAM_SINK, StreamSink *), 7071f9d714eSPhilippe Mathieu-Daudé DEFINE_PROP_LINK("dma", XlnxCSUDMA, dma_mr, 7081f9d714eSPhilippe Mathieu-Daudé TYPE_MEMORY_REGION, MemoryRegion *), 70935593573SXuzhou Cheng DEFINE_PROP_END_OF_LIST(), 71035593573SXuzhou Cheng }; 71135593573SXuzhou Cheng 71235593573SXuzhou Cheng static void xlnx_csu_dma_class_init(ObjectClass *klass, void *data) 71335593573SXuzhou Cheng { 71435593573SXuzhou Cheng DeviceClass *dc = DEVICE_CLASS(klass); 71535593573SXuzhou Cheng StreamSinkClass *ssc = STREAM_SINK_CLASS(klass); 71600f05c02SFrancisco Iglesias XlnxCSUDMAClass *xcdc = XLNX_CSU_DMA_CLASS(klass); 71735593573SXuzhou Cheng 71835593573SXuzhou Cheng dc->reset = xlnx_csu_dma_reset; 71935593573SXuzhou Cheng dc->realize = xlnx_csu_dma_realize; 72035593573SXuzhou Cheng dc->vmsd = &vmstate_xlnx_csu_dma; 72135593573SXuzhou Cheng device_class_set_props(dc, xlnx_csu_dma_properties); 72235593573SXuzhou Cheng 72335593573SXuzhou Cheng ssc->push = xlnx_csu_dma_stream_push; 72435593573SXuzhou Cheng ssc->can_push = xlnx_csu_dma_stream_can_push; 72500f05c02SFrancisco Iglesias 72600f05c02SFrancisco Iglesias xcdc->read = xlnx_csu_dma_class_read; 72735593573SXuzhou Cheng } 72835593573SXuzhou Cheng 72935593573SXuzhou Cheng static void xlnx_csu_dma_init(Object *obj) 73035593573SXuzhou Cheng { 73135593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(obj); 73235593573SXuzhou Cheng 73335593573SXuzhou Cheng memory_region_init(&s->iomem, obj, TYPE_XLNX_CSU_DMA, 73435593573SXuzhou Cheng XLNX_CSU_DMA_R_MAX * 4); 73535593573SXuzhou Cheng } 73635593573SXuzhou Cheng 73735593573SXuzhou Cheng static const TypeInfo xlnx_csu_dma_info = { 73835593573SXuzhou Cheng .name = TYPE_XLNX_CSU_DMA, 73935593573SXuzhou Cheng .parent = TYPE_SYS_BUS_DEVICE, 74035593573SXuzhou Cheng .instance_size = sizeof(XlnxCSUDMA), 74135593573SXuzhou Cheng .class_init = xlnx_csu_dma_class_init, 7429a09273fSPeter Maydell .class_size = sizeof(XlnxCSUDMAClass), 74335593573SXuzhou Cheng .instance_init = xlnx_csu_dma_init, 74435593573SXuzhou Cheng .interfaces = (InterfaceInfo[]) { 74535593573SXuzhou Cheng { TYPE_STREAM_SINK }, 74635593573SXuzhou Cheng { } 74735593573SXuzhou Cheng } 74835593573SXuzhou Cheng }; 74935593573SXuzhou Cheng 75035593573SXuzhou Cheng static void xlnx_csu_dma_register_types(void) 75135593573SXuzhou Cheng { 75235593573SXuzhou Cheng type_register_static(&xlnx_csu_dma_info); 75335593573SXuzhou Cheng } 75435593573SXuzhou Cheng 75535593573SXuzhou Cheng type_init(xlnx_csu_dma_register_types) 756