1d2c0bd84SPaolo Bonzini /*
2d2c0bd84SPaolo Bonzini * On-chip DMA controller framework.
3d2c0bd84SPaolo Bonzini *
4d2c0bd84SPaolo Bonzini * Copyright (C) 2008 Nokia Corporation
5d2c0bd84SPaolo Bonzini * Written by Andrzej Zaborowski <andrew@openedhand.com>
6d2c0bd84SPaolo Bonzini *
7d2c0bd84SPaolo Bonzini * This program is free software; you can redistribute it and/or
8d2c0bd84SPaolo Bonzini * modify it under the terms of the GNU General Public License as
9d2c0bd84SPaolo Bonzini * published by the Free Software Foundation; either version 2 or
10d2c0bd84SPaolo Bonzini * (at your option) version 3 of the License.
11d2c0bd84SPaolo Bonzini *
12d2c0bd84SPaolo Bonzini * This program is distributed in the hope that it will be useful,
13d2c0bd84SPaolo Bonzini * but WITHOUT ANY WARRANTY; without even the implied warranty of
14d2c0bd84SPaolo Bonzini * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15d2c0bd84SPaolo Bonzini * GNU General Public License for more details.
16d2c0bd84SPaolo Bonzini *
17d2c0bd84SPaolo Bonzini * You should have received a copy of the GNU General Public License along
18d2c0bd84SPaolo Bonzini * with this program; if not, see <http://www.gnu.org/licenses/>.
19d2c0bd84SPaolo Bonzini */
200430891cSPeter Maydell #include "qemu/osdep.h"
217f69a433SAlistair Francis #include "qemu/error-report.h"
22d2c0bd84SPaolo Bonzini #include "qemu/timer.h"
23d2c0bd84SPaolo Bonzini #include "hw/arm/soc_dma.h"
24d2c0bd84SPaolo Bonzini
transfer_mem2mem(struct soc_dma_ch_s * ch)25d2c0bd84SPaolo Bonzini static void transfer_mem2mem(struct soc_dma_ch_s *ch)
26d2c0bd84SPaolo Bonzini {
27d2c0bd84SPaolo Bonzini memcpy(ch->paddr[0], ch->paddr[1], ch->bytes);
28d2c0bd84SPaolo Bonzini ch->paddr[0] += ch->bytes;
29d2c0bd84SPaolo Bonzini ch->paddr[1] += ch->bytes;
30d2c0bd84SPaolo Bonzini }
31d2c0bd84SPaolo Bonzini
transfer_mem2fifo(struct soc_dma_ch_s * ch)32d2c0bd84SPaolo Bonzini static void transfer_mem2fifo(struct soc_dma_ch_s *ch)
33d2c0bd84SPaolo Bonzini {
34d2c0bd84SPaolo Bonzini ch->io_fn[1](ch->io_opaque[1], ch->paddr[0], ch->bytes);
35d2c0bd84SPaolo Bonzini ch->paddr[0] += ch->bytes;
36d2c0bd84SPaolo Bonzini }
37d2c0bd84SPaolo Bonzini
transfer_fifo2mem(struct soc_dma_ch_s * ch)38d2c0bd84SPaolo Bonzini static void transfer_fifo2mem(struct soc_dma_ch_s *ch)
39d2c0bd84SPaolo Bonzini {
40d2c0bd84SPaolo Bonzini ch->io_fn[0](ch->io_opaque[0], ch->paddr[1], ch->bytes);
41d2c0bd84SPaolo Bonzini ch->paddr[1] += ch->bytes;
42d2c0bd84SPaolo Bonzini }
43d2c0bd84SPaolo Bonzini
44d2c0bd84SPaolo Bonzini /* This is further optimisable but isn't very important because often
45d2c0bd84SPaolo Bonzini * DMA peripherals forbid this kind of transfers and even when they don't,
46d2c0bd84SPaolo Bonzini * oprating systems may not need to use them. */
47d2c0bd84SPaolo Bonzini static void *fifo_buf;
48d2c0bd84SPaolo Bonzini static int fifo_size;
transfer_fifo2fifo(struct soc_dma_ch_s * ch)49d2c0bd84SPaolo Bonzini static void transfer_fifo2fifo(struct soc_dma_ch_s *ch)
50d2c0bd84SPaolo Bonzini {
51d2c0bd84SPaolo Bonzini if (ch->bytes > fifo_size)
52d2c0bd84SPaolo Bonzini fifo_buf = g_realloc(fifo_buf, fifo_size = ch->bytes);
53d2c0bd84SPaolo Bonzini
54d2c0bd84SPaolo Bonzini /* Implement as transfer_fifo2linear + transfer_linear2fifo. */
55d2c0bd84SPaolo Bonzini ch->io_fn[0](ch->io_opaque[0], fifo_buf, ch->bytes);
56d2c0bd84SPaolo Bonzini ch->io_fn[1](ch->io_opaque[1], fifo_buf, ch->bytes);
57d2c0bd84SPaolo Bonzini }
58d2c0bd84SPaolo Bonzini
59d2c0bd84SPaolo Bonzini struct dma_s {
60d2c0bd84SPaolo Bonzini struct soc_dma_s soc;
61d2c0bd84SPaolo Bonzini int chnum;
62d2c0bd84SPaolo Bonzini uint64_t ch_enable_mask;
63d2c0bd84SPaolo Bonzini int64_t channel_freq;
64d2c0bd84SPaolo Bonzini int enabled_count;
65d2c0bd84SPaolo Bonzini
66d2c0bd84SPaolo Bonzini struct memmap_entry_s {
67d2c0bd84SPaolo Bonzini enum soc_dma_port_type type;
68d2c0bd84SPaolo Bonzini hwaddr addr;
69d2c0bd84SPaolo Bonzini union {
70d2c0bd84SPaolo Bonzini struct {
71d2c0bd84SPaolo Bonzini void *opaque;
72d2c0bd84SPaolo Bonzini soc_dma_io_t fn;
73d2c0bd84SPaolo Bonzini int out;
74d2c0bd84SPaolo Bonzini } fifo;
75d2c0bd84SPaolo Bonzini struct {
76d2c0bd84SPaolo Bonzini void *base;
77d2c0bd84SPaolo Bonzini size_t size;
78d2c0bd84SPaolo Bonzini } mem;
79d2c0bd84SPaolo Bonzini } u;
80d2c0bd84SPaolo Bonzini } *memmap;
81d2c0bd84SPaolo Bonzini int memmap_size;
82d2c0bd84SPaolo Bonzini
83f7795e40SPhilippe Mathieu-Daudé struct soc_dma_ch_s ch[];
84d2c0bd84SPaolo Bonzini };
85d2c0bd84SPaolo Bonzini
soc_dma_ch_schedule(struct soc_dma_ch_s * ch,int delay_bytes)86d2c0bd84SPaolo Bonzini static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, int delay_bytes)
87d2c0bd84SPaolo Bonzini {
88bc72ad67SAlex Bligh int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
89d2c0bd84SPaolo Bonzini struct dma_s *dma = (struct dma_s *) ch->dma;
90d2c0bd84SPaolo Bonzini
91bc72ad67SAlex Bligh timer_mod(ch->timer, now + delay_bytes / dma->channel_freq);
92d2c0bd84SPaolo Bonzini }
93d2c0bd84SPaolo Bonzini
soc_dma_ch_run(void * opaque)94d2c0bd84SPaolo Bonzini static void soc_dma_ch_run(void *opaque)
95d2c0bd84SPaolo Bonzini {
96d2c0bd84SPaolo Bonzini struct soc_dma_ch_s *ch = (struct soc_dma_ch_s *) opaque;
97d2c0bd84SPaolo Bonzini
98d2c0bd84SPaolo Bonzini ch->running = 1;
99d2c0bd84SPaolo Bonzini ch->dma->setup_fn(ch);
100d2c0bd84SPaolo Bonzini ch->transfer_fn(ch);
101d2c0bd84SPaolo Bonzini ch->running = 0;
102d2c0bd84SPaolo Bonzini
103d2c0bd84SPaolo Bonzini if (ch->enable)
104d2c0bd84SPaolo Bonzini soc_dma_ch_schedule(ch, ch->bytes);
105d2c0bd84SPaolo Bonzini ch->bytes = 0;
106d2c0bd84SPaolo Bonzini }
107d2c0bd84SPaolo Bonzini
soc_dma_lookup(struct dma_s * dma,hwaddr addr)108d2c0bd84SPaolo Bonzini static inline struct memmap_entry_s *soc_dma_lookup(struct dma_s *dma,
109d2c0bd84SPaolo Bonzini hwaddr addr)
110d2c0bd84SPaolo Bonzini {
111d2c0bd84SPaolo Bonzini struct memmap_entry_s *lo;
112d2c0bd84SPaolo Bonzini int hi;
113d2c0bd84SPaolo Bonzini
114d2c0bd84SPaolo Bonzini lo = dma->memmap;
115d2c0bd84SPaolo Bonzini hi = dma->memmap_size;
116d2c0bd84SPaolo Bonzini
117d2c0bd84SPaolo Bonzini while (hi > 1) {
118d2c0bd84SPaolo Bonzini hi /= 2;
119d2c0bd84SPaolo Bonzini if (lo[hi].addr <= addr)
120d2c0bd84SPaolo Bonzini lo += hi;
121d2c0bd84SPaolo Bonzini }
122d2c0bd84SPaolo Bonzini
123d2c0bd84SPaolo Bonzini return lo;
124d2c0bd84SPaolo Bonzini }
125d2c0bd84SPaolo Bonzini
soc_dma_ch_update_type(struct soc_dma_ch_s * ch,int port)126d2c0bd84SPaolo Bonzini static inline enum soc_dma_port_type soc_dma_ch_update_type(
127d2c0bd84SPaolo Bonzini struct soc_dma_ch_s *ch, int port)
128d2c0bd84SPaolo Bonzini {
129d2c0bd84SPaolo Bonzini struct dma_s *dma = (struct dma_s *) ch->dma;
130d2c0bd84SPaolo Bonzini struct memmap_entry_s *entry = soc_dma_lookup(dma, ch->vaddr[port]);
131d2c0bd84SPaolo Bonzini
132d2c0bd84SPaolo Bonzini if (entry->type == soc_dma_port_fifo) {
133d2c0bd84SPaolo Bonzini while (entry < dma->memmap + dma->memmap_size &&
134d2c0bd84SPaolo Bonzini entry->u.fifo.out != port)
135d2c0bd84SPaolo Bonzini entry ++;
136d2c0bd84SPaolo Bonzini if (entry->addr != ch->vaddr[port] || entry->u.fifo.out != port)
137d2c0bd84SPaolo Bonzini return soc_dma_port_other;
138d2c0bd84SPaolo Bonzini
139d2c0bd84SPaolo Bonzini if (ch->type[port] != soc_dma_access_const)
140d2c0bd84SPaolo Bonzini return soc_dma_port_other;
141d2c0bd84SPaolo Bonzini
142d2c0bd84SPaolo Bonzini ch->io_fn[port] = entry->u.fifo.fn;
143d2c0bd84SPaolo Bonzini ch->io_opaque[port] = entry->u.fifo.opaque;
144d2c0bd84SPaolo Bonzini return soc_dma_port_fifo;
145d2c0bd84SPaolo Bonzini } else if (entry->type == soc_dma_port_mem) {
146d2c0bd84SPaolo Bonzini if (entry->addr > ch->vaddr[port] ||
147d2c0bd84SPaolo Bonzini entry->addr + entry->u.mem.size <= ch->vaddr[port])
148d2c0bd84SPaolo Bonzini return soc_dma_port_other;
149d2c0bd84SPaolo Bonzini
150d2c0bd84SPaolo Bonzini /* TODO: support constant memory address for source port as used for
151d2c0bd84SPaolo Bonzini * drawing solid rectangles by PalmOS(R). */
152d2c0bd84SPaolo Bonzini if (ch->type[port] != soc_dma_access_const)
153d2c0bd84SPaolo Bonzini return soc_dma_port_other;
154d2c0bd84SPaolo Bonzini
155d2c0bd84SPaolo Bonzini ch->paddr[port] = (uint8_t *) entry->u.mem.base +
156d2c0bd84SPaolo Bonzini (ch->vaddr[port] - entry->addr);
157d2c0bd84SPaolo Bonzini /* TODO: save bytes left to the end of the mapping somewhere so we
158d2c0bd84SPaolo Bonzini * can check we're not reading beyond it. */
159d2c0bd84SPaolo Bonzini return soc_dma_port_mem;
160d2c0bd84SPaolo Bonzini } else
161d2c0bd84SPaolo Bonzini return soc_dma_port_other;
162d2c0bd84SPaolo Bonzini }
163d2c0bd84SPaolo Bonzini
soc_dma_ch_update(struct soc_dma_ch_s * ch)164d2c0bd84SPaolo Bonzini void soc_dma_ch_update(struct soc_dma_ch_s *ch)
165d2c0bd84SPaolo Bonzini {
166d2c0bd84SPaolo Bonzini enum soc_dma_port_type src, dst;
167d2c0bd84SPaolo Bonzini
168d2c0bd84SPaolo Bonzini src = soc_dma_ch_update_type(ch, 0);
169d2c0bd84SPaolo Bonzini if (src == soc_dma_port_other) {
170d2c0bd84SPaolo Bonzini ch->update = 0;
171d2c0bd84SPaolo Bonzini ch->transfer_fn = ch->dma->transfer_fn;
172d2c0bd84SPaolo Bonzini return;
173d2c0bd84SPaolo Bonzini }
174d2c0bd84SPaolo Bonzini dst = soc_dma_ch_update_type(ch, 1);
175d2c0bd84SPaolo Bonzini
176d2c0bd84SPaolo Bonzini /* TODO: use src and dst as array indices. */
177d2c0bd84SPaolo Bonzini if (src == soc_dma_port_mem && dst == soc_dma_port_mem)
178d2c0bd84SPaolo Bonzini ch->transfer_fn = transfer_mem2mem;
179d2c0bd84SPaolo Bonzini else if (src == soc_dma_port_mem && dst == soc_dma_port_fifo)
180d2c0bd84SPaolo Bonzini ch->transfer_fn = transfer_mem2fifo;
181d2c0bd84SPaolo Bonzini else if (src == soc_dma_port_fifo && dst == soc_dma_port_mem)
182d2c0bd84SPaolo Bonzini ch->transfer_fn = transfer_fifo2mem;
183d2c0bd84SPaolo Bonzini else if (src == soc_dma_port_fifo && dst == soc_dma_port_fifo)
184d2c0bd84SPaolo Bonzini ch->transfer_fn = transfer_fifo2fifo;
185d2c0bd84SPaolo Bonzini else
186d2c0bd84SPaolo Bonzini ch->transfer_fn = ch->dma->transfer_fn;
187d2c0bd84SPaolo Bonzini
188d2c0bd84SPaolo Bonzini ch->update = (dst != soc_dma_port_other);
189d2c0bd84SPaolo Bonzini }
190d2c0bd84SPaolo Bonzini
soc_dma_ch_freq_update(struct dma_s * s)191d2c0bd84SPaolo Bonzini static void soc_dma_ch_freq_update(struct dma_s *s)
192d2c0bd84SPaolo Bonzini {
193d2c0bd84SPaolo Bonzini if (s->enabled_count)
194d2c0bd84SPaolo Bonzini /* We completely ignore channel priorities and stuff */
195d2c0bd84SPaolo Bonzini s->channel_freq = s->soc.freq / s->enabled_count;
196d2c0bd84SPaolo Bonzini else {
197d2c0bd84SPaolo Bonzini /* TODO: Signal that we want to disable the functional clock and let
198d2c0bd84SPaolo Bonzini * the platform code decide what to do with it, i.e. check that
199d2c0bd84SPaolo Bonzini * auto-idle is enabled in the clock controller and if we are stopping
200d2c0bd84SPaolo Bonzini * the clock, do the same with any parent clocks that had only one
201d2c0bd84SPaolo Bonzini * user keeping them on and auto-idle enabled. */
202d2c0bd84SPaolo Bonzini }
203d2c0bd84SPaolo Bonzini }
204d2c0bd84SPaolo Bonzini
soc_dma_set_request(struct soc_dma_ch_s * ch,int level)205d2c0bd84SPaolo Bonzini void soc_dma_set_request(struct soc_dma_ch_s *ch, int level)
206d2c0bd84SPaolo Bonzini {
207d2c0bd84SPaolo Bonzini struct dma_s *dma = (struct dma_s *) ch->dma;
208d2c0bd84SPaolo Bonzini
209d2c0bd84SPaolo Bonzini dma->enabled_count += level - ch->enable;
210d2c0bd84SPaolo Bonzini
211d2c0bd84SPaolo Bonzini if (level)
212*c3a68dfdSAnastasia Belova dma->ch_enable_mask |= (uint64_t)1 << ch->num;
213d2c0bd84SPaolo Bonzini else
214*c3a68dfdSAnastasia Belova dma->ch_enable_mask &= ~((uint64_t)1 << ch->num);
215d2c0bd84SPaolo Bonzini
216d2c0bd84SPaolo Bonzini if (level != ch->enable) {
217d2c0bd84SPaolo Bonzini soc_dma_ch_freq_update(dma);
218d2c0bd84SPaolo Bonzini ch->enable = level;
219d2c0bd84SPaolo Bonzini
220d2c0bd84SPaolo Bonzini if (!ch->enable)
221bc72ad67SAlex Bligh timer_del(ch->timer);
222d2c0bd84SPaolo Bonzini else if (!ch->running)
223d2c0bd84SPaolo Bonzini soc_dma_ch_run(ch);
224d2c0bd84SPaolo Bonzini else
225d2c0bd84SPaolo Bonzini soc_dma_ch_schedule(ch, 1);
226d2c0bd84SPaolo Bonzini }
227d2c0bd84SPaolo Bonzini }
228d2c0bd84SPaolo Bonzini
soc_dma_reset(struct soc_dma_s * soc)229d2c0bd84SPaolo Bonzini void soc_dma_reset(struct soc_dma_s *soc)
230d2c0bd84SPaolo Bonzini {
231d2c0bd84SPaolo Bonzini struct dma_s *s = (struct dma_s *) soc;
232d2c0bd84SPaolo Bonzini
233d2c0bd84SPaolo Bonzini s->soc.drqbmp = 0;
234d2c0bd84SPaolo Bonzini s->ch_enable_mask = 0;
235d2c0bd84SPaolo Bonzini s->enabled_count = 0;
236d2c0bd84SPaolo Bonzini soc_dma_ch_freq_update(s);
237d2c0bd84SPaolo Bonzini }
238d2c0bd84SPaolo Bonzini
239d2c0bd84SPaolo Bonzini /* TODO: take a functional-clock argument */
soc_dma_init(int n)240d2c0bd84SPaolo Bonzini struct soc_dma_s *soc_dma_init(int n)
241d2c0bd84SPaolo Bonzini {
242d2c0bd84SPaolo Bonzini int i;
243d2c0bd84SPaolo Bonzini struct dma_s *s = g_malloc0(sizeof(*s) + n * sizeof(*s->ch));
244d2c0bd84SPaolo Bonzini
245d2c0bd84SPaolo Bonzini s->chnum = n;
246d2c0bd84SPaolo Bonzini s->soc.ch = s->ch;
247d2c0bd84SPaolo Bonzini for (i = 0; i < n; i ++) {
248d2c0bd84SPaolo Bonzini s->ch[i].dma = &s->soc;
249d2c0bd84SPaolo Bonzini s->ch[i].num = i;
250bc72ad67SAlex Bligh s->ch[i].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, soc_dma_ch_run, &s->ch[i]);
251d2c0bd84SPaolo Bonzini }
252d2c0bd84SPaolo Bonzini
253d2c0bd84SPaolo Bonzini soc_dma_reset(&s->soc);
254d2c0bd84SPaolo Bonzini fifo_size = 0;
255d2c0bd84SPaolo Bonzini
256d2c0bd84SPaolo Bonzini return &s->soc;
257d2c0bd84SPaolo Bonzini }
258d2c0bd84SPaolo Bonzini
soc_dma_port_add_fifo(struct soc_dma_s * soc,hwaddr virt_base,soc_dma_io_t fn,void * opaque,int out)259d2c0bd84SPaolo Bonzini void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base,
260d2c0bd84SPaolo Bonzini soc_dma_io_t fn, void *opaque, int out)
261d2c0bd84SPaolo Bonzini {
262d2c0bd84SPaolo Bonzini struct memmap_entry_s *entry;
263d2c0bd84SPaolo Bonzini struct dma_s *dma = (struct dma_s *) soc;
264d2c0bd84SPaolo Bonzini
265d2c0bd84SPaolo Bonzini dma->memmap = g_realloc(dma->memmap, sizeof(*entry) *
266d2c0bd84SPaolo Bonzini (dma->memmap_size + 1));
267d2c0bd84SPaolo Bonzini entry = soc_dma_lookup(dma, virt_base);
268d2c0bd84SPaolo Bonzini
269d2c0bd84SPaolo Bonzini if (dma->memmap_size) {
270d2c0bd84SPaolo Bonzini if (entry->type == soc_dma_port_mem) {
271d2c0bd84SPaolo Bonzini if (entry->addr <= virt_base &&
272d2c0bd84SPaolo Bonzini entry->addr + entry->u.mem.size > virt_base) {
2737f69a433SAlistair Francis error_report("%s: FIFO at %"PRIx64
274580106dfSPaolo Bonzini " collides with RAM region at %"PRIx64
2757f69a433SAlistair Francis "-%"PRIx64, __func__,
276580106dfSPaolo Bonzini virt_base, entry->addr,
277d2c0bd84SPaolo Bonzini (entry->addr + entry->u.mem.size));
278d2c0bd84SPaolo Bonzini exit(-1);
279d2c0bd84SPaolo Bonzini }
280d2c0bd84SPaolo Bonzini
281d2c0bd84SPaolo Bonzini if (entry->addr <= virt_base)
282d2c0bd84SPaolo Bonzini entry ++;
283d2c0bd84SPaolo Bonzini } else
284d2c0bd84SPaolo Bonzini while (entry < dma->memmap + dma->memmap_size &&
285d2c0bd84SPaolo Bonzini entry->addr <= virt_base) {
286d2c0bd84SPaolo Bonzini if (entry->addr == virt_base && entry->u.fifo.out == out) {
2877f69a433SAlistair Francis error_report("%s: FIFO at %"PRIx64
2887f69a433SAlistair Francis " collides FIFO at %"PRIx64,
289580106dfSPaolo Bonzini __func__, virt_base, entry->addr);
290d2c0bd84SPaolo Bonzini exit(-1);
291d2c0bd84SPaolo Bonzini }
292d2c0bd84SPaolo Bonzini
293d2c0bd84SPaolo Bonzini entry ++;
294d2c0bd84SPaolo Bonzini }
295d2c0bd84SPaolo Bonzini
296d2c0bd84SPaolo Bonzini memmove(entry + 1, entry,
297d2c0bd84SPaolo Bonzini (uint8_t *) (dma->memmap + dma->memmap_size ++) -
298d2c0bd84SPaolo Bonzini (uint8_t *) entry);
299d2c0bd84SPaolo Bonzini } else
300d2c0bd84SPaolo Bonzini dma->memmap_size ++;
301d2c0bd84SPaolo Bonzini
302d2c0bd84SPaolo Bonzini entry->addr = virt_base;
303d2c0bd84SPaolo Bonzini entry->type = soc_dma_port_fifo;
304d2c0bd84SPaolo Bonzini entry->u.fifo.fn = fn;
305d2c0bd84SPaolo Bonzini entry->u.fifo.opaque = opaque;
306d2c0bd84SPaolo Bonzini entry->u.fifo.out = out;
307d2c0bd84SPaolo Bonzini }
308d2c0bd84SPaolo Bonzini
soc_dma_port_add_mem(struct soc_dma_s * soc,uint8_t * phys_base,hwaddr virt_base,size_t size)309d2c0bd84SPaolo Bonzini void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base,
310d2c0bd84SPaolo Bonzini hwaddr virt_base, size_t size)
311d2c0bd84SPaolo Bonzini {
312d2c0bd84SPaolo Bonzini struct memmap_entry_s *entry;
313d2c0bd84SPaolo Bonzini struct dma_s *dma = (struct dma_s *) soc;
314d2c0bd84SPaolo Bonzini
315d2c0bd84SPaolo Bonzini dma->memmap = g_realloc(dma->memmap, sizeof(*entry) *
316d2c0bd84SPaolo Bonzini (dma->memmap_size + 1));
317d2c0bd84SPaolo Bonzini entry = soc_dma_lookup(dma, virt_base);
318d2c0bd84SPaolo Bonzini
319d2c0bd84SPaolo Bonzini if (dma->memmap_size) {
320d2c0bd84SPaolo Bonzini if (entry->type == soc_dma_port_mem) {
321d2c0bd84SPaolo Bonzini if ((entry->addr >= virt_base && entry->addr < virt_base + size) ||
322d2c0bd84SPaolo Bonzini (entry->addr <= virt_base &&
323d2c0bd84SPaolo Bonzini entry->addr + entry->u.mem.size > virt_base)) {
3247f69a433SAlistair Francis error_report("%s: RAM at %"PRIx64 "-%"PRIx64
325580106dfSPaolo Bonzini " collides with RAM region at %"PRIx64
3267f69a433SAlistair Francis "-%"PRIx64, __func__,
327580106dfSPaolo Bonzini virt_base, virt_base + size,
328580106dfSPaolo Bonzini entry->addr, entry->addr + entry->u.mem.size);
329d2c0bd84SPaolo Bonzini exit(-1);
330d2c0bd84SPaolo Bonzini }
331d2c0bd84SPaolo Bonzini
332d2c0bd84SPaolo Bonzini if (entry->addr <= virt_base)
333d2c0bd84SPaolo Bonzini entry ++;
334d2c0bd84SPaolo Bonzini } else {
335d2c0bd84SPaolo Bonzini if (entry->addr >= virt_base &&
336d2c0bd84SPaolo Bonzini entry->addr < virt_base + size) {
3377f69a433SAlistair Francis error_report("%s: RAM at %"PRIx64 "-%"PRIx64
3387f69a433SAlistair Francis " collides with FIFO at %"PRIx64,
3397f69a433SAlistair Francis __func__, virt_base, virt_base + size,
340580106dfSPaolo Bonzini entry->addr);
341d2c0bd84SPaolo Bonzini exit(-1);
342d2c0bd84SPaolo Bonzini }
343d2c0bd84SPaolo Bonzini
344d2c0bd84SPaolo Bonzini while (entry < dma->memmap + dma->memmap_size &&
345d2c0bd84SPaolo Bonzini entry->addr <= virt_base)
346d2c0bd84SPaolo Bonzini entry ++;
347d2c0bd84SPaolo Bonzini }
348d2c0bd84SPaolo Bonzini
349d2c0bd84SPaolo Bonzini memmove(entry + 1, entry,
350d2c0bd84SPaolo Bonzini (uint8_t *) (dma->memmap + dma->memmap_size ++) -
351d2c0bd84SPaolo Bonzini (uint8_t *) entry);
352d2c0bd84SPaolo Bonzini } else
353d2c0bd84SPaolo Bonzini dma->memmap_size ++;
354d2c0bd84SPaolo Bonzini
355d2c0bd84SPaolo Bonzini entry->addr = virt_base;
356d2c0bd84SPaolo Bonzini entry->type = soc_dma_port_mem;
357d2c0bd84SPaolo Bonzini entry->u.mem.base = phys_base;
358d2c0bd84SPaolo Bonzini entry->u.mem.size = size;
359d2c0bd84SPaolo Bonzini }
360d2c0bd84SPaolo Bonzini
361d2c0bd84SPaolo Bonzini /* TODO: port removal for ports like PCMCIA memory */
362