xref: /openbmc/qemu/hw/dma/xlnx_csu_dma.c (revision f6476697)
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  */
39 REG32(ADDR, 0x0)
40     FIELD(ADDR, ADDR, 2, 30) /* wo */
41 REG32(SIZE, 0x4)
42     FIELD(SIZE, SIZE, 2, 27) /* wo */
43     FIELD(SIZE, LAST_WORD, 0, 1) /* rw, only exists in SRC */
44 REG32(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 */
49 REG32(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 */
58 REG32(CRC, 0x10)
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 */
86 REG32(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 */
95 REG32(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 */
101 REG32(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 
117 static 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 
127 static bool xlnx_csu_dma_get_eop(XlnxCSUDMA *s)
128 {
129     return s->r_size_last_word;
130 }
131 
132 static bool xlnx_csu_dma_burst_is_fixed(XlnxCSUDMA *s)
133 {
134     return !!(s->regs[R_CTRL] & R_CTRL_AXI_BRST_TYPE_MASK);
135 }
136 
137 static bool xlnx_csu_dma_timeout_enabled(XlnxCSUDMA *s)
138 {
139     return !!(s->regs[R_CTRL2] & R_CTRL2_TIMEOUT_EN_MASK);
140 }
141 
142 static 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 
151 static 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 
187 static 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 */
193 static 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 */
223 static 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 
252 static 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 
264 static 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 
287 static 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 
329 static 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 
335 static 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 
352 static 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 
359 static 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 
385 static 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 
390 static 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 
405 static 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 
417 static 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 
424 static 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 
440 static 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 
447 static 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 
463 static 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 
470 static uint64_t addr_msb_pre_write(RegisterInfo *reg, uint64_t val)
471 {
472     return val & R_ADDR_MSB_ADDR_MSB_MASK;
473 }
474 
475 static const RegisterAccessInfo *xlnx_csu_dma_regs_info[] = {
476 #define DMACH_REGINFO(NAME, snd)                                              \
477     (const RegisterAccessInfo []) {                                           \
478         {                                                                     \
479             .name = #NAME "_ADDR",                                            \
480             .addr = A_ADDR,                                                   \
481             .pre_write = addr_pre_write                                       \
482         }, {                                                                  \
483             .name = #NAME "_SIZE",                                            \
484             .addr = A_SIZE,                                                   \
485             .pre_write = size_pre_write,                                      \
486             .post_write = size_post_write,                                    \
487             .post_read = size_post_read                                       \
488         }, {                                                                  \
489             .name = #NAME "_STATUS",                                          \
490             .addr = A_STATUS,                                                 \
491             .pre_write = status_pre_write,                                    \
492             .w1c = R_STATUS_DONE_CNT_MASK,                                    \
493             .ro = (R_STATUS_BUSY_MASK                                         \
494                    | R_STATUS_FIFO_LEVEL_MASK                                 \
495                    | R_STATUS_OUTSTANDING_MASK)                               \
496         }, {                                                                  \
497             .name = #NAME "_CTRL",                                            \
498             .addr = A_CTRL,                                                   \
499             .post_write = ctrl_post_write,                                    \
500             .reset = ((R_CTRL_TIMEOUT_VAL_RESET << R_CTRL_TIMEOUT_VAL_SHIFT)  \
501                       | (R_CTRL_FIFO_THRESH_RESET << R_CTRL_FIFO_THRESH_SHIFT)\
502                       | (snd ? 0 : R_CTRL_FIFOTHRESH_RESET                    \
503                          << R_CTRL_FIFOTHRESH_SHIFT))                         \
504         }, {                                                                  \
505             .name = #NAME "_CRC",                                             \
506             .addr = A_CRC,                                                    \
507         }, {                                                                  \
508             .name =  #NAME "_INT_STATUS",                                     \
509             .addr = A_INT_STATUS,                                             \
510             .pre_write = int_status_pre_write,                                \
511             .post_write = int_status_post_write                               \
512         }, {                                                                  \
513             .name = #NAME "_INT_ENABLE",                                      \
514             .addr = A_INT_ENABLE,                                             \
515             .pre_write = int_enable_pre_write,                                \
516             .post_write = int_enable_post_write                               \
517         }, {                                                                  \
518             .name = #NAME "_INT_DISABLE",                                     \
519             .addr = A_INT_DISABLE,                                            \
520             .pre_write = int_disable_pre_write,                               \
521             .post_write = int_disable_post_write                              \
522         }, {                                                                  \
523             .name = #NAME "_INT_MASK",                                        \
524             .addr = A_INT_MASK,                                               \
525             .ro = ~0,                                                         \
526             .reset = XLNX_CSU_DMA_INT_R_MASK                                  \
527         }, {                                                                  \
528             .name = #NAME "_CTRL2",                                           \
529             .addr = A_CTRL2,                                                  \
530             .reset = ((R_CTRL2_TIMEOUT_PRE_RESET                              \
531                        << R_CTRL2_TIMEOUT_PRE_SHIFT)                          \
532                       | (R_CTRL2_MAX_OUTS_CMDS_RESET                          \
533                          << R_CTRL2_MAX_OUTS_CMDS_SHIFT))                     \
534         }, {                                                                  \
535             .name = #NAME "_ADDR_MSB",                                        \
536             .addr = A_ADDR_MSB,                                               \
537             .pre_write = addr_msb_pre_write                                   \
538         }                                                                     \
539     }
540 
541     DMACH_REGINFO(DMA_SRC, true),
542     DMACH_REGINFO(DMA_DST, false)
543 };
544 
545 static const MemoryRegionOps xlnx_csu_dma_ops = {
546     .read = register_read_memory,
547     .write = register_write_memory,
548     .endianness = DEVICE_LITTLE_ENDIAN,
549     .valid = {
550         .min_access_size = 4,
551         .max_access_size = 4,
552     }
553 };
554 
555 static void xlnx_csu_dma_src_timeout_hit(void *opaque)
556 {
557     XlnxCSUDMA *s = XLNX_CSU_DMA(opaque);
558 
559     /* Ignore if the timeout is masked */
560     if (!xlnx_csu_dma_timeout_enabled(s)) {
561         return;
562     }
563 
564     s->regs[R_INT_STATUS] |= R_INT_STATUS_TIMEOUT_STRM_MASK;
565     xlnx_csu_dma_update_irq(s);
566 }
567 
568 static size_t xlnx_csu_dma_stream_push(StreamSink *obj, uint8_t *buf,
569                                        size_t len, bool eop)
570 {
571     XlnxCSUDMA *s = XLNX_CSU_DMA(obj);
572     uint32_t size = s->regs[R_SIZE];
573     uint32_t mlen = MIN(size, len) & (~3); /* Size is word aligned */
574 
575     /* Be called when it's DST */
576     assert(s->is_dst);
577 
578     if (size == 0 || len <= 0) {
579         return 0;
580     }
581 
582     if (len && (xlnx_csu_dma_is_paused(s) || mlen == 0)) {
583         qemu_log_mask(LOG_GUEST_ERROR,
584                       "csu-dma: DST channel dropping %zd b of data.\n", len);
585         s->regs[R_INT_STATUS] |= R_INT_STATUS_FIFO_OVERFLOW_MASK;
586         return len;
587     }
588 
589     if (xlnx_csu_dma_write(s, buf, mlen) != mlen) {
590         return 0;
591     }
592 
593     xlnx_csu_dma_advance(s, mlen);
594     xlnx_csu_dma_update_irq(s);
595 
596     return mlen;
597 }
598 
599 static bool xlnx_csu_dma_stream_can_push(StreamSink *obj,
600                                          StreamCanPushNotifyFn notify,
601                                          void *notify_opaque)
602 {
603     XlnxCSUDMA *s = XLNX_CSU_DMA(obj);
604 
605     if (s->regs[R_SIZE] != 0) {
606         return true;
607     } else {
608         s->notify = notify;
609         s->notify_opaque = notify_opaque;
610         return false;
611     }
612 }
613 
614 static void xlnx_csu_dma_reset(DeviceState *dev)
615 {
616     XlnxCSUDMA *s = XLNX_CSU_DMA(dev);
617     unsigned int i;
618 
619     for (i = 0; i < ARRAY_SIZE(s->regs_info); ++i) {
620         register_reset(&s->regs_info[i]);
621     }
622 }
623 
624 static void xlnx_csu_dma_realize(DeviceState *dev, Error **errp)
625 {
626     XlnxCSUDMA *s = XLNX_CSU_DMA(dev);
627     RegisterInfoArray *reg_array;
628 
629     reg_array =
630         register_init_block32(dev, xlnx_csu_dma_regs_info[!!s->is_dst],
631                               XLNX_CSU_DMA_R_MAX,
632                               s->regs_info, s->regs,
633                               &xlnx_csu_dma_ops,
634                               XLNX_CSU_DMA_ERR_DEBUG,
635                               XLNX_CSU_DMA_R_MAX * 4);
636     memory_region_add_subregion(&s->iomem,
637                                 0x0,
638                                 &reg_array->mem);
639 
640     sysbus_init_mmio(SYS_BUS_DEVICE(dev), &s->iomem);
641     sysbus_init_irq(SYS_BUS_DEVICE(dev), &s->irq);
642 
643     if (!s->is_dst && !s->tx_dev) {
644         error_setg(errp, "zynqmp.csu-dma: Stream not connected");
645         return;
646     }
647 
648     s->src_timer = ptimer_init(xlnx_csu_dma_src_timeout_hit,
649                                s, PTIMER_POLICY_DEFAULT);
650 
651     if (s->dma_mr) {
652         s->dma_as = g_malloc0(sizeof(AddressSpace));
653         address_space_init(s->dma_as, s->dma_mr, NULL);
654     } else {
655         s->dma_as = &address_space_memory;
656     }
657 
658     s->attr = MEMTXATTRS_UNSPECIFIED;
659 
660     s->r_size_last_word = 0;
661 }
662 
663 static const VMStateDescription vmstate_xlnx_csu_dma = {
664     .name = TYPE_XLNX_CSU_DMA,
665     .version_id = 0,
666     .minimum_version_id = 0,
667     .minimum_version_id_old = 0,
668     .fields = (VMStateField[]) {
669         VMSTATE_PTIMER(src_timer, XlnxCSUDMA),
670         VMSTATE_UINT16(width, XlnxCSUDMA),
671         VMSTATE_BOOL(is_dst, XlnxCSUDMA),
672         VMSTATE_BOOL(r_size_last_word, XlnxCSUDMA),
673         VMSTATE_UINT32_ARRAY(regs, XlnxCSUDMA, XLNX_CSU_DMA_R_MAX),
674         VMSTATE_END_OF_LIST(),
675     }
676 };
677 
678 static Property xlnx_csu_dma_properties[] = {
679     /*
680      * Ref PG021, Stream Data Width:
681      * Data width in bits of the AXI S2MM AXI4-Stream Data bus.
682      * This value must be equal or less than the Memory Map Data Width.
683      * Valid values are 8, 16, 32, 64, 128, 512 and 1024.
684      * "dma-width" is the byte value of the "Stream Data Width".
685      */
686     DEFINE_PROP_UINT16("dma-width", XlnxCSUDMA, width, 4),
687     /*
688      * The CSU DMA is a two-channel, simple DMA, allowing separate control of
689      * the SRC (read) channel and DST (write) channel. "is-dst" is used to mark
690      * which channel the device is connected to.
691      */
692     DEFINE_PROP_BOOL("is-dst", XlnxCSUDMA, is_dst, true),
693     DEFINE_PROP_END_OF_LIST(),
694 };
695 
696 static void xlnx_csu_dma_class_init(ObjectClass *klass, void *data)
697 {
698     DeviceClass *dc = DEVICE_CLASS(klass);
699     StreamSinkClass *ssc = STREAM_SINK_CLASS(klass);
700 
701     dc->reset = xlnx_csu_dma_reset;
702     dc->realize = xlnx_csu_dma_realize;
703     dc->vmsd = &vmstate_xlnx_csu_dma;
704     device_class_set_props(dc, xlnx_csu_dma_properties);
705 
706     ssc->push = xlnx_csu_dma_stream_push;
707     ssc->can_push = xlnx_csu_dma_stream_can_push;
708 }
709 
710 static void xlnx_csu_dma_init(Object *obj)
711 {
712     XlnxCSUDMA *s = XLNX_CSU_DMA(obj);
713 
714     memory_region_init(&s->iomem, obj, TYPE_XLNX_CSU_DMA,
715                        XLNX_CSU_DMA_R_MAX * 4);
716 
717     object_property_add_link(obj, "stream-connected-dma", TYPE_STREAM_SINK,
718                              (Object **)&s->tx_dev,
719                              qdev_prop_allow_set_link_before_realize,
720                              OBJ_PROP_LINK_STRONG);
721     object_property_add_link(obj, "dma", TYPE_MEMORY_REGION,
722                              (Object **)&s->dma_mr,
723                              qdev_prop_allow_set_link_before_realize,
724                              OBJ_PROP_LINK_STRONG);
725 }
726 
727 static const TypeInfo xlnx_csu_dma_info = {
728     .name          = TYPE_XLNX_CSU_DMA,
729     .parent        = TYPE_SYS_BUS_DEVICE,
730     .instance_size = sizeof(XlnxCSUDMA),
731     .class_init    = xlnx_csu_dma_class_init,
732     .instance_init = xlnx_csu_dma_init,
733     .interfaces = (InterfaceInfo[]) {
734         { TYPE_STREAM_SINK },
735         { }
736     }
737 };
738 
739 static void xlnx_csu_dma_register_types(void)
740 {
741     type_register_static(&xlnx_csu_dma_info);
742 }
743 
744 type_init(xlnx_csu_dma_register_types)
745