[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v2 04/20] arm: add Faraday FTDMAC020 AHB DMA support
From: |
Kuo-Jung Su |
Subject: |
[Qemu-devel] [PATCH v2 04/20] arm: add Faraday FTDMAC020 AHB DMA support |
Date: |
Fri, 25 Jan 2013 16:19:40 +0800 |
From: Kuo-Jung Su <address@hidden>
The Faraday FTDMAC020 is designed to enhance the system performance
and reduce the processor-interrupt generation. The system efficiency
is improved by employing the high-speed data transfers between the
system and device. The DMA controller provides eight configurable
channels for the memory-to-memory, memory-to-peripheral,
peripheral-to-peripheral, and peripheral-to-memory transfers
with shared buffer.
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/ftdmac020.c | 667 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/ftdmac020.h | 105 +++++++++
2 files changed, 772 insertions(+)
create mode 100644 hw/ftdmac020.c
create mode 100644 hw/ftdmac020.h
diff --git a/hw/ftdmac020.c b/hw/ftdmac020.c
new file mode 100644
index 0000000..58b453e
--- /dev/null
+++ b/hw/ftdmac020.c
@@ -0,0 +1,667 @@
+/*
+ * 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.
+ */
+
+#include "sysbus.h"
+#include "sysemu/sysemu.h"
+#include "sysemu/blockdev.h"
+
+#include "ftdmac020.h"
+
+#define TYPE_FTDMAC020 "ftdmac020"
+
+enum ftdmac020_irqpin {
+ IRQ_ALL = 0,
+ IRQ_TC,
+ IRQ_ERR,
+};
+
+typedef struct ftdmac020_state ftdmac020_state;
+
+/**
+ * struct ftdmac020_lld - 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 ftdmac020_lld {
+ uint32_t src;
+ uint32_t dst;
+ uint32_t next;
+ uint32_t ctrl;
+ uint32_t size;
+} ftdmac020_lld;
+
+typedef struct ftdmac020_chan {
+ ftdmac020_state *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;
+} ftdmac020_chan;
+
+typedef struct ftdmac020_state {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ qemu_irq irq[3];
+
+ ftdmac020_chan chan[8];
+ qemu_irq ack[16];
+ uint32_t req;
+
+ int busy; /* Busy Channel ID */
+ QEMUTimer *qtimer;
+
+ /* 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;
+} ftdmac020_state;
+
+#define FTDMAC020(obj) \
+ OBJECT_CHECK(ftdmac020_state, obj, TYPE_FTDMAC020)
+
+static void ftdmac020_update_irq(ftdmac020_state *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(ftdmac020_chan *c)
+{
+ uint32_t tmp;
+
+ /* 1. decode burst size */
+ tmp = (c->ccr >> 16) & 0x07;
+ c->burst = 1 << (tmp ? tmp + 1 : 0);
+
+ /* 2. decode source width */
+ tmp = (c->ccr >> 11) & 0x03;
+ c->src_bw = 8 << tmp;
+
+ /* 3. decode destination width */
+ tmp = (c->ccr >> 8) & 0x03;
+ c->dst_bw = 8 << tmp;
+
+ /* 4. decode source address stride */
+ tmp = (c->ccr >> 5) & 0x03;
+ if (tmp == 2) {
+ c->src_stride = 0;
+ } else {
+ c->src_stride = c->src_bw >> 3;
+ }
+
+ /* 5. decode destination address stride */
+ tmp = (c->ccr >> 3) & 0x03;
+ if (tmp == 2) {
+ c->dst_stride = 0;
+ } else {
+ c->dst_stride = c->dst_bw >> 3;
+ }
+}
+
+static void ftdmac020_chan_start(ftdmac020_chan *c)
+{
+ ftdmac020_state *s = c->chip;
+ hwaddr src, dst, src_len, dst_len;
+ int src_hs, dst_hs;
+ const uint8_t *src_map = NULL, *src_ptr = NULL;
+ uint8_t *dst_map = NULL, *dst_ptr = NULL;
+ uint8_t buf[4096];
+ ftdmac020_lld desc;
+ int len = 0;
+
+ if (!(c->ccr & 0x01)) {
+ return;
+ }
+
+ s->busy = c->id;
+
+ /* DMA src/dst address */
+ src = c->src;
+ dst = c->dst;
+ /* DMA hardware handshake id */
+ src_hs = (c->cfg & 0x0080) ? ((c->cfg >> 3) & 0x0f) : -1;
+ dst_hs = (c->cfg & 0x2000) ? ((c->cfg >> 9) & 0x0f) : -1;
+ /* DMA src/dst data length */
+ src_len = c->src_stride ? (c->len * (c->src_bw >> 3)) : (c->src_bw >> 3);
+ dst_len = c->dst_stride ? (c->len * (c->dst_bw >> 3)) : (c->dst_bw >> 3);
+ if (!cpu_physical_memory_is_io(c->src)) {
+ src_map = src_ptr = cpu_physical_memory_map(c->src, &src_len, 0);
+ }
+ if (!cpu_physical_memory_is_io(c->dst)) {
+ dst_map = dst_ptr = cpu_physical_memory_map(c->dst, &dst_len, 1);
+ }
+
+ while (c->len > 0) {
+ /*
+ * Postpone this DMA action
+ * if the corresponding dma request is not asserted
+ */
+ if ((src_hs >= 0) && !(s->req & (1 << src_hs))) {
+ break;
+ }
+ if ((dst_hs >= 0) && !(s->req & (1 << dst_hs))) {
+ break;
+ }
+
+ len = MIN(sizeof(buf), c->burst * (c->src_bw >> 3));
+
+ /* load data from source into local buffer */
+ if (src_ptr) {
+ if (c->src_stride) {
+ memcpy(buf, src_ptr, len);
+ src += len;
+ src_ptr += len;
+ } else {
+ int i;
+ switch (c->src_bw) {
+ case 8:
+ for (i = 0; i < len; i += 1) {
+ *(uint8_t *)(buf + i) = *(const uint8_t *)src_ptr;
+ }
+ break;
+ case 16:
+ for (i = 0; i < len; i += 2) {
+ *(uint16_t *)(buf + i) = *(const uint16_t *)src_ptr;
+ }
+ break;
+ case 32:
+ for (i = 0; i < len; i += 4) {
+ *(uint32_t *)(buf + i) = *(const uint32_t *)src_ptr;
+ }
+ break;
+ default:
+ for (i = 0; i < len; i += 8) {
+ *(uint64_t *)(buf + i) = *(const uint64_t *)src_ptr;
+ }
+ break;
+ }
+ }
+ } else {
+ uint32_t rl, stride = c->src_bw >> 3;
+ for (rl = 0; rl < len; rl += stride, src += c->src_stride) {
+ cpu_physical_memory_read(src, (uint64_t *)(buf + rl), stride);
+ }
+ }
+
+ /* DMA Hardware Handshake */
+ if (src_hs >= 0) {
+ qemu_set_irq(s->ack[src_hs], 1);
+ }
+
+ /* store data into destination from local buffer */
+ if (dst_ptr) {
+ if (c->dst_stride) {
+ memcpy(dst_ptr, buf, len);
+ dst += len;
+ dst_ptr += len;
+ } else {
+ int i;
+ switch (c->dst_bw) {
+ case 8:
+ for (i = 0; i < len; i += 1) {
+ *(uint8_t *)dst_ptr = *(uint8_t *)(buf + i);
+ }
+ break;
+ case 16:
+ for (i = 0; i < len; i += 2) {
+ *(uint16_t *)dst_ptr = *(uint16_t *)(buf + i);
+ }
+ break;
+ case 32:
+ for (i = 0; i < len; i += 4) {
+ *(uint32_t *)dst_ptr = *(uint32_t *)(buf + i);
+ }
+ break;
+ default:
+ for (i = 0; i < len; i += 8) {
+ *(uint64_t *)dst_ptr = *(uint64_t *)(buf + i);
+ }
+ break;
+ }
+ }
+ } else {
+ uint32_t wl, stride = c->dst_bw >> 3;
+ for (wl = 0; wl < len; wl += stride, dst += c->dst_stride) {
+ cpu_physical_memory_write(dst, (uint64_t *)(buf + wl), 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) {
+ /* release the memory mappings */
+ if (src_map) {
+ cpu_physical_memory_unmap((void *)src_map,
+ src_len,
+ 0,
+ (hwaddr)(src_ptr - src_map));
+ src_map = src_ptr = NULL;
+ }
+ if (dst_map) {
+ cpu_physical_memory_unmap(dst_map,
+ dst_len,
+ 1,
+ (hwaddr)(dst_ptr - dst_map));
+ dst_map = dst_ptr = NULL;
+ }
+ /* update the channel transfer status */
+ if (!(c->ccr & CCR_MASK_TC)) {
+ s->tcsr |= (1 << c->id);
+ if (!(c->cfg & CFG_MASK_TCI)) {
+ s->tcisr |= (1 << c->id);
+ }
+ ftdmac020_update_irq(s);
+ }
+ /* try to load next lld */
+ if (c->llp) {
+ c->llp_cnt += 1;
+ cpu_physical_memory_read(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)
+ | (((desc.ctrl >> 29) & 0x07) << 24)
+ | ((desc.ctrl & (1 << 28)) ? (1 << 31) : 0)
+ | (((desc.ctrl >> 25) & 0x07) << 11)
+ | (((desc.ctrl >> 22) & 0x07) << 8)
+ | (((desc.ctrl >> 20) & 0x03) << 5)
+ | (((desc.ctrl >> 18) & 0x03) << 3)
+ | (((desc.ctrl >> 16) & 0x03) << 1);
+ ftdmac020_chan_ccr_decode(c);
+
+ src = c->src;
+ dst = c->dst;
+ src_len = c->src_stride ? (c->len * (c->src_bw >> 3))
+ : (c->src_bw >> 3);
+ dst_len = c->dst_stride ? (c->len * (c->dst_bw >> 3))
+ : (c->dst_bw >> 3);
+ if (!cpu_physical_memory_is_io(c->src)) {
+ src_map = src_ptr = cpu_physical_memory_map(c->src,
+ &src_len,
+ 0);
+ }
+ if (!cpu_physical_memory_is_io(c->dst)) {
+ dst_map = dst_ptr = cpu_physical_memory_map(c->dst,
+ &dst_len,
+ 1);
+ }
+ } else {
+ /* clear dma start bit */
+ c->ccr &= 0xfffffffe;
+ }
+ }
+ }
+
+ /* release the memory mappings */
+ if (src_map) {
+ cpu_physical_memory_unmap((void *)src_map,
+ src_len,
+ 0,
+ (hwaddr)(src_ptr - src_map));
+ }
+ if (dst_map) {
+ cpu_physical_memory_unmap(dst_map,
+ dst_len,
+ 1,
+ (hwaddr)(dst_ptr - dst_map));
+ }
+
+ /* update dma src/dst address */
+ c->src = src;
+ c->dst = dst;
+
+ s->busy = -1;
+}
+
+static void ftdmac020_chan_reset(ftdmac020_chan *c)
+{
+ c->ccr = 0;
+ c->cfg = 0;
+ c->src = 0;
+ c->dst = 0;
+ c->llp = 0;
+ c->len = 0;
+}
+
+static void ftdmac020_timer_tick(void *opaque)
+{
+ ftdmac020_state *s = FTDMAC020(opaque);
+ ftdmac020_chan *c = NULL;
+ int i, jobs, done;
+
+ 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;
+ }
+ }
+ }
+
+ /* ToDo: Use mutex to skip this periodic checker */
+ if (jobs - done > 0) {
+ qemu_mod_timer(s->qtimer,
+ qemu_get_clock_ns(vm_clock) + 1);
+ } else {
+ qemu_mod_timer(s->qtimer,
+ qemu_get_clock_ns(vm_clock) + (get_ticks_per_sec() >> 2));
+ }
+}
+
+static void ftdmac020_handle_req(void *opaque, int line, int level)
+{
+ ftdmac020_state *s = FTDMAC020(opaque);
+
+ if (level) {
+ s->req |= (1 << line);
+ } else {
+ s->req &= ~(1 << line);
+ qemu_set_irq(s->ack[line], 0);
+ }
+}
+
+static void ftdmac020_chip_reset(ftdmac020_state *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)
+{
+ ftdmac020_state *s = FTDMAC020(opaque);
+ ftdmac020_chan *c = NULL;
+ uint32_t i, ret = 0;
+
+ if (addr < 0x100) {
+ 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:
+ ret = 0;
+ for (i = 0; i < 8; ++i) {
+ c = s->chan + i;
+ ret |= (c->ccr & 0x01) ? (1 << i) : 0;
+ }
+ break;
+ case REG_CBSR:
+ return (s->busy > 0) ? (1 << s->busy) : 0;
+ case REG_CSR:
+ return s->csr;
+ case REG_SYNC:
+ return s->sync;
+ case REG_REVISION:
+ return 0x00011300;
+ case REG_FEATURE:
+ return 0x00008105;
+ default:
+ break;
+ }
+ } else if (addr < 0x1f8) {
+ 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;
+ }
+ }
+
+ return ret;
+}
+
+static void ftdmac020_mem_write(void *opaque,
+ hwaddr addr,
+ uint64_t val,
+ unsigned size)
+{
+ ftdmac020_state *s = FTDMAC020(opaque);
+ ftdmac020_chan *c = NULL;
+
+ if (addr < 0x100) {
+ 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;
+ default:
+ break;
+ }
+ } else if (addr < 0x1f8) {
+ 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_mod_timer(s->qtimer, qemu_get_clock_ns(vm_clock) + 1);
+ }
+ 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:
+ break;
+ }
+ }
+}
+
+static const MemoryRegionOps ftdmac020_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)
+{
+ SysBusDevice *busdev = SYS_BUS_DEVICE(ds);
+ ftdmac020_state *s = FTDMAC020(FROM_SYSBUS(ftdmac020_state, busdev));
+
+ ftdmac020_chip_reset(s);
+}
+
+static int ftdmac020_init(SysBusDevice *dev)
+{
+ ftdmac020_state *s = FTDMAC020(FROM_SYSBUS(ftdmac020_state, dev));
+ int i;
+
+ memory_region_init_io(&s->iomem,
+ &ftdmac020_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->qtimer = qemu_new_timer_ns(vm_clock, ftdmac020_timer_tick, s);
+ for (i = 0; i < 8; ++i) {
+ ftdmac020_chan *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, ftdmac020_state),
+ VMSTATE_UINT32(eaisr, ftdmac020_state),
+ VMSTATE_UINT32(tcsr, ftdmac020_state),
+ VMSTATE_UINT32(easr, ftdmac020_state),
+ VMSTATE_UINT32(cesr, ftdmac020_state),
+ VMSTATE_UINT32(cbsr, ftdmac020_state),
+ VMSTATE_UINT32(csr, ftdmac020_state),
+ VMSTATE_UINT32(sync, ftdmac020_state),
+ 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(ftdmac020_state),
+ .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..e240a92
--- /dev/null
+++ b/hw/ftdmac020.h
@@ -0,0 +1,105 @@
+/*
+ * 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.
+ *
+ * Note: The FTDMAC020 decreasing address mode is not implemented.
+ */
+
+#ifndef __FTDMAC020_H
+#define __FTDMAC020_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) & (1 << 10))
+#define FEATURE_DUALBUS(f) ((f) & (1 << 9))
+#define FEATURE_LLP(f) ((f) & (1 << 8))
+#define FEATURE_FIFOAW(f) ((f) & 0xF)
+
+/*
+ * Channel control register
+ */
+#define CCR_START (1 << 0)
+#define CCR_DST_M1 (1 << 1)
+#define CCR_SRC_M1 (1 << 2)
+#define CCR_DST_INC (0 << 3)
+#define CCR_DST_DEC (1 << 3)
+#define CCR_DST_FIXED (2 << 3)
+#define CCR_SRC_INC (0 << 5)
+#define CCR_SRC_DEC (1 << 5)
+#define CCR_SRC_FIXED (2 << 5)
+#define CCR_HANDSHAKE (1 << 7)
+#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 (1 << 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 (1 << 19)
+#define CCR_BUFFERABLE (1 << 20)
+#define CCR_CACHEABLE (1 << 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 (1 << 31)
+
+/*
+ * Channel configuration register
+ */
+#define CFG_MASK_TCI (1 << 0) /* mask(disable) tc interrupt */
+#define CFG_MASK_EI (1 << 1) /* mask(disable) error interrupt */
+#define CFG_MASK_AI (1 << 2) /* mask(disable) abort interrupt */
+#define CFG_SRC_HANDSHAKE(x) (((x) & 0xf) << 3)
+#define CFG_SRC_HANDSHAKE_EN (1 << 7)
+#define CFG_BUSY (1 << 8)
+#define CFG_DST_HANDSHAKE(x) (((x) & 0xf) << 9)
+#define CFG_DST_HANDSHAKE_EN (1 << 13)
+#define CFG_LLP_CNT(cfg) (((cfg) >> 16) & 0xf)
+
+#endif /* __FTDMAC020_H */
--
1.7.9.5
- Re: [Qemu-devel] [PATCH v2 01/20] arm: add Faraday a36x SoC platform support, (continued)
[Qemu-devel] [PATCH v2 04/20] arm: add Faraday FTDMAC020 AHB DMA support,
Kuo-Jung Su <=
[Qemu-devel] [PATCH v2 05/20] arm: add Faraday FTGMAC100 1Gbps ethernet support, Kuo-Jung Su, 2013/01/25
[Qemu-devel] [PATCH v2 06/20] arm: add Faraday FTMAC110 10/100Mbps ethernet support, Kuo-Jung Su, 2013/01/25
[Qemu-devel] [PATCH v2 07/20] arm: add Faraday FTI2C010 I2C controller support, Kuo-Jung Su, 2013/01/25
[Qemu-devel] [PATCH v2 09/20] arm: add Faraday FTNANDC021 nand flash controller support, Kuo-Jung Su, 2013/01/25
[Qemu-devel] [PATCH v2 08/20] arm: add Faraday FTLCDC200 LCD controller support, Kuo-Jung Su, 2013/01/25
[Qemu-devel] [PATCH v2 10/20] arm: add Faraday FTSDC010 MMC/SD controller support, Kuo-Jung Su, 2013/01/25