Statistics
| Branch: | Revision:

root / hw / soc_dma.c @ 47e699dc

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