qemu/hw/dma/csu_stream_dma.c
<<
>>
Prefs
   1/*
   2 * QEMU model of ZynqMP CSU Stream DMA
   3 *
   4 * Copyright (c) 2013 Xilinx Inc
   5 * Copyright (c) 2013 Peter Crosthwaite <peter.crosthwaite@xilinx.com>
   6 * Copyright (c) 2013 Edgar E. Iglesias <edgar.iglesias@xilinx.com>
   7 *
   8 * Permission is hereby granted, free of charge, to any person obtaining a copy
   9 * of this software and associated documentation files (the "Software"), to deal
  10 * in the Software without restriction, including without limitation the rights
  11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12 * copies of the Software, and to permit persons to whom the Software is
  13 * furnished to do so, subject to the following conditions:
  14 *
  15 * The above copyright notice and this permission notice shall be included in
  16 * all copies or substantial portions of the Software.
  17 *
  18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
  21 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24 * THE SOFTWARE.
  25 */
  26
  27#include "qemu/osdep.h"
  28#include "hw/sysbus.h"
  29#include "qemu/log.h"
  30
  31#include "hw/stream.h"
  32#include "hw/ptimer.h"
  33#include "qemu/bitops.h"
  34#include "sysemu/dma.h"
  35#include "hw/register-dep.h"
  36#include "qapi/error.h"
  37#include "qemu/main-loop.h"
  38
  39#include "hw/fdt_generic_util.h"
  40
  41#define TYPE_ZYNQMP_CSU_DMA "zynqmp.csu-dma"
  42
  43#define ZYNQMP_CSU_DMA(obj) \
  44     OBJECT_CHECK(ZynqMPCSUDMA, (obj), TYPE_ZYNQMP_CSU_DMA)
  45
  46#ifndef ZYNQMP_CSU_DMA_ERR_DEBUG
  47#define ZYNQMP_CSU_DMA_ERR_DEBUG 0
  48#endif
  49
  50#define DB_PRINT_L(lvl, fmt, args...) do {\
  51    if (ZYNQMP_CSU_DMA_ERR_DEBUG > lvl) {\
  52        fprintf(stderr, TYPE_ZYNQMP_CSU_DMA ": %s:" fmt, __func__, ## args);\
  53    } \
  54} while (0);
  55
  56#define DB_PRINT(fmt, args...) DB_PRINT_L(0, fmt, ##args)
  57
  58enum {
  59    R_ADDR         = 0x00 / 4,
  60    R_SIZE         = 0x04 / 4,
  61    R_STATUS       = 0x08 / 4,
  62    R_CTRL         = 0x0c / 4,
  63    R_CRC          = 0x10 / 4,
  64    R_INT_STATUS   = 0x14 / 4,
  65    R_INT_ENABLE   = 0x18 / 4,
  66    R_INT_DISABLE  = 0x1c / 4,
  67    R_INT_MASK     = 0x20 / 4,
  68    R_CTRL2        = 0x24 / 4,
  69
  70    R_MAX          = R_CTRL2 + 1
  71};
  72
  73enum {
  74    STATUS_DMA_BUSY          = (1 << 0),
  75    STATUS_RSVD              = 0
  76};
  77
  78/* The DMA_DONE_CNT is write to clear.  */
  79DEP_FIELD(STATUS, DMA_DONE_CNT, 3, 13)
  80
  81enum {
  82    CTRL_PAUSE_MEM                  = (1 << 0),
  83    CTRL_PAUSE_STRM                 = (1 << 1),
  84
  85    CTRL_FIFO_THRESH_SHIFT          = 2,
  86    CTRL_AXI_BURST_FIXED            = (1 << 22),
  87    CTRL_ENDIANNESS                 = (1 << 23),
  88    CTRL_ERR_RESP                   = (1 << 24),
  89    CTRL_SSS_FIFOTHRESH_SHIFT       = 25,
  90    CTRL_RSVD                       = (~((1 << 25) - 1))
  91};
  92
  93DEP_FIELD(CTRL, TIMEOUT, 12, 10)
  94
  95enum {
  96    INT_FIFO_OVERFLOW               = 1 << 7,
  97    INT_INVALID_APB_ACCESS          = 1 << 6,
  98    INT_FIFO_THRESH_HIT             = 1 << 5,
  99    INT_TIMEOUT_MEM                 = 1 << 4,
 100    INT_TIMEOUT_STRM                = 1 << 3,
 101    INT_AXI_RDERR                   = 1 << 2,
 102    INT_DONE                        = 1 << 1,
 103    INT_MEM_DONE                    = 1 << 0,
 104    INT_RSVD                        = (~((1 << 8) - 1)),
 105    INT_ALL_SRC                     = ~INT_RSVD & ~INT_FIFO_OVERFLOW,
 106    INT_ALL_DST                     = ~INT_RSVD & ~INT_MEM_DONE,
 107};
 108
 109enum {
 110    CTRL2_MAX_OUTS_CMDS_SHIFT       = 0,
 111    CTRL2_TIMEOUT_EN                = 1 << 22,
 112    CTRL2_TIMEOUT_PRE_SHIFT         = 4,
 113    CTRL2_RSVD                      = (~((1 << 28) - 1))
 114};
 115
 116typedef struct ZynqMPCSUDMA {
 117    SysBusDevice busdev;
 118    MemoryRegion iomem;
 119    MemTxAttrs *attr;
 120    MemoryRegion *dma_mr;
 121    AddressSpace *dma_as;
 122    qemu_irq irq;
 123    StreamSlave *tx_dev;
 124    QEMUBH *bh;
 125    ptimer_state *src_timer;
 126
 127    bool is_dst;
 128
 129    StreamCanPushNotifyFn notify;
 130    void *notify_opaque;
 131
 132    uint32_t regs[R_MAX];
 133    DepRegisterInfo regs_info[R_MAX];
 134} ZynqMPCSUDMA;
 135
 136/* This is a zynqmp specific CSU hack.  */
 137static int dmach_validate_addr(ZynqMPCSUDMA *s)
 138{
 139    /* priv ROM access?  */
 140    if (s->regs[R_ADDR] >= 0xffc00000 && s->regs[R_ADDR] < 0xffc20000) {
 141        return 1;
 142    }
 143    /* priv RAM access?  */
 144    if (s->regs[R_ADDR] >= 0xffc40000 && s->regs[R_ADDR] < 0xffc48000) {
 145        return 1;
 146    }
 147    return 0;
 148}
 149
 150static bool dmach_is_paused(ZynqMPCSUDMA *s)
 151{
 152    bool paused;
 153
 154    paused = !!(s->regs[R_CTRL] & CTRL_PAUSE_STRM);
 155    paused |= !!(s->regs[R_CTRL] & CTRL_PAUSE_MEM);
 156    return paused;
 157}
 158
 159static bool dmach_get_eop(ZynqMPCSUDMA *s)
 160{
 161    return s->regs[R_SIZE] & 1;
 162}
 163
 164static uint32_t dmach_get_size(ZynqMPCSUDMA *s)
 165{
 166    return s->regs[R_SIZE] & ~3;
 167}
 168
 169static void dmach_set_size(ZynqMPCSUDMA *s, uint32_t size)
 170{
 171    assert((size & 3) == 0);
 172
 173    s->regs[R_SIZE] &= 1;
 174    s->regs[R_SIZE] |= size;
 175}
 176
 177static bool dmach_burst_is_fixed(ZynqMPCSUDMA *s)
 178{
 179    return !!(s->regs[R_CTRL] & CTRL_AXI_BURST_FIXED);
 180}
 181
 182static bool dmach_timeout_enabled(ZynqMPCSUDMA *s)
 183{
 184    return s->regs[R_CTRL2] & CTRL2_TIMEOUT_EN;
 185}
 186
 187static inline void dmach_update_dma_cnt(ZynqMPCSUDMA *s, int a)
 188{
 189    int cnt;
 190
 191    /* Increase dma_cnt.  */
 192    cnt = DEP_AF_EX32(s->regs, STATUS, DMA_DONE_CNT) + a;
 193    DEP_AF_DP32(s->regs, STATUS, DMA_DONE_CNT, cnt);
 194}
 195
 196static void dmach_done(ZynqMPCSUDMA *s)
 197{
 198    dmach_update_dma_cnt(s, +1);
 199    s->regs[R_STATUS] &= ~STATUS_DMA_BUSY;
 200
 201    DB_PRINT("\n");
 202    s->regs[R_INT_STATUS] |= INT_DONE;
 203    if (!s->is_dst) {
 204        s->regs[R_INT_STATUS] |= INT_MEM_DONE;
 205    }
 206}
 207
 208static void dmach_advance(ZynqMPCSUDMA *s, unsigned int len)
 209{
 210    uint32_t size = dmach_get_size(s);
 211
 212    /* Has to be 32bit aligned.  */
 213    assert((len & 3) == 0);
 214    assert(len <= size);
 215
 216    if (!dmach_burst_is_fixed(s)) {
 217        s->regs[R_ADDR] += len;
 218    }
 219
 220    size -= len;
 221    dmach_set_size(s, size);
 222
 223    if (size == 0) {
 224        dmach_done(s);
 225    }
 226}
 227
 228static void dmach_data_process(ZynqMPCSUDMA *s, uint8_t *buf, unsigned int len)
 229{
 230    unsigned int bswap;
 231    unsigned int i;
 232
 233    /* Xor only for src channel.  */
 234    bswap = s->regs[R_CTRL] & CTRL_ENDIANNESS;
 235    if (s->is_dst && !bswap) {
 236        /* Fast!  */
 237        return;
 238    }
 239
 240    /* buf might not be 32bit aligned... slooow.  */
 241    assert((len & 3) == 0);
 242    /* FIXME: move me to bitops.c for global reusability */
 243    for (i = 0; i < len; i += 4) {
 244        uint8_t *b = &buf[i];
 245        union {
 246            uint8_t u8[4];
 247            uint32_t u32;
 248        } v = {
 249            .u8 = { b[0], b[1], b[2], b[3] }
 250        };
 251
 252        if (!s->is_dst) {
 253            s->regs[R_CRC] += v.u32;
 254        }
 255        if (bswap) {
 256            /* No point using bswap, we need to writeback
 257               into a potentially unaligned pointer..   */
 258            b[0] = v.u8[3];
 259            b[1] = v.u8[2];
 260            b[2] = v.u8[1];
 261            b[3] = v.u8[0];
 262        }
 263    }
 264}
 265
 266/* len is in bytes.  */
 267static void dmach_write(ZynqMPCSUDMA *s, uint8_t *buf, unsigned int len)
 268{
 269    int err = dmach_validate_addr(s);
 270
 271    if (err) {
 272        return;
 273    }
 274
 275    dmach_data_process(s, buf, len);
 276    if (dmach_burst_is_fixed(s)) {
 277        unsigned int i;
 278
 279        for (i = 0; i < len; i += 4) {
 280            address_space_rw(s->dma_as, s->regs[R_ADDR], *s->attr, buf, 4,
 281                                  true);
 282            buf += 4;
 283        }
 284    } else {
 285        address_space_rw(s->dma_as, s->regs[R_ADDR], *s->attr, buf, len,
 286                              true);
 287    }
 288}
 289
 290/* len is in bytes.  */
 291static inline void dmach_read(ZynqMPCSUDMA *s, uint8_t *buf, unsigned int len)
 292{
 293    int raz = dmach_validate_addr(s);
 294
 295    if (raz) {
 296        qemu_log_mask(LOG_GUEST_ERROR,
 297                      "csu-dma: Reading from unaccessible memory addr=%x\n",
 298                      s->regs[R_ADDR]);
 299        /* This maybe raises an exception instead... */
 300        memset(buf, 0, len);
 301        return;
 302    }
 303
 304    if (dmach_burst_is_fixed(s)) {
 305        unsigned int i;
 306
 307        for (i = 0; i < len; i += 4) {
 308            address_space_rw(s->dma_as, s->regs[R_ADDR], *s->attr, buf + i, 4,
 309                             false);
 310        }
 311    } else {
 312        address_space_rw(s->dma_as, s->regs[R_ADDR], *s->attr, buf, len,
 313                              false);
 314    }
 315    dmach_data_process(s, buf, len);
 316}
 317
 318static void ronaldu_csu_dma_update_irq(ZynqMPCSUDMA *s)
 319{
 320    qemu_set_irq(s->irq, !!(s->regs[R_INT_STATUS] & ~s->regs[R_INT_MASK]));
 321}
 322
 323static void zynqmp_csu_dma_reset(DeviceState *dev)
 324{
 325    ZynqMPCSUDMA *s = ZYNQMP_CSU_DMA(dev);
 326    int i;
 327
 328    for (i = 0; i < R_MAX; i++) {
 329        dep_register_reset(&s->regs_info[i]);
 330    }
 331}
 332
 333static size_t zynqmp_csu_dma_stream_push(StreamSlave *obj, uint8_t *buf,
 334                                          size_t len, uint32_t attr)
 335{
 336    ZynqMPCSUDMA *s = ZYNQMP_CSU_DMA(obj);
 337    uint32_t size = dmach_get_size(s);
 338    uint32_t btt = MIN(size, len);
 339
 340    assert(s->is_dst);
 341    if (len && (dmach_is_paused(s) || btt == 0)) {
 342        qemu_log_mask(LOG_GUEST_ERROR,
 343                      "csu-dma: DST channel dropping %zd b of data.\n", len);
 344        s->regs[R_INT_STATUS] |= INT_FIFO_OVERFLOW;
 345        return len;
 346    }
 347
 348    if (!btt) {
 349        return 0;
 350    }
 351
 352    /* DMA transfer.  */
 353    dmach_write(s, buf, btt);
 354    dmach_advance(s, btt);
 355    ronaldu_csu_dma_update_irq(s);
 356    return btt;
 357}
 358
 359static bool zynqmp_csu_dma_stream_can_push(StreamSlave *obj,
 360                                            StreamCanPushNotifyFn notify,
 361                                            void *notify_opaque)
 362{
 363    /* DST channel side has no flow-control.  */
 364    return true;
 365}
 366
 367static void zynqmp_csu_dma_src_notify(void *opaque)
 368{
 369    ZynqMPCSUDMA *s = ZYNQMP_CSU_DMA(opaque);
 370    unsigned char buf[4 * 1024];
 371
 372    /* Stop the backpreassure timer.  */
 373    ptimer_stop(s->src_timer);
 374
 375    while (dmach_get_size(s) && !dmach_is_paused(s) &&
 376           stream_can_push(s->tx_dev, zynqmp_csu_dma_src_notify, s)) {
 377        uint32_t size = dmach_get_size(s);
 378        unsigned int plen = MIN(size, sizeof buf);
 379        uint32_t attr = 0;
 380        size_t ret;
 381
 382        /* Did we fit it all?  */
 383        if (size == plen && dmach_get_eop(s)) {
 384            attr |= STREAM_ATTR_EOP;
 385        }
 386
 387        /* DMA transfer.  */
 388        dmach_read(s, buf, plen);
 389        ret = stream_push(s->tx_dev, buf, plen, attr);
 390        dmach_advance(s, ret);
 391    }
 392
 393    /* REMOVE-ME?: Check for flow-control timeout. This is all theoretical as
 394       we currently never see backpreassure.  */
 395    if (dmach_timeout_enabled(s) && dmach_get_size(s)
 396        && !stream_can_push(s->tx_dev, zynqmp_csu_dma_src_notify, s)) {
 397        unsigned int timeout = DEP_AF_EX32(s->regs, CTRL, TIMEOUT);
 398        unsigned int div = extract32(s->regs[R_CTRL2], 4, 12) + 1;
 399        unsigned int freq = 400 * 1000 * 1000;
 400
 401        freq /= div;
 402        ptimer_set_freq(s->src_timer, freq);
 403        ptimer_set_count(s->src_timer, timeout);
 404        ptimer_run(s->src_timer, 1);
 405    }
 406
 407    ronaldu_csu_dma_update_irq(s);
 408}
 409
 410static void r_ctrl_post_write(DepRegisterInfo *reg, uint64_t val)
 411{
 412    ZynqMPCSUDMA *s = ZYNQMP_CSU_DMA(reg->opaque);
 413
 414    if (!s->is_dst) {
 415        if (!dmach_is_paused(s)) {
 416            zynqmp_csu_dma_src_notify(s);
 417        }
 418    } else {
 419        if (!dmach_is_paused(s) && s->notify) {
 420            s->notify(s->notify_opaque);
 421        }
 422    }
 423}
 424
 425static uint64_t size_pre_write(DepRegisterInfo *reg, uint64_t val)
 426{
 427    ZynqMPCSUDMA *s = ZYNQMP_CSU_DMA(reg->opaque);
 428    if (dmach_get_size(s) != 0) {
 429        qemu_log_mask(LOG_GUEST_ERROR,
 430                      "csu-dma: Starting DMA while already running.\n");
 431    }
 432    return val;
 433}
 434
 435static void size_post_write(DepRegisterInfo *reg, uint64_t val)
 436{
 437    ZynqMPCSUDMA *s = ZYNQMP_CSU_DMA(reg->opaque);
 438
 439    s->regs[R_STATUS] |= STATUS_DMA_BUSY;
 440    /* When starting the DMA channel with a zero length, it signals
 441       done immediately.  */
 442    if (dmach_get_size(s) == 0) {
 443        dmach_done(s);
 444        ronaldu_csu_dma_update_irq(s);
 445        return;
 446    }
 447
 448    if (!s->is_dst) {
 449        zynqmp_csu_dma_src_notify(s);
 450    } else {
 451        if (s->notify) {
 452            s->notify(s->notify_opaque);
 453        }
 454    }
 455}
 456
 457static uint64_t int_status_pre_write(DepRegisterInfo *reg, uint64_t val)
 458{
 459    ZynqMPCSUDMA *s = ZYNQMP_CSU_DMA(reg->opaque);
 460
 461    /* DMA counter decrements on interrupt clear */
 462    if (~val & s->regs[R_INT_STATUS] & INT_DONE) {
 463        dmach_update_dma_cnt(s, -1);
 464    }
 465
 466    return val;
 467}
 468
 469static void int_status_post_write(DepRegisterInfo *reg, uint64_t val)
 470{
 471    ZynqMPCSUDMA *s = ZYNQMP_CSU_DMA(reg->opaque);
 472
 473    ronaldu_csu_dma_update_irq(s);
 474}
 475
 476static uint64_t int_enable_pre_write(DepRegisterInfo *reg, uint64_t val)
 477{
 478    ZynqMPCSUDMA *s = ZYNQMP_CSU_DMA(reg->opaque);
 479    uint32_t v32 = val;
 480
 481    s->regs[R_INT_MASK] &= ~v32;
 482    ronaldu_csu_dma_update_irq(s);
 483    return 0;
 484}
 485
 486static uint64_t int_disable_pre_write(DepRegisterInfo *reg, uint64_t val)
 487{
 488    ZynqMPCSUDMA *s = ZYNQMP_CSU_DMA(reg->opaque);
 489    uint32_t v32 = val;
 490
 491    s->regs[R_INT_MASK] |= v32;
 492    ronaldu_csu_dma_update_irq(s);
 493    return 0;
 494}
 495
 496static void src_timeout_hit(void *opaque)
 497{
 498    ZynqMPCSUDMA *s = ZYNQMP_CSU_DMA(opaque);
 499
 500    /* Ignore if the timeout is masked.  */
 501    if (!dmach_timeout_enabled(s)) {
 502        return;
 503    }
 504
 505    s->regs[R_INT_STATUS] |= INT_TIMEOUT_STRM;
 506    ronaldu_csu_dma_update_irq(s);
 507}
 508
 509static const DepRegisterAccessInfo *zynqmp_csu_dma_regs_info[] = {
 510#define DMACH_REGINFO(NAME, snd)                                              \
 511(const DepRegisterAccessInfo []) {                                               \
 512    [R_ADDR] = { .name =  #NAME "_ADDR" },                                    \
 513    [R_SIZE] = { .name =  #NAME "_SIZE",                                      \
 514                               .pre_write = size_pre_write,                   \
 515                               .post_write = size_post_write },               \
 516    [R_STATUS] = { .name =  #NAME "_STATUS",                                  \
 517            .ro = STATUS_RSVD,                                                \
 518            .w1c = R_STATUS_DMA_DONE_CNT_MASK,                                \
 519            .ge1 = (DepRegisterAccessError[]) {                                  \
 520                 { .mask = ~(R_STATUS_DMA_DONE_CNT_MASK),                     \
 521                   .reason = "cannot write to status register" },             \
 522                   {},                                                        \
 523            },                                                                \
 524    },                                                                        \
 525    [R_CTRL] = { .name = #NAME "_CTRL",                                       \
 526        .ro = (snd) ? CTRL_RSVD : 0,                                          \
 527        .reset = ((snd) ? 0 : 0x40 << CTRL_SSS_FIFOTHRESH_SHIFT) |            \
 528                 R_CTRL_TIMEOUT_MASK | 0x80 << CTRL_FIFO_THRESH_SHIFT,        \
 529        .ge1 = (DepRegisterAccessError[]) {                                      \
 530            { .mask = (snd) ? CTRL_RSVD : 0,                                  \
 531              .reason = "write of 1 to reserved bit" },                       \
 532            {},                                                               \
 533        },                                                                    \
 534        .post_write = r_ctrl_post_write,                                      \
 535    },                                                                        \
 536    [R_CRC] = { .name =  #NAME "_CRC" },                                      \
 537    [R_INT_STATUS] = { .name =  #NAME "_INT_STATUS",                          \
 538                                     .w1c = ~0,                               \
 539                                     .pre_write = int_status_pre_write,       \
 540                                     .post_write = int_status_post_write },   \
 541    [R_INT_ENABLE] = { .name =  #NAME "_INT_ENABLE",                          \
 542                                     .pre_write = int_enable_pre_write },     \
 543    [R_INT_DISABLE] = { .name =  #NAME "_INT_DISABLE",                        \
 544                                     .pre_write = int_disable_pre_write },    \
 545    [R_INT_MASK] = { .name =  #NAME "_INT_MASK",                              \
 546                                     .reset = snd ? INT_ALL_SRC : INT_ALL_DST,\
 547                                     .ro = ~0 },                              \
 548    [R_CTRL2] = { .name =  #NAME "_CTRL2",                                    \
 549        .reset = 0x8 << CTRL2_MAX_OUTS_CMDS_SHIFT |                           \
 550                 0xFFF << CTRL2_TIMEOUT_PRE_SHIFT | 0x081b0000,               \
 551        .ro = CTRL2_RSVD,                                                     \
 552        .ge0 = (DepRegisterAccessError[]) {                                      \
 553            { .mask = 0x00090000, .reason = "reserved - do not modify" },     \
 554            {}                                                                \
 555        },                                                                    \
 556        .ge1 = (DepRegisterAccessError[]) {                                      \
 557            { .mask = 0x00F60000, .reason = "reserved - do not modify" },     \
 558            {}                                                                \
 559        }                                                                     \
 560    }                                                                         \
 561}
 562    DMACH_REGINFO(DMA_SRC, true),
 563    DMACH_REGINFO(DMA_DST, false)
 564};
 565
 566static const MemoryRegionOps zynqmp_csu_dma_ops = {
 567    .read = dep_register_read_memory_le,
 568    .write = dep_register_write_memory_le,
 569    .endianness = DEVICE_LITTLE_ENDIAN,
 570    .valid = {
 571        .min_access_size = 4,
 572        .max_access_size = 4,
 573    }
 574};
 575
 576static void map_dma_channel(const char *prefix, ZynqMPCSUDMA *s)
 577{
 578    int i;
 579
 580    for (i = 0; i < R_MAX; ++i) {
 581        DepRegisterInfo *r = &s->regs_info[i];
 582
 583        *r = (DepRegisterInfo) {
 584            .data = (uint8_t *)&s->regs[i],
 585            .data_size = sizeof(uint32_t),
 586            .access = &zynqmp_csu_dma_regs_info[!!s->is_dst][i],
 587            .debug = ZYNQMP_CSU_DMA_ERR_DEBUG,
 588            .prefix = prefix,
 589            .opaque = s,
 590        };
 591        memory_region_init_io(&r->mem, OBJECT(s), &zynqmp_csu_dma_ops, r,
 592                              r->access->name, 4);
 593        memory_region_add_subregion(&s->iomem, i * 4, &r->mem);
 594    }
 595}
 596
 597static void zynqmp_csu_dma_realize(DeviceState *dev, Error **errp)
 598{
 599    ZynqMPCSUDMA *s = ZYNQMP_CSU_DMA(dev);
 600    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
 601
 602    memory_region_init(&s->iomem, OBJECT(dev), "zynqmp.csu-dma", 0x800);
 603    sysbus_init_mmio(sbd, &s->iomem);
 604
 605    const char *prefix = object_get_canonical_path(OBJECT(dev));
 606
 607    map_dma_channel(prefix, s);
 608
 609    s->bh = qemu_bh_new(src_timeout_hit, s);
 610    s->src_timer = ptimer_init(s->bh);
 611
 612    s->dma_as = s->dma_mr ? address_space_init_shareable(s->dma_mr, NULL)
 613                          : &address_space_memory;
 614
 615    if (!s->attr) {
 616        s->attr = MEMORY_TRANSACTION_ATTR(
 617                      object_new(TYPE_MEMORY_TRANSACTION_ATTR));
 618    }
 619}
 620
 621static void zynqmp_csu_dma_init(Object *obj)
 622{
 623    ZynqMPCSUDMA *s = ZYNQMP_CSU_DMA(obj);
 624    SysBusDevice *sbd = SYS_BUS_DEVICE(obj);
 625
 626    sysbus_init_irq(sbd, &s->irq);
 627
 628    object_property_add_link(obj, "stream-connected-dma", TYPE_STREAM_SLAVE,
 629                             (Object **) &s->tx_dev,
 630                             qdev_prop_allow_set_link_before_realize,
 631                             OBJ_PROP_LINK_UNREF_ON_RELEASE,
 632                             NULL);
 633    object_property_add_link(obj, "dma", TYPE_MEMORY_REGION,
 634                             (Object **)&s->dma_mr,
 635                             qdev_prop_allow_set_link_before_realize,
 636                             OBJ_PROP_LINK_UNREF_ON_RELEASE,
 637                             &error_abort);
 638    object_property_add_link(obj, "memattr", TYPE_MEMORY_TRANSACTION_ATTR,
 639                             (Object **)&s->attr,
 640                             qdev_prop_allow_set_link_before_realize,
 641                             OBJ_PROP_LINK_UNREF_ON_RELEASE,
 642                             &error_abort);
 643
 644}
 645
 646static const VMStateDescription vmstate_zynqmp_csu_dma = {
 647    .name = "zynqmp_csu_dma",
 648    .version_id = 2,
 649    .minimum_version_id = 2,
 650    .minimum_version_id_old = 2,
 651    .fields = (VMStateField[]) {
 652        VMSTATE_PTIMER(src_timer, ZynqMPCSUDMA),
 653        VMSTATE_UINT32_ARRAY(regs, ZynqMPCSUDMA, R_MAX),
 654        VMSTATE_END_OF_LIST(),
 655    }
 656};
 657
 658static Property zynqmp_csu_dma_properties [] = {
 659    DEFINE_PROP_BOOL("is-dst", ZynqMPCSUDMA, is_dst, false),
 660    DEFINE_PROP_END_OF_LIST(),
 661};
 662
 663static void zynqmp_csu_dma_class_init(ObjectClass *klass, void *data)
 664{
 665    DeviceClass *dc = DEVICE_CLASS(klass);
 666    StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass);
 667
 668    dc->reset = zynqmp_csu_dma_reset;
 669    dc->realize = zynqmp_csu_dma_realize;
 670    dc->vmsd = &vmstate_zynqmp_csu_dma;
 671    dc->props = zynqmp_csu_dma_properties;
 672
 673    ssc->push = zynqmp_csu_dma_stream_push;
 674    ssc->can_push = zynqmp_csu_dma_stream_can_push;
 675}
 676
 677static const TypeInfo zynqmp_csu_dma_info = {
 678    .name          = TYPE_ZYNQMP_CSU_DMA,
 679    .parent        = TYPE_SYS_BUS_DEVICE,
 680    .instance_size = sizeof(ZynqMPCSUDMA),
 681    .class_init    = zynqmp_csu_dma_class_init,
 682    .instance_init = zynqmp_csu_dma_init,
 683    .interfaces = (InterfaceInfo[]) {
 684        { TYPE_STREAM_SLAVE },
 685        { }
 686    }
 687};
 688
 689static void zynqmp_csu_dma_register_types(void)
 690{
 691    type_register_static(&zynqmp_csu_dma_info);
 692}
 693
 694type_init(zynqmp_csu_dma_register_types)
 695