qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v9 10/24] hw/arm: add FTDMAC020 AHB DMA support


From: Peter Crosthwaite
Subject: Re: [Qemu-devel] [PATCH v9 10/24] hw/arm: add FTDMAC020 AHB DMA support
Date: Fri, 29 Mar 2013 10:22:37 +1000

Hi Kuo-Jung,

On Mon, Mar 25, 2013 at 10:09 PM, Kuo-Jung Su <address@hidden> wrote:
> From: Kuo-Jung Su <address@hidden>
>
> The Faraday FTDMAC020 provides eight configurable
> channels for the memory-to-memory, memory-to-peripheral,
> peripheral-to-peripheral, and peripheral-to-memory transfers.
>
> Each DMA channel supports chain transfer and can be programmed
> to one of the 16 handshaking channels in the hardware handshake mode.
>
> The main function of the hardware handshake mode is to provide an
> indication of the device status. Users can also disable the hardware
> handshake mode by programming the register when a DMA transfer is not
> necessary of referring to the handshaking channels.
>
> Signed-off-by: Kuo-Jung Su <address@hidden>
> ---
>  hw/arm/Makefile.objs    |    2 +-
>  hw/arm/ftplat_a369soc.c |   14 ++
>  hw/ftdmac020.c          |  599 
> +++++++++++++++++++++++++++++++++++++++++++++++
>  hw/ftdmac020.h          |  107 +++++++++
>  4 files changed, 721 insertions(+), 1 deletion(-)
>  create mode 100644 hw/ftdmac020.c
>  create mode 100644 hw/ftdmac020.h
>
> diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
> index 6a41b21..6510c51 100644
> --- a/hw/arm/Makefile.objs
> +++ b/hw/arm/Makefile.objs
> @@ -25,7 +25,7 @@ obj-y += strongarm.o
>  obj-y += imx_serial.o imx_ccm.o imx_timer.o imx_avic.o
>  obj-$(CONFIG_KVM) += kvm/arm_gic.o
>  obj-y += ftintc020.o ftahbc020.o ftddrii030.o ftpwmtmr010.o ftwdt010.o \
> -                ftrtc011.o
> +                ftrtc011.o ftdmac020.o
>
>  obj-y := $(addprefix ../,$(obj-y))
>
> diff --git a/hw/arm/ftplat_a369soc.c b/hw/arm/ftplat_a369soc.c
> index bd696c4..59e2c61 100644
> --- a/hw/arm/ftplat_a369soc.c
> +++ b/hw/arm/ftplat_a369soc.c
> @@ -168,6 +168,20 @@ static void a369soc_chip_init(FaradaySoCState *s)
>      sysbus_connect_irq(SYS_BUS_DEVICE(ds), 3, s->pic[44]);
>      /* Hour (Edge) */
>      sysbus_connect_irq(SYS_BUS_DEVICE(ds), 4, s->pic[45]);
> +
> +    /* ftdmac020 */
> +    s->hdma[0] = sysbus_create_varargs("ftdmac020",
> +                                       0x90300000,
> +                                       s->pic[0],  /* ALL (NC in A369) */
> +                                       s->pic[15], /* TC */
> +                                       s->pic[16], /* ERR */
> +                                       NULL);
> +    s->hdma[1] = sysbus_create_varargs("ftdmac020",
> +                                       0x96100000,
> +                                       s->pic[0],  /* ALL (NC in A369) */
> +                                       s->pic[17], /* TC */
> +                                       s->pic[18], /* ERR */
> +                                       NULL);
>  }
>
>  static void a369soc_realize(DeviceState *dev, Error **errp)
> diff --git a/hw/ftdmac020.c b/hw/ftdmac020.c
> new file mode 100644
> index 0000000..81b49b2
> --- /dev/null
> +++ b/hw/ftdmac020.c
> @@ -0,0 +1,599 @@
> +/*
> + * QEMU model of the FTDMAC020 DMA Controller
> + *
> + * Copyright (C) 2012 Faraday Technology
> + * Written by Dante Su <address@hidden>
> + *
> + * This file is licensed under GNU GPL v2+.
> + *
> + * Note: The FTDMAC020 descending address mode is not implemented.
> + */
> +
> +#include "hw/sysbus.h"
> +#include "sysemu/dma.h"
> +#include "sysemu/sysemu.h"
> +#include "sysemu/blockdev.h"
> +
> +#include "hw/ftdmac020.h"
> +
> +#define TYPE_FTDMAC020    "ftdmac020"
> +
> +enum ftdmac020_irqpin {
> +    IRQ_ALL = 0,
> +    IRQ_TC,
> +    IRQ_ERR,
> +};
> +
> +typedef struct Ftdmac020State Ftdmac020State;
> +
> +/**
> + * struct Ftdmac020LLD - hardware link list descriptor.
> + * @src: source physical address
> + * @dst: destination physical addr
> + * @next: phsical address to the next link list descriptor
> + * @ctrl: control field
> + * @size: transfer size
> + *
> + * should be word aligned
> + */
> +typedef struct Ftdmac020LLD {
> +    uint32_t src;
> +    uint32_t dst;
> +    uint32_t next;
> +    uint32_t ctrl;
> +    uint32_t size;
> +} Ftdmac020LLD;
> +
> +typedef struct Ftdmac020Chan {
> +    Ftdmac020State *chip;
> +
> +    int id;
> +    int burst;
> +    int llp_cnt;
> +    int src_bw;
> +    int src_stride;
> +    int dst_bw;
> +    int dst_stride;
> +
> +    /* HW register cache */
> +    uint32_t ccr;
> +    uint32_t cfg;
> +    uint32_t src;
> +    uint32_t dst;
> +    uint32_t llp;
> +    uint32_t len;
> +} Ftdmac020Chan;
> +
> +typedef struct Ftdmac020State {
> +    /*< private >*/
> +    SysBusDevice parent;
> +
> +    /*< public >*/
> +    MemoryRegion iomem;
> +    qemu_irq irq[3];
> +
> +    Ftdmac020Chan chan[8];
> +    qemu_irq      ack[16];
> +    uint32_t      req;
> +
> +    int busy;    /* Busy Channel ID */
> +    int bh_owner;
> +    QEMUBH *bh;
> +    DMAContext *dma;
> +
> +    /* HW register cache */
> +    uint32_t tcisr;
> +    uint32_t eaisr;
> +    uint32_t tcsr;
> +    uint32_t easr;
> +    uint32_t cesr;
> +    uint32_t cbsr;
> +    uint32_t csr;
> +    uint32_t sync;
> +} Ftdmac020State;
> +
> +#define FTDMAC020(obj) \
> +    OBJECT_CHECK(Ftdmac020State, obj, TYPE_FTDMAC020)
> +
> +static void ftdmac020_update_irq(Ftdmac020State *s)
> +{
> +    uint32_t tc, err;
> +
> +    /* 1. Checking TC interrupts */
> +    tc = s->tcisr & 0xff;
> +    qemu_set_irq(s->irq[IRQ_TC], tc ? 1 : 0);
> +
> +    /* 2. Checking Error/Abort interrupts */
> +    err = s->eaisr & 0x00ff00ff;
> +    qemu_set_irq(s->irq[IRQ_ERR], err ? 1 : 0);
> +
> +    /* 3. Checking interrupt summary (TC | Error | Abort) */
> +    qemu_set_irq(s->irq[IRQ_ALL], (tc || err) ? 1 : 0);
> +}
> +
> +static void ftdmac020_chan_ccr_decode(Ftdmac020Chan *c)
> +{
> +    uint32_t tmp;
> +
> +    /* 1. decode burst size */
> +    tmp = extract32(c->ccr, 16, 3);
> +    c->burst  = 1 << (tmp ? tmp + 1 : 0);
> +
> +    /* 2. decode source width */
> +    tmp = extract32(c->ccr, 11, 2);
> +    c->src_bw = 8 << tmp;
> +
> +    /* 3. decode destination width */
> +    tmp = extract32(c->ccr, 8, 2);
> +    c->dst_bw = 8 << tmp;
> +
> +    /* 4. decode source address stride */
> +    tmp = extract32(c->ccr, 5, 2);
> +    if (tmp == 2) {
> +        c->src_stride = 0;
> +    } else {
> +        c->src_stride = c->src_bw >> 3;
> +    }
> +
> +    /* 5. decode destination address stride */
> +    tmp = extract32(c->ccr, 3, 2);
> +    if (tmp == 2) {
> +        c->dst_stride = 0;
> +    } else {
> +        c->dst_stride = c->dst_bw >> 3;
> +    }
> +}
> +
> +static void ftdmac020_chan_start(Ftdmac020Chan *c)
> +{
> +    Ftdmac020State *s = c->chip;
> +    Ftdmac020LLD desc;
> +    hwaddr src, dst;
> +    uint8_t buf[4096] __attribute__ ((aligned (8)));
> +    int i, len, stride, src_hs, dst_hs;
> +
> +    if (!(c->ccr & CCR_START)) {
> +        return;
> +    }
> +
> +    s->busy = c->id;
> +
> +    /* DMA src/dst address */
> +    src = c->src;
> +    dst = c->dst;
> +
> +    /* DMA hardware handshake id */
> +    src_hs = (c->cfg & CFG_SRC_HANDSHAKE_EN) ? extract32(c->cfg, 3, 4) : -1;
> +    dst_hs = (c->cfg & CFG_DST_HANDSHAKE_EN) ? extract32(c->cfg, 9, 4) : -1;
> +
> +    /* DMA src/dst sanity check */
> +    if (cpu_physical_memory_is_io(src) && c->src_stride) {
> +        fprintf(stderr,
> +            "ftdmac020: src is an I/O device with non-fixed address?\n");
> +        abort();
> +    }

This doesn't look like a QEMU fatal to me. Its seems like software
usage policy of the DMA. Does the real hardware actually do any
preventative action on striding io accesses? If not then I would
either remove this assertion or at least demote it to a
LOG_GUEST_ERROR. There may be corner case applications where this is
valid. I think its also worthwhile trying to avoid use of
cpu_physical_foo() functions from device land.

> +    if (cpu_physical_memory_is_io(dst) && c->dst_stride) {
> +        fprintf(stderr,
> +            "ftdmac020: dst is an I/O device with non-fixed address?\n");
> +        abort();
> +    }
> +
> +    while (c->len > 0) {
> +        /*
> +         * Postpone this DMA action
> +         * if the corresponding dma request is not asserted
> +         */
> +        if ((src_hs >= 0) && !(s->req & BIT(src_hs))) {
> +            break;
> +        }
> +        if ((dst_hs >= 0) && !(s->req & BIT(dst_hs))) {
> +            break;
> +        }
> +
> +        len = MIN(sizeof(buf), c->burst * (c->src_bw >> 3));
> +
> +        /* load data from source into local buffer */
> +        if (c->src_stride) {
> +            dma_memory_read(s->dma, src, buf, len);
> +            src += len;
> +        } else {
> +            stride = c->src_bw >> 3;
> +            for (i = 0; i < len; i += stride) {
> +                dma_memory_read(s->dma, src, buf + i, stride);
> +            }
> +        }
> +
> +        /* DMA Hardware Handshake */
> +        if (src_hs >= 0) {
> +            qemu_set_irq(s->ack[src_hs], 1);
> +        }
> +
> +        /* store data into destination from local buffer */
> +        if (c->dst_stride) {
> +            dma_memory_write(s->dma, dst, buf, len);
> +            dst += len;
> +        } else {
> +            stride = c->dst_bw >> 3;
> +            for (i = 0; i < len; i += stride) {
> +                dma_memory_write(s->dma, dst, buf + i, stride);
> +            }
> +        }
> +
> +        /* DMA Hardware Handshake */
> +        if (dst_hs >= 0) {
> +            qemu_set_irq(s->ack[dst_hs], 1);
> +        }
> +
> +        /* update the channel transfer size */
> +        c->len -= len / (c->src_bw >> 3);
> +
> +        if (c->len == 0) {
> +            /* update the channel transfer status */
> +            if (!(c->ccr & CCR_MASK_TC)) {
> +                s->tcsr |= BIT(c->id);
> +                if (!(c->cfg & CFG_MASK_TCI)) {
> +                    s->tcisr |= BIT(c->id);
> +                }
> +                ftdmac020_update_irq(s);
> +            }
> +            /* try to load next lld */
> +            if (c->llp) {
> +                c->llp_cnt += 1;
> +                dma_memory_read(s->dma, c->llp, &desc, sizeof(desc));
> +
> +                desc.src  = le32_to_cpu(desc.src);
> +                desc.dst  = le32_to_cpu(desc.dst);
> +                desc.next = le32_to_cpu(desc.next);
> +                desc.size = le32_to_cpu(desc.size);
> +                desc.ctrl = le32_to_cpu(desc.ctrl);
> +
> +                c->src = desc.src;
> +                c->dst = desc.dst;
> +                c->llp = desc.next & 0xfffffffc;
> +                c->len = desc.size & 0x003fffff;
> +                c->ccr = (c->ccr & 0x78f8c081)
> +                       | (extract32(desc.ctrl, 29, 3) << 24)
> +                       | ((desc.ctrl & BIT(28)) ? CCR_MASK_TC : 0)
> +                       | (extract32(desc.ctrl, 25, 3) << 11)
> +                       | (extract32(desc.ctrl, 22, 3) << 8)
> +                       | (extract32(desc.ctrl, 20, 2) << 5)
> +                       | (extract32(desc.ctrl, 18, 2) << 3)
> +                       | (extract32(desc.ctrl, 16, 2) << 1);
> +                ftdmac020_chan_ccr_decode(c);
> +
> +                src = c->src;
> +                dst = c->dst;
> +            } else {
> +                /* clear dma start bit */
> +                c->ccr &= ~CCR_START;
> +            }
> +        }
> +    }
> +
> +    /* update dma src/dst address */
> +    c->src = src;
> +    c->dst = dst;
> +
> +    s->busy = -1;
> +}
> +
> +static void ftdmac020_chan_reset(Ftdmac020Chan *c)
> +{
> +    c->ccr = 0;
> +    c->cfg = 0;
> +    c->src = 0;
> +    c->dst = 0;
> +    c->llp = 0;
> +    c->len = 0;
> +}
> +
> +static void ftdmac020_bh(void *opaque)
> +{
> +    Ftdmac020State *s = FTDMAC020(opaque);
> +    Ftdmac020Chan  *c = NULL;
> +    int i, jobs, done;
> +
> +    ++s->bh_owner;
> +    jobs = 0;
> +    done = 0;
> +    for (i = 0; i < 8; ++i) {
> +        c = s->chan + i;
> +        if (c->ccr & CCR_START) {
> +            ++jobs;
> +            ftdmac020_chan_start(c);
> +            if (!(c->ccr & CCR_START)) {
> +                ++done;
> +            }
> +        }
> +    }
> +    --s->bh_owner;
> +
> +    /*
> +     * Devices those with an infinite FIFO (always ready for R/W)

Grammar (- the "those"?).

> +     * would trigger a new DMA handshake transaction here.
> +     * (i.e. ftnandc021, ftsdc010)
> +     */
> +    if ((jobs - done) && s->req) {
> +        qemu_bh_schedule(s->bh);
> +    }
> +}
> +
> +static void ftdmac020_handle_req(void *opaque, int line, int level)
> +{
> +    Ftdmac020State *s = FTDMAC020(opaque);
> +
> +    if (level) {
> +        /*
> +         * Devices those wait for data from externaI/O

s/those/that

> +         * would trigger a new DMA handshake transaction here.
> +         * (i.e. ftssp010)

A nitpick, but do you mean e.g. instead of i.e.?

Regards,
Peter

> +         */
> +        if (!(s->req & BIT(line))) {
> +            /* a simple workaround for BH reentry issue */
> +            if (!s->bh_owner) {
> +                qemu_bh_schedule(s->bh);
> +            }
> +        }
> +        s->req |= BIT(line);
> +    } else {
> +        s->req &= ~BIT(line);
> +        qemu_set_irq(s->ack[line], 0);
> +    }
> +}
> +
> +static void ftdmac020_chip_reset(Ftdmac020State *s)
> +{
> +    int i;
> +
> +    s->tcisr = 0;
> +    s->eaisr = 0;
> +    s->tcsr = 0;
> +    s->easr = 0;
> +    s->cesr = 0;
> +    s->cbsr = 0;
> +    s->csr  = 0;
> +    s->sync = 0;
> +
> +    for (i = 0; i < 8; ++i) {
> +        ftdmac020_chan_reset(s->chan + i);
> +    }
> +
> +    /* We can assume our GPIO have been wired up now */
> +    for (i = 0; i < 16; ++i) {
> +        qemu_set_irq(s->ack[i], 0);
> +    }
> +    s->req = 0;
> +}
> +
> +static uint64_t ftdmac020_mem_read(void *opaque, hwaddr addr, unsigned size)
> +{
> +    Ftdmac020State *s = FTDMAC020(opaque);
> +    Ftdmac020Chan  *c = NULL;
> +    uint32_t i, ret = 0;
> +
> +    switch (addr) {
> +    case REG_ISR:
> +        /* 1. Checking TC interrupts */
> +        ret |= s->tcisr & 0xff;
> +        /* 2. Checking Error interrupts */
> +        ret |= s->eaisr & 0xff;
> +        /* 3. Checking Abort interrupts */
> +        ret |= (s->eaisr >> 16) & 0xff;
> +        break;
> +    case REG_TCISR:
> +        return s->tcisr;
> +    case REG_EAISR:
> +        return s->eaisr;
> +    case REG_TCSR:
> +        return s->tcsr;
> +    case REG_EASR:
> +        return s->easr;
> +    case REG_CESR:
> +        for (i = 0; i < 8; ++i) {
> +            c = s->chan + i;
> +            ret |= (c->ccr & CCR_START) ? BIT(i) : 0;
> +        }
> +        break;
> +    case REG_CBSR:
> +        return (s->busy > 0) ? BIT(s->busy) : 0;
> +    case REG_CSR:
> +        return s->csr;
> +    case REG_SYNC:
> +        return s->sync;
> +    case REG_REVISION:
> +        /* rev. = 1.13.0 */
> +        return 0x00011300;
> +    case REG_FEATURE:
> +        /* fifo = 32 bytes, support linked list, 8 channels, AHB0 only */
> +        return 0x00008105;
> +    case REG_CHAN_BASE(0) ... REG_CHAN_BASE(7) + 0x14:
> +        c = s->chan + REG_CHAN_ID(addr);
> +        switch (addr & 0x1f) {
> +        case REG_CHAN_CCR:
> +            return c->ccr;
> +        case REG_CHAN_CFG:
> +            ret = c->cfg;
> +            ret |= (s->busy == c->id) ? (1 << 8) : 0;
> +            ret |= (c->llp_cnt & 0x0f) << 16;
> +            break;
> +        case REG_CHAN_SRC:
> +            return c->src;
> +        case REG_CHAN_DST:
> +            return c->dst;
> +        case REG_CHAN_LLP:
> +            return c->llp;
> +        case REG_CHAN_LEN:
> +            return c->len;
> +        default:
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "ftdmac020: undefined memory address@hidden" HWADDR_PRIx 
> "\n",
> +                addr);
> +            break;
> +        }
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +            "ftdmac020: undefined memory address@hidden" HWADDR_PRIx "\n", 
> addr);
> +        break;
> +    }
> +
> +    return ret;
> +}
> +
> +static void ftdmac020_mem_write(void    *opaque,
> +                                hwaddr   addr,
> +                                uint64_t val,
> +                                unsigned size)
> +{
> +    Ftdmac020State *s = FTDMAC020(opaque);
> +    Ftdmac020Chan  *c = NULL;
> +
> +    switch (addr) {
> +    case REG_TCCLR:
> +        s->tcisr &= ~((uint32_t)val);
> +        s->tcsr &= ~((uint32_t)val);
> +        ftdmac020_update_irq(s);
> +        break;
> +    case REG_EACLR:
> +        s->eaisr &= ~((uint32_t)val);
> +        s->easr &= ~((uint32_t)val);
> +        ftdmac020_update_irq(s);
> +        break;
> +    case REG_CSR:
> +        s->csr = (uint32_t)val;
> +        break;
> +    case REG_SYNC:
> +        /* In QEMU, devices are all in the same clock domain
> +         * so there is nothing needs to be done.
> +         */
> +        s->sync = (uint32_t)val;
> +        break;
> +    case REG_CHAN_BASE(0) ... REG_CHAN_BASE(7) + 0x14:
> +        c = s->chan + REG_CHAN_ID(addr);
> +        switch (addr & 0x1f) {
> +        case REG_CHAN_CCR:
> +            if (!(c->ccr & CCR_START) && (val & CCR_START)) {
> +                c->llp_cnt = 0;
> +            }
> +            c->ccr = (uint32_t)val & 0x87FFBFFF;
> +            if (c->ccr & CCR_START) {
> +                ftdmac020_chan_ccr_decode(c);
> +                /* kick-off DMA engine */
> +                qemu_bh_schedule(s->bh);
> +            }
> +            break;
> +        case REG_CHAN_CFG:
> +            c->cfg = (uint32_t)val & 0x3eff;
> +            break;
> +        case REG_CHAN_SRC:
> +            c->src = (uint32_t)val;
> +            break;
> +        case REG_CHAN_DST:
> +            c->dst = (uint32_t)val;
> +            break;
> +        case REG_CHAN_LLP:
> +            c->llp = (uint32_t)val & 0xfffffffc;
> +            break;
> +        case REG_CHAN_LEN:
> +            c->len = (uint32_t)val & 0x003fffff;
> +            break;
> +        default:
> +            qemu_log_mask(LOG_GUEST_ERROR,
> +                "ftdmac020: undefined memory address@hidden" HWADDR_PRIx 
> "\n",
> +                addr);
> +            break;
> +        }
> +        break;
> +    default:
> +        qemu_log_mask(LOG_GUEST_ERROR,
> +            "ftdmac020: undefined memory address@hidden" HWADDR_PRIx "\n", 
> addr);
> +        break;
> +    }
> +}
> +
> +static const MemoryRegionOps mmio_ops = {
> +    .read  = ftdmac020_mem_read,
> +    .write = ftdmac020_mem_write,
> +    .endianness = DEVICE_LITTLE_ENDIAN,
> +    .valid = {
> +        .min_access_size = 4,
> +        .max_access_size = 4
> +    }
> +};
> +
> +static void ftdmac020_reset(DeviceState *ds)
> +{
> +    Ftdmac020State *s = FTDMAC020(SYS_BUS_DEVICE(ds));
> +
> +    ftdmac020_chip_reset(s);
> +}
> +
> +static void ftdmac020_realize(DeviceState *dev, Error **errp)
> +{
> +    Ftdmac020State *s = FTDMAC020(dev);
> +    SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
> +    int i;
> +
> +    memory_region_init_io(&s->iomem,
> +                          &mmio_ops,
> +                          s,
> +                          TYPE_FTDMAC020,
> +                          0x1000);
> +    sysbus_init_mmio(sbd, &s->iomem);
> +    for (i = 0; i < 3; ++i) {
> +        sysbus_init_irq(sbd, &s->irq[i]);
> +    }
> +    qdev_init_gpio_in(&sbd->qdev, ftdmac020_handle_req, 16);
> +    qdev_init_gpio_out(&sbd->qdev, s->ack, 16);
> +
> +    s->busy = -1;
> +    s->dma = &dma_context_memory;
> +    s->bh = qemu_bh_new(ftdmac020_bh, s);
> +    for (i = 0; i < 8; ++i) {
> +        Ftdmac020Chan *c = s->chan + i;
> +        c->id   = i;
> +        c->chip = s;
> +    }
> +}
> +
> +static const VMStateDescription vmstate_ftdmac020 = {
> +    .name = TYPE_FTDMAC020,
> +    .version_id = 1,
> +    .minimum_version_id = 1,
> +    .minimum_version_id_old = 1,
> +    .fields = (VMStateField[]) {
> +        VMSTATE_UINT32(tcisr, Ftdmac020State),
> +        VMSTATE_UINT32(eaisr, Ftdmac020State),
> +        VMSTATE_UINT32(tcsr, Ftdmac020State),
> +        VMSTATE_UINT32(easr, Ftdmac020State),
> +        VMSTATE_UINT32(cesr, Ftdmac020State),
> +        VMSTATE_UINT32(cbsr, Ftdmac020State),
> +        VMSTATE_UINT32(csr, Ftdmac020State),
> +        VMSTATE_UINT32(sync, Ftdmac020State),
> +        VMSTATE_END_OF_LIST()
> +    }
> +};
> +
> +static void ftdmac020_class_init(ObjectClass *klass, void *data)
> +{
> +    DeviceClass *dc = DEVICE_CLASS(klass);
> +
> +    dc->vmsd    = &vmstate_ftdmac020;
> +    dc->reset   = ftdmac020_reset;
> +    dc->realize = ftdmac020_realize;
> +    dc->no_user = 1;
> +}
> +
> +static const TypeInfo ftdmac020_info = {
> +    .name           = TYPE_FTDMAC020,
> +    .parent         = TYPE_SYS_BUS_DEVICE,
> +    .instance_size  = sizeof(Ftdmac020State),
> +    .class_init     = ftdmac020_class_init,
> +};
> +
> +static void ftdmac020_register_types(void)
> +{
> +    type_register_static(&ftdmac020_info);
> +}
> +
> +type_init(ftdmac020_register_types)
> diff --git a/hw/ftdmac020.h b/hw/ftdmac020.h
> new file mode 100644
> index 0000000..86ee58c
> --- /dev/null
> +++ b/hw/ftdmac020.h
> @@ -0,0 +1,107 @@
> +/*
> + * QEMU model of the FTDMAC020 DMA Controller
> + *
> + * Copyright (C) 2012 Faraday Technology
> + * Written by Dante Su <address@hidden>
> + *
> + * This file is licensed under GNU GPL v2+.
> + *
> + * Note: The FTDMAC020 decreasing address mode is not implemented.
> + */
> +
> +#ifndef HW_ARM_FTDMAC020_H
> +#define HW_ARM_FTDMAC020_H
> +
> +#include "qemu/bitops.h"
> +
> +#define REG_ISR         0x00    /* Interrupt Status Register */
> +#define REG_TCISR       0x04    /* Terminal Count Interrupt Status Register 
> */
> +#define REG_TCCLR       0x08    /* Terminal Count Status Clear Register */
> +#define REG_EAISR       0x0c    /* Error/Abort Interrupt Status Register */
> +#define REG_EACLR       0x10    /* Error/Abort Status Clear Register */
> +#define REG_TCSR        0x14    /* Terminal Count Status Register */
> +#define REG_EASR        0x18    /* Error/Abort Status Register */
> +#define REG_CESR        0x1c    /* Channel Enable Status Register */
> +#define REG_CBSR        0x20    /* Channel Busy Status Register */
> +#define REG_CSR         0x24    /* Configuration Status Register */
> +#define REG_SYNC        0x28    /* Synchronization Register */
> +#define REG_REVISION    0x30
> +#define REG_FEATURE     0x34
> +
> +#define REG_CHAN_ID(addr)   (((addr) - 0x100) >> 5)
> +#define REG_CHAN_BASE(ch)   (0x100 + ((ch) << 5))
> +
> +#define REG_CHAN_CCR        0x00
> +#define REG_CHAN_CFG        0x04
> +#define REG_CHAN_SRC        0x08
> +#define REG_CHAN_DST        0x0C
> +#define REG_CHAN_LLP        0x10
> +#define REG_CHAN_LEN        0x14
> +
> +/*
> + * Feature register
> + */
> +#define FEATURE_NCHAN(f)    (((f) >> 12) & 0xF)
> +#define FEATURE_BRIDGE(f)   ((f) & BIT(10))
> +#define FEATURE_DUALBUS(f)  ((f) & BIT(9))
> +#define FEATURE_LLP(f)      ((f) & BIT(8))
> +#define FEATURE_FIFOAW(f)   ((f) & 0xF)
> +
> +/*
> + * Channel control register
> + */
> +#define CCR_START           BIT(0)
> +#define CCR_DST_M1          BIT(1)  /* dst is a slave device of AHB1 */
> +#define CCR_SRC_M1          BIT(2)  /* src is a slave device of AHB1 */
> +#define CCR_DST_INC         (0 << 3)/* dst addr: next = curr++ */
> +#define CCR_DST_DEC         (1 << 3)/* dst addr: next = curr-- */
> +#define CCR_DST_FIXED       (2 << 3)
> +#define CCR_SRC_INC         (0 << 5)/* src addr: next = curr++ */
> +#define CCR_SRC_DEC         (1 << 5)/* src addr: next = curr-- */
> +#define CCR_SRC_FIXED       (2 << 5)
> +#define CCR_HANDSHAKE       BIT(7)  /* DMA HW handshake enabled */
> +#define CCR_DST_WIDTH_8     (0 << 8)
> +#define CCR_DST_WIDTH_16    (1 << 8)
> +#define CCR_DST_WIDTH_32    (2 << 8)
> +#define CCR_DST_WIDTH_64    (3 << 8)
> +#define CCR_SRC_WIDTH_8     (0 << 11)
> +#define CCR_SRC_WIDTH_16    (1 << 11)
> +#define CCR_SRC_WIDTH_32    (2 << 11)
> +#define CCR_SRC_WIDTH_64    (3 << 11)
> +#define CCR_ABORT           BIT(15)
> +#define CCR_BURST_1         (0 << 16)
> +#define CCR_BURST_4         (1 << 16)
> +#define CCR_BURST_8         (2 << 16)
> +#define CCR_BURST_16        (3 << 16)
> +#define CCR_BURST_32        (4 << 16)
> +#define CCR_BURST_64        (5 << 16)
> +#define CCR_BURST_128       (6 << 16)
> +#define CCR_BURST_256       (7 << 16)
> +#define CCR_PRIVILEGED      BIT(19)
> +#define CCR_BUFFERABLE      BIT(20)
> +#define CCR_CACHEABLE       BIT(21)
> +#define CCR_PRIO_0          (0 << 22)
> +#define CCR_PRIO_1          (1 << 22)
> +#define CCR_PRIO_2          (2 << 22)
> +#define CCR_PRIO_3          (3 << 22)
> +#define CCR_FIFOTH_1        (0 << 24)
> +#define CCR_FIFOTH_2        (1 << 24)
> +#define CCR_FIFOTH_4        (2 << 24)
> +#define CCR_FIFOTH_8        (3 << 24)
> +#define CCR_FIFOTH_16       (4 << 24)
> +#define CCR_MASK_TC         BIT(31) /* DMA Transfer terminated(finished) */
> +
> +/*
> + * Channel configuration register
> + */
> +#define CFG_MASK_TCI            BIT(0)    /* disable tc interrupt */
> +#define CFG_MASK_EI             BIT(1)    /* disable error interrupt */
> +#define CFG_MASK_AI             BIT(2)    /* disable abort interrupt */
> +#define CFG_SRC_HANDSHAKE(x)    (((x) & 0xf) << 3)
> +#define CFG_SRC_HANDSHAKE_EN    BIT(7)
> +#define CFG_BUSY                BIT(8)
> +#define CFG_DST_HANDSHAKE(x)    (((x) & 0xf) << 9)
> +#define CFG_DST_HANDSHAKE_EN    BIT(13)
> +#define CFG_LLP_CNT(cfg)        (((cfg) >> 16) & 0xf)
> +
> +#endif    /* HW_ARM_FTDMAC020_H */
> --
> 1.7.9.5
>
>



reply via email to

[Prev in Thread] Current Thread [Next in Thread]