[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: |
Kuo-Jung Su |
Subject: |
Re: [Qemu-devel] [PATCH v9 10/24] hw/arm: add FTDMAC020 AHB DMA support |
Date: |
Fri, 29 Mar 2013 15:23:44 +0800 |
2013/3/29 Peter Crosthwaite <address@hidden>:
> 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.
>
It's a programming error, which is used to identify possible bug in driver.
And I'll remove this sanity check in next release.
,>> + 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"?).
>
Awkward, now you know my English really sucks. :P
>> + * 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.?
>
Both, because FTSSP010 is the only one would trigger DMA in that way.
> 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
>>
>>
--
Best wishes,
Kuo-Jung Su
- [Qemu-devel] [PATCH v9 19/24] hw/arm: add FTLCDC200 LCD controller support, (continued)
- [Qemu-devel] [PATCH v9 19/24] hw/arm: add FTLCDC200 LCD controller support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 23/24] hw/arm: add FTTMR010 timer support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 22/24] hw/arm: add FTMAC110 10/100Mbps ethernet support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 24/24] hw/arm: add FTSPI020 SPI flash controller support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 08/24] hw/arm: add FTRTC011 RTC timer support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 14/24] hw: Add AudioCodecClass for wm87xx audio class abstration., Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 10/24] hw/arm: add FTDMAC020 AHB DMA support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 11/24] hw/arm: add FTAPBBRG020 APB DMA support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 12/24] hw/arm: add FTNANDC021 nand flash controller support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 15/24] hw: add WM8731 audio codec support, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 09/24] tests: add QTest for FTRTC011, Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 16/24] The FTSSP010 is a multi-function synchronous serial port interface controller which supports SSP, SPI, I2S, AC97 and SPDIF., Kuo-Jung Su, 2013/03/25
- [Qemu-devel] [PATCH v9 13/24] hw/arm: add FTI2C010 I2C controller support, Kuo-Jung Su, 2013/03/25