Annotation of qemu/hw/soc_dma.c, revision 1.1.1.1

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 */

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.