[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v7 11/24] hw/arm: add FTAPBBRG020 APB DMA support
From: |
Kuo-Jung Su |
Subject: |
[Qemu-devel] [PATCH v7 11/24] hw/arm: add FTAPBBRG020 APB DMA support |
Date: |
Tue, 12 Mar 2013 14:12:56 +0800 |
From: Kuo-Jung Su <address@hidden>
The FTAPBBRG020 supports the DMA functions for the AHB-to-AHB,
AHB-to-APB, APB-to-AHB, and APB-to-APB transactions.
The DMA engine can support up to 4 DMA channels (A, B, C, and D)
and 15 handshaking channels. A DMA channel granted by the arbiter
block is the only channel starts transfers. Each DMA channel can
be programmed to one of the 15 handshaking channels in the hardware
handshake mode to act as the source device or act as the destination
device.
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 | 1 +
hw/arm/faraday_a369_soc.c | 9 +
hw/arm/ftapbbrg020.c | 475 +++++++++++++++++++++++++++++++++++++++++++++
hw/arm/ftapbbrg020.h | 44 +++++
4 files changed, 529 insertions(+)
create mode 100644 hw/arm/ftapbbrg020.c
create mode 100644 hw/arm/ftapbbrg020.h
diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs
index 7a6e94a..cc5e343 100644
--- a/hw/arm/Makefile.objs
+++ b/hw/arm/Makefile.objs
@@ -44,3 +44,4 @@ obj-y += ftpwmtmr010.o
obj-y += ftwdt010.o
obj-y += ftrtc011.o
obj-y += ftdmac020.o
+obj-y += ftapbbrg020.o
diff --git a/hw/arm/faraday_a369_soc.c b/hw/arm/faraday_a369_soc.c
index b0d0ff6..ab13f70 100644
--- a/hw/arm/faraday_a369_soc.c
+++ b/hw/arm/faraday_a369_soc.c
@@ -223,6 +223,15 @@ a369soc_device_init(FaradaySoCState *s)
s->pic[17], /* TC */
s->pic[18], /* ERR */
NULL);
+
+ /* ftapbbrg020 */
+ ds = sysbus_create_simple("ftapbbrg020", 0x90f00000, s->pic[14]);
+ s->pdma[0] = ds;
+ object_property_set_link(OBJECT(ds), OBJECT(s), "soc", &local_errp);
+ if (local_errp) {
+ fprintf(stderr, "a369soc: Unable to set soc link for FTAPBBRG020\n");
+ exit(1);
+ }
}
static int a369soc_init(SysBusDevice *dev)
diff --git a/hw/arm/ftapbbrg020.c b/hw/arm/ftapbbrg020.c
new file mode 100644
index 0000000..c3cbe65
--- /dev/null
+++ b/hw/arm/ftapbbrg020.c
@@ -0,0 +1,475 @@
+/*
+ * QEMU model of the FTAPBBRG020 DMA Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <address@hidden>
+ *
+ * This file is licensed under GNU GPL v2+.
+ *
+ * Note: The FTAPBBRG020 DMA 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 "ftapbbrg020.h"
+
+#define TYPE_FTAPBBRG020 "ftapbbrg020"
+
+typedef struct Ftapbbrg020State Ftapbbrg020State;
+
+typedef struct Ftapbbrg020Chan {
+ Ftapbbrg020State *chip;
+
+ int id;
+ int burst;
+ int src_bw;
+ int src_stride;
+ int dst_bw;
+ int dst_stride;
+
+ /* HW register caches */
+ uint32_t src;
+ uint32_t dst;
+ uint32_t len;
+ uint32_t cmd;
+} Ftapbbrg020Chan;
+
+typedef struct Ftapbbrg020State {
+ SysBusDevice busdev;
+ MemoryRegion iomem;
+ qemu_irq irq;
+
+ FaradaySoCState *soc;
+ Ftapbbrg020Chan chan[4];
+ qemu_irq ack[16];
+ uint32_t req;
+
+ int busy; /* Busy Channel ID */
+ int bh_owner;
+ QEMUBH *bh;
+ DMAContext *dma;
+} Ftapbbrg020State;
+
+#define FTAPBBRG020(obj) \
+ OBJECT_CHECK(Ftapbbrg020State, obj, TYPE_FTAPBBRG020)
+
+static uint32_t ftapbbrg020_get_isr(Ftapbbrg020State *s)
+{
+ int i;
+ uint32_t isr = 0;
+ Ftapbbrg020Chan *chan;
+
+ for (i = 0; i < 4; ++i) {
+ chan = s->chan + i;
+ isr |= (chan->cmd & CMD_INTR_STATUS);
+ }
+
+ return isr;
+}
+
+static void ftapbbrg020_update_irq(Ftapbbrg020State *s)
+{
+ uint32_t isr = ftapbbrg020_get_isr(s);
+
+ qemu_set_irq(s->irq, isr ? 1 : 0);
+}
+
+static void ftapbbrg020_chan_cmd_decode(Ftapbbrg020Chan *c)
+{
+ uint32_t tmp;
+
+ /* 1. decode burst size */
+ c->burst = (c->cmd & CMD_BURST4) ? 4 : 1;
+
+ /* 2. decode source/destination width */
+ tmp = extract32(c->cmd, 20, 2);
+ if (tmp > 2) {
+ tmp = 2;
+ }
+ c->src_bw = c->dst_bw = 8 << (2 - tmp);
+
+ /* 3. decode source address stride */
+ switch (extract32(c->cmd, 8, 2)) {
+ case 0:
+ c->src_stride = 0;
+ break;
+ case 1:
+ c->src_stride = c->src_bw >> 3;
+ break;
+ case 2:
+ c->src_stride = 2 * (c->src_bw >> 3);
+ break;
+ case 3:
+ c->src_stride = 4 * (c->src_bw >> 3);
+ break;
+ }
+
+ /* 4. decode destination address stride */
+ switch (extract32(c->cmd, 12, 2)) {
+ case 0:
+ c->dst_stride = 0;
+ break;
+ case 1:
+ c->dst_stride = c->dst_bw >> 3;
+ break;
+ case 2:
+ c->dst_stride = 2 * (c->dst_bw >> 3);
+ break;
+ case 3:
+ c->dst_stride = 4 * (c->dst_bw >> 3);
+ break;
+ }
+}
+
+static void ftapbbrg020_chan_start(Ftapbbrg020Chan *c)
+{
+ Ftapbbrg020State *s = c->chip;
+ hwaddr src, dst;
+ uint8_t buf[4096] __attribute__ ((aligned (8)));
+ int i, len, stride, src_hs, dst_hs;
+
+ if (!(c->cmd & CMD_START)) {
+ return;
+ }
+
+ s->busy = c->id;
+
+ /* DMA src/dst address */
+ src = c->src;
+ dst = c->dst;
+
+ /* DMA hardware handshake id */
+ src_hs = extract32(c->cmd, 24, 4);
+ dst_hs = extract32(c->cmd, 16, 4);
+
+ /* DMA src/dst sanity check */
+ if (cpu_physical_memory_is_io(src) && c->src_stride) {
+ fprintf(stderr,
+ "ftapbbrg020: 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,
+ "ftapbbrg020: 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 && !(s->req & BIT(src_hs))) {
+ break;
+ }
+ if (dst_hs && !(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) {
+ 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) {
+ 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->cmd & CMD_INTR_FIN_EN) {
+ c->cmd |= CMD_INTR_FIN;
+ ftapbbrg020_update_irq(s);
+ }
+ /* clear start bit */
+ c->cmd &= ~CMD_START;
+ }
+ }
+
+ /* update src/dst address */
+ c->src = src;
+ c->dst = dst;
+
+ s->busy = -1;
+}
+
+static void ftapbbrg020_chan_reset(Ftapbbrg020Chan *c)
+{
+ c->cmd = 0;
+ c->src = 0;
+ c->dst = 0;
+ c->len = 0;
+}
+
+static void ftapbbrg020_bh(void *opaque)
+{
+ Ftapbbrg020State *s = FTAPBBRG020(opaque);
+ Ftapbbrg020Chan *c = NULL;
+ int i, jobs, done;
+
+ ++s->bh_owner;
+ jobs = 0;
+ done = 0;
+ for (i = 0; i < 4; ++i) {
+ c = s->chan + i;
+ if (c->cmd & CMD_START) {
+ ++jobs;
+ ftapbbrg020_chan_start(c);
+ if (!(c->cmd & CMD_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 ftapbbrg020_handle_req(void *opaque, int line, int level)
+{
+ Ftapbbrg020State *s = FTAPBBRG020(opaque);
+
+ if (level) {
+ /*
+ * Devices those wait for data from external I/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 ftapbbrg020_chip_reset(Ftapbbrg020State *s)
+{
+ int i;
+
+ for (i = 0; i < 4; ++i) {
+ ftapbbrg020_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
+ftapbbrg020_mem_read(void *opaque, hwaddr addr, unsigned size)
+{
+ Ftapbbrg020State *s = FTAPBBRG020(opaque);
+ Ftapbbrg020Chan *c = NULL;
+ uint32_t ret = 0;
+
+ switch (addr) {
+ case REG_SLAVE(0) ... REG_SLAVE(31):
+ ret = s->soc->apb_slave[addr / 4];
+ break;
+ case REG_CHAN_BASE(0) ... REG_CHAN_BASE(3) + 0x0C:
+ c = s->chan + REG_CHAN_ID(addr);
+ switch (addr & 0x0f) {
+ case REG_CHAN_CMD:
+ return c->cmd;
+ case REG_CHAN_SRC:
+ return c->src;
+ case REG_CHAN_DST:
+ return c->dst;
+ case REG_CHAN_CYC:
+ return c->len;
+ }
+ break;
+ case REG_REVISION:
+ return 0x00010800; /* rev 1.8.0 */
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "ftapbbrg020: undefined memory address@hidden" HWADDR_PRIx "\n",
addr);
+ break;
+ }
+
+ return ret;
+}
+
+static void
+ftapbbrg020_mem_write(void *opaque, hwaddr addr, uint64_t val, unsigned size)
+{
+ Ftapbbrg020State *s = FTAPBBRG020(opaque);
+ Ftapbbrg020Chan *c = NULL;
+
+ switch (addr) {
+ case REG_CHAN_BASE(0) ... REG_CHAN_BASE(3) + 0x0C:
+ c = s->chan + REG_CHAN_ID(addr);
+ switch (addr & 0x0f) {
+ case REG_CHAN_CMD:
+ c->cmd = (uint32_t)val;
+ ftapbbrg020_update_irq(s);
+ if (c->cmd & CMD_START) {
+ ftapbbrg020_chan_cmd_decode(c);
+ /* kick-off DMA engine */
+ qemu_bh_schedule(s->bh);
+ }
+ break;
+ case REG_CHAN_SRC:
+ c->src = (uint32_t)val;
+ break;
+ case REG_CHAN_DST:
+ c->dst = (uint32_t)val;
+ break;
+ case REG_CHAN_CYC:
+ c->len = (uint32_t)val & 0x00ffffff;
+ break;
+ }
+ break;
+
+ default:
+ qemu_log_mask(LOG_GUEST_ERROR,
+ "ftapbbrg020: undefined memory address@hidden" HWADDR_PRIx "\n",
addr);
+ break;
+ }
+}
+
+static const MemoryRegionOps mmio_ops = {
+ .read = ftapbbrg020_mem_read,
+ .write = ftapbbrg020_mem_write,
+ .endianness = DEVICE_LITTLE_ENDIAN,
+ .valid = {
+ .min_access_size = 4,
+ .max_access_size = 4
+ }
+};
+
+static void ftapbbrg020_reset(DeviceState *ds)
+{
+ Ftapbbrg020State *s = FTAPBBRG020(SYS_BUS_DEVICE(ds));
+ Error *local_errp = NULL;
+
+ s->soc = FARADAY_SOC(object_property_get_link(OBJECT(s),
+ "soc",
+ &local_errp));
+ if (local_errp) {
+ fprintf(stderr, "ftapbbrg020: Unable to get soc link\n");
+ exit(1);
+ }
+
+ ftapbbrg020_chip_reset(s);
+}
+
+static int ftapbbrg020_init(SysBusDevice *dev)
+{
+ Ftapbbrg020State *s = FTAPBBRG020(dev);
+ Ftapbbrg020Chan *c;
+ Error *local_errp = NULL;
+ int i;
+
+ memory_region_init_io(&s->iomem,
+ &mmio_ops,
+ s,
+ TYPE_FTAPBBRG020,
+ 0x1000);
+ sysbus_init_mmio(dev, &s->iomem);
+ sysbus_init_irq(dev, &s->irq);
+ qdev_init_gpio_in(&s->busdev.qdev, ftapbbrg020_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(ftapbbrg020_bh, s);
+ for (i = 0; i < 4; ++i) {
+ c = s->chan + i;
+ c->id = i;
+ c->chip = s;
+ }
+
+ object_property_add_link(OBJECT(dev),
+ "soc",
+ TYPE_FARADAY_SOC,
+ (Object **) &s->soc,
+ &local_errp);
+ if (local_errp) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static const VMStateDescription vmstate_ftapbbrg020 = {
+ .name = TYPE_FTAPBBRG020,
+ .version_id = 1,
+ .minimum_version_id = 1,
+ .minimum_version_id_old = 1,
+ .fields = (VMStateField[]) {
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static void ftapbbrg020_class_init(ObjectClass *klass, void *data)
+{
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+ DeviceClass *dc = DEVICE_CLASS(klass);
+
+ k->init = ftapbbrg020_init;
+ dc->vmsd = &vmstate_ftapbbrg020;
+ dc->reset = ftapbbrg020_reset;
+ dc->no_user = 1;
+}
+
+static const TypeInfo ftapbbrg020_info = {
+ .name = TYPE_FTAPBBRG020,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .instance_size = sizeof(Ftapbbrg020State),
+ .class_init = ftapbbrg020_class_init,
+};
+
+static void ftapbbrg020_register_types(void)
+{
+ type_register_static(&ftapbbrg020_info);
+}
+
+type_init(ftapbbrg020_register_types)
diff --git a/hw/arm/ftapbbrg020.h b/hw/arm/ftapbbrg020.h
new file mode 100644
index 0000000..fc060dc
--- /dev/null
+++ b/hw/arm/ftapbbrg020.h
@@ -0,0 +1,44 @@
+/*
+ * QEMU model of the FTAPBBRG020 DMA Controller
+ *
+ * Copyright (C) 2012 Faraday Technology
+ * Written by Dante Su <address@hidden>
+ *
+ * This file is licensed under GNU GPL v2+.
+ *
+ * Note: The FTAPBBRG020 DMA decreasing address mode is not implemented.
+ */
+
+#ifndef HW_ARM_FTAPBBRG020_H
+#define HW_ARM_FTAPBBRG020_H
+
+#include "qemu/bitops.h"
+
+#define REG_SLAVE(n) ((n) * 4) /* Slave config (base & size) */
+#define REG_REVISION 0xC8
+
+/*
+ * Channel base address
+ * @ch: channle id (0 <= id <= 3)
+ * i.e. 0: Channel A
+ * 1: Channel B
+ * 2: Channel C
+ * 3: Channel D
+ */
+#define REG_CHAN_ID(off) (((off) - 0x80) >> 4)
+#define REG_CHAN_BASE(ch) (0x80 + ((ch) << 4))
+
+#define REG_CHAN_SRC 0x00
+#define REG_CHAN_DST 0x04
+#define REG_CHAN_CYC 0x08
+#define REG_CHAN_CMD 0x0C
+
+#define CMD_START BIT(0)
+#define CMD_INTR_FIN BIT(1)
+#define CMD_INTR_FIN_EN BIT(2)
+#define CMD_BURST4 BIT(3)
+#define CMD_INTR_ERR BIT(4)
+#define CMD_INTR_ERR_EN BIT(5)
+#define CMD_INTR_STATUS (CMD_INTR_FIN | CMD_INTR_ERR)
+
+#endif /* HW_ARM_FTAPBB020_H */
--
1.7.9.5
- [Qemu-devel] [PATCH v7 01/24] target-arm: add Faraday ARMv5TE processors support, (continued)
- [Qemu-devel] [PATCH v7 01/24] target-arm: add Faraday ARMv5TE processors support, Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 02/24] hw/arm: add Faraday a369 SoC platform support, Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 03/24] hw/arm: add FTINTC020 interrupt controller support, Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 04/24] hw/arm: add FTAHBC020 AHB controller support, Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 05/24] hw/arm: add FTDDRII030 DDRII controller support, Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 06/24] hw/arm: add FTPWMTMR010 timer support, Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 07/24] hw/arm: add FTWDT010 watchdog timer support, Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 08/24] hw/arm: add FTRTC011 RTC timer support, Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 09/24] tests: add QTest for FTRTC011, Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 10/24] hw/arm: add FTDMAC020 AHB DMA support, Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 11/24] hw/arm: add FTAPBBRG020 APB DMA support,
Kuo-Jung Su <=
- [Qemu-devel] [PATCH v7 12/24] hw/arm: add FTNANDC021 nand flash controller support, Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 13/24] hw/arm: add FTI2C010 I2C controller support, Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 14/24] hw: add AudioCodecClass for wm87xx audio class abstration., Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 15/24] hw: add WM8731 audio codec support, Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 17/24] qemu/bitops.h: add the bit ordering reversal functions, Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 16/24] hw/arm: add FTSSP010 multi-function controller support, Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 18/24] hw/arm: add FTGMAC100 1Gbps ethernet support, Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 19/24] hw/arm: add FTLCDC200 LCD controller support, Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 20/24] hw/arm: add FTTSC010 touchscreen controller support, Kuo-Jung Su, 2013/03/12
- [Qemu-devel] [PATCH v7 21/24] hw/arm: add FTSDC010 MMC/SD controller support, Kuo-Jung Su, 2013/03/12