root / hw / xilinx_axidma.c @ a0a7573b
History | View | Annotate | Download (12.9 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 | 93f1e401 | Edgar E. Iglesias | #include "qemu-log.h" |
29 | 93f1e401 | Edgar E. Iglesias | #include "qdev-addr.h" |
30 | 93f1e401 | Edgar E. Iglesias | |
31 | 93f1e401 | Edgar E. Iglesias | #include "xilinx_axidma.h" |
32 | 93f1e401 | Edgar E. Iglesias | |
33 | 93f1e401 | Edgar E. Iglesias | #define D(x)
|
34 | 93f1e401 | Edgar E. Iglesias | |
35 | 93f1e401 | Edgar E. Iglesias | #define R_DMACR (0x00 / 4) |
36 | 93f1e401 | Edgar E. Iglesias | #define R_DMASR (0x04 / 4) |
37 | 93f1e401 | Edgar E. Iglesias | #define R_CURDESC (0x08 / 4) |
38 | 93f1e401 | Edgar E. Iglesias | #define R_TAILDESC (0x10 / 4) |
39 | 93f1e401 | Edgar E. Iglesias | #define R_MAX (0x30 / 4) |
40 | 93f1e401 | Edgar E. Iglesias | |
41 | 93f1e401 | Edgar E. Iglesias | enum {
|
42 | 93f1e401 | Edgar E. Iglesias | DMACR_RUNSTOP = 1,
|
43 | 93f1e401 | Edgar E. Iglesias | DMACR_TAILPTR_MODE = 2,
|
44 | 93f1e401 | Edgar E. Iglesias | DMACR_RESET = 4
|
45 | 93f1e401 | Edgar E. Iglesias | }; |
46 | 93f1e401 | Edgar E. Iglesias | |
47 | 93f1e401 | Edgar E. Iglesias | enum {
|
48 | 93f1e401 | Edgar E. Iglesias | DMASR_HALTED = 1,
|
49 | 93f1e401 | Edgar E. Iglesias | DMASR_IDLE = 2,
|
50 | 93f1e401 | Edgar E. Iglesias | DMASR_IOC_IRQ = 1 << 12, |
51 | 93f1e401 | Edgar E. Iglesias | DMASR_DLY_IRQ = 1 << 13, |
52 | 93f1e401 | Edgar E. Iglesias | |
53 | 93f1e401 | Edgar E. Iglesias | DMASR_IRQ_MASK = 7 << 12 |
54 | 93f1e401 | Edgar E. Iglesias | }; |
55 | 93f1e401 | Edgar E. Iglesias | |
56 | 93f1e401 | Edgar E. Iglesias | struct SDesc {
|
57 | 93f1e401 | Edgar E. Iglesias | uint64_t nxtdesc; |
58 | 93f1e401 | Edgar E. Iglesias | uint64_t buffer_address; |
59 | 93f1e401 | Edgar E. Iglesias | uint64_t reserved; |
60 | 93f1e401 | Edgar E. Iglesias | uint32_t control; |
61 | 93f1e401 | Edgar E. Iglesias | uint32_t status; |
62 | 93f1e401 | Edgar E. Iglesias | uint32_t app[6];
|
63 | 93f1e401 | Edgar E. Iglesias | }; |
64 | 93f1e401 | Edgar E. Iglesias | |
65 | 93f1e401 | Edgar E. Iglesias | enum {
|
66 | 93f1e401 | Edgar E. Iglesias | SDESC_CTRL_EOF = (1 << 26), |
67 | 93f1e401 | Edgar E. Iglesias | SDESC_CTRL_SOF = (1 << 27), |
68 | 93f1e401 | Edgar E. Iglesias | |
69 | 93f1e401 | Edgar E. Iglesias | SDESC_CTRL_LEN_MASK = (1 << 23) - 1 |
70 | 93f1e401 | Edgar E. Iglesias | }; |
71 | 93f1e401 | Edgar E. Iglesias | |
72 | 93f1e401 | Edgar E. Iglesias | enum {
|
73 | 93f1e401 | Edgar E. Iglesias | SDESC_STATUS_EOF = (1 << 26), |
74 | 93f1e401 | Edgar E. Iglesias | SDESC_STATUS_SOF_BIT = 27,
|
75 | 93f1e401 | Edgar E. Iglesias | SDESC_STATUS_SOF = (1 << SDESC_STATUS_SOF_BIT),
|
76 | 93f1e401 | Edgar E. Iglesias | SDESC_STATUS_COMPLETE = (1 << 31) |
77 | 93f1e401 | Edgar E. Iglesias | }; |
78 | 93f1e401 | Edgar E. Iglesias | |
79 | 93f1e401 | Edgar E. Iglesias | struct AXIStream {
|
80 | 93f1e401 | Edgar E. Iglesias | QEMUBH *bh; |
81 | 93f1e401 | Edgar E. Iglesias | ptimer_state *ptimer; |
82 | 93f1e401 | Edgar E. Iglesias | qemu_irq irq; |
83 | 93f1e401 | Edgar E. Iglesias | |
84 | 93f1e401 | Edgar E. Iglesias | int nr;
|
85 | 93f1e401 | Edgar E. Iglesias | |
86 | 93f1e401 | Edgar E. Iglesias | struct SDesc desc;
|
87 | 93f1e401 | Edgar E. Iglesias | int pos;
|
88 | 93f1e401 | Edgar E. Iglesias | unsigned int complete_cnt; |
89 | 93f1e401 | Edgar E. Iglesias | uint32_t regs[R_MAX]; |
90 | 93f1e401 | Edgar E. Iglesias | }; |
91 | 93f1e401 | Edgar E. Iglesias | |
92 | 93f1e401 | Edgar E. Iglesias | struct XilinxAXIDMA {
|
93 | 93f1e401 | Edgar E. Iglesias | SysBusDevice busdev; |
94 | 93f1e401 | Edgar E. Iglesias | uint32_t freqhz; |
95 | 93f1e401 | Edgar E. Iglesias | void *dmach;
|
96 | 93f1e401 | Edgar E. Iglesias | |
97 | 93f1e401 | Edgar E. Iglesias | struct AXIStream streams[2]; |
98 | 93f1e401 | Edgar E. Iglesias | }; |
99 | 93f1e401 | Edgar E. Iglesias | |
100 | 93f1e401 | Edgar E. Iglesias | /*
|
101 | 93f1e401 | Edgar E. Iglesias | * Helper calls to extract info from desriptors and other trivial
|
102 | 93f1e401 | Edgar E. Iglesias | * state from regs.
|
103 | 93f1e401 | Edgar E. Iglesias | */
|
104 | 93f1e401 | Edgar E. Iglesias | static inline int stream_desc_sof(struct SDesc *d) |
105 | 93f1e401 | Edgar E. Iglesias | { |
106 | 93f1e401 | Edgar E. Iglesias | return d->control & SDESC_CTRL_SOF;
|
107 | 93f1e401 | Edgar E. Iglesias | } |
108 | 93f1e401 | Edgar E. Iglesias | |
109 | 93f1e401 | Edgar E. Iglesias | static inline int stream_desc_eof(struct SDesc *d) |
110 | 93f1e401 | Edgar E. Iglesias | { |
111 | 93f1e401 | Edgar E. Iglesias | return d->control & SDESC_CTRL_EOF;
|
112 | 93f1e401 | Edgar E. Iglesias | } |
113 | 93f1e401 | Edgar E. Iglesias | |
114 | 93f1e401 | Edgar E. Iglesias | static inline int stream_resetting(struct AXIStream *s) |
115 | 93f1e401 | Edgar E. Iglesias | { |
116 | 93f1e401 | Edgar E. Iglesias | return !!(s->regs[R_DMACR] & DMACR_RESET);
|
117 | 93f1e401 | Edgar E. Iglesias | } |
118 | 93f1e401 | Edgar E. Iglesias | |
119 | 93f1e401 | Edgar E. Iglesias | static inline int stream_running(struct AXIStream *s) |
120 | 93f1e401 | Edgar E. Iglesias | { |
121 | 93f1e401 | Edgar E. Iglesias | return s->regs[R_DMACR] & DMACR_RUNSTOP;
|
122 | 93f1e401 | Edgar E. Iglesias | } |
123 | 93f1e401 | Edgar E. Iglesias | |
124 | 93f1e401 | Edgar E. Iglesias | static inline int stream_halted(struct AXIStream *s) |
125 | 93f1e401 | Edgar E. Iglesias | { |
126 | 93f1e401 | Edgar E. Iglesias | return s->regs[R_DMASR] & DMASR_HALTED;
|
127 | 93f1e401 | Edgar E. Iglesias | } |
128 | 93f1e401 | Edgar E. Iglesias | |
129 | 93f1e401 | Edgar E. Iglesias | static inline int stream_idle(struct AXIStream *s) |
130 | 93f1e401 | Edgar E. Iglesias | { |
131 | 93f1e401 | Edgar E. Iglesias | return !!(s->regs[R_DMASR] & DMASR_IDLE);
|
132 | 93f1e401 | Edgar E. Iglesias | } |
133 | 93f1e401 | Edgar E. Iglesias | |
134 | 93f1e401 | Edgar E. Iglesias | static void stream_reset(struct AXIStream *s) |
135 | 93f1e401 | Edgar E. Iglesias | { |
136 | 93f1e401 | Edgar E. Iglesias | s->regs[R_DMASR] = DMASR_HALTED; /* starts up halted. */
|
137 | 0d50d616 | Stefan Weil | s->regs[R_DMACR] = 1 << 16; /* Starts with one in compl threshold. */ |
138 | 93f1e401 | Edgar E. Iglesias | } |
139 | 93f1e401 | Edgar E. Iglesias | |
140 | 0d50d616 | Stefan Weil | /* Map an offset addr into a channel index. */
|
141 | 93f1e401 | Edgar E. Iglesias | static inline int streamid_from_addr(target_phys_addr_t addr) |
142 | 93f1e401 | Edgar E. Iglesias | { |
143 | 93f1e401 | Edgar E. Iglesias | int sid;
|
144 | 93f1e401 | Edgar E. Iglesias | |
145 | 93f1e401 | Edgar E. Iglesias | sid = addr / (0x30);
|
146 | 93f1e401 | Edgar E. Iglesias | sid &= 1;
|
147 | 93f1e401 | Edgar E. Iglesias | return sid;
|
148 | 93f1e401 | Edgar E. Iglesias | } |
149 | 93f1e401 | Edgar E. Iglesias | |
150 | 93f1e401 | Edgar E. Iglesias | #ifdef DEBUG_ENET
|
151 | 93f1e401 | Edgar E. Iglesias | static void stream_desc_show(struct SDesc *d) |
152 | 93f1e401 | Edgar E. Iglesias | { |
153 | 93f1e401 | Edgar E. Iglesias | qemu_log("buffer_addr = " PRIx64 "\n", d->buffer_address); |
154 | 93f1e401 | Edgar E. Iglesias | qemu_log("nxtdesc = " PRIx64 "\n", d->nxtdesc); |
155 | 93f1e401 | Edgar E. Iglesias | qemu_log("control = %x\n", d->control);
|
156 | 93f1e401 | Edgar E. Iglesias | qemu_log("status = %x\n", d->status);
|
157 | 93f1e401 | Edgar E. Iglesias | } |
158 | 93f1e401 | Edgar E. Iglesias | #endif
|
159 | 93f1e401 | Edgar E. Iglesias | |
160 | 93f1e401 | Edgar E. Iglesias | static void stream_desc_load(struct AXIStream *s, target_phys_addr_t addr) |
161 | 93f1e401 | Edgar E. Iglesias | { |
162 | 93f1e401 | Edgar E. Iglesias | struct SDesc *d = &s->desc;
|
163 | 93f1e401 | Edgar E. Iglesias | int i;
|
164 | 93f1e401 | Edgar E. Iglesias | |
165 | 93f1e401 | Edgar E. Iglesias | cpu_physical_memory_read(addr, (void *) d, sizeof *d); |
166 | 93f1e401 | Edgar E. Iglesias | |
167 | 93f1e401 | Edgar E. Iglesias | /* Convert from LE into host endianness. */
|
168 | 93f1e401 | Edgar E. Iglesias | d->buffer_address = le64_to_cpu(d->buffer_address); |
169 | 93f1e401 | Edgar E. Iglesias | d->nxtdesc = le64_to_cpu(d->nxtdesc); |
170 | 93f1e401 | Edgar E. Iglesias | d->control = le32_to_cpu(d->control); |
171 | 93f1e401 | Edgar E. Iglesias | d->status = le32_to_cpu(d->status); |
172 | 93f1e401 | Edgar E. Iglesias | for (i = 0; i < ARRAY_SIZE(d->app); i++) { |
173 | 93f1e401 | Edgar E. Iglesias | d->app[i] = le32_to_cpu(d->app[i]); |
174 | 93f1e401 | Edgar E. Iglesias | } |
175 | 93f1e401 | Edgar E. Iglesias | } |
176 | 93f1e401 | Edgar E. Iglesias | |
177 | 93f1e401 | Edgar E. Iglesias | static void stream_desc_store(struct AXIStream *s, target_phys_addr_t addr) |
178 | 93f1e401 | Edgar E. Iglesias | { |
179 | 93f1e401 | Edgar E. Iglesias | struct SDesc *d = &s->desc;
|
180 | 93f1e401 | Edgar E. Iglesias | int i;
|
181 | 93f1e401 | Edgar E. Iglesias | |
182 | 93f1e401 | Edgar E. Iglesias | /* Convert from host endianness into LE. */
|
183 | 93f1e401 | Edgar E. Iglesias | d->buffer_address = cpu_to_le64(d->buffer_address); |
184 | 93f1e401 | Edgar E. Iglesias | d->nxtdesc = cpu_to_le64(d->nxtdesc); |
185 | 93f1e401 | Edgar E. Iglesias | d->control = cpu_to_le32(d->control); |
186 | 93f1e401 | Edgar E. Iglesias | d->status = cpu_to_le32(d->status); |
187 | 93f1e401 | Edgar E. Iglesias | for (i = 0; i < ARRAY_SIZE(d->app); i++) { |
188 | 93f1e401 | Edgar E. Iglesias | d->app[i] = cpu_to_le32(d->app[i]); |
189 | 93f1e401 | Edgar E. Iglesias | } |
190 | 93f1e401 | Edgar E. Iglesias | cpu_physical_memory_write(addr, (void *) d, sizeof *d); |
191 | 93f1e401 | Edgar E. Iglesias | } |
192 | 93f1e401 | Edgar E. Iglesias | |
193 | 93f1e401 | Edgar E. Iglesias | static void stream_update_irq(struct AXIStream *s) |
194 | 93f1e401 | Edgar E. Iglesias | { |
195 | 93f1e401 | Edgar E. Iglesias | unsigned int pending, mask, irq; |
196 | 93f1e401 | Edgar E. Iglesias | |
197 | 93f1e401 | Edgar E. Iglesias | pending = s->regs[R_DMASR] & DMASR_IRQ_MASK; |
198 | 93f1e401 | Edgar E. Iglesias | mask = s->regs[R_DMACR] & DMASR_IRQ_MASK; |
199 | 93f1e401 | Edgar E. Iglesias | |
200 | 93f1e401 | Edgar E. Iglesias | irq = pending & mask; |
201 | 93f1e401 | Edgar E. Iglesias | |
202 | 93f1e401 | Edgar E. Iglesias | qemu_set_irq(s->irq, !!irq); |
203 | 93f1e401 | Edgar E. Iglesias | } |
204 | 93f1e401 | Edgar E. Iglesias | |
205 | 93f1e401 | Edgar E. Iglesias | static void stream_reload_complete_cnt(struct AXIStream *s) |
206 | 93f1e401 | Edgar E. Iglesias | { |
207 | 93f1e401 | Edgar E. Iglesias | unsigned int comp_th; |
208 | 93f1e401 | Edgar E. Iglesias | comp_th = (s->regs[R_DMACR] >> 16) & 0xff; |
209 | 93f1e401 | Edgar E. Iglesias | s->complete_cnt = comp_th; |
210 | 93f1e401 | Edgar E. Iglesias | } |
211 | 93f1e401 | Edgar E. Iglesias | |
212 | 93f1e401 | Edgar E. Iglesias | static void timer_hit(void *opaque) |
213 | 93f1e401 | Edgar E. Iglesias | { |
214 | 93f1e401 | Edgar E. Iglesias | struct AXIStream *s = opaque;
|
215 | 93f1e401 | Edgar E. Iglesias | |
216 | 93f1e401 | Edgar E. Iglesias | stream_reload_complete_cnt(s); |
217 | 93f1e401 | Edgar E. Iglesias | s->regs[R_DMASR] |= DMASR_DLY_IRQ; |
218 | 93f1e401 | Edgar E. Iglesias | stream_update_irq(s); |
219 | 93f1e401 | Edgar E. Iglesias | } |
220 | 93f1e401 | Edgar E. Iglesias | |
221 | 93f1e401 | Edgar E. Iglesias | static void stream_complete(struct AXIStream *s) |
222 | 93f1e401 | Edgar E. Iglesias | { |
223 | 93f1e401 | Edgar E. Iglesias | unsigned int comp_delay; |
224 | 93f1e401 | Edgar E. Iglesias | |
225 | 93f1e401 | Edgar E. Iglesias | /* Start the delayed timer. */
|
226 | 93f1e401 | Edgar E. Iglesias | comp_delay = s->regs[R_DMACR] >> 24;
|
227 | 93f1e401 | Edgar E. Iglesias | if (comp_delay) {
|
228 | 93f1e401 | Edgar E. Iglesias | ptimer_stop(s->ptimer); |
229 | 93f1e401 | Edgar E. Iglesias | ptimer_set_count(s->ptimer, comp_delay); |
230 | 93f1e401 | Edgar E. Iglesias | ptimer_run(s->ptimer, 1);
|
231 | 93f1e401 | Edgar E. Iglesias | } |
232 | 93f1e401 | Edgar E. Iglesias | |
233 | 93f1e401 | Edgar E. Iglesias | s->complete_cnt--; |
234 | 93f1e401 | Edgar E. Iglesias | if (s->complete_cnt == 0) { |
235 | 93f1e401 | Edgar E. Iglesias | /* Raise the IOC irq. */
|
236 | 93f1e401 | Edgar E. Iglesias | s->regs[R_DMASR] |= DMASR_IOC_IRQ; |
237 | 93f1e401 | Edgar E. Iglesias | stream_reload_complete_cnt(s); |
238 | 93f1e401 | Edgar E. Iglesias | } |
239 | 93f1e401 | Edgar E. Iglesias | } |
240 | 93f1e401 | Edgar E. Iglesias | |
241 | 93f1e401 | Edgar E. Iglesias | static void stream_process_mem2s(struct AXIStream *s, |
242 | 93f1e401 | Edgar E. Iglesias | struct XilinxDMAConnection *dmach)
|
243 | 93f1e401 | Edgar E. Iglesias | { |
244 | 93f1e401 | Edgar E. Iglesias | uint32_t prev_d; |
245 | 93f1e401 | Edgar E. Iglesias | unsigned char txbuf[16 * 1024]; |
246 | 93f1e401 | Edgar E. Iglesias | unsigned int txlen; |
247 | 93f1e401 | Edgar E. Iglesias | uint32_t app[6];
|
248 | 93f1e401 | Edgar E. Iglesias | |
249 | 93f1e401 | Edgar E. Iglesias | if (!stream_running(s) || stream_idle(s)) {
|
250 | 93f1e401 | Edgar E. Iglesias | return;
|
251 | 93f1e401 | Edgar E. Iglesias | } |
252 | 93f1e401 | Edgar E. Iglesias | |
253 | 93f1e401 | Edgar E. Iglesias | while (1) { |
254 | 93f1e401 | Edgar E. Iglesias | stream_desc_load(s, s->regs[R_CURDESC]); |
255 | 93f1e401 | Edgar E. Iglesias | |
256 | 93f1e401 | Edgar E. Iglesias | if (s->desc.status & SDESC_STATUS_COMPLETE) {
|
257 | 93f1e401 | Edgar E. Iglesias | s->regs[R_DMASR] |= DMASR_IDLE; |
258 | 93f1e401 | Edgar E. Iglesias | break;
|
259 | 93f1e401 | Edgar E. Iglesias | } |
260 | 93f1e401 | Edgar E. Iglesias | |
261 | 93f1e401 | Edgar E. Iglesias | if (stream_desc_sof(&s->desc)) {
|
262 | 93f1e401 | Edgar E. Iglesias | s->pos = 0;
|
263 | 93f1e401 | Edgar E. Iglesias | memcpy(app, s->desc.app, sizeof app);
|
264 | 93f1e401 | Edgar E. Iglesias | } |
265 | 93f1e401 | Edgar E. Iglesias | |
266 | 93f1e401 | Edgar E. Iglesias | txlen = s->desc.control & SDESC_CTRL_LEN_MASK; |
267 | 93f1e401 | Edgar E. Iglesias | if ((txlen + s->pos) > sizeof txbuf) { |
268 | 93f1e401 | Edgar E. Iglesias | hw_error("%s: too small internal txbuf! %d\n", __func__,
|
269 | 93f1e401 | Edgar E. Iglesias | txlen + s->pos); |
270 | 93f1e401 | Edgar E. Iglesias | } |
271 | 93f1e401 | Edgar E. Iglesias | |
272 | 93f1e401 | Edgar E. Iglesias | cpu_physical_memory_read(s->desc.buffer_address, |
273 | 93f1e401 | Edgar E. Iglesias | txbuf + s->pos, txlen); |
274 | 93f1e401 | Edgar E. Iglesias | s->pos += txlen; |
275 | 93f1e401 | Edgar E. Iglesias | |
276 | 93f1e401 | Edgar E. Iglesias | if (stream_desc_eof(&s->desc)) {
|
277 | 93f1e401 | Edgar E. Iglesias | xlx_dma_push_to_client(dmach, txbuf, s->pos, app); |
278 | 93f1e401 | Edgar E. Iglesias | s->pos = 0;
|
279 | 93f1e401 | Edgar E. Iglesias | stream_complete(s); |
280 | 93f1e401 | Edgar E. Iglesias | } |
281 | 93f1e401 | Edgar E. Iglesias | |
282 | 93f1e401 | Edgar E. Iglesias | /* Update the descriptor. */
|
283 | 93f1e401 | Edgar E. Iglesias | s->desc.status = txlen | SDESC_STATUS_COMPLETE; |
284 | 93f1e401 | Edgar E. Iglesias | stream_desc_store(s, s->regs[R_CURDESC]); |
285 | 93f1e401 | Edgar E. Iglesias | |
286 | 93f1e401 | Edgar E. Iglesias | /* Advance. */
|
287 | 93f1e401 | Edgar E. Iglesias | prev_d = s->regs[R_CURDESC]; |
288 | 93f1e401 | Edgar E. Iglesias | s->regs[R_CURDESC] = s->desc.nxtdesc; |
289 | 93f1e401 | Edgar E. Iglesias | if (prev_d == s->regs[R_TAILDESC]) {
|
290 | 93f1e401 | Edgar E. Iglesias | s->regs[R_DMASR] |= DMASR_IDLE; |
291 | 93f1e401 | Edgar E. Iglesias | break;
|
292 | 93f1e401 | Edgar E. Iglesias | } |
293 | 93f1e401 | Edgar E. Iglesias | } |
294 | 93f1e401 | Edgar E. Iglesias | } |
295 | 93f1e401 | Edgar E. Iglesias | |
296 | 93f1e401 | Edgar E. Iglesias | static void stream_process_s2mem(struct AXIStream *s, |
297 | 93f1e401 | Edgar E. Iglesias | unsigned char *buf, size_t len, uint32_t *app) |
298 | 93f1e401 | Edgar E. Iglesias | { |
299 | 93f1e401 | Edgar E. Iglesias | uint32_t prev_d; |
300 | 93f1e401 | Edgar E. Iglesias | unsigned int rxlen; |
301 | 93f1e401 | Edgar E. Iglesias | int pos = 0; |
302 | 93f1e401 | Edgar E. Iglesias | int sof = 1; |
303 | 93f1e401 | Edgar E. Iglesias | |
304 | 93f1e401 | Edgar E. Iglesias | if (!stream_running(s) || stream_idle(s)) {
|
305 | 93f1e401 | Edgar E. Iglesias | return;
|
306 | 93f1e401 | Edgar E. Iglesias | } |
307 | 93f1e401 | Edgar E. Iglesias | |
308 | 93f1e401 | Edgar E. Iglesias | while (len) {
|
309 | 93f1e401 | Edgar E. Iglesias | stream_desc_load(s, s->regs[R_CURDESC]); |
310 | 93f1e401 | Edgar E. Iglesias | |
311 | 93f1e401 | Edgar E. Iglesias | if (s->desc.status & SDESC_STATUS_COMPLETE) {
|
312 | 93f1e401 | Edgar E. Iglesias | s->regs[R_DMASR] |= DMASR_IDLE; |
313 | 93f1e401 | Edgar E. Iglesias | break;
|
314 | 93f1e401 | Edgar E. Iglesias | } |
315 | 93f1e401 | Edgar E. Iglesias | |
316 | 93f1e401 | Edgar E. Iglesias | rxlen = s->desc.control & SDESC_CTRL_LEN_MASK; |
317 | 93f1e401 | Edgar E. Iglesias | if (rxlen > len) {
|
318 | 93f1e401 | Edgar E. Iglesias | /* It fits. */
|
319 | 93f1e401 | Edgar E. Iglesias | rxlen = len; |
320 | 93f1e401 | Edgar E. Iglesias | } |
321 | 93f1e401 | Edgar E. Iglesias | |
322 | 93f1e401 | Edgar E. Iglesias | cpu_physical_memory_write(s->desc.buffer_address, buf + pos, rxlen); |
323 | 93f1e401 | Edgar E. Iglesias | len -= rxlen; |
324 | 93f1e401 | Edgar E. Iglesias | pos += rxlen; |
325 | 93f1e401 | Edgar E. Iglesias | |
326 | 93f1e401 | Edgar E. Iglesias | /* Update the descriptor. */
|
327 | 93f1e401 | Edgar E. Iglesias | if (!len) {
|
328 | 93f1e401 | Edgar E. Iglesias | int i;
|
329 | 93f1e401 | Edgar E. Iglesias | |
330 | 93f1e401 | Edgar E. Iglesias | stream_complete(s); |
331 | 93f1e401 | Edgar E. Iglesias | for (i = 0; i < 5; i++) { |
332 | 93f1e401 | Edgar E. Iglesias | s->desc.app[i] = app[i]; |
333 | 93f1e401 | Edgar E. Iglesias | } |
334 | 93f1e401 | Edgar E. Iglesias | s->desc.status |= SDESC_STATUS_EOF; |
335 | 93f1e401 | Edgar E. Iglesias | } |
336 | 93f1e401 | Edgar E. Iglesias | |
337 | 93f1e401 | Edgar E. Iglesias | s->desc.status |= sof << SDESC_STATUS_SOF_BIT; |
338 | 93f1e401 | Edgar E. Iglesias | s->desc.status |= SDESC_STATUS_COMPLETE; |
339 | 93f1e401 | Edgar E. Iglesias | stream_desc_store(s, s->regs[R_CURDESC]); |
340 | 93f1e401 | Edgar E. Iglesias | sof = 0;
|
341 | 93f1e401 | Edgar E. Iglesias | |
342 | 93f1e401 | Edgar E. Iglesias | /* Advance. */
|
343 | 93f1e401 | Edgar E. Iglesias | prev_d = s->regs[R_CURDESC]; |
344 | 93f1e401 | Edgar E. Iglesias | s->regs[R_CURDESC] = s->desc.nxtdesc; |
345 | 93f1e401 | Edgar E. Iglesias | if (prev_d == s->regs[R_TAILDESC]) {
|
346 | 93f1e401 | Edgar E. Iglesias | s->regs[R_DMASR] |= DMASR_IDLE; |
347 | 93f1e401 | Edgar E. Iglesias | break;
|
348 | 93f1e401 | Edgar E. Iglesias | } |
349 | 93f1e401 | Edgar E. Iglesias | } |
350 | 93f1e401 | Edgar E. Iglesias | } |
351 | 93f1e401 | Edgar E. Iglesias | |
352 | 93f1e401 | Edgar E. Iglesias | static
|
353 | 93f1e401 | Edgar E. Iglesias | void axidma_push(void *opaque, unsigned char *buf, size_t len, uint32_t *app) |
354 | 93f1e401 | Edgar E. Iglesias | { |
355 | 93f1e401 | Edgar E. Iglesias | struct XilinxAXIDMA *d = opaque;
|
356 | 93f1e401 | Edgar E. Iglesias | struct AXIStream *s = &d->streams[1]; |
357 | 93f1e401 | Edgar E. Iglesias | |
358 | 93f1e401 | Edgar E. Iglesias | if (!app) {
|
359 | 93f1e401 | Edgar E. Iglesias | hw_error("No stream app data!\n");
|
360 | 93f1e401 | Edgar E. Iglesias | } |
361 | 93f1e401 | Edgar E. Iglesias | stream_process_s2mem(s, buf, len, app); |
362 | 93f1e401 | Edgar E. Iglesias | stream_update_irq(s); |
363 | 93f1e401 | Edgar E. Iglesias | } |
364 | 93f1e401 | Edgar E. Iglesias | |
365 | 93f1e401 | Edgar E. Iglesias | static uint32_t axidma_readl(void *opaque, target_phys_addr_t addr) |
366 | 93f1e401 | Edgar E. Iglesias | { |
367 | 93f1e401 | Edgar E. Iglesias | struct XilinxAXIDMA *d = opaque;
|
368 | 93f1e401 | Edgar E. Iglesias | struct AXIStream *s;
|
369 | 93f1e401 | Edgar E. Iglesias | uint32_t r = 0;
|
370 | 93f1e401 | Edgar E. Iglesias | int sid;
|
371 | 93f1e401 | Edgar E. Iglesias | |
372 | 93f1e401 | Edgar E. Iglesias | sid = streamid_from_addr(addr); |
373 | 93f1e401 | Edgar E. Iglesias | s = &d->streams[sid]; |
374 | 93f1e401 | Edgar E. Iglesias | |
375 | 93f1e401 | Edgar E. Iglesias | addr = addr % 0x30;
|
376 | 93f1e401 | Edgar E. Iglesias | addr >>= 2;
|
377 | 93f1e401 | Edgar E. Iglesias | switch (addr) {
|
378 | 93f1e401 | Edgar E. Iglesias | case R_DMACR:
|
379 | 93f1e401 | Edgar E. Iglesias | /* Simulate one cycles reset delay. */
|
380 | 93f1e401 | Edgar E. Iglesias | s->regs[addr] &= ~DMACR_RESET; |
381 | 93f1e401 | Edgar E. Iglesias | r = s->regs[addr]; |
382 | 93f1e401 | Edgar E. Iglesias | break;
|
383 | 93f1e401 | Edgar E. Iglesias | case R_DMASR:
|
384 | 93f1e401 | Edgar E. Iglesias | s->regs[addr] &= 0xffff;
|
385 | 93f1e401 | Edgar E. Iglesias | s->regs[addr] |= (s->complete_cnt & 0xff) << 16; |
386 | 93f1e401 | Edgar E. Iglesias | s->regs[addr] |= (ptimer_get_count(s->ptimer) & 0xff) << 24; |
387 | 93f1e401 | Edgar E. Iglesias | r = s->regs[addr]; |
388 | 93f1e401 | Edgar E. Iglesias | break;
|
389 | 93f1e401 | Edgar E. Iglesias | default:
|
390 | 93f1e401 | Edgar E. Iglesias | r = s->regs[addr]; |
391 | 93f1e401 | Edgar E. Iglesias | D(qemu_log("%s ch=%d addr=" TARGET_FMT_plx " v=%x\n", |
392 | 93f1e401 | Edgar E. Iglesias | __func__, sid, addr * 4, r));
|
393 | 93f1e401 | Edgar E. Iglesias | break;
|
394 | 93f1e401 | Edgar E. Iglesias | } |
395 | 93f1e401 | Edgar E. Iglesias | return r;
|
396 | 93f1e401 | Edgar E. Iglesias | |
397 | 93f1e401 | Edgar E. Iglesias | } |
398 | 93f1e401 | Edgar E. Iglesias | |
399 | 93f1e401 | Edgar E. Iglesias | static void |
400 | 93f1e401 | Edgar E. Iglesias | axidma_writel(void *opaque, target_phys_addr_t addr, uint32_t value)
|
401 | 93f1e401 | Edgar E. Iglesias | { |
402 | 93f1e401 | Edgar E. Iglesias | struct XilinxAXIDMA *d = opaque;
|
403 | 93f1e401 | Edgar E. Iglesias | struct AXIStream *s;
|
404 | 93f1e401 | Edgar E. Iglesias | int sid;
|
405 | 93f1e401 | Edgar E. Iglesias | |
406 | 93f1e401 | Edgar E. Iglesias | sid = streamid_from_addr(addr); |
407 | 93f1e401 | Edgar E. Iglesias | s = &d->streams[sid]; |
408 | 93f1e401 | Edgar E. Iglesias | |
409 | 93f1e401 | Edgar E. Iglesias | addr = addr % 0x30;
|
410 | 93f1e401 | Edgar E. Iglesias | addr >>= 2;
|
411 | 93f1e401 | Edgar E. Iglesias | switch (addr) {
|
412 | 93f1e401 | Edgar E. Iglesias | case R_DMACR:
|
413 | 93f1e401 | Edgar E. Iglesias | /* Tailptr mode is always on. */
|
414 | 93f1e401 | Edgar E. Iglesias | value |= DMACR_TAILPTR_MODE; |
415 | 93f1e401 | Edgar E. Iglesias | /* Remember our previous reset state. */
|
416 | 93f1e401 | Edgar E. Iglesias | value |= (s->regs[addr] & DMACR_RESET); |
417 | 93f1e401 | Edgar E. Iglesias | s->regs[addr] = value; |
418 | 93f1e401 | Edgar E. Iglesias | |
419 | 93f1e401 | Edgar E. Iglesias | if (value & DMACR_RESET) {
|
420 | 93f1e401 | Edgar E. Iglesias | stream_reset(s); |
421 | 93f1e401 | Edgar E. Iglesias | } |
422 | 93f1e401 | Edgar E. Iglesias | |
423 | 93f1e401 | Edgar E. Iglesias | if ((value & 1) && !stream_resetting(s)) { |
424 | 93f1e401 | Edgar E. Iglesias | /* Start processing. */
|
425 | 93f1e401 | Edgar E. Iglesias | s->regs[R_DMASR] &= ~(DMASR_HALTED | DMASR_IDLE); |
426 | 93f1e401 | Edgar E. Iglesias | } |
427 | 93f1e401 | Edgar E. Iglesias | stream_reload_complete_cnt(s); |
428 | 93f1e401 | Edgar E. Iglesias | break;
|
429 | 93f1e401 | Edgar E. Iglesias | |
430 | 93f1e401 | Edgar E. Iglesias | case R_DMASR:
|
431 | 93f1e401 | Edgar E. Iglesias | /* Mask away write to clear irq lines. */
|
432 | 93f1e401 | Edgar E. Iglesias | value &= ~(value & DMASR_IRQ_MASK); |
433 | 93f1e401 | Edgar E. Iglesias | s->regs[addr] = value; |
434 | 93f1e401 | Edgar E. Iglesias | break;
|
435 | 93f1e401 | Edgar E. Iglesias | |
436 | 93f1e401 | Edgar E. Iglesias | case R_TAILDESC:
|
437 | 93f1e401 | Edgar E. Iglesias | s->regs[addr] = value; |
438 | 93f1e401 | Edgar E. Iglesias | s->regs[R_DMASR] &= ~DMASR_IDLE; /* Not idle. */
|
439 | 93f1e401 | Edgar E. Iglesias | if (!sid) {
|
440 | 93f1e401 | Edgar E. Iglesias | stream_process_mem2s(s, d->dmach); |
441 | 93f1e401 | Edgar E. Iglesias | } |
442 | 93f1e401 | Edgar E. Iglesias | break;
|
443 | 93f1e401 | Edgar E. Iglesias | default:
|
444 | 93f1e401 | Edgar E. Iglesias | D(qemu_log("%s: ch=%d addr=" TARGET_FMT_plx " v=%x\n", |
445 | 93f1e401 | Edgar E. Iglesias | __func__, sid, addr * 4, value));
|
446 | 93f1e401 | Edgar E. Iglesias | s->regs[addr] = value; |
447 | 93f1e401 | Edgar E. Iglesias | break;
|
448 | 93f1e401 | Edgar E. Iglesias | } |
449 | 93f1e401 | Edgar E. Iglesias | stream_update_irq(s); |
450 | 93f1e401 | Edgar E. Iglesias | } |
451 | 93f1e401 | Edgar E. Iglesias | |
452 | 93f1e401 | Edgar E. Iglesias | static CPUReadMemoryFunc * const axidma_read[] = { |
453 | 93f1e401 | Edgar E. Iglesias | &axidma_readl, |
454 | 93f1e401 | Edgar E. Iglesias | &axidma_readl, |
455 | 93f1e401 | Edgar E. Iglesias | &axidma_readl, |
456 | 93f1e401 | Edgar E. Iglesias | }; |
457 | 93f1e401 | Edgar E. Iglesias | |
458 | 93f1e401 | Edgar E. Iglesias | static CPUWriteMemoryFunc * const axidma_write[] = { |
459 | 93f1e401 | Edgar E. Iglesias | &axidma_writel, |
460 | 93f1e401 | Edgar E. Iglesias | &axidma_writel, |
461 | 93f1e401 | Edgar E. Iglesias | &axidma_writel, |
462 | 93f1e401 | Edgar E. Iglesias | }; |
463 | 93f1e401 | Edgar E. Iglesias | |
464 | 93f1e401 | Edgar E. Iglesias | static int xilinx_axidma_init(SysBusDevice *dev) |
465 | 93f1e401 | Edgar E. Iglesias | { |
466 | 93f1e401 | Edgar E. Iglesias | struct XilinxAXIDMA *s = FROM_SYSBUS(typeof(*s), dev);
|
467 | 93f1e401 | Edgar E. Iglesias | int axidma_regs;
|
468 | 93f1e401 | Edgar E. Iglesias | int i;
|
469 | 93f1e401 | Edgar E. Iglesias | |
470 | 93f1e401 | Edgar E. Iglesias | sysbus_init_irq(dev, &s->streams[1].irq);
|
471 | 93f1e401 | Edgar E. Iglesias | sysbus_init_irq(dev, &s->streams[0].irq);
|
472 | 93f1e401 | Edgar E. Iglesias | |
473 | 93f1e401 | Edgar E. Iglesias | if (!s->dmach) {
|
474 | 93f1e401 | Edgar E. Iglesias | hw_error("Unconnected DMA channel.\n");
|
475 | 93f1e401 | Edgar E. Iglesias | } |
476 | 93f1e401 | Edgar E. Iglesias | |
477 | 93f1e401 | Edgar E. Iglesias | xlx_dma_connect_dma(s->dmach, s, axidma_push); |
478 | 93f1e401 | Edgar E. Iglesias | |
479 | 93f1e401 | Edgar E. Iglesias | axidma_regs = cpu_register_io_memory(axidma_read, axidma_write, s, |
480 | 93f1e401 | Edgar E. Iglesias | DEVICE_NATIVE_ENDIAN); |
481 | 93f1e401 | Edgar E. Iglesias | sysbus_init_mmio(dev, R_MAX * 4 * 2, axidma_regs); |
482 | 93f1e401 | Edgar E. Iglesias | |
483 | 93f1e401 | Edgar E. Iglesias | for (i = 0; i < 2; i++) { |
484 | 93f1e401 | Edgar E. Iglesias | stream_reset(&s->streams[i]); |
485 | 93f1e401 | Edgar E. Iglesias | s->streams[i].nr = i; |
486 | 93f1e401 | Edgar E. Iglesias | s->streams[i].bh = qemu_bh_new(timer_hit, &s->streams[i]); |
487 | 93f1e401 | Edgar E. Iglesias | s->streams[i].ptimer = ptimer_init(s->streams[i].bh); |
488 | 93f1e401 | Edgar E. Iglesias | ptimer_set_freq(s->streams[i].ptimer, s->freqhz); |
489 | 93f1e401 | Edgar E. Iglesias | } |
490 | 93f1e401 | Edgar E. Iglesias | return 0; |
491 | 93f1e401 | Edgar E. Iglesias | } |
492 | 93f1e401 | Edgar E. Iglesias | |
493 | 93f1e401 | Edgar E. Iglesias | static SysBusDeviceInfo axidma_info = {
|
494 | 93f1e401 | Edgar E. Iglesias | .init = xilinx_axidma_init, |
495 | 93f1e401 | Edgar E. Iglesias | .qdev.name = "xilinx,axidma",
|
496 | 93f1e401 | Edgar E. Iglesias | .qdev.size = sizeof(struct XilinxAXIDMA), |
497 | 93f1e401 | Edgar E. Iglesias | .qdev.props = (Property[]) { |
498 | 93f1e401 | Edgar E. Iglesias | DEFINE_PROP_UINT32("freqhz", struct XilinxAXIDMA, freqhz, 50000000), |
499 | 93f1e401 | Edgar E. Iglesias | DEFINE_PROP_PTR("dmach", struct XilinxAXIDMA, dmach), |
500 | 93f1e401 | Edgar E. Iglesias | DEFINE_PROP_END_OF_LIST(), |
501 | 93f1e401 | Edgar E. Iglesias | } |
502 | 93f1e401 | Edgar E. Iglesias | }; |
503 | 93f1e401 | Edgar E. Iglesias | |
504 | 93f1e401 | Edgar E. Iglesias | static void xilinx_axidma_register(void) |
505 | 93f1e401 | Edgar E. Iglesias | { |
506 | 93f1e401 | Edgar E. Iglesias | sysbus_register_withprop(&axidma_info); |
507 | 93f1e401 | Edgar E. Iglesias | } |
508 | 93f1e401 | Edgar E. Iglesias | |
509 | 93f1e401 | Edgar E. Iglesias | device_init(xilinx_axidma_register) |