xref: /openbmc/qemu/hw/dma/soc_dma.c (revision bc72ad67543f5c5d39c005ff0ca72da37642a1fb)
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  */
20d2c0bd84SPaolo Bonzini #include "qemu-common.h"
21d2c0bd84SPaolo Bonzini #include "qemu/timer.h"
22d2c0bd84SPaolo Bonzini #include "hw/arm/soc_dma.h"
23d2c0bd84SPaolo Bonzini 
24d2c0bd84SPaolo Bonzini static void transfer_mem2mem(struct soc_dma_ch_s *ch)
25d2c0bd84SPaolo Bonzini {
26d2c0bd84SPaolo Bonzini     memcpy(ch->paddr[0], ch->paddr[1], ch->bytes);
27d2c0bd84SPaolo Bonzini     ch->paddr[0] += ch->bytes;
28d2c0bd84SPaolo Bonzini     ch->paddr[1] += ch->bytes;
29d2c0bd84SPaolo Bonzini }
30d2c0bd84SPaolo Bonzini 
31d2c0bd84SPaolo Bonzini static void transfer_mem2fifo(struct soc_dma_ch_s *ch)
32d2c0bd84SPaolo Bonzini {
33d2c0bd84SPaolo Bonzini     ch->io_fn[1](ch->io_opaque[1], ch->paddr[0], ch->bytes);
34d2c0bd84SPaolo Bonzini     ch->paddr[0] += ch->bytes;
35d2c0bd84SPaolo Bonzini }
36d2c0bd84SPaolo Bonzini 
37d2c0bd84SPaolo Bonzini static void transfer_fifo2mem(struct soc_dma_ch_s *ch)
38d2c0bd84SPaolo Bonzini {
39d2c0bd84SPaolo Bonzini     ch->io_fn[0](ch->io_opaque[0], ch->paddr[1], ch->bytes);
40d2c0bd84SPaolo Bonzini     ch->paddr[1] += ch->bytes;
41d2c0bd84SPaolo Bonzini }
42d2c0bd84SPaolo Bonzini 
43d2c0bd84SPaolo Bonzini /* This is further optimisable but isn't very important because often
44d2c0bd84SPaolo Bonzini  * DMA peripherals forbid this kind of transfers and even when they don't,
45d2c0bd84SPaolo Bonzini  * oprating systems may not need to use them.  */
46d2c0bd84SPaolo Bonzini static void *fifo_buf;
47d2c0bd84SPaolo Bonzini static int fifo_size;
48d2c0bd84SPaolo Bonzini static void transfer_fifo2fifo(struct soc_dma_ch_s *ch)
49d2c0bd84SPaolo Bonzini {
50d2c0bd84SPaolo Bonzini     if (ch->bytes > fifo_size)
51d2c0bd84SPaolo Bonzini         fifo_buf = g_realloc(fifo_buf, fifo_size = ch->bytes);
52d2c0bd84SPaolo Bonzini 
53d2c0bd84SPaolo Bonzini     /* Implement as transfer_fifo2linear + transfer_linear2fifo.  */
54d2c0bd84SPaolo Bonzini     ch->io_fn[0](ch->io_opaque[0], fifo_buf, ch->bytes);
55d2c0bd84SPaolo Bonzini     ch->io_fn[1](ch->io_opaque[1], fifo_buf, ch->bytes);
56d2c0bd84SPaolo Bonzini }
57d2c0bd84SPaolo Bonzini 
58d2c0bd84SPaolo Bonzini struct dma_s {
59d2c0bd84SPaolo Bonzini     struct soc_dma_s soc;
60d2c0bd84SPaolo Bonzini     int chnum;
61d2c0bd84SPaolo Bonzini     uint64_t ch_enable_mask;
62d2c0bd84SPaolo Bonzini     int64_t channel_freq;
63d2c0bd84SPaolo Bonzini     int enabled_count;
64d2c0bd84SPaolo Bonzini 
65d2c0bd84SPaolo Bonzini     struct memmap_entry_s {
66d2c0bd84SPaolo Bonzini         enum soc_dma_port_type type;
67d2c0bd84SPaolo Bonzini         hwaddr addr;
68d2c0bd84SPaolo Bonzini         union {
69d2c0bd84SPaolo Bonzini            struct {
70d2c0bd84SPaolo Bonzini                void *opaque;
71d2c0bd84SPaolo Bonzini                soc_dma_io_t fn;
72d2c0bd84SPaolo Bonzini                int out;
73d2c0bd84SPaolo Bonzini            } fifo;
74d2c0bd84SPaolo Bonzini            struct {
75d2c0bd84SPaolo Bonzini                void *base;
76d2c0bd84SPaolo Bonzini                size_t size;
77d2c0bd84SPaolo Bonzini            } mem;
78d2c0bd84SPaolo Bonzini         } u;
79d2c0bd84SPaolo Bonzini     } *memmap;
80d2c0bd84SPaolo Bonzini     int memmap_size;
81d2c0bd84SPaolo Bonzini 
82d2c0bd84SPaolo Bonzini     struct soc_dma_ch_s ch[0];
83d2c0bd84SPaolo Bonzini };
84d2c0bd84SPaolo Bonzini 
85d2c0bd84SPaolo Bonzini static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, int delay_bytes)
86d2c0bd84SPaolo Bonzini {
87*bc72ad67SAlex Bligh     int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
88d2c0bd84SPaolo Bonzini     struct dma_s *dma = (struct dma_s *) ch->dma;
89d2c0bd84SPaolo Bonzini 
90*bc72ad67SAlex Bligh     timer_mod(ch->timer, now + delay_bytes / dma->channel_freq);
91d2c0bd84SPaolo Bonzini }
92d2c0bd84SPaolo Bonzini 
93d2c0bd84SPaolo Bonzini static void soc_dma_ch_run(void *opaque)
94d2c0bd84SPaolo Bonzini {
95d2c0bd84SPaolo Bonzini     struct soc_dma_ch_s *ch = (struct soc_dma_ch_s *) opaque;
96d2c0bd84SPaolo Bonzini 
97d2c0bd84SPaolo Bonzini     ch->running = 1;
98d2c0bd84SPaolo Bonzini     ch->dma->setup_fn(ch);
99d2c0bd84SPaolo Bonzini     ch->transfer_fn(ch);
100d2c0bd84SPaolo Bonzini     ch->running = 0;
101d2c0bd84SPaolo Bonzini 
102d2c0bd84SPaolo Bonzini     if (ch->enable)
103d2c0bd84SPaolo Bonzini         soc_dma_ch_schedule(ch, ch->bytes);
104d2c0bd84SPaolo Bonzini     ch->bytes = 0;
105d2c0bd84SPaolo Bonzini }
106d2c0bd84SPaolo Bonzini 
107d2c0bd84SPaolo Bonzini static inline struct memmap_entry_s *soc_dma_lookup(struct dma_s *dma,
108d2c0bd84SPaolo Bonzini                 hwaddr addr)
109d2c0bd84SPaolo Bonzini {
110d2c0bd84SPaolo Bonzini     struct memmap_entry_s *lo;
111d2c0bd84SPaolo Bonzini     int hi;
112d2c0bd84SPaolo Bonzini 
113d2c0bd84SPaolo Bonzini     lo = dma->memmap;
114d2c0bd84SPaolo Bonzini     hi = dma->memmap_size;
115d2c0bd84SPaolo Bonzini 
116d2c0bd84SPaolo Bonzini     while (hi > 1) {
117d2c0bd84SPaolo Bonzini         hi /= 2;
118d2c0bd84SPaolo Bonzini         if (lo[hi].addr <= addr)
119d2c0bd84SPaolo Bonzini             lo += hi;
120d2c0bd84SPaolo Bonzini     }
121d2c0bd84SPaolo Bonzini 
122d2c0bd84SPaolo Bonzini     return lo;
123d2c0bd84SPaolo Bonzini }
124d2c0bd84SPaolo Bonzini 
125d2c0bd84SPaolo Bonzini static inline enum soc_dma_port_type soc_dma_ch_update_type(
126d2c0bd84SPaolo Bonzini                 struct soc_dma_ch_s *ch, int port)
127d2c0bd84SPaolo Bonzini {
128d2c0bd84SPaolo Bonzini     struct dma_s *dma = (struct dma_s *) ch->dma;
129d2c0bd84SPaolo Bonzini     struct memmap_entry_s *entry = soc_dma_lookup(dma, ch->vaddr[port]);
130d2c0bd84SPaolo Bonzini 
131d2c0bd84SPaolo Bonzini     if (entry->type == soc_dma_port_fifo) {
132d2c0bd84SPaolo Bonzini         while (entry < dma->memmap + dma->memmap_size &&
133d2c0bd84SPaolo Bonzini                         entry->u.fifo.out != port)
134d2c0bd84SPaolo Bonzini             entry ++;
135d2c0bd84SPaolo Bonzini         if (entry->addr != ch->vaddr[port] || entry->u.fifo.out != port)
136d2c0bd84SPaolo Bonzini             return soc_dma_port_other;
137d2c0bd84SPaolo Bonzini 
138d2c0bd84SPaolo Bonzini         if (ch->type[port] != soc_dma_access_const)
139d2c0bd84SPaolo Bonzini             return soc_dma_port_other;
140d2c0bd84SPaolo Bonzini 
141d2c0bd84SPaolo Bonzini         ch->io_fn[port] = entry->u.fifo.fn;
142d2c0bd84SPaolo Bonzini         ch->io_opaque[port] = entry->u.fifo.opaque;
143d2c0bd84SPaolo Bonzini         return soc_dma_port_fifo;
144d2c0bd84SPaolo Bonzini     } else if (entry->type == soc_dma_port_mem) {
145d2c0bd84SPaolo Bonzini         if (entry->addr > ch->vaddr[port] ||
146d2c0bd84SPaolo Bonzini                         entry->addr + entry->u.mem.size <= ch->vaddr[port])
147d2c0bd84SPaolo Bonzini             return soc_dma_port_other;
148d2c0bd84SPaolo Bonzini 
149d2c0bd84SPaolo Bonzini         /* TODO: support constant memory address for source port as used for
150d2c0bd84SPaolo Bonzini          * drawing solid rectangles by PalmOS(R).  */
151d2c0bd84SPaolo Bonzini         if (ch->type[port] != soc_dma_access_const)
152d2c0bd84SPaolo Bonzini             return soc_dma_port_other;
153d2c0bd84SPaolo Bonzini 
154d2c0bd84SPaolo Bonzini         ch->paddr[port] = (uint8_t *) entry->u.mem.base +
155d2c0bd84SPaolo Bonzini                 (ch->vaddr[port] - entry->addr);
156d2c0bd84SPaolo Bonzini         /* TODO: save bytes left to the end of the mapping somewhere so we
157d2c0bd84SPaolo Bonzini          * can check we're not reading beyond it.  */
158d2c0bd84SPaolo Bonzini         return soc_dma_port_mem;
159d2c0bd84SPaolo Bonzini     } else
160d2c0bd84SPaolo Bonzini         return soc_dma_port_other;
161d2c0bd84SPaolo Bonzini }
162d2c0bd84SPaolo Bonzini 
163d2c0bd84SPaolo Bonzini void soc_dma_ch_update(struct soc_dma_ch_s *ch)
164d2c0bd84SPaolo Bonzini {
165d2c0bd84SPaolo Bonzini     enum soc_dma_port_type src, dst;
166d2c0bd84SPaolo Bonzini 
167d2c0bd84SPaolo Bonzini     src = soc_dma_ch_update_type(ch, 0);
168d2c0bd84SPaolo Bonzini     if (src == soc_dma_port_other) {
169d2c0bd84SPaolo Bonzini         ch->update = 0;
170d2c0bd84SPaolo Bonzini         ch->transfer_fn = ch->dma->transfer_fn;
171d2c0bd84SPaolo Bonzini         return;
172d2c0bd84SPaolo Bonzini     }
173d2c0bd84SPaolo Bonzini     dst = soc_dma_ch_update_type(ch, 1);
174d2c0bd84SPaolo Bonzini 
175d2c0bd84SPaolo Bonzini     /* TODO: use src and dst as array indices.  */
176d2c0bd84SPaolo Bonzini     if (src == soc_dma_port_mem && dst == soc_dma_port_mem)
177d2c0bd84SPaolo Bonzini         ch->transfer_fn = transfer_mem2mem;
178d2c0bd84SPaolo Bonzini     else if (src == soc_dma_port_mem && dst == soc_dma_port_fifo)
179d2c0bd84SPaolo Bonzini         ch->transfer_fn = transfer_mem2fifo;
180d2c0bd84SPaolo Bonzini     else if (src == soc_dma_port_fifo && dst == soc_dma_port_mem)
181d2c0bd84SPaolo Bonzini         ch->transfer_fn = transfer_fifo2mem;
182d2c0bd84SPaolo Bonzini     else if (src == soc_dma_port_fifo && dst == soc_dma_port_fifo)
183d2c0bd84SPaolo Bonzini         ch->transfer_fn = transfer_fifo2fifo;
184d2c0bd84SPaolo Bonzini     else
185d2c0bd84SPaolo Bonzini         ch->transfer_fn = ch->dma->transfer_fn;
186d2c0bd84SPaolo Bonzini 
187d2c0bd84SPaolo Bonzini     ch->update = (dst != soc_dma_port_other);
188d2c0bd84SPaolo Bonzini }
189d2c0bd84SPaolo Bonzini 
190d2c0bd84SPaolo Bonzini static void soc_dma_ch_freq_update(struct dma_s *s)
191d2c0bd84SPaolo Bonzini {
192d2c0bd84SPaolo Bonzini     if (s->enabled_count)
193d2c0bd84SPaolo Bonzini         /* We completely ignore channel priorities and stuff */
194d2c0bd84SPaolo Bonzini         s->channel_freq = s->soc.freq / s->enabled_count;
195d2c0bd84SPaolo Bonzini     else {
196d2c0bd84SPaolo Bonzini         /* TODO: Signal that we want to disable the functional clock and let
197d2c0bd84SPaolo Bonzini          * the platform code decide what to do with it, i.e. check that
198d2c0bd84SPaolo Bonzini          * auto-idle is enabled in the clock controller and if we are stopping
199d2c0bd84SPaolo Bonzini          * the clock, do the same with any parent clocks that had only one
200d2c0bd84SPaolo Bonzini          * user keeping them on and auto-idle enabled.  */
201d2c0bd84SPaolo Bonzini     }
202d2c0bd84SPaolo Bonzini }
203d2c0bd84SPaolo Bonzini 
204d2c0bd84SPaolo Bonzini void soc_dma_set_request(struct soc_dma_ch_s *ch, int level)
205d2c0bd84SPaolo Bonzini {
206d2c0bd84SPaolo Bonzini     struct dma_s *dma = (struct dma_s *) ch->dma;
207d2c0bd84SPaolo Bonzini 
208d2c0bd84SPaolo Bonzini     dma->enabled_count += level - ch->enable;
209d2c0bd84SPaolo Bonzini 
210d2c0bd84SPaolo Bonzini     if (level)
211d2c0bd84SPaolo Bonzini         dma->ch_enable_mask |= 1 << ch->num;
212d2c0bd84SPaolo Bonzini     else
213d2c0bd84SPaolo Bonzini         dma->ch_enable_mask &= ~(1 << ch->num);
214d2c0bd84SPaolo Bonzini 
215d2c0bd84SPaolo Bonzini     if (level != ch->enable) {
216d2c0bd84SPaolo Bonzini         soc_dma_ch_freq_update(dma);
217d2c0bd84SPaolo Bonzini         ch->enable = level;
218d2c0bd84SPaolo Bonzini 
219d2c0bd84SPaolo Bonzini         if (!ch->enable)
220*bc72ad67SAlex Bligh             timer_del(ch->timer);
221d2c0bd84SPaolo Bonzini         else if (!ch->running)
222d2c0bd84SPaolo Bonzini             soc_dma_ch_run(ch);
223d2c0bd84SPaolo Bonzini         else
224d2c0bd84SPaolo Bonzini             soc_dma_ch_schedule(ch, 1);
225d2c0bd84SPaolo Bonzini     }
226d2c0bd84SPaolo Bonzini }
227d2c0bd84SPaolo Bonzini 
228d2c0bd84SPaolo Bonzini void soc_dma_reset(struct soc_dma_s *soc)
229d2c0bd84SPaolo Bonzini {
230d2c0bd84SPaolo Bonzini     struct dma_s *s = (struct dma_s *) soc;
231d2c0bd84SPaolo Bonzini 
232d2c0bd84SPaolo Bonzini     s->soc.drqbmp = 0;
233d2c0bd84SPaolo Bonzini     s->ch_enable_mask = 0;
234d2c0bd84SPaolo Bonzini     s->enabled_count = 0;
235d2c0bd84SPaolo Bonzini     soc_dma_ch_freq_update(s);
236d2c0bd84SPaolo Bonzini }
237d2c0bd84SPaolo Bonzini 
238d2c0bd84SPaolo Bonzini /* TODO: take a functional-clock argument */
239d2c0bd84SPaolo Bonzini struct soc_dma_s *soc_dma_init(int n)
240d2c0bd84SPaolo Bonzini {
241d2c0bd84SPaolo Bonzini     int i;
242d2c0bd84SPaolo Bonzini     struct dma_s *s = g_malloc0(sizeof(*s) + n * sizeof(*s->ch));
243d2c0bd84SPaolo Bonzini 
244d2c0bd84SPaolo Bonzini     s->chnum = n;
245d2c0bd84SPaolo Bonzini     s->soc.ch = s->ch;
246d2c0bd84SPaolo Bonzini     for (i = 0; i < n; i ++) {
247d2c0bd84SPaolo Bonzini         s->ch[i].dma = &s->soc;
248d2c0bd84SPaolo Bonzini         s->ch[i].num = i;
249*bc72ad67SAlex Bligh         s->ch[i].timer = timer_new_ns(QEMU_CLOCK_VIRTUAL, soc_dma_ch_run, &s->ch[i]);
250d2c0bd84SPaolo Bonzini     }
251d2c0bd84SPaolo Bonzini 
252d2c0bd84SPaolo Bonzini     soc_dma_reset(&s->soc);
253d2c0bd84SPaolo Bonzini     fifo_size = 0;
254d2c0bd84SPaolo Bonzini 
255d2c0bd84SPaolo Bonzini     return &s->soc;
256d2c0bd84SPaolo Bonzini }
257d2c0bd84SPaolo Bonzini 
258d2c0bd84SPaolo Bonzini void soc_dma_port_add_fifo(struct soc_dma_s *soc, hwaddr virt_base,
259d2c0bd84SPaolo Bonzini                 soc_dma_io_t fn, void *opaque, int out)
260d2c0bd84SPaolo Bonzini {
261d2c0bd84SPaolo Bonzini     struct memmap_entry_s *entry;
262d2c0bd84SPaolo Bonzini     struct dma_s *dma = (struct dma_s *) soc;
263d2c0bd84SPaolo Bonzini 
264d2c0bd84SPaolo Bonzini     dma->memmap = g_realloc(dma->memmap, sizeof(*entry) *
265d2c0bd84SPaolo Bonzini                     (dma->memmap_size + 1));
266d2c0bd84SPaolo Bonzini     entry = soc_dma_lookup(dma, virt_base);
267d2c0bd84SPaolo Bonzini 
268d2c0bd84SPaolo Bonzini     if (dma->memmap_size) {
269d2c0bd84SPaolo Bonzini         if (entry->type == soc_dma_port_mem) {
270d2c0bd84SPaolo Bonzini             if (entry->addr <= virt_base &&
271d2c0bd84SPaolo Bonzini                             entry->addr + entry->u.mem.size > virt_base) {
272d2c0bd84SPaolo Bonzini                 fprintf(stderr, "%s: FIFO at " TARGET_FMT_lx
273d2c0bd84SPaolo Bonzini                                 " collides with RAM region at " TARGET_FMT_lx
274d2c0bd84SPaolo Bonzini                                 "-" TARGET_FMT_lx "\n", __FUNCTION__,
275d2c0bd84SPaolo Bonzini                                 (target_ulong) virt_base,
276d2c0bd84SPaolo Bonzini                                 (target_ulong) entry->addr, (target_ulong)
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) {
287d2c0bd84SPaolo Bonzini                     fprintf(stderr, "%s: FIFO at " TARGET_FMT_lx
288d2c0bd84SPaolo Bonzini                                     " collides FIFO at " TARGET_FMT_lx "\n",
289d2c0bd84SPaolo Bonzini                                     __FUNCTION__, (target_ulong) virt_base,
290d2c0bd84SPaolo Bonzini                                     (target_ulong) entry->addr);
291d2c0bd84SPaolo Bonzini                     exit(-1);
292d2c0bd84SPaolo Bonzini                 }
293d2c0bd84SPaolo Bonzini 
294d2c0bd84SPaolo Bonzini                 entry ++;
295d2c0bd84SPaolo Bonzini             }
296d2c0bd84SPaolo Bonzini 
297d2c0bd84SPaolo Bonzini         memmove(entry + 1, entry,
298d2c0bd84SPaolo Bonzini                         (uint8_t *) (dma->memmap + dma->memmap_size ++) -
299d2c0bd84SPaolo Bonzini                         (uint8_t *) entry);
300d2c0bd84SPaolo Bonzini     } else
301d2c0bd84SPaolo Bonzini         dma->memmap_size ++;
302d2c0bd84SPaolo Bonzini 
303d2c0bd84SPaolo Bonzini     entry->addr          = virt_base;
304d2c0bd84SPaolo Bonzini     entry->type          = soc_dma_port_fifo;
305d2c0bd84SPaolo Bonzini     entry->u.fifo.fn     = fn;
306d2c0bd84SPaolo Bonzini     entry->u.fifo.opaque = opaque;
307d2c0bd84SPaolo Bonzini     entry->u.fifo.out    = out;
308d2c0bd84SPaolo Bonzini }
309d2c0bd84SPaolo Bonzini 
310d2c0bd84SPaolo Bonzini void soc_dma_port_add_mem(struct soc_dma_s *soc, uint8_t *phys_base,
311d2c0bd84SPaolo Bonzini                 hwaddr virt_base, size_t size)
312d2c0bd84SPaolo Bonzini {
313d2c0bd84SPaolo Bonzini     struct memmap_entry_s *entry;
314d2c0bd84SPaolo Bonzini     struct dma_s *dma = (struct dma_s *) soc;
315d2c0bd84SPaolo Bonzini 
316d2c0bd84SPaolo Bonzini     dma->memmap = g_realloc(dma->memmap, sizeof(*entry) *
317d2c0bd84SPaolo Bonzini                     (dma->memmap_size + 1));
318d2c0bd84SPaolo Bonzini     entry = soc_dma_lookup(dma, virt_base);
319d2c0bd84SPaolo Bonzini 
320d2c0bd84SPaolo Bonzini     if (dma->memmap_size) {
321d2c0bd84SPaolo Bonzini         if (entry->type == soc_dma_port_mem) {
322d2c0bd84SPaolo Bonzini             if ((entry->addr >= virt_base && entry->addr < virt_base + size) ||
323d2c0bd84SPaolo Bonzini                             (entry->addr <= virt_base &&
324d2c0bd84SPaolo Bonzini                              entry->addr + entry->u.mem.size > virt_base)) {
325d2c0bd84SPaolo Bonzini                 fprintf(stderr, "%s: RAM at " TARGET_FMT_lx "-" TARGET_FMT_lx
326d2c0bd84SPaolo Bonzini                                 " collides with RAM region at " TARGET_FMT_lx
327d2c0bd84SPaolo Bonzini                                 "-" TARGET_FMT_lx "\n", __FUNCTION__,
328d2c0bd84SPaolo Bonzini                                 (target_ulong) virt_base,
329d2c0bd84SPaolo Bonzini                                 (target_ulong) (virt_base + size),
330d2c0bd84SPaolo Bonzini                                 (target_ulong) entry->addr, (target_ulong)
331d2c0bd84SPaolo Bonzini                                 (entry->addr + entry->u.mem.size));
332d2c0bd84SPaolo Bonzini                 exit(-1);
333d2c0bd84SPaolo Bonzini             }
334d2c0bd84SPaolo Bonzini 
335d2c0bd84SPaolo Bonzini             if (entry->addr <= virt_base)
336d2c0bd84SPaolo Bonzini                 entry ++;
337d2c0bd84SPaolo Bonzini         } else {
338d2c0bd84SPaolo Bonzini             if (entry->addr >= virt_base &&
339d2c0bd84SPaolo Bonzini                             entry->addr < virt_base + size) {
340d2c0bd84SPaolo Bonzini                 fprintf(stderr, "%s: RAM at " TARGET_FMT_lx "-" TARGET_FMT_lx
341d2c0bd84SPaolo Bonzini                                 " collides with FIFO at " TARGET_FMT_lx
342d2c0bd84SPaolo Bonzini                                 "\n", __FUNCTION__,
343d2c0bd84SPaolo Bonzini                                 (target_ulong) virt_base,
344d2c0bd84SPaolo Bonzini                                 (target_ulong) (virt_base + size),
345d2c0bd84SPaolo Bonzini                                 (target_ulong) entry->addr);
346d2c0bd84SPaolo Bonzini                 exit(-1);
347d2c0bd84SPaolo Bonzini             }
348d2c0bd84SPaolo Bonzini 
349d2c0bd84SPaolo Bonzini             while (entry < dma->memmap + dma->memmap_size &&
350d2c0bd84SPaolo Bonzini                             entry->addr <= virt_base)
351d2c0bd84SPaolo Bonzini                 entry ++;
352d2c0bd84SPaolo Bonzini 	}
353d2c0bd84SPaolo Bonzini 
354d2c0bd84SPaolo Bonzini         memmove(entry + 1, entry,
355d2c0bd84SPaolo Bonzini                         (uint8_t *) (dma->memmap + dma->memmap_size ++) -
356d2c0bd84SPaolo Bonzini                         (uint8_t *) entry);
357d2c0bd84SPaolo Bonzini     } else
358d2c0bd84SPaolo Bonzini         dma->memmap_size ++;
359d2c0bd84SPaolo Bonzini 
360d2c0bd84SPaolo Bonzini     entry->addr          = virt_base;
361d2c0bd84SPaolo Bonzini     entry->type          = soc_dma_port_mem;
362d2c0bd84SPaolo Bonzini     entry->u.mem.base    = phys_base;
363d2c0bd84SPaolo Bonzini     entry->u.mem.size    = size;
364d2c0bd84SPaolo Bonzini }
365d2c0bd84SPaolo Bonzini 
366d2c0bd84SPaolo Bonzini /* TODO: port removal for ports like PCMCIA memory */
367