diff options
Diffstat (limited to 'hw/soc_dma.c')
-rw-r--r-- | hw/soc_dma.c | 366 |
1 files changed, 0 insertions, 366 deletions
diff --git a/hw/soc_dma.c b/hw/soc_dma.c deleted file mode 100644 index db5d609388..0000000000 --- a/hw/soc_dma.c +++ /dev/null @@ -1,366 +0,0 @@ -/* - * On-chip DMA controller framework. - * - * Copyright (C) 2008 Nokia Corporation - * Written by Andrzej Zaborowski <andrew@openedhand.com> - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 or - * (at your option) version 3 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, see <http://www.gnu.org/licenses/>. - */ -#include "qemu-common.h" -#include "qemu/timer.h" -#include "hw/soc_dma.h" - -static void transfer_mem2mem(struct soc_dma_ch_s *ch) -{ - memcpy(ch->paddr[0], ch->paddr[1], ch->bytes); - ch->paddr[0] += ch->bytes; - ch->paddr[1] += ch->bytes; -} - -static void transfer_mem2fifo(struct soc_dma_ch_s *ch) -{ - ch->io_fn[1](ch->io_opaque[1], ch->paddr[0], ch->bytes); - ch->paddr[0] += ch->bytes; -} - -static void transfer_fifo2mem(struct soc_dma_ch_s *ch) -{ - ch->io_fn[0](ch->io_opaque[0], ch->paddr[1], ch->bytes); - ch->paddr[1] += ch->bytes; -} - -/* This is further optimisable but isn't very important because often - * DMA peripherals forbid this kind of transfers and even when they don't, - * oprating systems may not need to use them. */ -static void *fifo_buf; -static int fifo_size; -static void transfer_fifo2fifo(struct soc_dma_ch_s *ch) -{ - if (ch->bytes > fifo_size) - fifo_buf = g_realloc(fifo_buf, fifo_size = ch->bytes); - - /* Implement as transfer_fifo2linear + transfer_linear2fifo. */ - ch->io_fn[0](ch->io_opaque[0], fifo_buf, ch->bytes); - ch->io_fn[1](ch->io_opaque[1], fifo_buf, ch->bytes); -} - -struct dma_s { - struct soc_dma_s soc; - int chnum; - uint64_t ch_enable_mask; - int64_t channel_freq; - int enabled_count; - - struct memmap_entry_s { - enum soc_dma_port_type type; - hwaddr addr; - union { - struct { - void *opaque; - soc_dma_io_t fn; - int out; - } fifo; - struct { - void *base; - size_t size; - } mem; - } u; - } *memmap; - int memmap_size; - - struct soc_dma_ch_s ch[0]; -}; - -static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, int delay_bytes) -{ - int64_t now = qemu_get_clock_ns(vm_clock); - struct dma_s *dma = (struct dma_s *) ch->dma; - - qemu_mod_timer(ch->timer, now + delay_bytes / dma->channel_freq); -} - -static void soc_dma_ch_run(void *opaque) -{ - struct soc_dma_ch_s *ch = (struct soc_dma_ch_s *) opaque; - - ch->running = 1; - ch->dma->setup_fn(ch); - ch->transfer_fn(ch); - ch->running = 0; - - if (ch->enable) - soc_dma_ch_schedule(ch, ch->bytes); - ch->bytes = 0; -} - -static inline struct memmap_entry_s *soc_dma_lookup(struct dma_s *dma, - hwaddr addr) -{ - struct memmap_entry_s *lo; - int hi; - - lo = dma->memmap; - hi = dma->memmap_size; - - while (hi > 1) { - hi /= 2; - if (lo[hi].addr <= addr) - lo += hi; - } - - return lo; -} - -static inline enum soc_dma_port_type soc_dma_ch_update_type( - struct soc_dma_ch_s *ch, int port) -{ - struct dma_s *dma = (struct dma_s *) ch->dma; - struct memmap_entry_s *entry = soc_dma_lookup(dma, ch->vaddr[port]); - - if (entry->type == soc_dma_port_fifo) { - while (entry < dma->memmap + dma->memmap_size && - entry->u.fifo.out != port) - entry ++; - if (entry->addr != ch->vaddr[port] || entry->u.fifo.out != port) - return soc_dma_port_other; - - if (ch->type[port] != soc_dma_access_const) - return soc_dma_port_other; - - ch->io_fn[port] = entry->u.fifo.fn; - ch->io_opaque[port] = entry->u.fifo.opaque; - return soc_dma_port_fifo; - } else if (entry->type == soc_dma_port_mem) { - if (entry->addr > ch->vaddr[port] || - entry->addr + entry->u.mem.size <= ch->vaddr[port]) - return soc_dma_port_other; - - /* TODO: support constant memory address for source port as used for - * drawing solid rectangles by PalmOS(R). */ - if (ch->type[port] != soc_dma_access_const) - return soc_dma_port_other; - - ch->paddr[port] = (uint8_t *) entry->u.mem.base + - (ch->vaddr[port] - entry->addr); - /* TODO: save bytes left to the end of the mapping somewhere so we - * can check we're not reading beyond it. */ - return soc_dma_port_mem; - } else - return soc_dma_port_other; -} - -void soc_dma_ch_update(struct soc_dma_ch_s *ch) -{ - enum soc_dma_port_type src, dst; - - src = soc_dma_ch_update_type(ch, 0); - if (src == soc_dma_port_other) { - ch->update = 0; - ch->transfer_fn = ch->dma->transfer_fn; - return; - } - dst = soc_dma_ch_update_type(ch, 1); - - /* TODO: use src and dst as array indices. */ - if (src == soc_dma_port_mem && dst == soc_dma_port_mem) - ch->transfer_fn = transfer_mem2mem; - else if (src == soc_dma_port_mem && dst == soc_dma_port_fifo) - ch->transfer_fn = transfer_mem2fifo; - else if (src == soc_dma_port_fifo && dst == soc_dma_port_mem) - ch->transfer_fn = transfer_fifo2mem; - else if (src == soc_dma_port_fifo && dst == soc_dma_port_fifo) - ch->transfer_fn = transfer_fifo2fifo; - else - ch->transfer_fn = ch->dma->transfer_fn; - - ch->update = (dst != soc_dma_port_other); -} - -static void soc_dma_ch_freq_update(struct dma_s *s) -{ - if (s->enabled_count) - /* We completely ignore channel priorities and stuff */ - s->channel_freq = s->soc.freq / s->enabled_count; - else { - /* TODO: Signal that we want to disable the functional clock and let - * the platform code decide what to do with it, i.e. check that - * auto-idle is enabled in the clock controller and if we are stopping - * the clock, do the same with any parent clocks that had only one - * user keeping them on and auto-idle enabled. */ - } -} - -void soc_dma_set_request(struct soc_dma_ch_s *ch, int level) -{ - struct dma_s *dma = (struct dma_s *) ch->dma; - - dma->enabled_count += level - ch->enable; - - if (level) - dma->ch_enable_mask |= 1 << ch->num; - else - dma->ch_enable_mask &= ~(1 << ch->num); - - if (level != ch->enable) { - soc_dma_ch_freq_update(dma); - ch->enable = level; - - if (!ch->enable) - qemu_del_timer(ch->timer); - else if (!ch->running) - soc_dma_ch_run(ch); - else - soc_dma_ch_schedule(ch, 1); - } -} - -void soc_dma_reset(struct soc_dma_s *soc) -{ - struct dma_s *s = (struct dma_s *) soc; - - s->soc.drqbmp = 0; - s->ch_enable_mask = 0; - s->enabled_count = 0; - soc_dma_ch_freq_update(s); -} - -/* TODO: take a functional-clock argument */ -struct soc_dma_s *soc_dma_init(int n) -{ - int i; - struct dma_s *s = g_malloc0(sizeof(*s) + n * sizeof(*s->ch)); - - s->chnum = n; - s->soc.ch = s->ch; - for (i = 0; i < n; i ++) { - s->ch[i].dma = &s->soc; - s->ch[i].num = i; - s->ch[i].timer = qemu_new_timer_ns(vm_clock, soc_dma_ch_run, &s->ch[i]); - } - - soc_dma_reset(&s->soc); - fifo_size = 0; - - return &s->soc; -} - -void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base, - soc_dma_io_t fn, void *opaque, int out) -{ - struct memmap_entry_s *entry; - struct dma_s *dma = (struct dma_s *) soc; - - dma->memmap = g_realloc(dma->memmap, sizeof(*entry) * - (dma->memmap_size + 1)); - entry = soc_dma_lookup(dma, virt_base); - - if (dma->memmap_size) { - if (entry->type == soc_dma_port_mem) { - if (entry->addr <= virt_base && - entry->addr + entry->u.mem.size > virt_base) { - fprintf(stderr, "%s: FIFO at " TARGET_FMT_lx - " collides with RAM region at " TARGET_FMT_lx - "-" TARGET_FMT_lx "\n", __FUNCTION__, - (target_ulong) virt_base, - (target_ulong) entry->addr, (target_ulong) - (entry->addr + entry->u.mem.size)); - exit(-1); - } - - if (entry->addr <= virt_base) - entry ++; - } else - while (entry < dma->memmap + dma->memmap_size && - entry->addr <= virt_base) { - if (entry->addr == virt_base && entry->u.fifo.out == out) { - fprintf(stderr, "%s: FIFO at " TARGET_FMT_lx - " collides FIFO at " TARGET_FMT_lx "\n", - __FUNCTION__, (target_ulong) virt_base, - (target_ulong) entry->addr); - exit(-1); - } - - entry ++; - } - - memmove(entry + 1, entry, - (uint8_t *) (dma->memmap + dma->memmap_size ++) - - (uint8_t *) entry); - } else - dma->memmap_size ++; - - entry->addr = virt_base; - entry->type = soc_dma_port_fifo; - entry->u.fifo.fn = fn; - entry->u.fifo.opaque = opaque; - entry->u.fifo.out = out; -} - -void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base, - hwaddr virt_base, size_t size) -{ - struct memmap_entry_s *entry; - struct dma_s *dma = (struct dma_s *) soc; - - dma->memmap = g_realloc(dma->memmap, sizeof(*entry) * - (dma->memmap_size + 1)); - entry = soc_dma_lookup(dma, virt_base); - - if (dma->memmap_size) { - if (entry->type == soc_dma_port_mem) { - if ((entry->addr >= virt_base && entry->addr < virt_base + size) || - (entry->addr <= virt_base && - entry->addr + entry->u.mem.size > virt_base)) { - fprintf(stderr, "%s: RAM at " TARGET_FMT_lx "-" TARGET_FMT_lx - " collides with RAM region at " TARGET_FMT_lx - "-" TARGET_FMT_lx "\n", __FUNCTION__, - (target_ulong) virt_base, - (target_ulong) (virt_base + size), - (target_ulong) entry->addr, (target_ulong) - (entry->addr + entry->u.mem.size)); - exit(-1); - } - - if (entry->addr <= virt_base) - entry ++; - } else { - if (entry->addr >= virt_base && - entry->addr < virt_base + size) { - fprintf(stderr, "%s: RAM at " TARGET_FMT_lx "-" TARGET_FMT_lx - " collides with FIFO at " TARGET_FMT_lx - "\n", __FUNCTION__, - (target_ulong) virt_base, - (target_ulong) (virt_base + size), - (target_ulong) entry->addr); - exit(-1); - } - - while (entry < dma->memmap + dma->memmap_size && - entry->addr <= virt_base) - entry ++; - } - - memmove(entry + 1, entry, - (uint8_t *) (dma->memmap + dma->memmap_size ++) - - (uint8_t *) entry); - } else - dma->memmap_size ++; - - entry->addr = virt_base; - entry->type = soc_dma_port_mem; - entry->u.mem.base = phys_base; - entry->u.mem.size = size; -} - -/* TODO: port removal for ports like PCMCIA memory */ |