omap_dma.c
来自「xen虚拟机源代码安装包」· C语言 代码 · 共 1,842 行 · 第 1/4 页
C
1,842 行
/* * TI OMAP DMA gigacell. * * Copyright (C) 2006-2008 Andrzej Zaborowski <balrog@zabor.org> * Copyright (C) 2007-2008 Lauro Ramos Venancio <lauro.venancio@indt.org.br> * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */#include "qemu-common.h"#include "qemu-timer.h"#include "omap.h"#include "irq.h"struct omap_dma_channel_s { /* transfer data */ int burst[2]; int pack[2]; int endian[2]; int endian_lock[2]; int translate[2]; enum omap_dma_port port[2]; target_phys_addr_t addr[2]; omap_dma_addressing_t mode[2]; uint32_t elements; uint16_t frames; int32_t frame_index[2]; int16_t element_index[2]; int data_type; /* transfer type */ int transparent_copy; int constant_fill; uint32_t color; int prefetch; /* auto init and linked channel data */ int end_prog; int repeat; int auto_init; int link_enabled; int link_next_ch; /* interruption data */ int interrupts; int status; int cstatus; /* state data */ int active; int enable; int sync; int src_sync; int pending_request; int waiting_end_prog; uint16_t cpc; /* sync type */ int fs; int bs; /* compatibility */ int omap_3_1_compatible_disable; qemu_irq irq; struct omap_dma_channel_s *sibling; struct omap_dma_reg_set_s { target_phys_addr_t src, dest; int frame; int element; int pck_element; int frame_delta[2]; int elem_delta[2]; int frames; int elements; int pck_elements; } active_set; /* unused parameters */ int write_mode; int priority; int interleave_disabled; int type; int suspend; int buf_disable;};struct omap_dma_s { QEMUTimer *tm; struct omap_mpu_state_s *mpu; target_phys_addr_t base; omap_clk clk; int64_t delay; uint64_t drq; qemu_irq irq[4]; void (*intr_update)(struct omap_dma_s *s); enum omap_dma_model model; int omap_3_1_mapping_disabled; uint32_t gcr; uint32_t ocp; uint32_t caps[5]; uint32_t irqen[4]; uint32_t irqstat[4]; int run_count; int chans; struct omap_dma_channel_s ch[32]; struct omap_dma_lcd_channel_s lcd_ch;};/* Interrupts */#define TIMEOUT_INTR (1 << 0)#define EVENT_DROP_INTR (1 << 1)#define HALF_FRAME_INTR (1 << 2)#define END_FRAME_INTR (1 << 3)#define LAST_FRAME_INTR (1 << 4)#define END_BLOCK_INTR (1 << 5)#define SYNC (1 << 6)#define END_PKT_INTR (1 << 7)#define TRANS_ERR_INTR (1 << 8)#define MISALIGN_INTR (1 << 11)static inline void omap_dma_interrupts_update(struct omap_dma_s *s){ return s->intr_update(s);}static void omap_dma_channel_load(struct omap_dma_s *s, struct omap_dma_channel_s *ch){ struct omap_dma_reg_set_s *a = &ch->active_set; int i; int omap_3_1 = !ch->omap_3_1_compatible_disable; /* * TODO: verify address ranges and alignment * TODO: port endianness */ a->src = ch->addr[0]; a->dest = ch->addr[1]; a->frames = ch->frames; a->elements = ch->elements; a->pck_elements = ch->frame_index[!ch->src_sync]; a->frame = 0; a->element = 0; a->pck_element = 0; if (unlikely(!ch->elements || !ch->frames)) { printf("%s: bad DMA request\n", __FUNCTION__); return; } for (i = 0; i < 2; i ++) switch (ch->mode[i]) { case constant: a->elem_delta[i] = 0; a->frame_delta[i] = 0; break; case post_incremented: a->elem_delta[i] = ch->data_type; a->frame_delta[i] = 0; break; case single_index: a->elem_delta[i] = ch->data_type + ch->element_index[omap_3_1 ? 0 : i] - 1; a->frame_delta[i] = 0; break; case double_index: a->elem_delta[i] = ch->data_type + ch->element_index[omap_3_1 ? 0 : i] - 1; a->frame_delta[i] = ch->frame_index[omap_3_1 ? 0 : i] - ch->element_index[omap_3_1 ? 0 : i]; break; default: break; }}static void omap_dma_activate_channel(struct omap_dma_s *s, struct omap_dma_channel_s *ch){ if (!ch->active) { ch->active = 1; if (ch->sync) ch->status |= SYNC; s->run_count ++; } if (s->delay && !qemu_timer_pending(s->tm)) qemu_mod_timer(s->tm, qemu_get_clock(vm_clock) + s->delay);}static void omap_dma_deactivate_channel(struct omap_dma_s *s, struct omap_dma_channel_s *ch){ /* Update cpc */ ch->cpc = ch->active_set.dest & 0xffff; if (ch->pending_request && !ch->waiting_end_prog && ch->enable) { /* Don't deactivate the channel */ ch->pending_request = 0; return; } /* Don't deactive the channel if it is synchronized and the DMA request is active */ if (ch->sync && ch->enable && (s->drq & (1 << ch->sync))) return; if (ch->active) { ch->active = 0; ch->status &= ~SYNC; s->run_count --; } if (!s->run_count) qemu_del_timer(s->tm);}static void omap_dma_enable_channel(struct omap_dma_s *s, struct omap_dma_channel_s *ch){ if (!ch->enable) { ch->enable = 1; ch->waiting_end_prog = 0; omap_dma_channel_load(s, ch); /* TODO: theoretically if ch->sync && ch->prefetch && * !s->drq[ch->sync], we should also activate and fetch from source * and then stall until signalled. */ if ((!ch->sync) || (s->drq & (1 << ch->sync))) omap_dma_activate_channel(s, ch); }}static void omap_dma_disable_channel(struct omap_dma_s *s, struct omap_dma_channel_s *ch){ if (ch->enable) { ch->enable = 0; /* Discard any pending request */ ch->pending_request = 0; omap_dma_deactivate_channel(s, ch); }}static void omap_dma_channel_end_prog(struct omap_dma_s *s, struct omap_dma_channel_s *ch){ if (ch->waiting_end_prog) { ch->waiting_end_prog = 0; if (!ch->sync || ch->pending_request) { ch->pending_request = 0; omap_dma_activate_channel(s, ch); } }}static void omap_dma_interrupts_3_1_update(struct omap_dma_s *s){ struct omap_dma_channel_s *ch = s->ch; /* First three interrupts are shared between two channels each. */ if (ch[0].status | ch[6].status) qemu_irq_raise(ch[0].irq); if (ch[1].status | ch[7].status) qemu_irq_raise(ch[1].irq); if (ch[2].status | ch[8].status) qemu_irq_raise(ch[2].irq); if (ch[3].status) qemu_irq_raise(ch[3].irq); if (ch[4].status) qemu_irq_raise(ch[4].irq); if (ch[5].status) qemu_irq_raise(ch[5].irq);}static void omap_dma_interrupts_3_2_update(struct omap_dma_s *s){ struct omap_dma_channel_s *ch = s->ch; int i; for (i = s->chans; i; ch ++, i --) if (ch->status) qemu_irq_raise(ch->irq);}static void omap_dma_enable_3_1_mapping(struct omap_dma_s *s){ s->omap_3_1_mapping_disabled = 0; s->chans = 9; s->intr_update = omap_dma_interrupts_3_1_update;}static void omap_dma_disable_3_1_mapping(struct omap_dma_s *s){ s->omap_3_1_mapping_disabled = 1; s->chans = 16; s->intr_update = omap_dma_interrupts_3_2_update;}static void omap_dma_process_request(struct omap_dma_s *s, int request){ int channel; int drop_event = 0; struct omap_dma_channel_s *ch = s->ch; for (channel = 0; channel < s->chans; channel ++, ch ++) { if (ch->enable && ch->sync == request) { if (!ch->active) omap_dma_activate_channel(s, ch); else if (!ch->pending_request) ch->pending_request = 1; else { /* Request collision */ /* Second request received while processing other request */ ch->status |= EVENT_DROP_INTR; drop_event = 1; } } } if (drop_event) omap_dma_interrupts_update(s);}static void omap_dma_channel_run(struct omap_dma_s *s){ int n = s->chans; uint16_t status; uint8_t value[4]; struct omap_dma_port_if_s *src_p, *dest_p; struct omap_dma_reg_set_s *a; struct omap_dma_channel_s *ch; for (ch = s->ch; n; n --, ch ++) { if (!ch->active) continue; a = &ch->active_set; src_p = &s->mpu->port[ch->port[0]]; dest_p = &s->mpu->port[ch->port[1]]; if ((!ch->constant_fill && !src_p->addr_valid(s->mpu, a->src)) || (!dest_p->addr_valid(s->mpu, a->dest))) {#if 0 /* Bus time-out */ if (ch->interrupts & TIMEOUT_INTR) ch->status |= TIMEOUT_INTR; omap_dma_deactivate_channel(s, ch); continue;#endif printf("%s: Bus time-out in DMA%i operation\n", __FUNCTION__, s->chans - n); } status = ch->status; while (status == ch->status && ch->active) { /* Transfer a single element */ /* FIXME: check the endianness */ if (!ch->constant_fill) cpu_physical_memory_read(a->src, value, ch->data_type); else *(uint32_t *) value = ch->color; if (!ch->transparent_copy || *(uint32_t *) value != ch->color) cpu_physical_memory_write(a->dest, value, ch->data_type); a->src += a->elem_delta[0]; a->dest += a->elem_delta[1]; a->element ++; /* If the channel is element synchronized, deactivate it */ if (ch->sync && !ch->fs && !ch->bs) omap_dma_deactivate_channel(s, ch); /* If it is the last frame, set the LAST_FRAME interrupt */ if (a->element == 1 && a->frame == a->frames - 1) if (ch->interrupts & LAST_FRAME_INTR) ch->status |= LAST_FRAME_INTR; /* If the half of the frame was reached, set the HALF_FRAME interrupt */ if (a->element == (a->elements >> 1)) if (ch->interrupts & HALF_FRAME_INTR) ch->status |= HALF_FRAME_INTR; if (ch->fs && ch->bs) { a->pck_element ++; /* Check if a full packet has beed transferred. */ if (a->pck_element == a->pck_elements) { a->pck_element = 0; /* Set the END_PKT interrupt */ if ((ch->interrupts & END_PKT_INTR) && !ch->src_sync) ch->status |= END_PKT_INTR; /* If the channel is packet-synchronized, deactivate it */ if (ch->sync) omap_dma_deactivate_channel(s, ch); } } if (a->element == a->elements) { /* End of Frame */ a->element = 0; a->src += a->frame_delta[0]; a->dest += a->frame_delta[1]; a->frame ++; /* If the channel is frame synchronized, deactivate it */ if (ch->sync && ch->fs && !ch->bs) omap_dma_deactivate_channel(s, ch); /* If the channel is async, update cpc */ if (!ch->sync) ch->cpc = a->dest & 0xffff; /* Set the END_FRAME interrupt */ if (ch->interrupts & END_FRAME_INTR) ch->status |= END_FRAME_INTR; if (a->frame == a->frames) { /* End of Block */ /* Disable the channel */ if (ch->omap_3_1_compatible_disable) { omap_dma_disable_channel(s, ch); if (ch->link_enabled) omap_dma_enable_channel(s, &s->ch[ch->link_next_ch]); } else { if (!ch->auto_init) omap_dma_disable_channel(s, ch); else if (ch->repeat || ch->end_prog) omap_dma_channel_load(s, ch); else { ch->waiting_end_prog = 1; omap_dma_deactivate_channel(s, ch); } } if (ch->interrupts & END_BLOCK_INTR) ch->status |= END_BLOCK_INTR; } }
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?