root / hw / xilinx_axidma.c @ 03f48b07
History | View | Annotate | Download (13.1 kB)
1 | 93f1e401 | Edgar E. Iglesias | /*
|
---|---|---|---|
2 | 93f1e401 | Edgar E. Iglesias | * QEMU model of Xilinx AXI-DMA block.
|
3 | 93f1e401 | Edgar E. Iglesias | *
|
4 | 93f1e401 | Edgar E. Iglesias | * Copyright (c) 2011 Edgar E. Iglesias.
|
5 | 93f1e401 | Edgar E. Iglesias | *
|
6 | 93f1e401 | Edgar E. Iglesias | * Permission is hereby granted, free of charge, to any person obtaining a copy
|
7 | 93f1e401 | Edgar E. Iglesias | * of this software and associated documentation files (the "Software"), to deal
|
8 | 93f1e401 | Edgar E. Iglesias | * in the Software without restriction, including without limitation the rights
|
9 | 93f1e401 | Edgar E. Iglesias | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10 | 93f1e401 | Edgar E. Iglesias | * copies of the Software, and to permit persons to whom the Software is
|
11 | 93f1e401 | Edgar E. Iglesias | * furnished to do so, subject to the following conditions:
|
12 | 93f1e401 | Edgar E. Iglesias | *
|
13 | 93f1e401 | Edgar E. Iglesias | * The above copyright notice and this permission notice shall be included in
|
14 | 93f1e401 | Edgar E. Iglesias | * all copies or substantial portions of the Software.
|
15 | 93f1e401 | Edgar E. Iglesias | *
|
16 | 93f1e401 | Edgar E. Iglesias | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17 | 93f1e401 | Edgar E. Iglesias | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18 | 93f1e401 | Edgar E. Iglesias | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
19 | 93f1e401 | Edgar E. Iglesias | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20 | 93f1e401 | Edgar E. Iglesias | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21 | 93f1e401 | Edgar E. Iglesias | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22 | 93f1e401 | Edgar E. Iglesias | * THE SOFTWARE.
|
23 | 93f1e401 | Edgar E. Iglesias | */
|
24 | 93f1e401 | Edgar E. Iglesias | |
25 | 93f1e401 | Edgar E. Iglesias | #include "sysbus.h" |
26 | 93f1e401 | Edgar E. Iglesias | #include "qemu-char.h" |
27 | 93f1e401 | Edgar E. Iglesias | #include "qemu-timer.h" |
28 | 49d4d9b6 | Paolo Bonzini | #include "ptimer.h" |
29 | 93f1e401 | Edgar E. Iglesias | #include "qemu-log.h" |
30 | 93f1e401 | Edgar E. Iglesias | #include "qdev-addr.h" |
31 | 93f1e401 | Edgar E. Iglesias | |
32 | 93f1e401 | Edgar E. Iglesias | #include "xilinx_axidma.h" |
33 | 93f1e401 | Edgar E. Iglesias | |
34 | 93f1e401 | Edgar E. Iglesias | #define D(x)
|
35 | 93f1e401 | Edgar E. Iglesias | |
36 | 93f1e401 | Edgar E. Iglesias | #define R_DMACR (0x00 / 4) |
37 | 93f1e401 | Edgar E. Iglesias | #define R_DMASR (0x04 / 4) |
38 | 93f1e401 | Edgar E. Iglesias | #define R_CURDESC (0x08 / 4) |
39 | 93f1e401 | Edgar E. Iglesias | #define R_TAILDESC (0x10 / 4) |
40 | 93f1e401 | Edgar E. Iglesias | #define R_MAX (0x30 / 4) |
41 | 93f1e401 | Edgar E. Iglesias | |
42 | 93f1e401 | Edgar E. Iglesias | enum {
|
43 | 93f1e401 | Edgar E. Iglesias | DMACR_RUNSTOP = 1,
|
44 | 93f1e401 | Edgar E. Iglesias | DMACR_TAILPTR_MODE = 2,
|
45 | 93f1e401 | Edgar E. Iglesias | DMACR_RESET = 4
|
46 | 93f1e401 | Edgar E. Iglesias | }; |
47 | 93f1e401 | Edgar E. Iglesias | |
48 | 93f1e401 | Edgar E. Iglesias | enum {
|
49 | 93f1e401 | Edgar E. Iglesias | DMASR_HALTED = 1,
|
50 | 93f1e401 | Edgar E. Iglesias | DMASR_IDLE = 2,
|
51 | 93f1e401 | Edgar E. Iglesias | DMASR_IOC_IRQ = 1 << 12, |
52 | 93f1e401 | Edgar E. Iglesias | DMASR_DLY_IRQ = 1 << 13, |
53 | 93f1e401 | Edgar E. Iglesias | |
54 | 93f1e401 | Edgar E. Iglesias | DMASR_IRQ_MASK = 7 << 12 |
55 | 93f1e401 | Edgar E. Iglesias | }; |
56 | 93f1e401 | Edgar E. Iglesias | |
57 | 93f1e401 | Edgar E. Iglesias | struct SDesc {
|
58 | 93f1e401 | Edgar E. Iglesias | uint64_t nxtdesc; |
59 | 93f1e401 | Edgar E. Iglesias | uint64_t buffer_address; |
60 | 93f1e401 | Edgar E. Iglesias | uint64_t reserved; |
61 | 93f1e401 | Edgar E. Iglesias | uint32_t control; |
62 | 93f1e401 | Edgar E. Iglesias | uint32_t status; |
63 | 93f1e401 | Edgar E. Iglesias | uint32_t app[6];
|
64 | 93f1e401 | Edgar E. Iglesias | }; |
65 | 93f1e401 | Edgar E. Iglesias | |
66 | 93f1e401 | Edgar E. Iglesias | enum {
|
67 | 93f1e401 | Edgar E. Iglesias | SDESC_CTRL_EOF = (1 << 26), |
68 | 93f1e401 | Edgar E. Iglesias | SDESC_CTRL_SOF = (1 << 27), |
69 | 93f1e401 | Edgar E. Iglesias | |
70 | 93f1e401 | Edgar E. Iglesias | SDESC_CTRL_LEN_MASK = (1 << 23) - 1 |
71 | 93f1e401 | Edgar E. Iglesias | }; |
72 | 93f1e401 | Edgar E. Iglesias | |
73 | 93f1e401 | Edgar E. Iglesias | enum {
|
74 | 93f1e401 | Edgar E. Iglesias | SDESC_STATUS_EOF = (1 << 26), |
75 | 93f1e401 | Edgar E. Iglesias | SDESC_STATUS_SOF_BIT = 27,
|
76 | 93f1e401 | Edgar E. Iglesias | SDESC_STATUS_SOF = (1 << SDESC_STATUS_SOF_BIT),
|
77 | 93f1e401 | Edgar E. Iglesias | SDESC_STATUS_COMPLETE = (1 << 31) |
78 | 93f1e401 | Edgar E. Iglesias | }; |
79 | 93f1e401 | Edgar E. Iglesias | |
80 | 93f1e401 | Edgar E. Iglesias | struct AXIStream {
|
81 | 93f1e401 | Edgar E. Iglesias | QEMUBH *bh; |
82 | 93f1e401 | Edgar E. Iglesias | ptimer_state *ptimer; |
83 | 93f1e401 | Edgar E. Iglesias | qemu_irq irq; |
84 | 93f1e401 | Edgar E. Iglesias | |
85 | 93f1e401 | Edgar E. Iglesias | int nr;
|
86 | 93f1e401 | Edgar E. Iglesias | |
87 | 93f1e401 | Edgar E. Iglesias | struct SDesc desc;
|
88 | 93f1e401 | Edgar E. Iglesias | int pos;
|
89 | 93f1e401 | Edgar E. Iglesias | unsigned int complete_cnt; |
90 | 93f1e401 | Edgar E. Iglesias | uint32_t regs[R_MAX]; |
91 | 93f1e401 | Edgar E. Iglesias | }; |
92 | 93f1e401 | Edgar E. Iglesias | |
93 | 93f1e401 | Edgar E. Iglesias | struct XilinxAXIDMA {
|
94 | 93f1e401 | Edgar E. Iglesias | SysBusDevice busdev; |
95 | f810bc4a | Avi Kivity | MemoryRegion iomem; |
96 | 93f1e401 | Edgar E. Iglesias | uint32_t freqhz; |
97 | 93f1e401 | Edgar E. Iglesias | void *dmach;
|
98 | 93f1e401 | Edgar E. Iglesias | |
99 | 93f1e401 | Edgar E. Iglesias | struct AXIStream streams[2]; |
100 | 93f1e401 | Edgar E. Iglesias | }; |
101 | 93f1e401 | Edgar E. Iglesias | |
102 | 93f1e401 | Edgar E. Iglesias | /*
|
103 | 93f1e401 | Edgar E. Iglesias | * Helper calls to extract info from desriptors and other trivial
|
104 | 93f1e401 | Edgar E. Iglesias | * state from regs.
|
105 | 93f1e401 | Edgar E. Iglesias | */
|
106 | 93f1e401 | Edgar E. Iglesias | static inline int stream_desc_sof(struct SDesc *d) |
107 | 93f1e401 | Edgar E. Iglesias | { |
108 | 93f1e401 | Edgar E. Iglesias | return d->control & SDESC_CTRL_SOF;
|
109 | 93f1e401 | Edgar E. Iglesias | } |
110 | 93f1e401 | Edgar E. Iglesias | |
111 | 93f1e401 | Edgar E. Iglesias | static inline int stream_desc_eof(struct SDesc *d) |
112 | 93f1e401 | Edgar E. Iglesias | { |
113 | 93f1e401 | Edgar E. Iglesias | return d->control & SDESC_CTRL_EOF;
|
114 | 93f1e401 | Edgar E. Iglesias | } |
115 | 93f1e401 | Edgar E. Iglesias | |
116 | 93f1e401 | Edgar E. Iglesias | static inline int stream_resetting(struct AXIStream *s) |
117 | 93f1e401 | Edgar E. Iglesias | { |
118 | 93f1e401 | Edgar E. Iglesias | return !!(s->regs[R_DMACR] & DMACR_RESET);
|
119 | 93f1e401 | Edgar E. Iglesias | } |
120 | 93f1e401 | Edgar E. Iglesias | |
121 | 93f1e401 | Edgar E. Iglesias | static inline int stream_running(struct AXIStream *s) |
122 | 93f1e401 | Edgar E. Iglesias | { |
123 | 93f1e401 | Edgar E. Iglesias | return s->regs[R_DMACR] & DMACR_RUNSTOP;
|
124 | 93f1e401 | Edgar E. Iglesias | } |
125 | 93f1e401 | Edgar E. Iglesias | |
126 | 93f1e401 | Edgar E. Iglesias | static inline int stream_halted(struct AXIStream *s) |
127 | 93f1e401 | Edgar E. Iglesias | { |
128 | 93f1e401 | Edgar E. Iglesias | return s->regs[R_DMASR] & DMASR_HALTED;
|
129 | 93f1e401 | Edgar E. Iglesias | } |
130 | 93f1e401 | Edgar E. Iglesias | |
131 | 93f1e401 | Edgar E. Iglesias | static inline int stream_idle(struct AXIStream *s) |
132 | 93f1e401 | Edgar E. Iglesias | { |
133 | 93f1e401 | Edgar E. Iglesias | return !!(s->regs[R_DMASR] & DMASR_IDLE);
|
134 | 93f1e401 | Edgar E. Iglesias | } |
135 | 93f1e401 | Edgar E. Iglesias | |
136 | 93f1e401 | Edgar E. Iglesias | static void stream_reset(struct AXIStream *s) |
137 | 93f1e401 | Edgar E. Iglesias | { |
138 | 93f1e401 | Edgar E. Iglesias | s->regs[R_DMASR] = DMASR_HALTED; /* starts up halted. */
|
139 | 0d50d616 | Stefan Weil | s->regs[R_DMACR] = 1 << 16; /* Starts with one in compl threshold. */ |
140 | 93f1e401 | Edgar E. Iglesias | } |
141 | 93f1e401 | Edgar E. Iglesias | |
142 | 0d50d616 | Stefan Weil | /* Map an offset addr into a channel index. */
|
143 | 93f1e401 | Edgar E. Iglesias | static inline int streamid_from_addr(target_phys_addr_t addr) |
144 | 93f1e401 | Edgar E. Iglesias | { |
145 | 93f1e401 | Edgar E. Iglesias | int sid;
|
146 | 93f1e401 | Edgar E. Iglesias | |
147 | 93f1e401 | Edgar E. Iglesias | sid = addr / (0x30);
|
148 | 93f1e401 | Edgar E. Iglesias | sid &= 1;
|
149 | 93f1e401 | Edgar E. Iglesias | return sid;
|
150 | 93f1e401 | Edgar E. Iglesias | } |
151 | 93f1e401 | Edgar E. Iglesias | |
152 | 93f1e401 | Edgar E. Iglesias | #ifdef DEBUG_ENET
|
153 | 93f1e401 | Edgar E. Iglesias | static void stream_desc_show(struct SDesc *d) |
154 | 93f1e401 | Edgar E. Iglesias | { |
155 | 93f1e401 | Edgar E. Iglesias | qemu_log("buffer_addr = " PRIx64 "\n", d->buffer_address); |
156 | 93f1e401 | Edgar E. Iglesias | qemu_log("nxtdesc = " PRIx64 "\n", d->nxtdesc); |
157 | 93f1e401 | Edgar E. Iglesias | qemu_log("control = %x\n", d->control);
|
158 | 93f1e401 | Edgar E. Iglesias | qemu_log("status = %x\n", d->status);
|
159 | 93f1e401 | Edgar E. Iglesias | } |
160 | 93f1e401 | Edgar E. Iglesias | #endif
|
161 | 93f1e401 | Edgar E. Iglesias | |
162 | 93f1e401 | Edgar E. Iglesias | static void stream_desc_load(struct AXIStream *s, target_phys_addr_t addr) |
163 | 93f1e401 | Edgar E. Iglesias | { |
164 | 93f1e401 | Edgar E. Iglesias | struct SDesc *d = &s->desc;
|
165 | 93f1e401 | Edgar E. Iglesias | int i;
|
166 | 93f1e401 | Edgar E. Iglesias | |
167 | 93f1e401 | Edgar E. Iglesias | cpu_physical_memory_read(addr, (void *) d, sizeof *d); |
168 | 93f1e401 | Edgar E. Iglesias | |
169 | 93f1e401 | Edgar E. Iglesias | /* Convert from LE into host endianness. */
|
170 | 93f1e401 | Edgar E. Iglesias | d->buffer_address = le64_to_cpu(d->buffer_address); |
171 | 93f1e401 | Edgar E. Iglesias | d->nxtdesc = le64_to_cpu(d->nxtdesc); |
172 | 93f1e401 | Edgar E. Iglesias | d->control = le32_to_cpu(d->control); |
173 | 93f1e401 | Edgar E. Iglesias | d->status = le32_to_cpu(d->status); |
174 | 93f1e401 | Edgar E. Iglesias | for (i = 0; i < ARRAY_SIZE(d->app); i++) { |
175 | 93f1e401 | Edgar E. Iglesias | d->app[i] = le32_to_cpu(d->app[i]); |
176 | 93f1e401 | Edgar E. Iglesias | } |
177 | 93f1e401 | Edgar E. Iglesias | } |
178 | 93f1e401 | Edgar E. Iglesias | |
179 | 93f1e401 | Edgar E. Iglesias | static void stream_desc_store(struct AXIStream *s, target_phys_addr_t addr) |
180 | 93f1e401 | Edgar E. Iglesias | { |
181 | 93f1e401 | Edgar E. Iglesias | struct SDesc *d = &s->desc;
|
182 | 93f1e401 | Edgar E. Iglesias | int i;
|
183 | 93f1e401 | Edgar E. Iglesias | |
184 | 93f1e401 | Edgar E. Iglesias | /* Convert from host endianness into LE. */
|
185 | 93f1e401 | Edgar E. Iglesias | d->buffer_address = cpu_to_le64(d->buffer_address); |
186 | 93f1e401 | Edgar E. Iglesias | d->nxtdesc = cpu_to_le64(d->nxtdesc); |
187 | 93f1e401 | Edgar E. Iglesias | d->control = cpu_to_le32(d->control); |
188 | 93f1e401 | Edgar E. Iglesias | d->status = cpu_to_le32(d->status); |
189 | 93f1e401 | Edgar E. Iglesias | for (i = 0; i < ARRAY_SIZE(d->app); i++) { |
190 | 93f1e401 | Edgar E. Iglesias | d->app[i] = cpu_to_le32(d->app[i]); |
191 | 93f1e401 | Edgar E. Iglesias | } |
192 | 93f1e401 | Edgar E. Iglesias | cpu_physical_memory_write(addr, (void *) d, sizeof *d); |
193 | 93f1e401 | Edgar E. Iglesias | } |
194 | 93f1e401 | Edgar E. Iglesias | |
195 | 93f1e401 | Edgar E. Iglesias | static void stream_update_irq(struct AXIStream *s) |
196 | 93f1e401 | Edgar E. Iglesias | { |
197 | 93f1e401 | Edgar E. Iglesias | unsigned int pending, mask, irq; |
198 | 93f1e401 | Edgar E. Iglesias | |
199 | 93f1e401 | Edgar E. Iglesias | pending = s->regs[R_DMASR] & DMASR_IRQ_MASK; |
200 | 93f1e401 | Edgar E. Iglesias | mask = s->regs[R_DMACR] & DMASR_IRQ_MASK; |
201 | 93f1e401 | Edgar E. Iglesias | |
202 | 93f1e401 | Edgar E. Iglesias | irq = pending & mask; |
203 | 93f1e401 | Edgar E. Iglesias | |
204 | 93f1e401 | Edgar E. Iglesias | qemu_set_irq(s->irq, !!irq); |
205 | 93f1e401 | Edgar E. Iglesias | } |
206 | 93f1e401 | Edgar E. Iglesias | |
207 | 93f1e401 | Edgar E. Iglesias | static void stream_reload_complete_cnt(struct AXIStream *s) |
208 | 93f1e401 | Edgar E. Iglesias | { |
209 | 93f1e401 | Edgar E. Iglesias | unsigned int comp_th; |
210 | 93f1e401 | Edgar E. Iglesias | comp_th = (s->regs[R_DMACR] >> 16) & 0xff; |
211 | 93f1e401 | Edgar E. Iglesias | s->complete_cnt = comp_th; |
212 | 93f1e401 | Edgar E. Iglesias | } |
213 | 93f1e401 | Edgar E. Iglesias | |
214 | 93f1e401 | Edgar E. Iglesias | static void timer_hit(void *opaque) |
215 | 93f1e401 | Edgar E. Iglesias | { |
216 | 93f1e401 | Edgar E. Iglesias | struct AXIStream *s = opaque;
|
217 | 93f1e401 | Edgar E. Iglesias | |
218 | 93f1e401 | Edgar E. Iglesias | stream_reload_complete_cnt(s); |
219 | 93f1e401 | Edgar E. Iglesias | s->regs[R_DMASR] |= DMASR_DLY_IRQ; |
220 | 93f1e401 | Edgar E. Iglesias | stream_update_irq(s); |
221 | 93f1e401 | Edgar E. Iglesias | } |
222 | 93f1e401 | Edgar E. Iglesias | |
223 | 93f1e401 | Edgar E. Iglesias | static void stream_complete(struct AXIStream *s) |
224 | 93f1e401 | Edgar E. Iglesias | { |
225 | 93f1e401 | Edgar E. Iglesias | unsigned int comp_delay; |
226 | 93f1e401 | Edgar E. Iglesias | |
227 | 93f1e401 | Edgar E. Iglesias | /* Start the delayed timer. */
|
228 | 93f1e401 | Edgar E. Iglesias | comp_delay = s->regs[R_DMACR] >> 24;
|
229 | 93f1e401 | Edgar E. Iglesias | if (comp_delay) {
|
230 | 93f1e401 | Edgar E. Iglesias | ptimer_stop(s->ptimer); |
231 | 93f1e401 | Edgar E. Iglesias | ptimer_set_count(s->ptimer, comp_delay); |
232 | 93f1e401 | Edgar E. Iglesias | ptimer_run(s->ptimer, 1);
|
233 | 93f1e401 | Edgar E. Iglesias | } |
234 | 93f1e401 | Edgar E. Iglesias | |
235 | 93f1e401 | Edgar E. Iglesias | s->complete_cnt--; |
236 | 93f1e401 | Edgar E. Iglesias | if (s->complete_cnt == 0) { |
237 | 93f1e401 | Edgar E. Iglesias | /* Raise the IOC irq. */
|
238 | 93f1e401 | Edgar E. Iglesias | s->regs[R_DMASR] |= DMASR_IOC_IRQ; |
239 | 93f1e401 | Edgar E. Iglesias | stream_reload_complete_cnt(s); |
240 | 93f1e401 | Edgar E. Iglesias | } |
241 | 93f1e401 | Edgar E. Iglesias | } |
242 | 93f1e401 | Edgar E. Iglesias | |
243 | 93f1e401 | Edgar E. Iglesias | static void stream_process_mem2s(struct AXIStream *s, |
244 | 93f1e401 | Edgar E. Iglesias | struct XilinxDMAConnection *dmach)
|
245 | 93f1e401 | Edgar E. Iglesias | { |
246 | 93f1e401 | Edgar E. Iglesias | uint32_t prev_d; |
247 | 93f1e401 | Edgar E. Iglesias | unsigned char txbuf[16 * 1024]; |
248 | 93f1e401 | Edgar E. Iglesias | unsigned int txlen; |
249 | 93f1e401 | Edgar E. Iglesias | uint32_t app[6];
|
250 | 93f1e401 | Edgar E. Iglesias | |
251 | 93f1e401 | Edgar E. Iglesias | if (!stream_running(s) || stream_idle(s)) {
|
252 | 93f1e401 | Edgar E. Iglesias | return;
|
253 | 93f1e401 | Edgar E. Iglesias | } |
254 | 93f1e401 | Edgar E. Iglesias | |
255 | 93f1e401 | Edgar E. Iglesias | while (1) { |
256 | 93f1e401 | Edgar E. Iglesias | stream_desc_load(s, s->regs[R_CURDESC]); |
257 | 93f1e401 | Edgar E. Iglesias | |
258 | 93f1e401 | Edgar E. Iglesias | if (s->desc.status & SDESC_STATUS_COMPLETE) {
|
259 | 93f1e401 | Edgar E. Iglesias | s->regs[R_DMASR] |= DMASR_IDLE; |
260 | 93f1e401 | Edgar E. Iglesias | break;
|
261 | 93f1e401 | Edgar E. Iglesias | } |
262 | 93f1e401 | Edgar E. Iglesias | |
263 | 93f1e401 | Edgar E. Iglesias | if (stream_desc_sof(&s->desc)) {
|
264 | 93f1e401 | Edgar E. Iglesias | s->pos = 0;
|
265 | 93f1e401 | Edgar E. Iglesias | memcpy(app, s->desc.app, sizeof app);
|
266 | 93f1e401 | Edgar E. Iglesias | } |
267 | 93f1e401 | Edgar E. Iglesias | |
268 | 93f1e401 | Edgar E. Iglesias | txlen = s->desc.control & SDESC_CTRL_LEN_MASK; |
269 | 93f1e401 | Edgar E. Iglesias | if ((txlen + s->pos) > sizeof txbuf) { |
270 | 93f1e401 | Edgar E. Iglesias | hw_error("%s: too small internal txbuf! %d\n", __func__,
|
271 | 93f1e401 | Edgar E. Iglesias | txlen + s->pos); |
272 | 93f1e401 | Edgar E. Iglesias | } |
273 | 93f1e401 | Edgar E. Iglesias | |
274 | 93f1e401 | Edgar E. Iglesias | cpu_physical_memory_read(s->desc.buffer_address, |
275 | 93f1e401 | Edgar E. Iglesias | txbuf + s->pos, txlen); |
276 | 93f1e401 | Edgar E. Iglesias | s->pos += txlen; |
277 | 93f1e401 | Edgar E. Iglesias | |
278 | 93f1e401 | Edgar E. Iglesias | if (stream_desc_eof(&s->desc)) {
|
279 | 93f1e401 | Edgar E. Iglesias | xlx_dma_push_to_client(dmach, txbuf, s->pos, app); |
280 | 93f1e401 | Edgar E. Iglesias | s->pos = 0;
|
281 | 93f1e401 | Edgar E. Iglesias | stream_complete(s); |
282 | 93f1e401 | Edgar E. Iglesias | } |
283 | 93f1e401 | Edgar E. Iglesias | |
284 | 93f1e401 | Edgar E. Iglesias | /* Update the descriptor. */
|
285 | 93f1e401 | Edgar E. Iglesias | s->desc.status = txlen | SDESC_STATUS_COMPLETE; |
286 | 93f1e401 | Edgar E. Iglesias | stream_desc_store(s, s->regs[R_CURDESC]); |
287 | 93f1e401 | Edgar E. Iglesias | |
288 | 93f1e401 | Edgar E. Iglesias | /* Advance. */
|
289 | 93f1e401 | Edgar E. Iglesias | prev_d = s->regs[R_CURDESC]; |
290 | 93f1e401 | Edgar E. Iglesias | s->regs[R_CURDESC] = s->desc.nxtdesc; |
291 | 93f1e401 | Edgar E. Iglesias | if (prev_d == s->regs[R_TAILDESC]) {
|
292 | 93f1e401 | Edgar E. Iglesias | s->regs[R_DMASR] |= DMASR_IDLE; |
293 | 93f1e401 | Edgar E. Iglesias | break;
|
294 | 93f1e401 | Edgar E. Iglesias | } |
295 | 93f1e401 | Edgar E. Iglesias | } |
296 | 93f1e401 | Edgar E. Iglesias | } |
297 | 93f1e401 | Edgar E. Iglesias | |
298 | 93f1e401 | Edgar E. Iglesias | static void stream_process_s2mem(struct AXIStream *s, |
299 | 93f1e401 | Edgar E. Iglesias | unsigned char *buf, size_t len, uint32_t *app) |
300 | 93f1e401 | Edgar E. Iglesias | { |
301 | 93f1e401 | Edgar E. Iglesias | uint32_t prev_d; |
302 | 93f1e401 | Edgar E. Iglesias | unsigned int rxlen; |
303 | 93f1e401 | Edgar E. Iglesias | int pos = 0; |
304 | 93f1e401 | Edgar E. Iglesias | int sof = 1; |
305 | 93f1e401 | Edgar E. Iglesias | |
306 | 93f1e401 | Edgar E. Iglesias | if (!stream_running(s) || stream_idle(s)) {
|
307 | 93f1e401 | Edgar E. Iglesias | return;
|
308 | 93f1e401 | Edgar E. Iglesias | } |
309 | 93f1e401 | Edgar E. Iglesias | |
310 | 93f1e401 | Edgar E. Iglesias | while (len) {
|
311 | 93f1e401 | Edgar E. Iglesias | stream_desc_load(s, s->regs[R_CURDESC]); |
312 | 93f1e401 | Edgar E. Iglesias | |
313 | 93f1e401 | Edgar E. Iglesias | if (s->desc.status & SDESC_STATUS_COMPLETE) {
|
314 | 93f1e401 | Edgar E. Iglesias | s->regs[R_DMASR] |= DMASR_IDLE; |
315 | 93f1e401 | Edgar E. Iglesias | break;
|
316 | 93f1e401 | Edgar E. Iglesias | } |
317 | 93f1e401 | Edgar E. Iglesias | |
318 | 93f1e401 | Edgar E. Iglesias | rxlen = s->desc.control & SDESC_CTRL_LEN_MASK; |
319 | 93f1e401 | Edgar E. Iglesias | if (rxlen > len) {
|
320 | 93f1e401 | Edgar E. Iglesias | /* It fits. */
|
321 | 93f1e401 | Edgar E. Iglesias | rxlen = len; |
322 | 93f1e401 | Edgar E. Iglesias | } |
323 | 93f1e401 | Edgar E. Iglesias | |
324 | 93f1e401 | Edgar E. Iglesias | cpu_physical_memory_write(s->desc.buffer_address, buf + pos, rxlen); |
325 | 93f1e401 | Edgar E. Iglesias | len -= rxlen; |
326 | 93f1e401 | Edgar E. Iglesias | pos += rxlen; |
327 | 93f1e401 | Edgar E. Iglesias | |
328 | 93f1e401 | Edgar E. Iglesias | /* Update the descriptor. */
|
329 | 93f1e401 | Edgar E. Iglesias | if (!len) {
|
330 | 93f1e401 | Edgar E. Iglesias | int i;
|
331 | 93f1e401 | Edgar E. Iglesias | |
332 | 93f1e401 | Edgar E. Iglesias | stream_complete(s); |
333 | 93f1e401 | Edgar E. Iglesias | for (i = 0; i < 5; i++) { |
334 | 93f1e401 | Edgar E. Iglesias | s->desc.app[i] = app[i]; |
335 | 93f1e401 | Edgar E. Iglesias | } |
336 | 93f1e401 | Edgar E. Iglesias | s->desc.status |= SDESC_STATUS_EOF; |
337 | 93f1e401 | Edgar E. Iglesias | } |
338 | 93f1e401 | Edgar E. Iglesias | |
339 | 93f1e401 | Edgar E. Iglesias | s->desc.status |= sof << SDESC_STATUS_SOF_BIT; |
340 | 93f1e401 | Edgar E. Iglesias | s->desc.status |= SDESC_STATUS_COMPLETE; |
341 | 93f1e401 | Edgar E. Iglesias | stream_desc_store(s, s->regs[R_CURDESC]); |
342 | 93f1e401 | Edgar E. Iglesias | sof = 0;
|
343 | 93f1e401 | Edgar E. Iglesias | |
344 | 93f1e401 | Edgar E. Iglesias | /* Advance. */
|
345 | 93f1e401 | Edgar E. Iglesias | prev_d = s->regs[R_CURDESC]; |
346 | 93f1e401 | Edgar E. Iglesias | s->regs[R_CURDESC] = s->desc.nxtdesc; |
347 | 93f1e401 | Edgar E. Iglesias | if (prev_d == s->regs[R_TAILDESC]) {
|
348 | 93f1e401 | Edgar E. Iglesias | s->regs[R_DMASR] |= DMASR_IDLE; |
349 | 93f1e401 | Edgar E. Iglesias | break;
|
350 | 93f1e401 | Edgar E. Iglesias | } |
351 | 93f1e401 | Edgar E. Iglesias | } |
352 | 93f1e401 | Edgar E. Iglesias | } |
353 | 93f1e401 | Edgar E. Iglesias | |
354 | 93f1e401 | Edgar E. Iglesias | static
|
355 | 93f1e401 | Edgar E. Iglesias | void axidma_push(void *opaque, unsigned char *buf, size_t len, uint32_t *app) |
356 | 93f1e401 | Edgar E. Iglesias | { |
357 | 93f1e401 | Edgar E. Iglesias | struct XilinxAXIDMA *d = opaque;
|
358 | 93f1e401 | Edgar E. Iglesias | struct AXIStream *s = &d->streams[1]; |
359 | 93f1e401 | Edgar E. Iglesias | |
360 | 93f1e401 | Edgar E. Iglesias | if (!app) {
|
361 | 93f1e401 | Edgar E. Iglesias | hw_error("No stream app data!\n");
|
362 | 93f1e401 | Edgar E. Iglesias | } |
363 | 93f1e401 | Edgar E. Iglesias | stream_process_s2mem(s, buf, len, app); |
364 | 93f1e401 | Edgar E. Iglesias | stream_update_irq(s); |
365 | 93f1e401 | Edgar E. Iglesias | } |
366 | 93f1e401 | Edgar E. Iglesias | |
367 | f810bc4a | Avi Kivity | static uint64_t axidma_read(void *opaque, target_phys_addr_t addr, |
368 | f810bc4a | Avi Kivity | unsigned size)
|
369 | 93f1e401 | Edgar E. Iglesias | { |
370 | 93f1e401 | Edgar E. Iglesias | struct XilinxAXIDMA *d = opaque;
|
371 | 93f1e401 | Edgar E. Iglesias | struct AXIStream *s;
|
372 | 93f1e401 | Edgar E. Iglesias | uint32_t r = 0;
|
373 | 93f1e401 | Edgar E. Iglesias | int sid;
|
374 | 93f1e401 | Edgar E. Iglesias | |
375 | 93f1e401 | Edgar E. Iglesias | sid = streamid_from_addr(addr); |
376 | 93f1e401 | Edgar E. Iglesias | s = &d->streams[sid]; |
377 | 93f1e401 | Edgar E. Iglesias | |
378 | 93f1e401 | Edgar E. Iglesias | addr = addr % 0x30;
|
379 | 93f1e401 | Edgar E. Iglesias | addr >>= 2;
|
380 | 93f1e401 | Edgar E. Iglesias | switch (addr) {
|
381 | 93f1e401 | Edgar E. Iglesias | case R_DMACR:
|
382 | 93f1e401 | Edgar E. Iglesias | /* Simulate one cycles reset delay. */
|
383 | 93f1e401 | Edgar E. Iglesias | s->regs[addr] &= ~DMACR_RESET; |
384 | 93f1e401 | Edgar E. Iglesias | r = s->regs[addr]; |
385 | 93f1e401 | Edgar E. Iglesias | break;
|
386 | 93f1e401 | Edgar E. Iglesias | case R_DMASR:
|
387 | 93f1e401 | Edgar E. Iglesias | s->regs[addr] &= 0xffff;
|
388 | 93f1e401 | Edgar E. Iglesias | s->regs[addr] |= (s->complete_cnt & 0xff) << 16; |
389 | 93f1e401 | Edgar E. Iglesias | s->regs[addr] |= (ptimer_get_count(s->ptimer) & 0xff) << 24; |
390 | 93f1e401 | Edgar E. Iglesias | r = s->regs[addr]; |
391 | 93f1e401 | Edgar E. Iglesias | break;
|
392 | 93f1e401 | Edgar E. Iglesias | default:
|
393 | 93f1e401 | Edgar E. Iglesias | r = s->regs[addr]; |
394 | 93f1e401 | Edgar E. Iglesias | D(qemu_log("%s ch=%d addr=" TARGET_FMT_plx " v=%x\n", |
395 | 93f1e401 | Edgar E. Iglesias | __func__, sid, addr * 4, r));
|
396 | 93f1e401 | Edgar E. Iglesias | break;
|
397 | 93f1e401 | Edgar E. Iglesias | } |
398 | 93f1e401 | Edgar E. Iglesias | return r;
|
399 | 93f1e401 | Edgar E. Iglesias | |
400 | 93f1e401 | Edgar E. Iglesias | } |
401 | 93f1e401 | Edgar E. Iglesias | |
402 | f810bc4a | Avi Kivity | static void axidma_write(void *opaque, target_phys_addr_t addr, |
403 | f810bc4a | Avi Kivity | uint64_t value, unsigned size)
|
404 | 93f1e401 | Edgar E. Iglesias | { |
405 | 93f1e401 | Edgar E. Iglesias | struct XilinxAXIDMA *d = opaque;
|
406 | 93f1e401 | Edgar E. Iglesias | struct AXIStream *s;
|
407 | 93f1e401 | Edgar E. Iglesias | int sid;
|
408 | 93f1e401 | Edgar E. Iglesias | |
409 | 93f1e401 | Edgar E. Iglesias | sid = streamid_from_addr(addr); |
410 | 93f1e401 | Edgar E. Iglesias | s = &d->streams[sid]; |
411 | 93f1e401 | Edgar E. Iglesias | |
412 | 93f1e401 | Edgar E. Iglesias | addr = addr % 0x30;
|
413 | 93f1e401 | Edgar E. Iglesias | addr >>= 2;
|
414 | 93f1e401 | Edgar E. Iglesias | switch (addr) {
|
415 | 93f1e401 | Edgar E. Iglesias | case R_DMACR:
|
416 | 93f1e401 | Edgar E. Iglesias | /* Tailptr mode is always on. */
|
417 | 93f1e401 | Edgar E. Iglesias | value |= DMACR_TAILPTR_MODE; |
418 | 93f1e401 | Edgar E. Iglesias | /* Remember our previous reset state. */
|
419 | 93f1e401 | Edgar E. Iglesias | value |= (s->regs[addr] & DMACR_RESET); |
420 | 93f1e401 | Edgar E. Iglesias | s->regs[addr] = value; |
421 | 93f1e401 | Edgar E. Iglesias | |
422 | 93f1e401 | Edgar E. Iglesias | if (value & DMACR_RESET) {
|
423 | 93f1e401 | Edgar E. Iglesias | stream_reset(s); |
424 | 93f1e401 | Edgar E. Iglesias | } |
425 | 93f1e401 | Edgar E. Iglesias | |
426 | 93f1e401 | Edgar E. Iglesias | if ((value & 1) && !stream_resetting(s)) { |
427 | 93f1e401 | Edgar E. Iglesias | /* Start processing. */
|
428 | 93f1e401 | Edgar E. Iglesias | s->regs[R_DMASR] &= ~(DMASR_HALTED | DMASR_IDLE); |
429 | 93f1e401 | Edgar E. Iglesias | } |
430 | 93f1e401 | Edgar E. Iglesias | stream_reload_complete_cnt(s); |
431 | 93f1e401 | Edgar E. Iglesias | break;
|
432 | 93f1e401 | Edgar E. Iglesias | |
433 | 93f1e401 | Edgar E. Iglesias | case R_DMASR:
|
434 | 93f1e401 | Edgar E. Iglesias | /* Mask away write to clear irq lines. */
|
435 | 93f1e401 | Edgar E. Iglesias | value &= ~(value & DMASR_IRQ_MASK); |
436 | 93f1e401 | Edgar E. Iglesias | s->regs[addr] = value; |
437 | 93f1e401 | Edgar E. Iglesias | break;
|
438 | 93f1e401 | Edgar E. Iglesias | |
439 | 93f1e401 | Edgar E. Iglesias | case R_TAILDESC:
|
440 | 93f1e401 | Edgar E. Iglesias | s->regs[addr] = value; |
441 | 93f1e401 | Edgar E. Iglesias | s->regs[R_DMASR] &= ~DMASR_IDLE; /* Not idle. */
|
442 | 93f1e401 | Edgar E. Iglesias | if (!sid) {
|
443 | 93f1e401 | Edgar E. Iglesias | stream_process_mem2s(s, d->dmach); |
444 | 93f1e401 | Edgar E. Iglesias | } |
445 | 93f1e401 | Edgar E. Iglesias | break;
|
446 | 93f1e401 | Edgar E. Iglesias | default:
|
447 | 93f1e401 | Edgar E. Iglesias | D(qemu_log("%s: ch=%d addr=" TARGET_FMT_plx " v=%x\n", |
448 | 93f1e401 | Edgar E. Iglesias | __func__, sid, addr * 4, value));
|
449 | 93f1e401 | Edgar E. Iglesias | s->regs[addr] = value; |
450 | 93f1e401 | Edgar E. Iglesias | break;
|
451 | 93f1e401 | Edgar E. Iglesias | } |
452 | 93f1e401 | Edgar E. Iglesias | stream_update_irq(s); |
453 | 93f1e401 | Edgar E. Iglesias | } |
454 | 93f1e401 | Edgar E. Iglesias | |
455 | f810bc4a | Avi Kivity | static const MemoryRegionOps axidma_ops = { |
456 | f810bc4a | Avi Kivity | .read = axidma_read, |
457 | f810bc4a | Avi Kivity | .write = axidma_write, |
458 | f810bc4a | Avi Kivity | .endianness = DEVICE_NATIVE_ENDIAN, |
459 | 93f1e401 | Edgar E. Iglesias | }; |
460 | 93f1e401 | Edgar E. Iglesias | |
461 | 93f1e401 | Edgar E. Iglesias | static int xilinx_axidma_init(SysBusDevice *dev) |
462 | 93f1e401 | Edgar E. Iglesias | { |
463 | 93f1e401 | Edgar E. Iglesias | struct XilinxAXIDMA *s = FROM_SYSBUS(typeof(*s), dev);
|
464 | 93f1e401 | Edgar E. Iglesias | int i;
|
465 | 93f1e401 | Edgar E. Iglesias | |
466 | 93f1e401 | Edgar E. Iglesias | sysbus_init_irq(dev, &s->streams[1].irq);
|
467 | 93f1e401 | Edgar E. Iglesias | sysbus_init_irq(dev, &s->streams[0].irq);
|
468 | 93f1e401 | Edgar E. Iglesias | |
469 | 93f1e401 | Edgar E. Iglesias | if (!s->dmach) {
|
470 | 93f1e401 | Edgar E. Iglesias | hw_error("Unconnected DMA channel.\n");
|
471 | 93f1e401 | Edgar E. Iglesias | } |
472 | 93f1e401 | Edgar E. Iglesias | |
473 | 93f1e401 | Edgar E. Iglesias | xlx_dma_connect_dma(s->dmach, s, axidma_push); |
474 | 93f1e401 | Edgar E. Iglesias | |
475 | f810bc4a | Avi Kivity | memory_region_init_io(&s->iomem, &axidma_ops, s, |
476 | f810bc4a | Avi Kivity | "axidma", R_MAX * 4 * 2); |
477 | 750ecd44 | Avi Kivity | sysbus_init_mmio(dev, &s->iomem); |
478 | 93f1e401 | Edgar E. Iglesias | |
479 | 93f1e401 | Edgar E. Iglesias | for (i = 0; i < 2; i++) { |
480 | 93f1e401 | Edgar E. Iglesias | stream_reset(&s->streams[i]); |
481 | 93f1e401 | Edgar E. Iglesias | s->streams[i].nr = i; |
482 | 93f1e401 | Edgar E. Iglesias | s->streams[i].bh = qemu_bh_new(timer_hit, &s->streams[i]); |
483 | 93f1e401 | Edgar E. Iglesias | s->streams[i].ptimer = ptimer_init(s->streams[i].bh); |
484 | 93f1e401 | Edgar E. Iglesias | ptimer_set_freq(s->streams[i].ptimer, s->freqhz); |
485 | 93f1e401 | Edgar E. Iglesias | } |
486 | 93f1e401 | Edgar E. Iglesias | return 0; |
487 | 93f1e401 | Edgar E. Iglesias | } |
488 | 93f1e401 | Edgar E. Iglesias | |
489 | 999e12bb | Anthony Liguori | static Property axidma_properties[] = {
|
490 | 999e12bb | Anthony Liguori | DEFINE_PROP_UINT32("freqhz", struct XilinxAXIDMA, freqhz, 50000000), |
491 | 999e12bb | Anthony Liguori | DEFINE_PROP_PTR("dmach", struct XilinxAXIDMA, dmach), |
492 | 999e12bb | Anthony Liguori | DEFINE_PROP_END_OF_LIST(), |
493 | 999e12bb | Anthony Liguori | }; |
494 | 999e12bb | Anthony Liguori | |
495 | 999e12bb | Anthony Liguori | static void axidma_class_init(ObjectClass *klass, void *data) |
496 | 999e12bb | Anthony Liguori | { |
497 | 39bffca2 | Anthony Liguori | DeviceClass *dc = DEVICE_CLASS(klass); |
498 | 999e12bb | Anthony Liguori | SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass); |
499 | 999e12bb | Anthony Liguori | |
500 | 999e12bb | Anthony Liguori | k->init = xilinx_axidma_init; |
501 | 39bffca2 | Anthony Liguori | dc->props = axidma_properties; |
502 | 999e12bb | Anthony Liguori | } |
503 | 999e12bb | Anthony Liguori | |
504 | 39bffca2 | Anthony Liguori | static TypeInfo axidma_info = {
|
505 | 39bffca2 | Anthony Liguori | .name = "xilinx,axidma",
|
506 | 39bffca2 | Anthony Liguori | .parent = TYPE_SYS_BUS_DEVICE, |
507 | 39bffca2 | Anthony Liguori | .instance_size = sizeof(struct XilinxAXIDMA), |
508 | 39bffca2 | Anthony Liguori | .class_init = axidma_class_init, |
509 | 93f1e401 | Edgar E. Iglesias | }; |
510 | 93f1e401 | Edgar E. Iglesias | |
511 | 83f7d43a | Andreas Färber | static void xilinx_axidma_register_types(void) |
512 | 93f1e401 | Edgar E. Iglesias | { |
513 | 39bffca2 | Anthony Liguori | type_register_static(&axidma_info); |
514 | 93f1e401 | Edgar E. Iglesias | } |
515 | 93f1e401 | Edgar E. Iglesias | |
516 | 83f7d43a | Andreas Färber | type_init(xilinx_axidma_register_types) |