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 + -
显示快捷键?