1 | /* |
2 | * QEMU model of Xilinx AXI-DMA block. |
3 | * |
4 | * Copyright (c) 2011 Edgar E. Iglesias. |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | * of this software and associated documentation files (the "Software"), to deal |
8 | * in the Software without restriction, including without limitation the rights |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
10 | * copies of the Software, and to permit persons to whom the Software is |
11 | * furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included in |
14 | * all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
22 | * THE SOFTWARE. |
23 | */ |
24 | |
25 | #include "qemu/osdep.h" |
26 | #include "hw/sysbus.h" |
27 | #include "qapi/error.h" |
28 | #include "qemu/timer.h" |
29 | #include "hw/hw.h" |
30 | #include "hw/irq.h" |
31 | #include "hw/ptimer.h" |
32 | #include "hw/qdev-properties.h" |
33 | #include "qemu/log.h" |
34 | #include "qemu/main-loop.h" |
35 | #include "qemu/module.h" |
36 | |
37 | #include "hw/stream.h" |
38 | |
39 | #define D(x) |
40 | |
41 | #define TYPE_XILINX_AXI_DMA "xlnx.axi-dma" |
42 | #define TYPE_XILINX_AXI_DMA_DATA_STREAM "xilinx-axi-dma-data-stream" |
43 | #define TYPE_XILINX_AXI_DMA_CONTROL_STREAM "xilinx-axi-dma-control-stream" |
44 | |
45 | #define XILINX_AXI_DMA(obj) \ |
46 | OBJECT_CHECK(XilinxAXIDMA, (obj), TYPE_XILINX_AXI_DMA) |
47 | |
48 | #define XILINX_AXI_DMA_DATA_STREAM(obj) \ |
49 | OBJECT_CHECK(XilinxAXIDMAStreamSlave, (obj),\ |
50 | TYPE_XILINX_AXI_DMA_DATA_STREAM) |
51 | |
52 | #define XILINX_AXI_DMA_CONTROL_STREAM(obj) \ |
53 | OBJECT_CHECK(XilinxAXIDMAStreamSlave, (obj),\ |
54 | TYPE_XILINX_AXI_DMA_CONTROL_STREAM) |
55 | |
56 | #define R_DMACR (0x00 / 4) |
57 | #define R_DMASR (0x04 / 4) |
58 | #define R_CURDESC (0x08 / 4) |
59 | #define R_TAILDESC (0x10 / 4) |
60 | #define R_MAX (0x30 / 4) |
61 | |
62 | #define CONTROL_PAYLOAD_WORDS 5 |
63 | #define CONTROL_PAYLOAD_SIZE (CONTROL_PAYLOAD_WORDS * (sizeof(uint32_t))) |
64 | |
65 | typedef struct XilinxAXIDMA XilinxAXIDMA; |
66 | typedef struct XilinxAXIDMAStreamSlave XilinxAXIDMAStreamSlave; |
67 | |
68 | enum { |
69 | DMACR_RUNSTOP = 1, |
70 | DMACR_TAILPTR_MODE = 2, |
71 | DMACR_RESET = 4 |
72 | }; |
73 | |
74 | enum { |
75 | DMASR_HALTED = 1, |
76 | DMASR_IDLE = 2, |
77 | DMASR_IOC_IRQ = 1 << 12, |
78 | DMASR_DLY_IRQ = 1 << 13, |
79 | |
80 | DMASR_IRQ_MASK = 7 << 12 |
81 | }; |
82 | |
83 | struct SDesc { |
84 | uint64_t nxtdesc; |
85 | uint64_t buffer_address; |
86 | uint64_t reserved; |
87 | uint32_t control; |
88 | uint32_t status; |
89 | uint8_t app[CONTROL_PAYLOAD_SIZE]; |
90 | }; |
91 | |
92 | enum { |
93 | SDESC_CTRL_EOF = (1 << 26), |
94 | SDESC_CTRL_SOF = (1 << 27), |
95 | |
96 | SDESC_CTRL_LEN_MASK = (1 << 23) - 1 |
97 | }; |
98 | |
99 | enum { |
100 | SDESC_STATUS_EOF = (1 << 26), |
101 | SDESC_STATUS_SOF_BIT = 27, |
102 | SDESC_STATUS_SOF = (1 << SDESC_STATUS_SOF_BIT), |
103 | SDESC_STATUS_COMPLETE = (1 << 31) |
104 | }; |
105 | |
106 | struct Stream { |
107 | QEMUBH *bh; |
108 | ptimer_state *ptimer; |
109 | qemu_irq irq; |
110 | |
111 | int nr; |
112 | |
113 | struct SDesc desc; |
114 | int pos; |
115 | unsigned int complete_cnt; |
116 | uint32_t regs[R_MAX]; |
117 | uint8_t app[20]; |
118 | unsigned char txbuf[16 * 1024]; |
119 | }; |
120 | |
121 | struct XilinxAXIDMAStreamSlave { |
122 | Object parent; |
123 | |
124 | struct XilinxAXIDMA *dma; |
125 | }; |
126 | |
127 | struct XilinxAXIDMA { |
128 | SysBusDevice busdev; |
129 | MemoryRegion iomem; |
130 | uint32_t freqhz; |
131 | StreamSlave *tx_data_dev; |
132 | StreamSlave *tx_control_dev; |
133 | XilinxAXIDMAStreamSlave rx_data_dev; |
134 | XilinxAXIDMAStreamSlave rx_control_dev; |
135 | |
136 | struct Stream streams[2]; |
137 | |
138 | StreamCanPushNotifyFn notify; |
139 | void *notify_opaque; |
140 | }; |
141 | |
142 | /* |
143 | * Helper calls to extract info from descriptors and other trivial |
144 | * state from regs. |
145 | */ |
146 | static inline int stream_desc_sof(struct SDesc *d) |
147 | { |
148 | return d->control & SDESC_CTRL_SOF; |
149 | } |
150 | |
151 | static inline int stream_desc_eof(struct SDesc *d) |
152 | { |
153 | return d->control & SDESC_CTRL_EOF; |
154 | } |
155 | |
156 | static inline int stream_resetting(struct Stream *s) |
157 | { |
158 | return !!(s->regs[R_DMACR] & DMACR_RESET); |
159 | } |
160 | |
161 | static inline int stream_running(struct Stream *s) |
162 | { |
163 | return s->regs[R_DMACR] & DMACR_RUNSTOP; |
164 | } |
165 | |
166 | static inline int stream_idle(struct Stream *s) |
167 | { |
168 | return !!(s->regs[R_DMASR] & DMASR_IDLE); |
169 | } |
170 | |
171 | static void stream_reset(struct Stream *s) |
172 | { |
173 | s->regs[R_DMASR] = DMASR_HALTED; /* starts up halted. */ |
174 | s->regs[R_DMACR] = 1 << 16; /* Starts with one in compl threshold. */ |
175 | } |
176 | |
177 | /* Map an offset addr into a channel index. */ |
178 | static inline int streamid_from_addr(hwaddr addr) |
179 | { |
180 | int sid; |
181 | |
182 | sid = addr / (0x30); |
183 | sid &= 1; |
184 | return sid; |
185 | } |
186 | |
187 | static void stream_desc_load(struct Stream *s, hwaddr addr) |
188 | { |
189 | struct SDesc *d = &s->desc; |
190 | |
191 | cpu_physical_memory_read(addr, d, sizeof *d); |
192 | |
193 | /* Convert from LE into host endianness. */ |
194 | d->buffer_address = le64_to_cpu(d->buffer_address); |
195 | d->nxtdesc = le64_to_cpu(d->nxtdesc); |
196 | d->control = le32_to_cpu(d->control); |
197 | d->status = le32_to_cpu(d->status); |
198 | } |
199 | |
200 | static void stream_desc_store(struct Stream *s, hwaddr addr) |
201 | { |
202 | struct SDesc *d = &s->desc; |
203 | |
204 | /* Convert from host endianness into LE. */ |
205 | d->buffer_address = cpu_to_le64(d->buffer_address); |
206 | d->nxtdesc = cpu_to_le64(d->nxtdesc); |
207 | d->control = cpu_to_le32(d->control); |
208 | d->status = cpu_to_le32(d->status); |
209 | cpu_physical_memory_write(addr, d, sizeof *d); |
210 | } |
211 | |
212 | static void stream_update_irq(struct Stream *s) |
213 | { |
214 | unsigned int pending, mask, irq; |
215 | |
216 | pending = s->regs[R_DMASR] & DMASR_IRQ_MASK; |
217 | mask = s->regs[R_DMACR] & DMASR_IRQ_MASK; |
218 | |
219 | irq = pending & mask; |
220 | |
221 | qemu_set_irq(s->irq, !!irq); |
222 | } |
223 | |
224 | static void stream_reload_complete_cnt(struct Stream *s) |
225 | { |
226 | unsigned int comp_th; |
227 | comp_th = (s->regs[R_DMACR] >> 16) & 0xff; |
228 | s->complete_cnt = comp_th; |
229 | } |
230 | |
231 | static void timer_hit(void *opaque) |
232 | { |
233 | struct Stream *s = opaque; |
234 | |
235 | stream_reload_complete_cnt(s); |
236 | s->regs[R_DMASR] |= DMASR_DLY_IRQ; |
237 | stream_update_irq(s); |
238 | } |
239 | |
240 | static void stream_complete(struct Stream *s) |
241 | { |
242 | unsigned int comp_delay; |
243 | |
244 | /* Start the delayed timer. */ |
245 | comp_delay = s->regs[R_DMACR] >> 24; |
246 | if (comp_delay) { |
247 | ptimer_stop(s->ptimer); |
248 | ptimer_set_count(s->ptimer, comp_delay); |
249 | ptimer_run(s->ptimer, 1); |
250 | } |
251 | |
252 | s->complete_cnt--; |
253 | if (s->complete_cnt == 0) { |
254 | /* Raise the IOC irq. */ |
255 | s->regs[R_DMASR] |= DMASR_IOC_IRQ; |
256 | stream_reload_complete_cnt(s); |
257 | } |
258 | } |
259 | |
260 | static void stream_process_mem2s(struct Stream *s, StreamSlave *tx_data_dev, |
261 | StreamSlave *tx_control_dev) |
262 | { |
263 | uint32_t prev_d; |
264 | unsigned int txlen; |
265 | |
266 | if (!stream_running(s) || stream_idle(s)) { |
267 | return; |
268 | } |
269 | |
270 | while (1) { |
271 | stream_desc_load(s, s->regs[R_CURDESC]); |
272 | |
273 | if (s->desc.status & SDESC_STATUS_COMPLETE) { |
274 | s->regs[R_DMASR] |= DMASR_HALTED; |
275 | break; |
276 | } |
277 | |
278 | if (stream_desc_sof(&s->desc)) { |
279 | s->pos = 0; |
280 | stream_push(tx_control_dev, s->desc.app, sizeof(s->desc.app)); |
281 | } |
282 | |
283 | txlen = s->desc.control & SDESC_CTRL_LEN_MASK; |
284 | if ((txlen + s->pos) > sizeof s->txbuf) { |
285 | hw_error("%s: too small internal txbuf! %d\n" , __func__, |
286 | txlen + s->pos); |
287 | } |
288 | |
289 | cpu_physical_memory_read(s->desc.buffer_address, |
290 | s->txbuf + s->pos, txlen); |
291 | s->pos += txlen; |
292 | |
293 | if (stream_desc_eof(&s->desc)) { |
294 | stream_push(tx_data_dev, s->txbuf, s->pos); |
295 | s->pos = 0; |
296 | stream_complete(s); |
297 | } |
298 | |
299 | /* Update the descriptor. */ |
300 | s->desc.status = txlen | SDESC_STATUS_COMPLETE; |
301 | stream_desc_store(s, s->regs[R_CURDESC]); |
302 | |
303 | /* Advance. */ |
304 | prev_d = s->regs[R_CURDESC]; |
305 | s->regs[R_CURDESC] = s->desc.nxtdesc; |
306 | if (prev_d == s->regs[R_TAILDESC]) { |
307 | s->regs[R_DMASR] |= DMASR_IDLE; |
308 | break; |
309 | } |
310 | } |
311 | } |
312 | |
313 | static size_t stream_process_s2mem(struct Stream *s, unsigned char *buf, |
314 | size_t len) |
315 | { |
316 | uint32_t prev_d; |
317 | unsigned int rxlen; |
318 | size_t pos = 0; |
319 | int sof = 1; |
320 | |
321 | if (!stream_running(s) || stream_idle(s)) { |
322 | return 0; |
323 | } |
324 | |
325 | while (len) { |
326 | stream_desc_load(s, s->regs[R_CURDESC]); |
327 | |
328 | if (s->desc.status & SDESC_STATUS_COMPLETE) { |
329 | s->regs[R_DMASR] |= DMASR_HALTED; |
330 | break; |
331 | } |
332 | |
333 | rxlen = s->desc.control & SDESC_CTRL_LEN_MASK; |
334 | if (rxlen > len) { |
335 | /* It fits. */ |
336 | rxlen = len; |
337 | } |
338 | |
339 | cpu_physical_memory_write(s->desc.buffer_address, buf + pos, rxlen); |
340 | len -= rxlen; |
341 | pos += rxlen; |
342 | |
343 | /* Update the descriptor. */ |
344 | if (!len) { |
345 | stream_complete(s); |
346 | memcpy(s->desc.app, s->app, sizeof(s->desc.app)); |
347 | s->desc.status |= SDESC_STATUS_EOF; |
348 | } |
349 | |
350 | s->desc.status |= sof << SDESC_STATUS_SOF_BIT; |
351 | s->desc.status |= SDESC_STATUS_COMPLETE; |
352 | stream_desc_store(s, s->regs[R_CURDESC]); |
353 | sof = 0; |
354 | |
355 | /* Advance. */ |
356 | prev_d = s->regs[R_CURDESC]; |
357 | s->regs[R_CURDESC] = s->desc.nxtdesc; |
358 | if (prev_d == s->regs[R_TAILDESC]) { |
359 | s->regs[R_DMASR] |= DMASR_IDLE; |
360 | break; |
361 | } |
362 | } |
363 | |
364 | return pos; |
365 | } |
366 | |
367 | static void xilinx_axidma_reset(DeviceState *dev) |
368 | { |
369 | int i; |
370 | XilinxAXIDMA *s = XILINX_AXI_DMA(dev); |
371 | |
372 | for (i = 0; i < 2; i++) { |
373 | stream_reset(&s->streams[i]); |
374 | } |
375 | } |
376 | |
377 | static size_t |
378 | xilinx_axidma_control_stream_push(StreamSlave *obj, unsigned char *buf, |
379 | size_t len) |
380 | { |
381 | XilinxAXIDMAStreamSlave *cs = XILINX_AXI_DMA_CONTROL_STREAM(obj); |
382 | struct Stream *s = &cs->dma->streams[1]; |
383 | |
384 | if (len != CONTROL_PAYLOAD_SIZE) { |
385 | hw_error("AXI DMA requires %d byte control stream payload\n" , |
386 | (int)CONTROL_PAYLOAD_SIZE); |
387 | } |
388 | |
389 | memcpy(s->app, buf, len); |
390 | return len; |
391 | } |
392 | |
393 | static bool |
394 | xilinx_axidma_data_stream_can_push(StreamSlave *obj, |
395 | StreamCanPushNotifyFn notify, |
396 | void *notify_opaque) |
397 | { |
398 | XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(obj); |
399 | struct Stream *s = &ds->dma->streams[1]; |
400 | |
401 | if (!stream_running(s) || stream_idle(s)) { |
402 | ds->dma->notify = notify; |
403 | ds->dma->notify_opaque = notify_opaque; |
404 | return false; |
405 | } |
406 | |
407 | return true; |
408 | } |
409 | |
410 | static size_t |
411 | xilinx_axidma_data_stream_push(StreamSlave *obj, unsigned char *buf, size_t len) |
412 | { |
413 | XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(obj); |
414 | struct Stream *s = &ds->dma->streams[1]; |
415 | size_t ret; |
416 | |
417 | ret = stream_process_s2mem(s, buf, len); |
418 | stream_update_irq(s); |
419 | return ret; |
420 | } |
421 | |
422 | static uint64_t axidma_read(void *opaque, hwaddr addr, |
423 | unsigned size) |
424 | { |
425 | XilinxAXIDMA *d = opaque; |
426 | struct Stream *s; |
427 | uint32_t r = 0; |
428 | int sid; |
429 | |
430 | sid = streamid_from_addr(addr); |
431 | s = &d->streams[sid]; |
432 | |
433 | addr = addr % 0x30; |
434 | addr >>= 2; |
435 | switch (addr) { |
436 | case R_DMACR: |
437 | /* Simulate one cycles reset delay. */ |
438 | s->regs[addr] &= ~DMACR_RESET; |
439 | r = s->regs[addr]; |
440 | break; |
441 | case R_DMASR: |
442 | s->regs[addr] &= 0xffff; |
443 | s->regs[addr] |= (s->complete_cnt & 0xff) << 16; |
444 | s->regs[addr] |= (ptimer_get_count(s->ptimer) & 0xff) << 24; |
445 | r = s->regs[addr]; |
446 | break; |
447 | default: |
448 | r = s->regs[addr]; |
449 | D(qemu_log("%s ch=%d addr=" TARGET_FMT_plx " v=%x\n" , |
450 | __func__, sid, addr * 4, r)); |
451 | break; |
452 | } |
453 | return r; |
454 | |
455 | } |
456 | |
457 | static void axidma_write(void *opaque, hwaddr addr, |
458 | uint64_t value, unsigned size) |
459 | { |
460 | XilinxAXIDMA *d = opaque; |
461 | struct Stream *s; |
462 | int sid; |
463 | |
464 | sid = streamid_from_addr(addr); |
465 | s = &d->streams[sid]; |
466 | |
467 | addr = addr % 0x30; |
468 | addr >>= 2; |
469 | switch (addr) { |
470 | case R_DMACR: |
471 | /* Tailptr mode is always on. */ |
472 | value |= DMACR_TAILPTR_MODE; |
473 | /* Remember our previous reset state. */ |
474 | value |= (s->regs[addr] & DMACR_RESET); |
475 | s->regs[addr] = value; |
476 | |
477 | if (value & DMACR_RESET) { |
478 | stream_reset(s); |
479 | } |
480 | |
481 | if ((value & 1) && !stream_resetting(s)) { |
482 | /* Start processing. */ |
483 | s->regs[R_DMASR] &= ~(DMASR_HALTED | DMASR_IDLE); |
484 | } |
485 | stream_reload_complete_cnt(s); |
486 | break; |
487 | |
488 | case R_DMASR: |
489 | /* Mask away write to clear irq lines. */ |
490 | value &= ~(value & DMASR_IRQ_MASK); |
491 | s->regs[addr] = value; |
492 | break; |
493 | |
494 | case R_TAILDESC: |
495 | s->regs[addr] = value; |
496 | s->regs[R_DMASR] &= ~DMASR_IDLE; /* Not idle. */ |
497 | if (!sid) { |
498 | stream_process_mem2s(s, d->tx_data_dev, d->tx_control_dev); |
499 | } |
500 | break; |
501 | default: |
502 | D(qemu_log("%s: ch=%d addr=" TARGET_FMT_plx " v=%x\n" , |
503 | __func__, sid, addr * 4, (unsigned)value)); |
504 | s->regs[addr] = value; |
505 | break; |
506 | } |
507 | if (sid == 1 && d->notify) { |
508 | StreamCanPushNotifyFn notifytmp = d->notify; |
509 | d->notify = NULL; |
510 | notifytmp(d->notify_opaque); |
511 | } |
512 | stream_update_irq(s); |
513 | } |
514 | |
515 | static const MemoryRegionOps axidma_ops = { |
516 | .read = axidma_read, |
517 | .write = axidma_write, |
518 | .endianness = DEVICE_NATIVE_ENDIAN, |
519 | }; |
520 | |
521 | static void xilinx_axidma_realize(DeviceState *dev, Error **errp) |
522 | { |
523 | XilinxAXIDMA *s = XILINX_AXI_DMA(dev); |
524 | XilinxAXIDMAStreamSlave *ds = XILINX_AXI_DMA_DATA_STREAM(&s->rx_data_dev); |
525 | XilinxAXIDMAStreamSlave *cs = XILINX_AXI_DMA_CONTROL_STREAM( |
526 | &s->rx_control_dev); |
527 | Error *local_err = NULL; |
528 | |
529 | object_property_add_link(OBJECT(ds), "dma" , TYPE_XILINX_AXI_DMA, |
530 | (Object **)&ds->dma, |
531 | object_property_allow_set_link, |
532 | OBJ_PROP_LINK_STRONG, |
533 | &local_err); |
534 | object_property_add_link(OBJECT(cs), "dma" , TYPE_XILINX_AXI_DMA, |
535 | (Object **)&cs->dma, |
536 | object_property_allow_set_link, |
537 | OBJ_PROP_LINK_STRONG, |
538 | &local_err); |
539 | if (local_err) { |
540 | goto xilinx_axidma_realize_fail; |
541 | } |
542 | object_property_set_link(OBJECT(ds), OBJECT(s), "dma" , &local_err); |
543 | object_property_set_link(OBJECT(cs), OBJECT(s), "dma" , &local_err); |
544 | if (local_err) { |
545 | goto xilinx_axidma_realize_fail; |
546 | } |
547 | |
548 | int i; |
549 | |
550 | for (i = 0; i < 2; i++) { |
551 | struct Stream *st = &s->streams[i]; |
552 | |
553 | st->nr = i; |
554 | st->bh = qemu_bh_new(timer_hit, st); |
555 | st->ptimer = ptimer_init(st->bh, PTIMER_POLICY_DEFAULT); |
556 | ptimer_set_freq(st->ptimer, s->freqhz); |
557 | } |
558 | return; |
559 | |
560 | xilinx_axidma_realize_fail: |
561 | error_propagate(errp, local_err); |
562 | } |
563 | |
564 | static void xilinx_axidma_init(Object *obj) |
565 | { |
566 | XilinxAXIDMA *s = XILINX_AXI_DMA(obj); |
567 | SysBusDevice *sbd = SYS_BUS_DEVICE(obj); |
568 | |
569 | object_initialize_child(OBJECT(s), "axistream-connected-target" , |
570 | &s->rx_data_dev, sizeof(s->rx_data_dev), |
571 | TYPE_XILINX_AXI_DMA_DATA_STREAM, &error_abort, |
572 | NULL); |
573 | object_initialize_child(OBJECT(s), "axistream-control-connected-target" , |
574 | &s->rx_control_dev, sizeof(s->rx_control_dev), |
575 | TYPE_XILINX_AXI_DMA_CONTROL_STREAM, &error_abort, |
576 | NULL); |
577 | |
578 | sysbus_init_irq(sbd, &s->streams[0].irq); |
579 | sysbus_init_irq(sbd, &s->streams[1].irq); |
580 | |
581 | memory_region_init_io(&s->iomem, obj, &axidma_ops, s, |
582 | "xlnx.axi-dma" , R_MAX * 4 * 2); |
583 | sysbus_init_mmio(sbd, &s->iomem); |
584 | } |
585 | |
586 | static Property axidma_properties[] = { |
587 | DEFINE_PROP_UINT32("freqhz" , XilinxAXIDMA, freqhz, 50000000), |
588 | DEFINE_PROP_LINK("axistream-connected" , XilinxAXIDMA, |
589 | tx_data_dev, TYPE_STREAM_SLAVE, StreamSlave *), |
590 | DEFINE_PROP_LINK("axistream-control-connected" , XilinxAXIDMA, |
591 | tx_control_dev, TYPE_STREAM_SLAVE, StreamSlave *), |
592 | DEFINE_PROP_END_OF_LIST(), |
593 | }; |
594 | |
595 | static void axidma_class_init(ObjectClass *klass, void *data) |
596 | { |
597 | DeviceClass *dc = DEVICE_CLASS(klass); |
598 | |
599 | dc->realize = xilinx_axidma_realize, |
600 | dc->reset = xilinx_axidma_reset; |
601 | dc->props = axidma_properties; |
602 | } |
603 | |
604 | static StreamSlaveClass xilinx_axidma_data_stream_class = { |
605 | .push = xilinx_axidma_data_stream_push, |
606 | .can_push = xilinx_axidma_data_stream_can_push, |
607 | }; |
608 | |
609 | static StreamSlaveClass xilinx_axidma_control_stream_class = { |
610 | .push = xilinx_axidma_control_stream_push, |
611 | }; |
612 | |
613 | static void xilinx_axidma_stream_class_init(ObjectClass *klass, void *data) |
614 | { |
615 | StreamSlaveClass *ssc = STREAM_SLAVE_CLASS(klass); |
616 | |
617 | ssc->push = ((StreamSlaveClass *)data)->push; |
618 | ssc->can_push = ((StreamSlaveClass *)data)->can_push; |
619 | } |
620 | |
621 | static const TypeInfo axidma_info = { |
622 | .name = TYPE_XILINX_AXI_DMA, |
623 | .parent = TYPE_SYS_BUS_DEVICE, |
624 | .instance_size = sizeof(XilinxAXIDMA), |
625 | .class_init = axidma_class_init, |
626 | .instance_init = xilinx_axidma_init, |
627 | }; |
628 | |
629 | static const TypeInfo xilinx_axidma_data_stream_info = { |
630 | .name = TYPE_XILINX_AXI_DMA_DATA_STREAM, |
631 | .parent = TYPE_OBJECT, |
632 | .instance_size = sizeof(struct XilinxAXIDMAStreamSlave), |
633 | .class_init = xilinx_axidma_stream_class_init, |
634 | .class_data = &xilinx_axidma_data_stream_class, |
635 | .interfaces = (InterfaceInfo[]) { |
636 | { TYPE_STREAM_SLAVE }, |
637 | { } |
638 | } |
639 | }; |
640 | |
641 | static const TypeInfo xilinx_axidma_control_stream_info = { |
642 | .name = TYPE_XILINX_AXI_DMA_CONTROL_STREAM, |
643 | .parent = TYPE_OBJECT, |
644 | .instance_size = sizeof(struct XilinxAXIDMAStreamSlave), |
645 | .class_init = xilinx_axidma_stream_class_init, |
646 | .class_data = &xilinx_axidma_control_stream_class, |
647 | .interfaces = (InterfaceInfo[]) { |
648 | { TYPE_STREAM_SLAVE }, |
649 | { } |
650 | } |
651 | }; |
652 | |
653 | static void xilinx_axidma_register_types(void) |
654 | { |
655 | type_register_static(&axidma_info); |
656 | type_register_static(&xilinx_axidma_data_stream_info); |
657 | type_register_static(&xilinx_axidma_control_stream_info); |
658 | } |
659 | |
660 | type_init(xilinx_axidma_register_types) |
661 | |