Annotation of qemu/hw/soc_dma.c, revision 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.