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