hw/misc/edu: Convert to realize()
[qemu/ar7.git] / hw / dma / soc_dma.c
blob94be376127020bfc243bfc9a4ff947ef1ac9b17f
1 /*
2 * On-chip DMA controller framework.
4 * Copyright (C) 2008 Nokia Corporation
5 * Written by Andrzej Zaborowski <andrew@openedhand.com>
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 or
10 * (at your option) version 3 of the License.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, see <http://www.gnu.org/licenses/>.
20 #include "qemu-common.h"
21 #include "qemu/timer.h"
22 #include "hw/arm/soc_dma.h"
24 static void transfer_mem2mem(struct soc_dma_ch_s *ch)
26 memcpy(ch->paddr[0], ch->paddr[1], ch->bytes);
27 ch->paddr[0] += ch->bytes;
28 ch->paddr[1] += ch->bytes;
31 static void transfer_mem2fifo(struct soc_dma_ch_s *ch)
33 ch->io_fn[1](ch->io_opaque[1], ch->paddr[0], ch->bytes);
34 ch->paddr[0] += ch->bytes;
37 static void transfer_fifo2mem(struct soc_dma_ch_s *ch)
39 ch->io_fn[0](ch->io_opaque[0], ch->paddr[1], ch->bytes);
40 ch->paddr[1] += ch->bytes;
43 /* This is further optimisable but isn't very important because often
44 * DMA peripherals forbid this kind of transfers and even when they don't,
45 * oprating systems may not need to use them. */
46 static void *fifo_buf;
47 static int fifo_size;
48 static void transfer_fifo2fifo(struct soc_dma_ch_s *ch)
50 if (ch->bytes > fifo_size)
51 fifo_buf = g_realloc(fifo_buf, fifo_size = ch->bytes);
53 /* Implement as transfer_fifo2linear + transfer_linear2fifo. */
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);
58 struct 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;
65 struct memmap_entry_s {
66 enum soc_dma_port_type type;
67 hwaddr 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;
82 struct soc_dma_ch_s ch[0];
85 static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, int delay_bytes)
87 int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
88 struct dma_s *dma = (struct dma_s *) ch->dma;
90 timer_mod(ch->timer, now + delay_bytes / dma->channel_freq);
93 static void soc_dma_ch_run(void *opaque)
95 struct soc_dma_ch_s *ch = (struct soc_dma_ch_s *) opaque;
97 ch->running = 1;
98 ch->dma->setup_fn(ch);
99 ch->transfer_fn(ch);
100 ch->running = 0;
102 if (ch->enable)
103 soc_dma_ch_schedule(ch, ch->bytes);
104 ch->bytes = 0;
107 static inline struct memmap_entry_s *soc_dma_lookup(struct dma_s *dma,
108 hwaddr addr)
110 struct memmap_entry_s *lo;
111 int hi;
113 lo = dma->memmap;
114 hi = dma->memmap_size;
116 while (hi > 1) {
117 hi /= 2;
118 if (lo[hi].addr <= addr)
119 lo += hi;
122 return lo;
125 static inline enum soc_dma_port_type soc_dma_ch_update_type(
126 struct soc_dma_ch_s *ch, int port)
128 struct dma_s *dma = (struct dma_s *) ch->dma;
129 struct memmap_entry_s *entry = soc_dma_lookup(dma, ch->vaddr[port]);
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;
138 if (ch->type[port] != soc_dma_access_const)
139 return soc_dma_port_other;
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;
149 /* TODO: support constant memory address for source port as used for
150 * drawing solid rectangles by PalmOS(R). */
151 if (ch->type[port] != soc_dma_access_const)
152 return soc_dma_port_other;
154 ch->paddr[port] = (uint8_t *) entry->u.mem.base +
155 (ch->vaddr[port] - entry->addr);
156 /* TODO: save bytes left to the end of the mapping somewhere so we
157 * can check we're not reading beyond it. */
158 return soc_dma_port_mem;
159 } else
160 return soc_dma_port_other;
163 void soc_dma_ch_update(struct soc_dma_ch_s *ch)
165 enum soc_dma_port_type src, dst;
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;
173 dst = soc_dma_ch_update_type(ch, 1);
175 /* TODO: use src and dst as array indices. */
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;
187 ch->update = (dst != soc_dma_port_other);
190 static void soc_dma_ch_freq_update(struct dma_s *s)
192 if (s->enabled_count)
193 /* We completely ignore channel priorities and stuff */
194 s->channel_freq = s->soc.freq / s->enabled_count;
195 else {
196 /* TODO: Signal that we want to disable the functional clock and let
197 * the platform code decide what to do with it, i.e. check that
198 * auto-idle is enabled in the clock controller and if we are stopping
199 * the clock, do the same with any parent clocks that had only one
200 * user keeping them on and auto-idle enabled. */
204 void soc_dma_set_request(struct soc_dma_ch_s *ch, int level)
206 struct dma_s *dma = (struct dma_s *) ch->dma;
208 dma->enabled_count += level - ch->enable;
210 if (level)
211 dma->ch_enable_mask |= 1 << ch->num;
212 else
213 dma->ch_enable_mask &= ~(1 << ch->num);
215 if (level != ch->enable) {
216 soc_dma_ch_freq_update(dma);
217 ch->enable = level;
219 if (!ch->enable)
220 timer_del(ch->timer);
221 else if (!ch->running)
222 soc_dma_ch_run(ch);
223 else
224 soc_dma_ch_schedule(ch, 1);
228 void soc_dma_reset(struct soc_dma_s *soc)
230 struct dma_s *s = (struct dma_s *) soc;
232 s->soc.drqbmp = 0;
233 s->ch_enable_mask = 0;
234 s->enabled_count = 0;
235 soc_dma_ch_freq_update(s);
238 /* TODO: take a functional-clock argument */
239 struct soc_dma_s *soc_dma_init(int n)
241 int i;
242 struct dma_s *s = g_malloc0(sizeof(*s) + n * sizeof(*s->ch));
244 s->chnum = n;
245 s->soc.ch = s->ch;
246 for (i = 0; i < n; i ++) {
247 s->ch[i].dma = &s->soc;
248 s->ch[i].num = i;
249 s->ch[i].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, soc_dma_ch_run, &s->ch[i]);
252 soc_dma_reset(&s->soc);
253 fifo_size = 0;
255 return &s->soc;
258 void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base,
259 soc_dma_io_t fn, void *opaque, int out)
261 struct memmap_entry_s *entry;
262 struct dma_s *dma = (struct dma_s *) soc;
264 dma->memmap = g_realloc(dma->memmap, sizeof(*entry) *
265 (dma->memmap_size + 1));
266 entry = soc_dma_lookup(dma, virt_base);
268 if (dma->memmap_size) {
269 if (entry->type == soc_dma_port_mem) {
270 if (entry->addr <= virt_base &&
271 entry->addr + entry->u.mem.size > virt_base) {
272 fprintf(stderr, "%s: FIFO at %"PRIx64
273 " collides with RAM region at %"PRIx64
274 "-%"PRIx64 "\n", __func__,
275 virt_base, entry->addr,
276 (entry->addr + entry->u.mem.size));
277 exit(-1);
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 %"PRIx64
287 " collides FIFO at %"PRIx64 "\n",
288 __func__, virt_base, entry->addr);
289 exit(-1);
292 entry ++;
295 memmove(entry + 1, entry,
296 (uint8_t *) (dma->memmap + dma->memmap_size ++) -
297 (uint8_t *) entry);
298 } else
299 dma->memmap_size ++;
301 entry->addr = virt_base;
302 entry->type = soc_dma_port_fifo;
303 entry->u.fifo.fn = fn;
304 entry->u.fifo.opaque = opaque;
305 entry->u.fifo.out = out;
308 void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base,
309 hwaddr virt_base, size_t size)
311 struct memmap_entry_s *entry;
312 struct dma_s *dma = (struct dma_s *) soc;
314 dma->memmap = g_realloc(dma->memmap, sizeof(*entry) *
315 (dma->memmap_size + 1));
316 entry = soc_dma_lookup(dma, virt_base);
318 if (dma->memmap_size) {
319 if (entry->type == soc_dma_port_mem) {
320 if ((entry->addr >= virt_base && entry->addr < virt_base + size) ||
321 (entry->addr <= virt_base &&
322 entry->addr + entry->u.mem.size > virt_base)) {
323 fprintf(stderr, "%s: RAM at %"PRIx64 "-%"PRIx64
324 " collides with RAM region at %"PRIx64
325 "-%"PRIx64 "\n", __func__,
326 virt_base, virt_base + size,
327 entry->addr, entry->addr + entry->u.mem.size);
328 exit(-1);
331 if (entry->addr <= virt_base)
332 entry ++;
333 } else {
334 if (entry->addr >= virt_base &&
335 entry->addr < virt_base + size) {
336 fprintf(stderr, "%s: RAM at %"PRIx64 "-%"PRIx64
337 " collides with FIFO at %"PRIx64
338 "\n", __func__,
339 virt_base, virt_base + size,
340 entry->addr);
341 exit(-1);
344 while (entry < dma->memmap + dma->memmap_size &&
345 entry->addr <= virt_base)
346 entry ++;
349 memmove(entry + 1, entry,
350 (uint8_t *) (dma->memmap + dma->memmap_size ++) -
351 (uint8_t *) entry);
352 } else
353 dma->memmap_size ++;
355 entry->addr = virt_base;
356 entry->type = soc_dma_port_mem;
357 entry->u.mem.base = phys_base;
358 entry->u.mem.size = size;
361 /* TODO: port removal for ports like PCMCIA memory */