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