2 * Xilinx Platform CSU Stream DMA emulation
4 * This implementation is based on
5 * https://github.com/Xilinx/qemu/blob/master/hw/dma/csu_stream_dma.c
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.
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.
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/>.
21 #include "qemu/osdep.h"
23 #include "qapi/error.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"
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
40 FIELD(ADDR
, ADDR
, 2, 30) /* wo */
42 FIELD(SIZE
, SIZE
, 2, 27) /* wo */
43 FIELD(SIZE
, LAST_WORD
, 0, 1) /* rw, only exists in SRC */
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 */
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 */
59 REG32(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 */
68 REG32(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 */
77 REG32(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 */
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 */
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 */
101 REG32(ADDR_MSB
, 0x28)
102 FIELD(ADDR_MSB
, ADDR_MSB
, 0, 17) /* wo */
104 #define R_CTRL_TIMEOUT_VAL_RESET (0xFFE)
105 #define R_CTRL_FIFO_THRESH_RESET (0x80)
106 #define R_CTRL_FIFOTHRESH_RESET (0x40)
108 #define R_CTRL2_TIMEOUT_PRE_RESET (0xFFF)
109 #define R_CTRL2_MAX_OUTS_CMDS_RESET (0x8)
111 #define XLNX_CSU_DMA_ERR_DEBUG (0)
112 #define XLNX_CSU_DMA_INT_R_MASK (0xff)
114 /* UG1807: Set the prescaler value for the timeout in clk (~2.5ns) cycles */
115 #define XLNX_CSU_DMA_TIMER_FREQ (400 * 1000 * 1000)
117 static bool xlnx_csu_dma_is_paused(XlnxCSUDMA
*s
)
121 paused
= !!(s
->regs
[R_CTRL
] & R_CTRL_PAUSE_STRM_MASK
);
122 paused
|= !!(s
->regs
[R_CTRL
] & R_CTRL_PAUSE_MEM_MASK
);
127 static bool xlnx_csu_dma_get_eop(XlnxCSUDMA
*s
)
129 return s
->r_size_last_word
;
132 static bool xlnx_csu_dma_burst_is_fixed(XlnxCSUDMA
*s
)
134 return !!(s
->regs
[R_CTRL
] & R_CTRL_AXI_BRST_TYPE_MASK
);
137 static bool xlnx_csu_dma_timeout_enabled(XlnxCSUDMA
*s
)
139 return !!(s
->regs
[R_CTRL2
] & R_CTRL2_TIMEOUT_EN_MASK
);
142 static void xlnx_csu_dma_update_done_cnt(XlnxCSUDMA
*s
, int a
)
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
);
151 static void xlnx_csu_dma_data_process(XlnxCSUDMA
*s
, uint8_t *buf
, uint32_t len
)
156 bswap
= s
->regs
[R_CTRL
] & R_CTRL_ENDIANNESS_MASK
;
157 if (s
->is_dst
&& !bswap
) {
158 /* Fast when ENDIANNESS cleared */
162 for (i
= 0; i
< len
; i
+= 4) {
163 uint8_t *b
= &buf
[i
];
168 .u8
= { b
[0], b
[1], b
[2], b
[3] }
172 s
->regs
[R_CRC
] += v
.u32
;
176 * No point using bswap, we need to writeback
177 * into a potentially unaligned pointer.
187 static void xlnx_csu_dma_update_irq(XlnxCSUDMA
*s
)
189 qemu_set_irq(s
->irq
, !!(s
->regs
[R_INT_STATUS
] & ~s
->regs
[R_INT_MASK
]));
192 /* len is in bytes */
193 static uint32_t xlnx_csu_dma_read(XlnxCSUDMA
*s
, uint8_t *buf
, uint32_t len
)
195 hwaddr addr
= (hwaddr
)s
->regs
[R_ADDR_MSB
] << 32 | s
->regs
[R_ADDR
];
196 MemTxResult result
= MEMTX_OK
;
198 if (xlnx_csu_dma_burst_is_fixed(s
)) {
201 for (i
= 0; i
< len
&& (result
== MEMTX_OK
); i
+= s
->width
) {
202 uint32_t mlen
= MIN(len
- i
, s
->width
);
204 result
= address_space_rw(&s
->dma_as
, addr
, s
->attr
,
205 buf
+ i
, mlen
, false);
208 result
= address_space_rw(&s
->dma_as
, addr
, s
->attr
, buf
, len
, false);
211 if (result
== MEMTX_OK
) {
212 xlnx_csu_dma_data_process(s
, buf
, len
);
214 qemu_log_mask(LOG_GUEST_ERROR
, "%s: Bad address " HWADDR_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
);
222 /* len is in bytes */
223 static uint32_t xlnx_csu_dma_write(XlnxCSUDMA
*s
, uint8_t *buf
, uint32_t len
)
225 hwaddr addr
= (hwaddr
)s
->regs
[R_ADDR_MSB
] << 32 | s
->regs
[R_ADDR
];
226 MemTxResult result
= MEMTX_OK
;
228 xlnx_csu_dma_data_process(s
, buf
, len
);
229 if (xlnx_csu_dma_burst_is_fixed(s
)) {
232 for (i
= 0; i
< len
&& (result
== MEMTX_OK
); i
+= s
->width
) {
233 uint32_t mlen
= MIN(len
- i
, s
->width
);
235 result
= address_space_rw(&s
->dma_as
, addr
, s
->attr
,
240 result
= address_space_rw(&s
->dma_as
, addr
, s
->attr
, buf
, len
, true);
243 if (result
!= MEMTX_OK
) {
244 qemu_log_mask(LOG_GUEST_ERROR
, "%s: Bad address " HWADDR_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
);
252 static void xlnx_csu_dma_done(XlnxCSUDMA
*s
)
254 s
->regs
[R_STATUS
] &= ~R_STATUS_BUSY_MASK
;
255 s
->regs
[R_INT_STATUS
] |= R_INT_STATUS_DONE_MASK
;
258 s
->regs
[R_INT_STATUS
] |= R_INT_STATUS_MEM_DONE_MASK
;
261 xlnx_csu_dma_update_done_cnt(s
, 1);
264 static uint32_t xlnx_csu_dma_advance(XlnxCSUDMA
*s
, uint32_t len
)
266 uint32_t size
= s
->regs
[R_SIZE
];
267 hwaddr dst
= (hwaddr
)s
->regs
[R_ADDR_MSB
] << 32 | s
->regs
[R_ADDR
];
272 s
->regs
[R_SIZE
] = size
;
274 if (!xlnx_csu_dma_burst_is_fixed(s
)) {
276 s
->regs
[R_ADDR
] = (uint32_t) dst
;
277 s
->regs
[R_ADDR_MSB
] = dst
>> 32;
281 xlnx_csu_dma_done(s
);
287 static void xlnx_csu_dma_src_notify(void *opaque
)
289 XlnxCSUDMA
*s
= XLNX_CSU_DMA(opaque
);
290 unsigned char buf
[4 * 1024];
293 ptimer_transaction_begin(s
->src_timer
);
294 /* Stop the backpreassure timer */
295 ptimer_stop(s
->src_timer
);
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
);
302 /* Did we fit it all? */
303 if (s
->regs
[R_SIZE
] == plen
&& xlnx_csu_dma_get_eop(s
)) {
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
);
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
;
320 ptimer_set_freq(s
->src_timer
, freq
);
321 ptimer_set_count(s
->src_timer
, timeout
);
322 ptimer_run(s
->src_timer
, 1);
325 ptimer_transaction_commit(s
->src_timer
);
326 xlnx_csu_dma_update_irq(s
);
329 static uint64_t addr_pre_write(RegisterInfo
*reg
, uint64_t val
)
331 /* Address is word aligned */
332 return val
& R_ADDR_ADDR_MASK
;
335 static uint64_t size_pre_write(RegisterInfo
*reg
, uint64_t val
)
337 XlnxCSUDMA
*s
= XLNX_CSU_DMA(reg
->opaque
);
339 if (s
->regs
[R_SIZE
] != 0) {
340 qemu_log_mask(LOG_GUEST_ERROR
,
341 "%s: Starting DMA while already running.\n", __func__
);
345 s
->r_size_last_word
= !!(val
& R_SIZE_LAST_WORD_MASK
);
348 /* Size is word aligned */
349 return val
& R_SIZE_SIZE_MASK
;
352 static uint64_t size_post_read(RegisterInfo
*reg
, uint64_t val
)
354 XlnxCSUDMA
*s
= XLNX_CSU_DMA(reg
->opaque
);
356 return val
| s
->r_size_last_word
;
359 static void size_post_write(RegisterInfo
*reg
, uint64_t val
)
361 XlnxCSUDMA
*s
= XLNX_CSU_DMA(reg
->opaque
);
363 s
->regs
[R_STATUS
] |= R_STATUS_BUSY_MASK
;
366 * Note that if SIZE is programmed to 0, and the DMA is started,
367 * the interrupts DONE and MEM_DONE will be asserted.
369 if (s
->regs
[R_SIZE
] == 0) {
370 xlnx_csu_dma_done(s
);
371 xlnx_csu_dma_update_irq(s
);
375 /* Set SIZE is considered the last step in transfer configuration */
377 xlnx_csu_dma_src_notify(s
);
380 s
->notify(s
->notify_opaque
);
385 static uint64_t status_pre_write(RegisterInfo
*reg
, uint64_t val
)
387 return val
& (R_STATUS_DONE_CNT_MASK
| R_STATUS_BUSY_MASK
);
390 static void ctrl_post_write(RegisterInfo
*reg
, uint64_t val
)
392 XlnxCSUDMA
*s
= XLNX_CSU_DMA(reg
->opaque
);
395 if (!xlnx_csu_dma_is_paused(s
)) {
396 xlnx_csu_dma_src_notify(s
);
399 if (!xlnx_csu_dma_is_paused(s
) && s
->notify
) {
400 s
->notify(s
->notify_opaque
);
405 static uint64_t int_status_pre_write(RegisterInfo
*reg
, uint64_t val
)
407 XlnxCSUDMA
*s
= XLNX_CSU_DMA(reg
->opaque
);
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);
414 return s
->regs
[R_INT_STATUS
] & ~val
;
417 static void int_status_post_write(RegisterInfo
*reg
, uint64_t val
)
419 XlnxCSUDMA
*s
= XLNX_CSU_DMA(reg
->opaque
);
421 xlnx_csu_dma_update_irq(s
);
424 static uint64_t int_enable_pre_write(RegisterInfo
*reg
, uint64_t val
)
426 XlnxCSUDMA
*s
= XLNX_CSU_DMA(reg
->opaque
);
430 * R_INT_ENABLE doesn't have its own state.
431 * It is used to indirectly modify R_INT_MASK.
433 * 1: Enable this interrupt field (the mask bit will be cleared to 0)
436 s
->regs
[R_INT_MASK
] &= ~v32
;
440 static void int_enable_post_write(RegisterInfo
*reg
, uint64_t val
)
442 XlnxCSUDMA
*s
= XLNX_CSU_DMA(reg
->opaque
);
444 xlnx_csu_dma_update_irq(s
);
447 static uint64_t int_disable_pre_write(RegisterInfo
*reg
, uint64_t val
)
449 XlnxCSUDMA
*s
= XLNX_CSU_DMA(reg
->opaque
);
453 * R_INT_DISABLE doesn't have its own state.
454 * It is used to indirectly modify R_INT_MASK.
456 * 1: Disable this interrupt field (the mask bit will be set to 1)
459 s
->regs
[R_INT_MASK
] |= v32
;
463 static void int_disable_post_write(RegisterInfo
*reg
, uint64_t val
)
465 XlnxCSUDMA
*s
= XLNX_CSU_DMA(reg
->opaque
);
467 xlnx_csu_dma_update_irq(s
);
470 static uint64_t addr_msb_pre_write(RegisterInfo
*reg
, uint64_t val
)
472 return val
& R_ADDR_MSB_ADDR_MSB_MASK
;
475 static MemTxResult
xlnx_csu_dma_class_read(XlnxCSUDMA
*s
, hwaddr addr
,
478 RegisterInfo
*reg
= &s
->regs_info
[R_SIZE
];
479 uint64_t we
= MAKE_64BIT_MASK(0, 4 * 8);
481 s
->regs
[R_ADDR
] = addr
;
482 s
->regs
[R_ADDR_MSB
] = (uint64_t)addr
>> 32;
484 register_write(reg
, len
, we
, object_get_typename(OBJECT(s
)), false);
486 return (s
->regs
[R_SIZE
] == 0) ? MEMTX_OK
: MEMTX_ERROR
;
489 static const RegisterAccessInfo
*xlnx_csu_dma_regs_info
[] = {
490 #define DMACH_REGINFO(NAME, snd) \
491 (const RegisterAccessInfo []) { \
493 .name = #NAME "_ADDR", \
495 .pre_write = addr_pre_write \
497 .name = #NAME "_SIZE", \
499 .pre_write = size_pre_write, \
500 .post_write = size_post_write, \
501 .post_read = size_post_read \
503 .name = #NAME "_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) \
511 .name = #NAME "_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)) \
519 .name = #NAME "_CRC", \
522 .name = #NAME "_INT_STATUS", \
523 .addr = A_INT_STATUS, \
524 .pre_write = int_status_pre_write, \
525 .post_write = int_status_post_write \
527 .name = #NAME "_INT_ENABLE", \
528 .addr = A_INT_ENABLE, \
529 .pre_write = int_enable_pre_write, \
530 .post_write = int_enable_post_write \
532 .name = #NAME "_INT_DISABLE", \
533 .addr = A_INT_DISABLE, \
534 .pre_write = int_disable_pre_write, \
535 .post_write = int_disable_post_write \
537 .name = #NAME "_INT_MASK", \
538 .addr = A_INT_MASK, \
540 .reset = XLNX_CSU_DMA_INT_R_MASK \
542 .name = #NAME "_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)) \
549 .name = #NAME "_ADDR_MSB", \
550 .addr = A_ADDR_MSB, \
551 .pre_write = addr_msb_pre_write \
555 DMACH_REGINFO(DMA_SRC
, true),
556 DMACH_REGINFO(DMA_DST
, false)
559 static const MemoryRegionOps xlnx_csu_dma_ops
= {
560 .read
= register_read_memory
,
561 .write
= register_write_memory
,
562 .endianness
= DEVICE_LITTLE_ENDIAN
,
564 .min_access_size
= 4,
565 .max_access_size
= 4,
569 static void xlnx_csu_dma_src_timeout_hit(void *opaque
)
571 XlnxCSUDMA
*s
= XLNX_CSU_DMA(opaque
);
573 /* Ignore if the timeout is masked */
574 if (!xlnx_csu_dma_timeout_enabled(s
)) {
578 s
->regs
[R_INT_STATUS
] |= R_INT_STATUS_TIMEOUT_STRM_MASK
;
579 xlnx_csu_dma_update_irq(s
);
582 static size_t xlnx_csu_dma_stream_push(StreamSink
*obj
, uint8_t *buf
,
583 size_t len
, bool eop
)
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 */
589 /* Be called when it's DST */
592 if (size
== 0 || len
<= 0) {
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
;
603 if (xlnx_csu_dma_write(s
, buf
, mlen
) != mlen
) {
607 xlnx_csu_dma_advance(s
, mlen
);
608 xlnx_csu_dma_update_irq(s
);
613 static bool xlnx_csu_dma_stream_can_push(StreamSink
*obj
,
614 StreamCanPushNotifyFn notify
,
617 XlnxCSUDMA
*s
= XLNX_CSU_DMA(obj
);
619 if (s
->regs
[R_SIZE
] != 0) {
623 s
->notify_opaque
= notify_opaque
;
628 static void xlnx_csu_dma_reset(DeviceState
*dev
)
630 XlnxCSUDMA
*s
= XLNX_CSU_DMA(dev
);
633 for (i
= 0; i
< ARRAY_SIZE(s
->regs_info
); ++i
) {
634 register_reset(&s
->regs_info
[i
]);
638 static void xlnx_csu_dma_realize(DeviceState
*dev
, Error
**errp
)
640 XlnxCSUDMA
*s
= XLNX_CSU_DMA(dev
);
641 RegisterInfoArray
*reg_array
;
643 if (!s
->is_dst
&& !s
->tx_dev
) {
644 error_setg(errp
, "zynqmp.csu-dma: Stream not connected");
649 error_setg(errp
, TYPE_XLNX_CSU_DMA
" 'dma' link not set");
652 address_space_init(&s
->dma_as
, s
->dma_mr
, "csu-dma");
655 register_init_block32(dev
, xlnx_csu_dma_regs_info
[!!s
->is_dst
],
657 s
->regs_info
, s
->regs
,
659 XLNX_CSU_DMA_ERR_DEBUG
,
660 XLNX_CSU_DMA_R_MAX
* 4);
661 memory_region_add_subregion(&s
->iomem
,
665 sysbus_init_mmio(SYS_BUS_DEVICE(dev
), &s
->iomem
);
666 sysbus_init_irq(SYS_BUS_DEVICE(dev
), &s
->irq
);
668 s
->src_timer
= ptimer_init(xlnx_csu_dma_src_timeout_hit
,
669 s
, PTIMER_POLICY_LEGACY
);
671 s
->attr
= MEMTXATTRS_UNSPECIFIED
;
673 s
->r_size_last_word
= 0;
676 static const VMStateDescription vmstate_xlnx_csu_dma
= {
677 .name
= TYPE_XLNX_CSU_DMA
,
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(),
690 static Property xlnx_csu_dma_properties
[] = {
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".
698 DEFINE_PROP_UINT16("dma-width", XlnxCSUDMA
, width
, 4),
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.
704 DEFINE_PROP_BOOL("is-dst", XlnxCSUDMA
, is_dst
, true),
705 DEFINE_PROP_END_OF_LIST(),
708 static void xlnx_csu_dma_class_init(ObjectClass
*klass
, void *data
)
710 DeviceClass
*dc
= DEVICE_CLASS(klass
);
711 StreamSinkClass
*ssc
= STREAM_SINK_CLASS(klass
);
712 XlnxCSUDMAClass
*xcdc
= XLNX_CSU_DMA_CLASS(klass
);
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
);
719 ssc
->push
= xlnx_csu_dma_stream_push
;
720 ssc
->can_push
= xlnx_csu_dma_stream_can_push
;
722 xcdc
->read
= xlnx_csu_dma_class_read
;
725 static void xlnx_csu_dma_init(Object
*obj
)
727 XlnxCSUDMA
*s
= XLNX_CSU_DMA(obj
);
729 memory_region_init(&s
->iomem
, obj
, TYPE_XLNX_CSU_DMA
,
730 XLNX_CSU_DMA_R_MAX
* 4);
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
);
742 static 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
},
755 static void xlnx_csu_dma_register_types(void)
757 type_register_static(&xlnx_csu_dma_info
);
760 type_init(xlnx_csu_dma_register_types
)