Statistics
| Branch: | Revision:

root / hw / soc_dma.c @ b584726d

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