Patchwork [v6,09/24] hw/arm: add Faraday FTDMAC020 AHB DMA support

login
register
mail settings
Submitter Kuo-Jung Su
Date March 6, 2013, 7:27 a.m.
Message ID <1362554857-3896-10-git-send-email-dantesu@gmail.com>
Download mbox | patch
Permalink /patch/225387/
State New
Headers show

Comments

Kuo-Jung Su - March 6, 2013, 7:27 a.m.
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 <dantesu@gmail.com>
---
 hw/arm/Makefile.objs      |    1 +
 hw/arm/faraday_a369_soc.c |   14 ++
 hw/arm/ftdmac020.c        |  599 +++++++++++++++++++++++++++++++++++++++++++++
 hw/arm/ftdmac020.h        |  107 ++++++++
 4 files changed, 721 insertions(+)
 create mode 100644 hw/arm/ftdmac020.c
 create mode 100644 hw/arm/ftdmac020.h

Patch

diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 6dbac2f..5acdaf9 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -43,3 +43,4 @@  obj-y += ftddrii030.o
 obj-y += ftpwmtmr010.o
 obj-y += ftwdt010.o
 obj-y += ftrtc011.o
+obj-y += ftdmac020.o
diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
index 4371a3a..9d3e7a1 100644
--- a/hw/arm/faraday_a369_soc.c
+++ b/hw/arm/faraday_a369_soc.c
@@ -199,6 +199,20 @@  a369soc_device_init(FaradaySoCState *s)
                           s->pic[44],    /* Minute (Edge) */
                           s->pic[45],    /* Hour (Edge) */
                           NULL);
+
+    /* 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 int a369soc_init(SysBusDevice *dev)
diff --git a/hw/arm/ftdmac020.c b/hw/arm/ftdmac020.c
new file mode 100644
index 0000000..1330d50
--- /dev/null
+++ b/hw/arm/ftdmac020.c
@@ -0,0 +1,599 @@ 
+/*
+ * QEMU model of the FTDMAC020 DMA Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * This file is licensed under GNU GPL v2+.
+ *
+ * Note: The FTDMAC020 decreasing address mode is not implemented.
+ */
+
+#include "hw/sysbus.h"
+#include "sysemu/dma.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/blockdev.h"
+
+#include "faraday.h"
+#include "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 {
+    SysBusDevice busdev;
+    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");
+        exit(1);
+    }
+    if (cpu_physical_memory_is_io(dst) && c->dst_stride) {
+        fprintf(stderr,
+            "ftdmac020: dst is an I/O device with non-fixed address?\n");
+        exit(1);
+    }
+
+    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)
+     * 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
+         * would trigger a new DMA handshake transaction here.
+         * (i.e. ftssp010)
+         */
+        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 access@%#" HWADDR_PRIx "\n",
+                addr);
+            break;
+        }
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftdmac020: undefined memory access@%#" 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 access@%#" HWADDR_PRIx "\n",
+                addr);
+            break;
+        }
+        break;
+    default:
+        qemu_log_mask(LOG_GUEST_ERROR,
+            "ftdmac020: undefined memory access@%#" 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 int ftdmac020_init(SysBusDevice *dev)
+{
+    Ftdmac020State *s = FTDMAC020(dev);
+    int i;
+
+    memory_region_init_io(&s->iomem,
+                          &mmio_ops,
+                          s,
+                          TYPE_FTDMAC020,
+                          0x1000);
+    sysbus_init_mmio(dev, &s->iomem);
+    for (i = 0; i < 3; ++i) {
+        sysbus_init_irq(dev, &s->irq[i]);
+    }
+    qdev_init_gpio_in(&s->busdev.qdev, ftdmac020_handle_req, 16);
+    qdev_init_gpio_out(&s->busdev.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;
+    }
+
+    return 0;
+}
+
+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)
+{
+    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+    DeviceClass *dc = DEVICE_CLASS(klass);
+
+    k->init     = ftdmac020_init;
+    dc->vmsd    = &vmstate_ftdmac020;
+    dc->reset   = ftdmac020_reset;
+    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/arm/ftdmac020.h b/hw/arm/ftdmac020.h
new file mode 100644
index 0000000..86ee58c
--- /dev/null
+++ b/hw/arm/ftdmac020.h
@@ -0,0 +1,107 @@ 
+/*
+ * QEMU model of the FTDMAC020 DMA Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <dantesu@faraday-tech.com>
+ *
+ * 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 */