From patchwork Tue Feb 26 09:14:00 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kuo-Jung Su X-Patchwork-Id: 223187 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [208.118.235.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id A21382C0311 for ; Tue, 26 Feb 2013 21:29:07 +1100 (EST) Received: from localhost ([::1]:52054 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UAGdh-000259-HQ for incoming@patchwork.ozlabs.org; Tue, 26 Feb 2013 04:15:57 -0500 Received: from eggs.gnu.org ([208.118.235.92]:34402) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UAGcz-0001RR-0Z for qemu-devel@nongnu.org; Tue, 26 Feb 2013 04:15:20 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1UAGcr-0005T7-DX for qemu-devel@nongnu.org; Tue, 26 Feb 2013 04:15:11 -0500 Received: from mail-da0-f41.google.com ([209.85.210.41]:65178) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1UAGcq-0005Rb-Uz for qemu-devel@nongnu.org; Tue, 26 Feb 2013 04:15:05 -0500 Received: by mail-da0-f41.google.com with SMTP id j17so523614dan.14 for ; Tue, 26 Feb 2013 01:15:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=x-received:from:to:cc:subject:date:message-id:x-mailer:in-reply-to :references; bh=Met9ka9AOt8ifLnGFzuxQy4dnZf/tlpO3L185Cu8WX0=; b=DlYHGDXQBUbf+o3juYh1HBBzebGao6xYcNxVMphaOFmy7SMKtItghOpUQ7c1c78PBr dd9i5sPmeNrqg3J9iedA3T7ijzGYsN5histZoyzsuw2nJcTUQzyQvp66rWN7cmiSos7u 9X5weOT4GoKz3byijsZmqHkxpY8eVmP1sG/PxXB6QOVL+Y6Qs+W8AaaYYhnC3LM1ov3H O6dYks3L1mk/g5gOiQ3F1jVlJ4E8/xwsPP7HIdNLNeLEeFAjZmQhvr37OcmvPB44p5oa F/hbr5eGZQxoqldgrQYSUKDZl9TCNAZq8LVhLdrsdr4Sii4Tm9u7y2b4dCm9tO86ON8y 9v1Q== X-Received: by 10.66.8.37 with SMTP id o5mr688288paa.201.1361870104136; Tue, 26 Feb 2013 01:15:04 -0800 (PST) Received: from localhost.localdomain ([220.132.37.35]) by mx.google.com with ESMTPS id t4sm823747pax.0.2013.02.26.01.15.01 (version=TLSv1 cipher=DES-CBC3-SHA bits=168/168); Tue, 26 Feb 2013 01:15:03 -0800 (PST) From: Kuo-Jung Su To: qemu-devel@nongnu.org Date: Tue, 26 Feb 2013 17:14:00 +0800 Message-Id: <1361870054-18564-10-git-send-email-dantesu@gmail.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1361870054-18564-1-git-send-email-dantesu@gmail.com> References: <1361870054-18564-1-git-send-email-dantesu@gmail.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 3.x [fuzzy] X-Received-From: 209.85.210.41 Cc: Peter Maydell , i.mitsyanko@samsung.com, Blue Swirl , Paul Brook , Kuo-Jung Su , Andreas , fred.konrad@greensocs.com Subject: [Qemu-devel] [PATCH v4 09/23] hw/arm: add Faraday FTDMAC020 AHB DMA support X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org From: Kuo-Jung Su 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 --- hw/arm/Makefile.objs | 1 + hw/arm/faraday_a369_soc.c | 14 ++ hw/arm/ftdmac020.c | 588 +++++++++++++++++++++++++++++++++++++++++++++ hw/arm/ftdmac020.h | 105 ++++++++ 4 files changed, 708 insertions(+) create mode 100644 hw/arm/ftdmac020.c create mode 100644 hw/arm/ftdmac020.h diff --git a/hw/arm/Makefile.objs b/hw/arm/Makefile.objs index 5e28539..be27435 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 d3f7f68..a79ff6c 100644 --- a/hw/arm/faraday_a369_soc.c +++ b/hw/arm/faraday_a369_soc.c @@ -194,6 +194,20 @@ a369soc_device_init(FaradaySoCState *s) pic[44], /* Minute (Edge) */ pic[45], /* Hour (Edge) */ NULL); + + /* ftdmac020 */ + s->hdma[0] = sysbus_create_varargs("ftdmac020", + 0x90300000, + pic[0], /* ALL (NC in A369) */ + pic[15], /* TC */ + pic[16], /* ERR */ + NULL); + s->hdma[1] = sysbus_create_varargs("ftdmac020", + 0x96100000, + pic[0], /* ALL (NC in A369) */ + pic[17], /* TC */ + pic[18], /* ERR */ + NULL); } static int a369soc_init(SysBusDevice *busdev) diff --git a/hw/arm/ftdmac020.c b/hw/arm/ftdmac020.c new file mode 100644 index 0000000..bd14c1e --- /dev/null +++ b/hw/arm/ftdmac020.c @@ -0,0 +1,588 @@ +/* + * QEMU model of the FTDMAC020 DMA Controller + * + * Copyright (C) 2012 Faraday Technology + * Written by Dante Su + * + * 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) { + hw_error("ftdmac020: src is an I/O device with non-fixed address?\n"); + exit(1); + } + if (cpu_physical_memory_is_io(dst) && c->dst_stride) { + hw_error("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: + break; + } + break; + default: + 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: + break; + } + 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); + Ftdmac020State *s = FTDMAC020(FROM_SYSBUS(Ftdmac020State, busdev)); + + ftdmac020_chip_reset(s); +} + +static int ftdmac020_init(SysBusDevice *dev) +{ + Ftdmac020State *s = FTDMAC020(FROM_SYSBUS(Ftdmac020State, 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->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..bc1d7b8 --- /dev/null +++ b/hw/arm/ftdmac020.h @@ -0,0 +1,105 @@ +/* + * QEMU model of the FTDMAC020 DMA Controller + * + * Copyright (C) 2012 Faraday Technology + * Written by Dante Su + * + * 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 + +#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 */