Statistics
| Branch: | Revision:

root / hw / soc_dma.c @ 3b4b86aa

History | View | Annotate | Download (11.6 kB)

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