1*35593573SXuzhou Cheng /* 2*35593573SXuzhou Cheng * Xilinx Platform CSU Stream DMA emulation 3*35593573SXuzhou Cheng * 4*35593573SXuzhou Cheng * This implementation is based on 5*35593573SXuzhou Cheng * https://github.com/Xilinx/qemu/blob/master/hw/dma/csu_stream_dma.c 6*35593573SXuzhou Cheng * 7*35593573SXuzhou Cheng * This program is free software; you can redistribute it and/or 8*35593573SXuzhou Cheng * modify it under the terms of the GNU General Public License as 9*35593573SXuzhou Cheng * published by the Free Software Foundation; either version 2 or 10*35593573SXuzhou Cheng * (at your option) version 3 of the License. 11*35593573SXuzhou Cheng * 12*35593573SXuzhou Cheng * This program is distributed in the hope that it will be useful, 13*35593573SXuzhou Cheng * but WITHOUT ANY WARRANTY; without even the implied warranty of 14*35593573SXuzhou Cheng * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15*35593573SXuzhou Cheng * GNU General Public License for more details. 16*35593573SXuzhou Cheng * 17*35593573SXuzhou Cheng * You should have received a copy of the GNU General Public License along 18*35593573SXuzhou Cheng * with this program; if not, see <http://www.gnu.org/licenses/>. 19*35593573SXuzhou Cheng */ 20*35593573SXuzhou Cheng 21*35593573SXuzhou Cheng #include "qemu/osdep.h" 22*35593573SXuzhou Cheng #include "qemu/log.h" 23*35593573SXuzhou Cheng #include "qapi/error.h" 24*35593573SXuzhou Cheng #include "hw/hw.h" 25*35593573SXuzhou Cheng #include "hw/irq.h" 26*35593573SXuzhou Cheng #include "hw/qdev-properties.h" 27*35593573SXuzhou Cheng #include "hw/sysbus.h" 28*35593573SXuzhou Cheng #include "migration/vmstate.h" 29*35593573SXuzhou Cheng #include "sysemu/dma.h" 30*35593573SXuzhou Cheng #include "hw/ptimer.h" 31*35593573SXuzhou Cheng #include "hw/stream.h" 32*35593573SXuzhou Cheng #include "hw/register.h" 33*35593573SXuzhou Cheng #include "hw/dma/xlnx_csu_dma.h" 34*35593573SXuzhou Cheng 35*35593573SXuzhou Cheng /* 36*35593573SXuzhou Cheng * Ref: UG1087 (v1.7) February 8, 2019 37*35593573SXuzhou Cheng * https://www.xilinx.com/html_docs/registers/ug1087/ug1087-zynq-ultrascale-registers.html 38*35593573SXuzhou Cheng * CSUDMA Module section 39*35593573SXuzhou Cheng */ 40*35593573SXuzhou Cheng REG32(ADDR, 0x0) 41*35593573SXuzhou Cheng FIELD(ADDR, ADDR, 2, 30) /* wo */ 42*35593573SXuzhou Cheng REG32(SIZE, 0x4) 43*35593573SXuzhou Cheng FIELD(SIZE, SIZE, 2, 27) /* wo */ 44*35593573SXuzhou Cheng FIELD(SIZE, LAST_WORD, 0, 1) /* rw, only exists in SRC */ 45*35593573SXuzhou Cheng REG32(STATUS, 0x8) 46*35593573SXuzhou Cheng FIELD(STATUS, DONE_CNT, 13, 3) /* wtc */ 47*35593573SXuzhou Cheng FIELD(STATUS, FIFO_LEVEL, 5, 8) /* ro */ 48*35593573SXuzhou Cheng FIELD(STATUS, OUTSTANDING, 1, 4) /* ro */ 49*35593573SXuzhou Cheng FIELD(STATUS, BUSY, 0, 1) /* ro */ 50*35593573SXuzhou Cheng REG32(CTRL, 0xc) 51*35593573SXuzhou Cheng FIELD(CTRL, FIFOTHRESH, 25, 7) /* rw, only exists in DST, reset 0x40 */ 52*35593573SXuzhou Cheng FIELD(CTRL, APB_ERR_RESP, 24, 1) /* rw */ 53*35593573SXuzhou Cheng FIELD(CTRL, ENDIANNESS, 23, 1) /* rw */ 54*35593573SXuzhou Cheng FIELD(CTRL, AXI_BRST_TYPE, 22, 1) /* rw */ 55*35593573SXuzhou Cheng FIELD(CTRL, TIMEOUT_VAL, 10, 12) /* rw, reset: 0xFFE */ 56*35593573SXuzhou Cheng FIELD(CTRL, FIFO_THRESH, 2, 8) /* rw, reset: 0x80 */ 57*35593573SXuzhou Cheng FIELD(CTRL, PAUSE_STRM, 1, 1) /* rw */ 58*35593573SXuzhou Cheng FIELD(CTRL, PAUSE_MEM, 0, 1) /* rw */ 59*35593573SXuzhou Cheng REG32(CRC, 0x10) 60*35593573SXuzhou Cheng REG32(INT_STATUS, 0x14) 61*35593573SXuzhou Cheng FIELD(INT_STATUS, FIFO_OVERFLOW, 7, 1) /* wtc */ 62*35593573SXuzhou Cheng FIELD(INT_STATUS, INVALID_APB, 6, 1) /* wtc */ 63*35593573SXuzhou Cheng FIELD(INT_STATUS, THRESH_HIT, 5, 1) /* wtc */ 64*35593573SXuzhou Cheng FIELD(INT_STATUS, TIMEOUT_MEM, 4, 1) /* wtc */ 65*35593573SXuzhou Cheng FIELD(INT_STATUS, TIMEOUT_STRM, 3, 1) /* wtc */ 66*35593573SXuzhou Cheng FIELD(INT_STATUS, AXI_BRESP_ERR, 2, 1) /* wtc, SRC: AXI_RDERR */ 67*35593573SXuzhou Cheng FIELD(INT_STATUS, DONE, 1, 1) /* wtc */ 68*35593573SXuzhou Cheng FIELD(INT_STATUS, MEM_DONE, 0, 1) /* wtc */ 69*35593573SXuzhou Cheng REG32(INT_ENABLE, 0x18) 70*35593573SXuzhou Cheng FIELD(INT_ENABLE, FIFO_OVERFLOW, 7, 1) /* wtc */ 71*35593573SXuzhou Cheng FIELD(INT_ENABLE, INVALID_APB, 6, 1) /* wtc */ 72*35593573SXuzhou Cheng FIELD(INT_ENABLE, THRESH_HIT, 5, 1) /* wtc */ 73*35593573SXuzhou Cheng FIELD(INT_ENABLE, TIMEOUT_MEM, 4, 1) /* wtc */ 74*35593573SXuzhou Cheng FIELD(INT_ENABLE, TIMEOUT_STRM, 3, 1) /* wtc */ 75*35593573SXuzhou Cheng FIELD(INT_ENABLE, AXI_BRESP_ERR, 2, 1) /* wtc, SRC: AXI_RDERR */ 76*35593573SXuzhou Cheng FIELD(INT_ENABLE, DONE, 1, 1) /* wtc */ 77*35593573SXuzhou Cheng FIELD(INT_ENABLE, MEM_DONE, 0, 1) /* wtc */ 78*35593573SXuzhou Cheng REG32(INT_DISABLE, 0x1c) 79*35593573SXuzhou Cheng FIELD(INT_DISABLE, FIFO_OVERFLOW, 7, 1) /* wtc */ 80*35593573SXuzhou Cheng FIELD(INT_DISABLE, INVALID_APB, 6, 1) /* wtc */ 81*35593573SXuzhou Cheng FIELD(INT_DISABLE, THRESH_HIT, 5, 1) /* wtc */ 82*35593573SXuzhou Cheng FIELD(INT_DISABLE, TIMEOUT_MEM, 4, 1) /* wtc */ 83*35593573SXuzhou Cheng FIELD(INT_DISABLE, TIMEOUT_STRM, 3, 1) /* wtc */ 84*35593573SXuzhou Cheng FIELD(INT_DISABLE, AXI_BRESP_ERR, 2, 1) /* wtc, SRC: AXI_RDERR */ 85*35593573SXuzhou Cheng FIELD(INT_DISABLE, DONE, 1, 1) /* wtc */ 86*35593573SXuzhou Cheng FIELD(INT_DISABLE, MEM_DONE, 0, 1) /* wtc */ 87*35593573SXuzhou Cheng REG32(INT_MASK, 0x20) 88*35593573SXuzhou Cheng FIELD(INT_MASK, FIFO_OVERFLOW, 7, 1) /* ro, reset: 0x1 */ 89*35593573SXuzhou Cheng FIELD(INT_MASK, INVALID_APB, 6, 1) /* ro, reset: 0x1 */ 90*35593573SXuzhou Cheng FIELD(INT_MASK, THRESH_HIT, 5, 1) /* ro, reset: 0x1 */ 91*35593573SXuzhou Cheng FIELD(INT_MASK, TIMEOUT_MEM, 4, 1) /* ro, reset: 0x1 */ 92*35593573SXuzhou Cheng FIELD(INT_MASK, TIMEOUT_STRM, 3, 1) /* ro, reset: 0x1 */ 93*35593573SXuzhou Cheng FIELD(INT_MASK, AXI_BRESP_ERR, 2, 1) /* ro, reset: 0x1, SRC: AXI_RDERR */ 94*35593573SXuzhou Cheng FIELD(INT_MASK, DONE, 1, 1) /* ro, reset: 0x1 */ 95*35593573SXuzhou Cheng FIELD(INT_MASK, MEM_DONE, 0, 1) /* ro, reset: 0x1 */ 96*35593573SXuzhou Cheng REG32(CTRL2, 0x24) 97*35593573SXuzhou Cheng FIELD(CTRL2, ARCACHE, 24, 3) /* rw */ 98*35593573SXuzhou Cheng FIELD(CTRL2, ROUTE_BIT, 23, 1) /* rw */ 99*35593573SXuzhou Cheng FIELD(CTRL2, TIMEOUT_EN, 22, 1) /* rw */ 100*35593573SXuzhou Cheng FIELD(CTRL2, TIMEOUT_PRE, 4, 12) /* rw, reset: 0xFFF */ 101*35593573SXuzhou Cheng FIELD(CTRL2, MAX_OUTS_CMDS, 0, 4) /* rw, reset: 0x8 */ 102*35593573SXuzhou Cheng REG32(ADDR_MSB, 0x28) 103*35593573SXuzhou Cheng FIELD(ADDR_MSB, ADDR_MSB, 0, 17) /* wo */ 104*35593573SXuzhou Cheng 105*35593573SXuzhou Cheng #define R_CTRL_TIMEOUT_VAL_RESET (0xFFE) 106*35593573SXuzhou Cheng #define R_CTRL_FIFO_THRESH_RESET (0x80) 107*35593573SXuzhou Cheng #define R_CTRL_FIFOTHRESH_RESET (0x40) 108*35593573SXuzhou Cheng 109*35593573SXuzhou Cheng #define R_CTRL2_TIMEOUT_PRE_RESET (0xFFF) 110*35593573SXuzhou Cheng #define R_CTRL2_MAX_OUTS_CMDS_RESET (0x8) 111*35593573SXuzhou Cheng 112*35593573SXuzhou Cheng #define XLNX_CSU_DMA_ERR_DEBUG (0) 113*35593573SXuzhou Cheng #define XLNX_CSU_DMA_INT_R_MASK (0xff) 114*35593573SXuzhou Cheng 115*35593573SXuzhou Cheng /* UG1807: Set the prescaler value for the timeout in clk (~2.5ns) cycles */ 116*35593573SXuzhou Cheng #define XLNX_CSU_DMA_TIMER_FREQ (400 * 1000 * 1000) 117*35593573SXuzhou Cheng 118*35593573SXuzhou Cheng static bool xlnx_csu_dma_is_paused(XlnxCSUDMA *s) 119*35593573SXuzhou Cheng { 120*35593573SXuzhou Cheng bool paused; 121*35593573SXuzhou Cheng 122*35593573SXuzhou Cheng paused = !!(s->regs[R_CTRL] & R_CTRL_PAUSE_STRM_MASK); 123*35593573SXuzhou Cheng paused |= !!(s->regs[R_CTRL] & R_CTRL_PAUSE_MEM_MASK); 124*35593573SXuzhou Cheng 125*35593573SXuzhou Cheng return paused; 126*35593573SXuzhou Cheng } 127*35593573SXuzhou Cheng 128*35593573SXuzhou Cheng static bool xlnx_csu_dma_get_eop(XlnxCSUDMA *s) 129*35593573SXuzhou Cheng { 130*35593573SXuzhou Cheng return s->r_size_last_word; 131*35593573SXuzhou Cheng } 132*35593573SXuzhou Cheng 133*35593573SXuzhou Cheng static bool xlnx_csu_dma_burst_is_fixed(XlnxCSUDMA *s) 134*35593573SXuzhou Cheng { 135*35593573SXuzhou Cheng return !!(s->regs[R_CTRL] & R_CTRL_AXI_BRST_TYPE_MASK); 136*35593573SXuzhou Cheng } 137*35593573SXuzhou Cheng 138*35593573SXuzhou Cheng static bool xlnx_csu_dma_timeout_enabled(XlnxCSUDMA *s) 139*35593573SXuzhou Cheng { 140*35593573SXuzhou Cheng return !!(s->regs[R_CTRL2] & R_CTRL2_TIMEOUT_EN_MASK); 141*35593573SXuzhou Cheng } 142*35593573SXuzhou Cheng 143*35593573SXuzhou Cheng static void xlnx_csu_dma_update_done_cnt(XlnxCSUDMA *s, int a) 144*35593573SXuzhou Cheng { 145*35593573SXuzhou Cheng int cnt; 146*35593573SXuzhou Cheng 147*35593573SXuzhou Cheng /* Increase DONE_CNT */ 148*35593573SXuzhou Cheng cnt = ARRAY_FIELD_EX32(s->regs, STATUS, DONE_CNT) + a; 149*35593573SXuzhou Cheng ARRAY_FIELD_DP32(s->regs, STATUS, DONE_CNT, cnt); 150*35593573SXuzhou Cheng } 151*35593573SXuzhou Cheng 152*35593573SXuzhou Cheng static void xlnx_csu_dma_data_process(XlnxCSUDMA *s, uint8_t *buf, uint32_t len) 153*35593573SXuzhou Cheng { 154*35593573SXuzhou Cheng uint32_t bswap; 155*35593573SXuzhou Cheng uint32_t i; 156*35593573SXuzhou Cheng 157*35593573SXuzhou Cheng bswap = s->regs[R_CTRL] & R_CTRL_ENDIANNESS_MASK; 158*35593573SXuzhou Cheng if (s->is_dst && !bswap) { 159*35593573SXuzhou Cheng /* Fast when ENDIANNESS cleared */ 160*35593573SXuzhou Cheng return; 161*35593573SXuzhou Cheng } 162*35593573SXuzhou Cheng 163*35593573SXuzhou Cheng for (i = 0; i < len; i += 4) { 164*35593573SXuzhou Cheng uint8_t *b = &buf[i]; 165*35593573SXuzhou Cheng union { 166*35593573SXuzhou Cheng uint8_t u8[4]; 167*35593573SXuzhou Cheng uint32_t u32; 168*35593573SXuzhou Cheng } v = { 169*35593573SXuzhou Cheng .u8 = { b[0], b[1], b[2], b[3] } 170*35593573SXuzhou Cheng }; 171*35593573SXuzhou Cheng 172*35593573SXuzhou Cheng if (!s->is_dst) { 173*35593573SXuzhou Cheng s->regs[R_CRC] += v.u32; 174*35593573SXuzhou Cheng } 175*35593573SXuzhou Cheng if (bswap) { 176*35593573SXuzhou Cheng /* 177*35593573SXuzhou Cheng * No point using bswap, we need to writeback 178*35593573SXuzhou Cheng * into a potentially unaligned pointer. 179*35593573SXuzhou Cheng */ 180*35593573SXuzhou Cheng b[0] = v.u8[3]; 181*35593573SXuzhou Cheng b[1] = v.u8[2]; 182*35593573SXuzhou Cheng b[2] = v.u8[1]; 183*35593573SXuzhou Cheng b[3] = v.u8[0]; 184*35593573SXuzhou Cheng } 185*35593573SXuzhou Cheng } 186*35593573SXuzhou Cheng } 187*35593573SXuzhou Cheng 188*35593573SXuzhou Cheng static void xlnx_csu_dma_update_irq(XlnxCSUDMA *s) 189*35593573SXuzhou Cheng { 190*35593573SXuzhou Cheng qemu_set_irq(s->irq, !!(s->regs[R_INT_STATUS] & ~s->regs[R_INT_MASK])); 191*35593573SXuzhou Cheng } 192*35593573SXuzhou Cheng 193*35593573SXuzhou Cheng /* len is in bytes */ 194*35593573SXuzhou Cheng static uint32_t xlnx_csu_dma_read(XlnxCSUDMA *s, uint8_t *buf, uint32_t len) 195*35593573SXuzhou Cheng { 196*35593573SXuzhou Cheng hwaddr addr = (hwaddr)s->regs[R_ADDR_MSB] << 32 | s->regs[R_ADDR]; 197*35593573SXuzhou Cheng MemTxResult result = MEMTX_OK; 198*35593573SXuzhou Cheng 199*35593573SXuzhou Cheng if (xlnx_csu_dma_burst_is_fixed(s)) { 200*35593573SXuzhou Cheng uint32_t i; 201*35593573SXuzhou Cheng 202*35593573SXuzhou Cheng for (i = 0; i < len && (result == MEMTX_OK); i += s->width) { 203*35593573SXuzhou Cheng uint32_t mlen = MIN(len - i, s->width); 204*35593573SXuzhou Cheng 205*35593573SXuzhou Cheng result = address_space_rw(s->dma_as, addr, s->attr, 206*35593573SXuzhou Cheng buf + i, mlen, false); 207*35593573SXuzhou Cheng } 208*35593573SXuzhou Cheng } else { 209*35593573SXuzhou Cheng result = address_space_rw(s->dma_as, addr, s->attr, buf, len, false); 210*35593573SXuzhou Cheng } 211*35593573SXuzhou Cheng 212*35593573SXuzhou Cheng if (result == MEMTX_OK) { 213*35593573SXuzhou Cheng xlnx_csu_dma_data_process(s, buf, len); 214*35593573SXuzhou Cheng } else { 215*35593573SXuzhou Cheng qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address " TARGET_FMT_plx 216*35593573SXuzhou Cheng " for mem read", __func__, addr); 217*35593573SXuzhou Cheng s->regs[R_INT_STATUS] |= R_INT_STATUS_AXI_BRESP_ERR_MASK; 218*35593573SXuzhou Cheng xlnx_csu_dma_update_irq(s); 219*35593573SXuzhou Cheng } 220*35593573SXuzhou Cheng return len; 221*35593573SXuzhou Cheng } 222*35593573SXuzhou Cheng 223*35593573SXuzhou Cheng /* len is in bytes */ 224*35593573SXuzhou Cheng static uint32_t xlnx_csu_dma_write(XlnxCSUDMA *s, uint8_t *buf, uint32_t len) 225*35593573SXuzhou Cheng { 226*35593573SXuzhou Cheng hwaddr addr = (hwaddr)s->regs[R_ADDR_MSB] << 32 | s->regs[R_ADDR]; 227*35593573SXuzhou Cheng MemTxResult result = MEMTX_OK; 228*35593573SXuzhou Cheng 229*35593573SXuzhou Cheng xlnx_csu_dma_data_process(s, buf, len); 230*35593573SXuzhou Cheng if (xlnx_csu_dma_burst_is_fixed(s)) { 231*35593573SXuzhou Cheng uint32_t i; 232*35593573SXuzhou Cheng 233*35593573SXuzhou Cheng for (i = 0; i < len && (result == MEMTX_OK); i += s->width) { 234*35593573SXuzhou Cheng uint32_t mlen = MIN(len - i, s->width); 235*35593573SXuzhou Cheng 236*35593573SXuzhou Cheng result = address_space_rw(s->dma_as, addr, s->attr, 237*35593573SXuzhou Cheng buf, mlen, true); 238*35593573SXuzhou Cheng buf += mlen; 239*35593573SXuzhou Cheng } 240*35593573SXuzhou Cheng } else { 241*35593573SXuzhou Cheng result = address_space_rw(s->dma_as, addr, s->attr, buf, len, true); 242*35593573SXuzhou Cheng } 243*35593573SXuzhou Cheng 244*35593573SXuzhou Cheng if (result != MEMTX_OK) { 245*35593573SXuzhou Cheng qemu_log_mask(LOG_GUEST_ERROR, "%s: Bad address " TARGET_FMT_plx 246*35593573SXuzhou Cheng " for mem write", __func__, addr); 247*35593573SXuzhou Cheng s->regs[R_INT_STATUS] |= R_INT_STATUS_AXI_BRESP_ERR_MASK; 248*35593573SXuzhou Cheng xlnx_csu_dma_update_irq(s); 249*35593573SXuzhou Cheng } 250*35593573SXuzhou Cheng return len; 251*35593573SXuzhou Cheng } 252*35593573SXuzhou Cheng 253*35593573SXuzhou Cheng static void xlnx_csu_dma_done(XlnxCSUDMA *s) 254*35593573SXuzhou Cheng { 255*35593573SXuzhou Cheng s->regs[R_STATUS] &= ~R_STATUS_BUSY_MASK; 256*35593573SXuzhou Cheng s->regs[R_INT_STATUS] |= R_INT_STATUS_DONE_MASK; 257*35593573SXuzhou Cheng 258*35593573SXuzhou Cheng if (!s->is_dst) { 259*35593573SXuzhou Cheng s->regs[R_INT_STATUS] |= R_INT_STATUS_MEM_DONE_MASK; 260*35593573SXuzhou Cheng } 261*35593573SXuzhou Cheng 262*35593573SXuzhou Cheng xlnx_csu_dma_update_done_cnt(s, 1); 263*35593573SXuzhou Cheng } 264*35593573SXuzhou Cheng 265*35593573SXuzhou Cheng static uint32_t xlnx_csu_dma_advance(XlnxCSUDMA *s, uint32_t len) 266*35593573SXuzhou Cheng { 267*35593573SXuzhou Cheng uint32_t size = s->regs[R_SIZE]; 268*35593573SXuzhou Cheng hwaddr dst = (hwaddr)s->regs[R_ADDR_MSB] << 32 | s->regs[R_ADDR]; 269*35593573SXuzhou Cheng 270*35593573SXuzhou Cheng assert(len <= size); 271*35593573SXuzhou Cheng 272*35593573SXuzhou Cheng size -= len; 273*35593573SXuzhou Cheng s->regs[R_SIZE] = size; 274*35593573SXuzhou Cheng 275*35593573SXuzhou Cheng if (!xlnx_csu_dma_burst_is_fixed(s)) { 276*35593573SXuzhou Cheng dst += len; 277*35593573SXuzhou Cheng s->regs[R_ADDR] = (uint32_t) dst; 278*35593573SXuzhou Cheng s->regs[R_ADDR_MSB] = dst >> 32; 279*35593573SXuzhou Cheng } 280*35593573SXuzhou Cheng 281*35593573SXuzhou Cheng if (size == 0) { 282*35593573SXuzhou Cheng xlnx_csu_dma_done(s); 283*35593573SXuzhou Cheng } 284*35593573SXuzhou Cheng 285*35593573SXuzhou Cheng return size; 286*35593573SXuzhou Cheng } 287*35593573SXuzhou Cheng 288*35593573SXuzhou Cheng static void xlnx_csu_dma_src_notify(void *opaque) 289*35593573SXuzhou Cheng { 290*35593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(opaque); 291*35593573SXuzhou Cheng unsigned char buf[4 * 1024]; 292*35593573SXuzhou Cheng size_t rlen = 0; 293*35593573SXuzhou Cheng 294*35593573SXuzhou Cheng ptimer_transaction_begin(s->src_timer); 295*35593573SXuzhou Cheng /* Stop the backpreassure timer */ 296*35593573SXuzhou Cheng ptimer_stop(s->src_timer); 297*35593573SXuzhou Cheng 298*35593573SXuzhou Cheng while (s->regs[R_SIZE] && !xlnx_csu_dma_is_paused(s) && 299*35593573SXuzhou Cheng stream_can_push(s->tx_dev, xlnx_csu_dma_src_notify, s)) { 300*35593573SXuzhou Cheng uint32_t plen = MIN(s->regs[R_SIZE], sizeof buf); 301*35593573SXuzhou Cheng bool eop = false; 302*35593573SXuzhou Cheng 303*35593573SXuzhou Cheng /* Did we fit it all? */ 304*35593573SXuzhou Cheng if (s->regs[R_SIZE] == plen && xlnx_csu_dma_get_eop(s)) { 305*35593573SXuzhou Cheng eop = true; 306*35593573SXuzhou Cheng } 307*35593573SXuzhou Cheng 308*35593573SXuzhou Cheng /* DMA transfer */ 309*35593573SXuzhou Cheng xlnx_csu_dma_read(s, buf, plen); 310*35593573SXuzhou Cheng rlen = stream_push(s->tx_dev, buf, plen, eop); 311*35593573SXuzhou Cheng xlnx_csu_dma_advance(s, rlen); 312*35593573SXuzhou Cheng } 313*35593573SXuzhou Cheng 314*35593573SXuzhou Cheng if (xlnx_csu_dma_timeout_enabled(s) && s->regs[R_SIZE] && 315*35593573SXuzhou Cheng !stream_can_push(s->tx_dev, xlnx_csu_dma_src_notify, s)) { 316*35593573SXuzhou Cheng uint32_t timeout = ARRAY_FIELD_EX32(s->regs, CTRL, TIMEOUT_VAL); 317*35593573SXuzhou Cheng uint32_t div = ARRAY_FIELD_EX32(s->regs, CTRL2, TIMEOUT_PRE) + 1; 318*35593573SXuzhou Cheng uint32_t freq = XLNX_CSU_DMA_TIMER_FREQ; 319*35593573SXuzhou Cheng 320*35593573SXuzhou Cheng freq /= div; 321*35593573SXuzhou Cheng ptimer_set_freq(s->src_timer, freq); 322*35593573SXuzhou Cheng ptimer_set_count(s->src_timer, timeout); 323*35593573SXuzhou Cheng ptimer_run(s->src_timer, 1); 324*35593573SXuzhou Cheng } 325*35593573SXuzhou Cheng 326*35593573SXuzhou Cheng ptimer_transaction_commit(s->src_timer); 327*35593573SXuzhou Cheng xlnx_csu_dma_update_irq(s); 328*35593573SXuzhou Cheng } 329*35593573SXuzhou Cheng 330*35593573SXuzhou Cheng static uint64_t addr_pre_write(RegisterInfo *reg, uint64_t val) 331*35593573SXuzhou Cheng { 332*35593573SXuzhou Cheng /* Address is word aligned */ 333*35593573SXuzhou Cheng return val & R_ADDR_ADDR_MASK; 334*35593573SXuzhou Cheng } 335*35593573SXuzhou Cheng 336*35593573SXuzhou Cheng static uint64_t size_pre_write(RegisterInfo *reg, uint64_t val) 337*35593573SXuzhou Cheng { 338*35593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 339*35593573SXuzhou Cheng 340*35593573SXuzhou Cheng if (s->regs[R_SIZE] != 0) { 341*35593573SXuzhou Cheng qemu_log_mask(LOG_GUEST_ERROR, 342*35593573SXuzhou Cheng "%s: Starting DMA while already running.\n", __func__); 343*35593573SXuzhou Cheng } 344*35593573SXuzhou Cheng 345*35593573SXuzhou Cheng if (!s->is_dst) { 346*35593573SXuzhou Cheng s->r_size_last_word = !!(val & R_SIZE_LAST_WORD_MASK); 347*35593573SXuzhou Cheng } 348*35593573SXuzhou Cheng 349*35593573SXuzhou Cheng /* Size is word aligned */ 350*35593573SXuzhou Cheng return val & R_SIZE_SIZE_MASK; 351*35593573SXuzhou Cheng } 352*35593573SXuzhou Cheng 353*35593573SXuzhou Cheng static uint64_t size_post_read(RegisterInfo *reg, uint64_t val) 354*35593573SXuzhou Cheng { 355*35593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 356*35593573SXuzhou Cheng 357*35593573SXuzhou Cheng return val | s->r_size_last_word; 358*35593573SXuzhou Cheng } 359*35593573SXuzhou Cheng 360*35593573SXuzhou Cheng static void size_post_write(RegisterInfo *reg, uint64_t val) 361*35593573SXuzhou Cheng { 362*35593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 363*35593573SXuzhou Cheng 364*35593573SXuzhou Cheng s->regs[R_STATUS] |= R_STATUS_BUSY_MASK; 365*35593573SXuzhou Cheng 366*35593573SXuzhou Cheng /* 367*35593573SXuzhou Cheng * Note that if SIZE is programmed to 0, and the DMA is started, 368*35593573SXuzhou Cheng * the interrupts DONE and MEM_DONE will be asserted. 369*35593573SXuzhou Cheng */ 370*35593573SXuzhou Cheng if (s->regs[R_SIZE] == 0) { 371*35593573SXuzhou Cheng xlnx_csu_dma_done(s); 372*35593573SXuzhou Cheng xlnx_csu_dma_update_irq(s); 373*35593573SXuzhou Cheng return; 374*35593573SXuzhou Cheng } 375*35593573SXuzhou Cheng 376*35593573SXuzhou Cheng /* Set SIZE is considered the last step in transfer configuration */ 377*35593573SXuzhou Cheng if (!s->is_dst) { 378*35593573SXuzhou Cheng xlnx_csu_dma_src_notify(s); 379*35593573SXuzhou Cheng } else { 380*35593573SXuzhou Cheng if (s->notify) { 381*35593573SXuzhou Cheng s->notify(s->notify_opaque); 382*35593573SXuzhou Cheng } 383*35593573SXuzhou Cheng } 384*35593573SXuzhou Cheng } 385*35593573SXuzhou Cheng 386*35593573SXuzhou Cheng static uint64_t status_pre_write(RegisterInfo *reg, uint64_t val) 387*35593573SXuzhou Cheng { 388*35593573SXuzhou Cheng return val & (R_STATUS_DONE_CNT_MASK | R_STATUS_BUSY_MASK); 389*35593573SXuzhou Cheng } 390*35593573SXuzhou Cheng 391*35593573SXuzhou Cheng static void ctrl_post_write(RegisterInfo *reg, uint64_t val) 392*35593573SXuzhou Cheng { 393*35593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 394*35593573SXuzhou Cheng 395*35593573SXuzhou Cheng if (!s->is_dst) { 396*35593573SXuzhou Cheng if (!xlnx_csu_dma_is_paused(s)) { 397*35593573SXuzhou Cheng xlnx_csu_dma_src_notify(s); 398*35593573SXuzhou Cheng } 399*35593573SXuzhou Cheng } else { 400*35593573SXuzhou Cheng if (!xlnx_csu_dma_is_paused(s) && s->notify) { 401*35593573SXuzhou Cheng s->notify(s->notify_opaque); 402*35593573SXuzhou Cheng } 403*35593573SXuzhou Cheng } 404*35593573SXuzhou Cheng } 405*35593573SXuzhou Cheng 406*35593573SXuzhou Cheng static uint64_t int_status_pre_write(RegisterInfo *reg, uint64_t val) 407*35593573SXuzhou Cheng { 408*35593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 409*35593573SXuzhou Cheng 410*35593573SXuzhou Cheng /* DMA counter decrements when flag 'DONE' is cleared */ 411*35593573SXuzhou Cheng if ((val & s->regs[R_INT_STATUS] & R_INT_STATUS_DONE_MASK)) { 412*35593573SXuzhou Cheng xlnx_csu_dma_update_done_cnt(s, -1); 413*35593573SXuzhou Cheng } 414*35593573SXuzhou Cheng 415*35593573SXuzhou Cheng return s->regs[R_INT_STATUS] & ~val; 416*35593573SXuzhou Cheng } 417*35593573SXuzhou Cheng 418*35593573SXuzhou Cheng static void int_status_post_write(RegisterInfo *reg, uint64_t val) 419*35593573SXuzhou Cheng { 420*35593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 421*35593573SXuzhou Cheng 422*35593573SXuzhou Cheng xlnx_csu_dma_update_irq(s); 423*35593573SXuzhou Cheng } 424*35593573SXuzhou Cheng 425*35593573SXuzhou Cheng static uint64_t int_enable_pre_write(RegisterInfo *reg, uint64_t val) 426*35593573SXuzhou Cheng { 427*35593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 428*35593573SXuzhou Cheng uint32_t v32 = val; 429*35593573SXuzhou Cheng 430*35593573SXuzhou Cheng /* 431*35593573SXuzhou Cheng * R_INT_ENABLE doesn't have its own state. 432*35593573SXuzhou Cheng * It is used to indirectly modify R_INT_MASK. 433*35593573SXuzhou Cheng * 434*35593573SXuzhou Cheng * 1: Enable this interrupt field (the mask bit will be cleared to 0) 435*35593573SXuzhou Cheng * 0: No effect 436*35593573SXuzhou Cheng */ 437*35593573SXuzhou Cheng s->regs[R_INT_MASK] &= ~v32; 438*35593573SXuzhou Cheng return 0; 439*35593573SXuzhou Cheng } 440*35593573SXuzhou Cheng 441*35593573SXuzhou Cheng static void int_enable_post_write(RegisterInfo *reg, uint64_t val) 442*35593573SXuzhou Cheng { 443*35593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 444*35593573SXuzhou Cheng 445*35593573SXuzhou Cheng xlnx_csu_dma_update_irq(s); 446*35593573SXuzhou Cheng } 447*35593573SXuzhou Cheng 448*35593573SXuzhou Cheng static uint64_t int_disable_pre_write(RegisterInfo *reg, uint64_t val) 449*35593573SXuzhou Cheng { 450*35593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 451*35593573SXuzhou Cheng uint32_t v32 = val; 452*35593573SXuzhou Cheng 453*35593573SXuzhou Cheng /* 454*35593573SXuzhou Cheng * R_INT_DISABLE doesn't have its own state. 455*35593573SXuzhou Cheng * It is used to indirectly modify R_INT_MASK. 456*35593573SXuzhou Cheng * 457*35593573SXuzhou Cheng * 1: Disable this interrupt field (the mask bit will be set to 1) 458*35593573SXuzhou Cheng * 0: No effect 459*35593573SXuzhou Cheng */ 460*35593573SXuzhou Cheng s->regs[R_INT_MASK] |= v32; 461*35593573SXuzhou Cheng return 0; 462*35593573SXuzhou Cheng } 463*35593573SXuzhou Cheng 464*35593573SXuzhou Cheng static void int_disable_post_write(RegisterInfo *reg, uint64_t val) 465*35593573SXuzhou Cheng { 466*35593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(reg->opaque); 467*35593573SXuzhou Cheng 468*35593573SXuzhou Cheng xlnx_csu_dma_update_irq(s); 469*35593573SXuzhou Cheng } 470*35593573SXuzhou Cheng 471*35593573SXuzhou Cheng static uint64_t addr_msb_pre_write(RegisterInfo *reg, uint64_t val) 472*35593573SXuzhou Cheng { 473*35593573SXuzhou Cheng return val & R_ADDR_MSB_ADDR_MSB_MASK; 474*35593573SXuzhou Cheng } 475*35593573SXuzhou Cheng 476*35593573SXuzhou Cheng static const RegisterAccessInfo *xlnx_csu_dma_regs_info[] = { 477*35593573SXuzhou Cheng #define DMACH_REGINFO(NAME, snd) \ 478*35593573SXuzhou Cheng (const RegisterAccessInfo []) { \ 479*35593573SXuzhou Cheng { \ 480*35593573SXuzhou Cheng .name = #NAME "_ADDR", \ 481*35593573SXuzhou Cheng .addr = A_ADDR, \ 482*35593573SXuzhou Cheng .pre_write = addr_pre_write \ 483*35593573SXuzhou Cheng }, { \ 484*35593573SXuzhou Cheng .name = #NAME "_SIZE", \ 485*35593573SXuzhou Cheng .addr = A_SIZE, \ 486*35593573SXuzhou Cheng .pre_write = size_pre_write, \ 487*35593573SXuzhou Cheng .post_write = size_post_write, \ 488*35593573SXuzhou Cheng .post_read = size_post_read \ 489*35593573SXuzhou Cheng }, { \ 490*35593573SXuzhou Cheng .name = #NAME "_STATUS", \ 491*35593573SXuzhou Cheng .addr = A_STATUS, \ 492*35593573SXuzhou Cheng .pre_write = status_pre_write, \ 493*35593573SXuzhou Cheng .w1c = R_STATUS_DONE_CNT_MASK, \ 494*35593573SXuzhou Cheng .ro = (R_STATUS_BUSY_MASK \ 495*35593573SXuzhou Cheng | R_STATUS_FIFO_LEVEL_MASK \ 496*35593573SXuzhou Cheng | R_STATUS_OUTSTANDING_MASK) \ 497*35593573SXuzhou Cheng }, { \ 498*35593573SXuzhou Cheng .name = #NAME "_CTRL", \ 499*35593573SXuzhou Cheng .addr = A_CTRL, \ 500*35593573SXuzhou Cheng .post_write = ctrl_post_write, \ 501*35593573SXuzhou Cheng .reset = ((R_CTRL_TIMEOUT_VAL_RESET << R_CTRL_TIMEOUT_VAL_SHIFT) \ 502*35593573SXuzhou Cheng | (R_CTRL_FIFO_THRESH_RESET << R_CTRL_FIFO_THRESH_SHIFT)\ 503*35593573SXuzhou Cheng | (snd ? 0 : R_CTRL_FIFOTHRESH_RESET \ 504*35593573SXuzhou Cheng << R_CTRL_FIFOTHRESH_SHIFT)) \ 505*35593573SXuzhou Cheng }, { \ 506*35593573SXuzhou Cheng .name = #NAME "_CRC", \ 507*35593573SXuzhou Cheng .addr = A_CRC, \ 508*35593573SXuzhou Cheng }, { \ 509*35593573SXuzhou Cheng .name = #NAME "_INT_STATUS", \ 510*35593573SXuzhou Cheng .addr = A_INT_STATUS, \ 511*35593573SXuzhou Cheng .pre_write = int_status_pre_write, \ 512*35593573SXuzhou Cheng .post_write = int_status_post_write \ 513*35593573SXuzhou Cheng }, { \ 514*35593573SXuzhou Cheng .name = #NAME "_INT_ENABLE", \ 515*35593573SXuzhou Cheng .addr = A_INT_ENABLE, \ 516*35593573SXuzhou Cheng .pre_write = int_enable_pre_write, \ 517*35593573SXuzhou Cheng .post_write = int_enable_post_write \ 518*35593573SXuzhou Cheng }, { \ 519*35593573SXuzhou Cheng .name = #NAME "_INT_DISABLE", \ 520*35593573SXuzhou Cheng .addr = A_INT_DISABLE, \ 521*35593573SXuzhou Cheng .pre_write = int_disable_pre_write, \ 522*35593573SXuzhou Cheng .post_write = int_disable_post_write \ 523*35593573SXuzhou Cheng }, { \ 524*35593573SXuzhou Cheng .name = #NAME "_INT_MASK", \ 525*35593573SXuzhou Cheng .addr = A_INT_MASK, \ 526*35593573SXuzhou Cheng .ro = ~0, \ 527*35593573SXuzhou Cheng .reset = XLNX_CSU_DMA_INT_R_MASK \ 528*35593573SXuzhou Cheng }, { \ 529*35593573SXuzhou Cheng .name = #NAME "_CTRL2", \ 530*35593573SXuzhou Cheng .addr = A_CTRL2, \ 531*35593573SXuzhou Cheng .reset = ((R_CTRL2_TIMEOUT_PRE_RESET \ 532*35593573SXuzhou Cheng << R_CTRL2_TIMEOUT_PRE_SHIFT) \ 533*35593573SXuzhou Cheng | (R_CTRL2_MAX_OUTS_CMDS_RESET \ 534*35593573SXuzhou Cheng << R_CTRL2_MAX_OUTS_CMDS_SHIFT)) \ 535*35593573SXuzhou Cheng }, { \ 536*35593573SXuzhou Cheng .name = #NAME "_ADDR_MSB", \ 537*35593573SXuzhou Cheng .addr = A_ADDR_MSB, \ 538*35593573SXuzhou Cheng .pre_write = addr_msb_pre_write \ 539*35593573SXuzhou Cheng } \ 540*35593573SXuzhou Cheng } 541*35593573SXuzhou Cheng 542*35593573SXuzhou Cheng DMACH_REGINFO(DMA_SRC, true), 543*35593573SXuzhou Cheng DMACH_REGINFO(DMA_DST, false) 544*35593573SXuzhou Cheng }; 545*35593573SXuzhou Cheng 546*35593573SXuzhou Cheng static const MemoryRegionOps xlnx_csu_dma_ops = { 547*35593573SXuzhou Cheng .read = register_read_memory, 548*35593573SXuzhou Cheng .write = register_write_memory, 549*35593573SXuzhou Cheng .endianness = DEVICE_LITTLE_ENDIAN, 550*35593573SXuzhou Cheng .valid = { 551*35593573SXuzhou Cheng .min_access_size = 4, 552*35593573SXuzhou Cheng .max_access_size = 4, 553*35593573SXuzhou Cheng } 554*35593573SXuzhou Cheng }; 555*35593573SXuzhou Cheng 556*35593573SXuzhou Cheng static void xlnx_csu_dma_src_timeout_hit(void *opaque) 557*35593573SXuzhou Cheng { 558*35593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(opaque); 559*35593573SXuzhou Cheng 560*35593573SXuzhou Cheng /* Ignore if the timeout is masked */ 561*35593573SXuzhou Cheng if (!xlnx_csu_dma_timeout_enabled(s)) { 562*35593573SXuzhou Cheng return; 563*35593573SXuzhou Cheng } 564*35593573SXuzhou Cheng 565*35593573SXuzhou Cheng s->regs[R_INT_STATUS] |= R_INT_STATUS_TIMEOUT_STRM_MASK; 566*35593573SXuzhou Cheng xlnx_csu_dma_update_irq(s); 567*35593573SXuzhou Cheng } 568*35593573SXuzhou Cheng 569*35593573SXuzhou Cheng static size_t xlnx_csu_dma_stream_push(StreamSink *obj, uint8_t *buf, 570*35593573SXuzhou Cheng size_t len, bool eop) 571*35593573SXuzhou Cheng { 572*35593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(obj); 573*35593573SXuzhou Cheng uint32_t size = s->regs[R_SIZE]; 574*35593573SXuzhou Cheng uint32_t mlen = MIN(size, len) & (~3); /* Size is word aligned */ 575*35593573SXuzhou Cheng 576*35593573SXuzhou Cheng /* Be called when it's DST */ 577*35593573SXuzhou Cheng assert(s->is_dst); 578*35593573SXuzhou Cheng 579*35593573SXuzhou Cheng if (size == 0 || len <= 0) { 580*35593573SXuzhou Cheng return 0; 581*35593573SXuzhou Cheng } 582*35593573SXuzhou Cheng 583*35593573SXuzhou Cheng if (len && (xlnx_csu_dma_is_paused(s) || mlen == 0)) { 584*35593573SXuzhou Cheng qemu_log_mask(LOG_GUEST_ERROR, 585*35593573SXuzhou Cheng "csu-dma: DST channel dropping %zd b of data.\n", len); 586*35593573SXuzhou Cheng s->regs[R_INT_STATUS] |= R_INT_STATUS_FIFO_OVERFLOW_MASK; 587*35593573SXuzhou Cheng return len; 588*35593573SXuzhou Cheng } 589*35593573SXuzhou Cheng 590*35593573SXuzhou Cheng if (xlnx_csu_dma_write(s, buf, mlen) != mlen) { 591*35593573SXuzhou Cheng return 0; 592*35593573SXuzhou Cheng } 593*35593573SXuzhou Cheng 594*35593573SXuzhou Cheng xlnx_csu_dma_advance(s, mlen); 595*35593573SXuzhou Cheng xlnx_csu_dma_update_irq(s); 596*35593573SXuzhou Cheng 597*35593573SXuzhou Cheng return mlen; 598*35593573SXuzhou Cheng } 599*35593573SXuzhou Cheng 600*35593573SXuzhou Cheng static bool xlnx_csu_dma_stream_can_push(StreamSink *obj, 601*35593573SXuzhou Cheng StreamCanPushNotifyFn notify, 602*35593573SXuzhou Cheng void *notify_opaque) 603*35593573SXuzhou Cheng { 604*35593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(obj); 605*35593573SXuzhou Cheng 606*35593573SXuzhou Cheng if (s->regs[R_SIZE] != 0) { 607*35593573SXuzhou Cheng return true; 608*35593573SXuzhou Cheng } else { 609*35593573SXuzhou Cheng s->notify = notify; 610*35593573SXuzhou Cheng s->notify_opaque = notify_opaque; 611*35593573SXuzhou Cheng return false; 612*35593573SXuzhou Cheng } 613*35593573SXuzhou Cheng } 614*35593573SXuzhou Cheng 615*35593573SXuzhou Cheng static void xlnx_csu_dma_reset(DeviceState *dev) 616*35593573SXuzhou Cheng { 617*35593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(dev); 618*35593573SXuzhou Cheng unsigned int i; 619*35593573SXuzhou Cheng 620*35593573SXuzhou Cheng for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) { 621*35593573SXuzhou Cheng register_reset(&s->regs_info[i]); 622*35593573SXuzhou Cheng } 623*35593573SXuzhou Cheng } 624*35593573SXuzhou Cheng 625*35593573SXuzhou Cheng static void xlnx_csu_dma_realize(DeviceState *dev, Error **errp) 626*35593573SXuzhou Cheng { 627*35593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(dev); 628*35593573SXuzhou Cheng RegisterInfoArray *reg_array; 629*35593573SXuzhou Cheng 630*35593573SXuzhou Cheng reg_array = 631*35593573SXuzhou Cheng register_init_block32(dev, xlnx_csu_dma_regs_info[!!s->is_dst], 632*35593573SXuzhou Cheng XLNX_CSU_DMA_R_MAX, 633*35593573SXuzhou Cheng s->regs_info, s->regs, 634*35593573SXuzhou Cheng &xlnx_csu_dma_ops, 635*35593573SXuzhou Cheng XLNX_CSU_DMA_ERR_DEBUG, 636*35593573SXuzhou Cheng XLNX_CSU_DMA_R_MAX * 4); 637*35593573SXuzhou Cheng memory_region_add_subregion(&s->iomem, 638*35593573SXuzhou Cheng 0x0, 639*35593573SXuzhou Cheng ®_array->mem); 640*35593573SXuzhou Cheng 641*35593573SXuzhou Cheng sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem); 642*35593573SXuzhou Cheng sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq); 643*35593573SXuzhou Cheng 644*35593573SXuzhou Cheng if (!s->is_dst && !s->tx_dev) { 645*35593573SXuzhou Cheng error_setg(errp, "zynqmp.csu-dma: Stream not connected"); 646*35593573SXuzhou Cheng return; 647*35593573SXuzhou Cheng } 648*35593573SXuzhou Cheng 649*35593573SXuzhou Cheng s->src_timer = ptimer_init(xlnx_csu_dma_src_timeout_hit, 650*35593573SXuzhou Cheng s, PTIMER_POLICY_DEFAULT); 651*35593573SXuzhou Cheng 652*35593573SXuzhou Cheng if (s->dma_mr) { 653*35593573SXuzhou Cheng s->dma_as = g_malloc0(sizeof(AddressSpace)); 654*35593573SXuzhou Cheng address_space_init(s->dma_as, s->dma_mr, NULL); 655*35593573SXuzhou Cheng } else { 656*35593573SXuzhou Cheng s->dma_as = &address_space_memory; 657*35593573SXuzhou Cheng } 658*35593573SXuzhou Cheng 659*35593573SXuzhou Cheng s->attr = MEMTXATTRS_UNSPECIFIED; 660*35593573SXuzhou Cheng 661*35593573SXuzhou Cheng s->r_size_last_word = 0; 662*35593573SXuzhou Cheng } 663*35593573SXuzhou Cheng 664*35593573SXuzhou Cheng static const VMStateDescription vmstate_xlnx_csu_dma = { 665*35593573SXuzhou Cheng .name = TYPE_XLNX_CSU_DMA, 666*35593573SXuzhou Cheng .version_id = 0, 667*35593573SXuzhou Cheng .minimum_version_id = 0, 668*35593573SXuzhou Cheng .minimum_version_id_old = 0, 669*35593573SXuzhou Cheng .fields = (VMStateField[]) { 670*35593573SXuzhou Cheng VMSTATE_PTIMER(src_timer, XlnxCSUDMA), 671*35593573SXuzhou Cheng VMSTATE_UINT16(width, XlnxCSUDMA), 672*35593573SXuzhou Cheng VMSTATE_BOOL(is_dst, XlnxCSUDMA), 673*35593573SXuzhou Cheng VMSTATE_BOOL(r_size_last_word, XlnxCSUDMA), 674*35593573SXuzhou Cheng VMSTATE_UINT32_ARRAY(regs, XlnxCSUDMA, XLNX_CSU_DMA_R_MAX), 675*35593573SXuzhou Cheng VMSTATE_END_OF_LIST(), 676*35593573SXuzhou Cheng } 677*35593573SXuzhou Cheng }; 678*35593573SXuzhou Cheng 679*35593573SXuzhou Cheng static Property xlnx_csu_dma_properties[] = { 680*35593573SXuzhou Cheng /* 681*35593573SXuzhou Cheng * Ref PG021, Stream Data Width: 682*35593573SXuzhou Cheng * Data width in bits of the AXI S2MM AXI4-Stream Data bus. 683*35593573SXuzhou Cheng * This value must be equal or less than the Memory Map Data Width. 684*35593573SXuzhou Cheng * Valid values are 8, 16, 32, 64, 128, 512 and 1024. 685*35593573SXuzhou Cheng * "dma-width" is the byte value of the "Stream Data Width". 686*35593573SXuzhou Cheng */ 687*35593573SXuzhou Cheng DEFINE_PROP_UINT16("dma-width", XlnxCSUDMA, width, 4), 688*35593573SXuzhou Cheng /* 689*35593573SXuzhou Cheng * The CSU DMA is a two-channel, simple DMA, allowing separate control of 690*35593573SXuzhou Cheng * the SRC (read) channel and DST (write) channel. "is-dst" is used to mark 691*35593573SXuzhou Cheng * which channel the device is connected to. 692*35593573SXuzhou Cheng */ 693*35593573SXuzhou Cheng DEFINE_PROP_BOOL("is-dst", XlnxCSUDMA, is_dst, true), 694*35593573SXuzhou Cheng DEFINE_PROP_END_OF_LIST(), 695*35593573SXuzhou Cheng }; 696*35593573SXuzhou Cheng 697*35593573SXuzhou Cheng static void xlnx_csu_dma_class_init(ObjectClass *klass, void *data) 698*35593573SXuzhou Cheng { 699*35593573SXuzhou Cheng DeviceClass *dc = DEVICE_CLASS(klass); 700*35593573SXuzhou Cheng StreamSinkClass *ssc = STREAM_SINK_CLASS(klass); 701*35593573SXuzhou Cheng 702*35593573SXuzhou Cheng dc->reset = xlnx_csu_dma_reset; 703*35593573SXuzhou Cheng dc->realize = xlnx_csu_dma_realize; 704*35593573SXuzhou Cheng dc->vmsd = &vmstate_xlnx_csu_dma; 705*35593573SXuzhou Cheng device_class_set_props(dc, xlnx_csu_dma_properties); 706*35593573SXuzhou Cheng 707*35593573SXuzhou Cheng ssc->push = xlnx_csu_dma_stream_push; 708*35593573SXuzhou Cheng ssc->can_push = xlnx_csu_dma_stream_can_push; 709*35593573SXuzhou Cheng } 710*35593573SXuzhou Cheng 711*35593573SXuzhou Cheng static void xlnx_csu_dma_init(Object *obj) 712*35593573SXuzhou Cheng { 713*35593573SXuzhou Cheng XlnxCSUDMA *s = XLNX_CSU_DMA(obj); 714*35593573SXuzhou Cheng 715*35593573SXuzhou Cheng memory_region_init(&s->iomem, obj, TYPE_XLNX_CSU_DMA, 716*35593573SXuzhou Cheng XLNX_CSU_DMA_R_MAX * 4); 717*35593573SXuzhou Cheng 718*35593573SXuzhou Cheng object_property_add_link(obj, "stream-connected-dma", TYPE_STREAM_SINK, 719*35593573SXuzhou Cheng (Object **)&s->tx_dev, 720*35593573SXuzhou Cheng qdev_prop_allow_set_link_before_realize, 721*35593573SXuzhou Cheng OBJ_PROP_LINK_STRONG); 722*35593573SXuzhou Cheng object_property_add_link(obj, "dma", TYPE_MEMORY_REGION, 723*35593573SXuzhou Cheng (Object **)&s->dma_mr, 724*35593573SXuzhou Cheng qdev_prop_allow_set_link_before_realize, 725*35593573SXuzhou Cheng OBJ_PROP_LINK_STRONG); 726*35593573SXuzhou Cheng } 727*35593573SXuzhou Cheng 728*35593573SXuzhou Cheng static const TypeInfo xlnx_csu_dma_info = { 729*35593573SXuzhou Cheng .name = TYPE_XLNX_CSU_DMA, 730*35593573SXuzhou Cheng .parent = TYPE_SYS_BUS_DEVICE, 731*35593573SXuzhou Cheng .instance_size = sizeof(XlnxCSUDMA), 732*35593573SXuzhou Cheng .class_init = xlnx_csu_dma_class_init, 733*35593573SXuzhou Cheng .instance_init = xlnx_csu_dma_init, 734*35593573SXuzhou Cheng .interfaces = (InterfaceInfo[]) { 735*35593573SXuzhou Cheng { TYPE_STREAM_SINK }, 736*35593573SXuzhou Cheng { } 737*35593573SXuzhou Cheng } 738*35593573SXuzhou Cheng }; 739*35593573SXuzhou Cheng 740*35593573SXuzhou Cheng static void xlnx_csu_dma_register_types(void) 741*35593573SXuzhou Cheng { 742*35593573SXuzhou Cheng type_register_static(&xlnx_csu_dma_info); 743*35593573SXuzhou Cheng } 744*35593573SXuzhou Cheng 745*35593573SXuzhou Cheng type_init(xlnx_csu_dma_register_types) 746