|
|
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
1.1.1.2 root 18: * with this program; if not, see <http://www.gnu.org/licenses/>.
1.1 root 19: */
20: #include "qemu-common.h"
21: #include "qemu-timer.h"
22: #include "soc_dma.h"
23:
24: static void transfer_mem2mem(struct soc_dma_ch_s *ch)
25: {
26: memcpy(ch->paddr[0], ch->paddr[1], ch->bytes);
27: ch->paddr[0] += ch->bytes;
28: ch->paddr[1] += ch->bytes;
29: }
30:
31: static void transfer_mem2fifo(struct soc_dma_ch_s *ch)
32: {
33: ch->io_fn[1](ch->io_opaque[1], ch->paddr[0], ch->bytes);
34: ch->paddr[0] += ch->bytes;
35: }
36:
37: static void transfer_fifo2mem(struct soc_dma_ch_s *ch)
38: {
39: ch->io_fn[0](ch->io_opaque[0], ch->paddr[1], ch->bytes);
40: ch->paddr[1] += ch->bytes;
41: }
42:
43: /* This is further optimisable but isn't very important because often
44: * DMA peripherals forbid this kind of transfers and even when they don't,
45: * oprating systems may not need to use them. */
46: static void *fifo_buf;
47: static int fifo_size;
48: static void transfer_fifo2fifo(struct soc_dma_ch_s *ch)
49: {
50: if (ch->bytes > fifo_size)
1.1.1.5 ! root 51: fifo_buf = g_realloc(fifo_buf, fifo_size = ch->bytes);
1.1 root 52:
53: /* Implement as transfer_fifo2linear + transfer_linear2fifo. */
54: ch->io_fn[0](ch->io_opaque[0], fifo_buf, ch->bytes);
55: ch->io_fn[1](ch->io_opaque[1], fifo_buf, ch->bytes);
56: }
57:
58: struct dma_s {
59: struct soc_dma_s soc;
60: int chnum;
61: uint64_t ch_enable_mask;
62: int64_t channel_freq;
63: int enabled_count;
64:
65: struct memmap_entry_s {
66: enum soc_dma_port_type type;
67: target_phys_addr_t addr;
68: union {
69: struct {
70: void *opaque;
71: soc_dma_io_t fn;
72: int out;
73: } fifo;
74: struct {
75: void *base;
76: size_t size;
77: } mem;
78: } u;
79: } *memmap;
80: int memmap_size;
81:
82: struct soc_dma_ch_s ch[0];
83: };
84:
85: static void soc_dma_ch_schedule(struct soc_dma_ch_s *ch, int delay_bytes)
86: {
1.1.1.4 root 87: int64_t now = qemu_get_clock_ns(vm_clock);
1.1 root 88: struct dma_s *dma = (struct dma_s *) ch->dma;
89:
90: qemu_mod_timer(ch->timer, now + delay_bytes / dma->channel_freq);
91: }
92:
93: static void soc_dma_ch_run(void *opaque)
94: {
95: struct soc_dma_ch_s *ch = (struct soc_dma_ch_s *) opaque;
96:
97: ch->running = 1;
98: ch->dma->setup_fn(ch);
99: ch->transfer_fn(ch);
100: ch->running = 0;
101:
102: if (ch->enable)
103: soc_dma_ch_schedule(ch, ch->bytes);
104: ch->bytes = 0;
105: }
106:
107: static inline struct memmap_entry_s *soc_dma_lookup(struct dma_s *dma,
108: target_phys_addr_t addr)
109: {
110: struct memmap_entry_s *lo;
111: int hi;
112:
113: lo = dma->memmap;
114: hi = dma->memmap_size;
115:
116: while (hi > 1) {
117: hi /= 2;
118: if (lo[hi].addr <= addr)
119: lo += hi;
120: }
121:
122: return lo;
123: }
124:
125: static inline enum soc_dma_port_type soc_dma_ch_update_type(
126: struct soc_dma_ch_s *ch, int port)
127: {
128: struct dma_s *dma = (struct dma_s *) ch->dma;
129: struct memmap_entry_s *entry = soc_dma_lookup(dma, ch->vaddr[port]);
130:
131: if (entry->type == soc_dma_port_fifo) {
132: while (entry < dma->memmap + dma->memmap_size &&
133: entry->u.fifo.out != port)
134: entry ++;
135: if (entry->addr != ch->vaddr[port] || entry->u.fifo.out != port)
136: return soc_dma_port_other;
137:
138: if (ch->type[port] != soc_dma_access_const)
139: return soc_dma_port_other;
140:
141: ch->io_fn[port] = entry->u.fifo.fn;
142: ch->io_opaque[port] = entry->u.fifo.opaque;
143: return soc_dma_port_fifo;
144: } else if (entry->type == soc_dma_port_mem) {
145: if (entry->addr > ch->vaddr[port] ||
146: entry->addr + entry->u.mem.size <= ch->vaddr[port])
147: return soc_dma_port_other;
148:
149: /* TODO: support constant memory address for source port as used for
150: * drawing solid rectangles by PalmOS(R). */
151: if (ch->type[port] != soc_dma_access_const)
152: return soc_dma_port_other;
153:
154: ch->paddr[port] = (uint8_t *) entry->u.mem.base +
155: (ch->vaddr[port] - entry->addr);
156: /* TODO: save bytes left to the end of the mapping somewhere so we
157: * can check we're not reading beyond it. */
158: return soc_dma_port_mem;
159: } else
160: return soc_dma_port_other;
161: }
162:
163: void soc_dma_ch_update(struct soc_dma_ch_s *ch)
164: {
165: enum soc_dma_port_type src, dst;
166:
167: src = soc_dma_ch_update_type(ch, 0);
168: if (src == soc_dma_port_other) {
169: ch->update = 0;
170: ch->transfer_fn = ch->dma->transfer_fn;
171: return;
172: }
173: dst = soc_dma_ch_update_type(ch, 1);
174:
175: /* TODO: use src and dst as array indices. */
176: if (src == soc_dma_port_mem && dst == soc_dma_port_mem)
177: ch->transfer_fn = transfer_mem2mem;
178: else if (src == soc_dma_port_mem && dst == soc_dma_port_fifo)
179: ch->transfer_fn = transfer_mem2fifo;
180: else if (src == soc_dma_port_fifo && dst == soc_dma_port_mem)
181: ch->transfer_fn = transfer_fifo2mem;
182: else if (src == soc_dma_port_fifo && dst == soc_dma_port_fifo)
183: ch->transfer_fn = transfer_fifo2fifo;
184: else
185: ch->transfer_fn = ch->dma->transfer_fn;
186:
187: ch->update = (dst != soc_dma_port_other);
188: }
189:
190: static void soc_dma_ch_freq_update(struct dma_s *s)
191: {
192: if (s->enabled_count)
193: /* We completely ignore channel priorities and stuff */
194: s->channel_freq = s->soc.freq / s->enabled_count;
1.1.1.3 root 195: else {
1.1 root 196: /* TODO: Signal that we want to disable the functional clock and let
197: * the platform code decide what to do with it, i.e. check that
198: * auto-idle is enabled in the clock controller and if we are stopping
199: * the clock, do the same with any parent clocks that had only one
1.1.1.3 root 200: * user keeping them on and auto-idle enabled. */
201: }
1.1 root 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;
1.1.1.5 ! root 242: struct dma_s *s = g_malloc0(sizeof(*s) + n * sizeof(*s->ch));
1.1 root 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;
1.1.1.4 root 249: s->ch[i].timer = qemu_new_timer_ns(vm_clock, soc_dma_ch_run, &s->ch[i]);
1.1 root 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:
1.1.1.5 ! root 264: dma->memmap = g_realloc(dma->memmap, sizeof(*entry) *
1.1 root 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:
1.1.1.5 ! root 316: dma->memmap = g_realloc(dma->memmap, sizeof(*entry) *
1.1 root 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.