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