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