|
|
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.