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