1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#include "qemu/osdep.h"
21#include "qemu-common.h"
22#include "qemu/timer.h"
23#include "hw/arm/soc_dma.h"
24
25static void transfer_mem2mem(struct soc_dma_ch_s *ch)
26{
27 memcpy(ch->paddr[0], ch->paddr[1], ch->bytes);
28 ch->paddr[0] += ch->bytes;
29 ch->paddr[1] += ch->bytes;
30}
31
32static void transfer_mem2fifo(struct soc_dma_ch_s *ch)
33{
34 ch->io_fn[1](ch->io_opaque[1], ch->paddr[0], ch->bytes);
35 ch->paddr[0] += ch->bytes;
36}
37
38static void transfer_fifo2mem(struct soc_dma_ch_s *ch)
39{
40 ch->io_fn[0](ch->io_opaque[0], ch->paddr[1], ch->bytes);
41 ch->paddr[1] += ch->bytes;
42}
43
44
45
46
47static void *fifo_buf;
48static int fifo_size;
49static void transfer_fifo2fifo(struct soc_dma_ch_s *ch)
50{
51 if (ch->bytes > fifo_size)
52 fifo_buf = g_realloc(fifo_buf, fifo_size = ch->bytes);
53
54
55 ch->io_fn[0](ch->io_opaque[0], fifo_buf, ch->bytes);
56 ch->io_fn[1](ch->io_opaque[1], fifo_buf, ch->bytes);
57}
58
59struct dma_s {
60 struct soc_dma_s soc;
61 int chnum;
62 uint64_t ch_enable_mask;
63 int64_t channel_freq;
64 int enabled_count;
65
66 struct memmap_entry_s {
67 enum soc_dma_port_type type;
68 hwaddr addr;
69 union {
70 struct {
71 void *opaque;
72 soc_dma_io_t fn;
73 int out;
74 } fifo;
75 struct {
76 void *base;
77 size_t size;
78 } mem;
79 } u;
80 } *memmap;
81 int memmap_size;
82
83 struct soc_dma_ch_s ch[0];
84};
85
86static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, int delay_bytes)
87{
88 int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
89 struct dma_s *dma = (struct dma_s *) ch->dma;
90
91 timer_mod(ch->timer, now + delay_bytes / dma->channel_freq);
92}
93
94static void soc_dma_ch_run(void *opaque)
95{
96 struct soc_dma_ch_s *ch = (struct soc_dma_ch_s *) opaque;
97
98 ch->running = 1;
99 ch->dma->setup_fn(ch);
100 ch->transfer_fn(ch);
101 ch->running = 0;
102
103 if (ch->enable)
104 soc_dma_ch_schedule(ch, ch->bytes);
105 ch->bytes = 0;
106}
107
108static inline struct memmap_entry_s *soc_dma_lookup(struct dma_s *dma,
109 hwaddr addr)
110{
111 struct memmap_entry_s *lo;
112 int hi;
113
114 lo = dma->memmap;
115 hi = dma->memmap_size;
116
117 while (hi > 1) {
118 hi /= 2;
119 if (lo[hi].addr <= addr)
120 lo += hi;
121 }
122
123 return lo;
124}
125
126static inline enum soc_dma_port_type soc_dma_ch_update_type(
127 struct soc_dma_ch_s *ch, int port)
128{
129 struct dma_s *dma = (struct dma_s *) ch->dma;
130 struct memmap_entry_s *entry = soc_dma_lookup(dma, ch->vaddr[port]);
131
132 if (entry->type == soc_dma_port_fifo) {
133 while (entry < dma->memmap + dma->memmap_size &&
134 entry->u.fifo.out != port)
135 entry ++;
136 if (entry->addr != ch->vaddr[port] || entry->u.fifo.out != port)
137 return soc_dma_port_other;
138
139 if (ch->type[port] != soc_dma_access_const)
140 return soc_dma_port_other;
141
142 ch->io_fn[port] = entry->u.fifo.fn;
143 ch->io_opaque[port] = entry->u.fifo.opaque;
144 return soc_dma_port_fifo;
145 } else if (entry->type == soc_dma_port_mem) {
146 if (entry->addr > ch->vaddr[port] ||
147 entry->addr + entry->u.mem.size <= ch->vaddr[port])
148 return soc_dma_port_other;
149
150
151
152 if (ch->type[port] != soc_dma_access_const)
153 return soc_dma_port_other;
154
155 ch->paddr[port] = (uint8_t *) entry->u.mem.base +
156 (ch->vaddr[port] - entry->addr);
157
158
159 return soc_dma_port_mem;
160 } else
161 return soc_dma_port_other;
162}
163
164void soc_dma_ch_update(struct soc_dma_ch_s *ch)
165{
166 enum soc_dma_port_type src, dst;
167
168 src = soc_dma_ch_update_type(ch, 0);
169 if (src == soc_dma_port_other) {
170 ch->update = 0;
171 ch->transfer_fn = ch->dma->transfer_fn;
172 return;
173 }
174 dst = soc_dma_ch_update_type(ch, 1);
175
176
177 if (src == soc_dma_port_mem && dst == soc_dma_port_mem)
178 ch->transfer_fn = transfer_mem2mem;
179 else if (src == soc_dma_port_mem && dst == soc_dma_port_fifo)
180 ch->transfer_fn = transfer_mem2fifo;
181 else if (src == soc_dma_port_fifo && dst == soc_dma_port_mem)
182 ch->transfer_fn = transfer_fifo2mem;
183 else if (src == soc_dma_port_fifo && dst == soc_dma_port_fifo)
184 ch->transfer_fn = transfer_fifo2fifo;
185 else
186 ch->transfer_fn = ch->dma->transfer_fn;
187
188 ch->update = (dst != soc_dma_port_other);
189}
190
191static void soc_dma_ch_freq_update(struct dma_s *s)
192{
193 if (s->enabled_count)
194
195 s->channel_freq = s->soc.freq / s->enabled_count;
196 else {
197
198
199
200
201
202 }
203}
204
205void soc_dma_set_request(struct soc_dma_ch_s *ch, int level)
206{
207 struct dma_s *dma = (struct dma_s *) ch->dma;
208
209 dma->enabled_count += level - ch->enable;
210
211 if (level)
212 dma->ch_enable_mask |= 1 << ch->num;
213 else
214 dma->ch_enable_mask &= ~(1 << ch->num);
215
216 if (level != ch->enable) {
217 soc_dma_ch_freq_update(dma);
218 ch->enable = level;
219
220 if (!ch->enable)
221 timer_del(ch->timer);
222 else if (!ch->running)
223 soc_dma_ch_run(ch);
224 else
225 soc_dma_ch_schedule(ch, 1);
226 }
227}
228
229void soc_dma_reset(struct soc_dma_s *soc)
230{
231 struct dma_s *s = (struct dma_s *) soc;
232
233 s->soc.drqbmp = 0;
234 s->ch_enable_mask = 0;
235 s->enabled_count = 0;
236 soc_dma_ch_freq_update(s);
237}
238
239
240struct soc_dma_s *soc_dma_init(int n)
241{
242 int i;
243 struct dma_s *s = g_malloc0(sizeof(*s) + n * sizeof(*s->ch));
244
245 s->chnum = n;
246 s->soc.ch = s->ch;
247 for (i = 0; i < n; i ++) {
248 s->ch[i].dma = &s->soc;
249 s->ch[i].num = i;
250 s->ch[i].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, soc_dma_ch_run, &s->ch[i]);
251 }
252
253 soc_dma_reset(&s->soc);
254 fifo_size = 0;
255
256 return &s->soc;
257}
258
259void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base,
260 soc_dma_io_t fn, void *opaque, int out)
261{
262 struct memmap_entry_s *entry;
263 struct dma_s *dma = (struct dma_s *) soc;
264
265 dma->memmap = g_realloc(dma->memmap, sizeof(*entry) *
266 (dma->memmap_size + 1));
267 entry = soc_dma_lookup(dma, virt_base);
268
269 if (dma->memmap_size) {
270 if (entry->type == soc_dma_port_mem) {
271 if (entry->addr <= virt_base &&
272 entry->addr + entry->u.mem.size > virt_base) {
273 fprintf(stderr, "%s: FIFO at %"PRIx64
274 " collides with RAM region at %"PRIx64
275 "-%"PRIx64 "\n", __func__,
276 virt_base, entry->addr,
277 (entry->addr + entry->u.mem.size));
278 exit(-1);
279 }
280
281 if (entry->addr <= virt_base)
282 entry ++;
283 } else
284 while (entry < dma->memmap + dma->memmap_size &&
285 entry->addr <= virt_base) {
286 if (entry->addr == virt_base && entry->u.fifo.out == out) {
287 fprintf(stderr, "%s: FIFO at %"PRIx64
288 " collides FIFO at %"PRIx64 "\n",
289 __func__, virt_base, entry->addr);
290 exit(-1);
291 }
292
293 entry ++;
294 }
295
296 memmove(entry + 1, entry,
297 (uint8_t *) (dma->memmap + dma->memmap_size ++) -
298 (uint8_t *) entry);
299 } else
300 dma->memmap_size ++;
301
302 entry->addr = virt_base;
303 entry->type = soc_dma_port_fifo;
304 entry->u.fifo.fn = fn;
305 entry->u.fifo.opaque = opaque;
306 entry->u.fifo.out = out;
307}
308
309void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base,
310 hwaddr virt_base, size_t size)
311{
312 struct memmap_entry_s *entry;
313 struct dma_s *dma = (struct dma_s *) soc;
314
315 dma->memmap = g_realloc(dma->memmap, sizeof(*entry) *
316 (dma->memmap_size + 1));
317 entry = soc_dma_lookup(dma, virt_base);
318
319 if (dma->memmap_size) {
320 if (entry->type == soc_dma_port_mem) {
321 if ((entry->addr >= virt_base && entry->addr < virt_base + size) ||
322 (entry->addr <= virt_base &&
323 entry->addr + entry->u.mem.size > virt_base)) {
324 fprintf(stderr, "%s: RAM at %"PRIx64 "-%"PRIx64
325 " collides with RAM region at %"PRIx64
326 "-%"PRIx64 "\n", __func__,
327 virt_base, virt_base + size,
328 entry->addr, entry->addr + entry->u.mem.size);
329 exit(-1);
330 }
331
332 if (entry->addr <= virt_base)
333 entry ++;
334 } else {
335 if (entry->addr >= virt_base &&
336 entry->addr < virt_base + size) {
337 fprintf(stderr, "%s: RAM at %"PRIx64 "-%"PRIx64
338 " collides with FIFO at %"PRIx64
339 "\n", __func__,
340 virt_base, virt_base + size,
341 entry->addr);
342 exit(-1);
343 }
344
345 while (entry < dma->memmap + dma->memmap_size &&
346 entry->addr <= virt_base)
347 entry ++;
348 }
349
350 memmove(entry + 1, entry,
351 (uint8_t *) (dma->memmap + dma->memmap_size ++) -
352 (uint8_t *) entry);
353 } else
354 dma->memmap_size ++;
355
356 entry->addr = virt_base;
357 entry->type = soc_dma_port_mem;
358 entry->u.mem.base = phys_base;
359 entry->u.mem.size = size;
360}
361
362
363