From patchwork Fri Jan 18 06:28:11 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dante X-Patchwork-Id: 213484 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 695262C0087 for ; Fri, 18 Jan 2013 17:30:00 +1100 (EST) Received: from localhost ([::1]:54886 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Tw5Sg-0002mA-Gt for incoming@patchwork.ozlabs.org; Fri, 18 Jan 2013 01:29:58 -0500 Received: from eggs.gnu.org ([208.118.235.92]:38480) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Tw5RZ-00007V-Rf for qemu-devel@nongnu.org; Fri, 18 Jan 2013 01:28:54 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Tw5RV-0004Zg-61 for qemu-devel@nongnu.org; Fri, 18 Jan 2013 01:28:49 -0500 Received: from ftcsun4.faraday-tech.com ([60.248.181.187]:56147 helo=ftcsun4.faraday.com.tw) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Tw5RU-0004YK-0N for qemu-devel@nongnu.org; Fri, 18 Jan 2013 01:28:45 -0500 Received: from ftcpcs100.faraday.com.tw ([192.168.31.10]) by ftcsun4.faraday.com.tw (8.11.7p1+Sun/8.11.6) with ESMTP id r0I6Sem07166 for ; Fri, 18 Jan 2013 14:28:40 +0800 (CST) Received: from FTCPCS63.faraday.com.tw (unverified [192.168.32.63]) by ftcpcs100.faraday.com.tw (Clearswift SMTPRS 5.2.9) with ESMTP id ; Fri, 18 Jan 2013 14:28:41 +0800 Received: from vmware.faraday.com.tw (192.168.65.96) by FTCPCS63.faraday.com.tw (192.168.32.65) with Microsoft SMTP Server (TLS) id 14.2.328.9; Fri, 18 Jan 2013 14:28:40 +0800 From: Dante To: Date: Fri, 18 Jan 2013 14:28:11 +0800 Message-ID: <1358490506-18281-3-git-send-email-dantesu@faraday-tech.com> X-Mailer: git-send-email 1.7.9.5 In-Reply-To: <1358490506-18281-1-git-send-email-dantesu@faraday-tech.com> References: <1358490506-18281-1-git-send-email-dantesu@faraday-tech.com> MIME-Version: 1.0 X-Originating-IP: [192.168.65.96] X-KSE-Antivirus-Interceptor-Info: scan successful X-KSE-Antivirus-Info: Clean X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 60.248.181.187 Cc: peter.maydell@linaro.org, paul@codesourcery.com, dantesu@faraday-tech.com Subject: [Qemu-devel] [PATCH 03/18] hw: add QEMU model for Faraday AHB DMA 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 Signed-off-by: Kuo-Jung Su --- hw/ftdmac020.c | 625 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ hw/ftdmac020.h | 117 +++++++++++ 2 files changed, 742 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..2841455 --- /dev/null +++ b/hw/ftdmac020.c @@ -0,0 +1,625 @@ +/* + * QEMU model of the FTDMAC020 DMA Controller + * + * Copyright (C) 2012 Faraday Technology + * Copyright (C) 2012 Dante Su + * + * Note: The FTDMAC020 decreasing address mode is not implemented. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ + +#include "sysbus.h" +#include "sysemu/sysemu.h" +#include "sysemu/blockdev.h" + +#include "ftdmac020.h" + +#ifndef min +#define min(a, b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef max +#define max(a, b) ((a) > (b) ? (a) : (b)) +#endif + +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; + + 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; + +static inline uint32_t +ftdmac020_get_isr(ftdmac020_state *s) +{ + uint32_t isr = 0; + + /* 1. Checking TC interrupts */ + isr |= s->tcisr & 0xff; + /* 2. Checking Error interrupts */ + isr |= s->eaisr & 0xff; + /* 3. Checking Abort interrupts */ + isr |= (s->eaisr >> 16) & 0xff; + + return isr; +} + +static inline void +ftdmac020_update_irq(ftdmac020_state *s) +{ + uint32_t isr = ftdmac020_get_isr(s); + + if (isr) + qemu_set_irq(s->irq, 1); + else + qemu_set_irq(s->irq, 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; + uint8_t *src_map = NULL, *dst_map = NULL; + uint8_t *src_ptr = NULL, *dst_ptr = NULL; + uint8_t buf[4096]; + ftdmac020_lld desc; + int len = 0; + + if (!(c->ccr & 0x01)) + return; + + s->busy = c->id; + + src = c->src; + dst = c->dst; + src_hs = (c->cfg & 0x0080) ? ((c->cfg >> 3) & 0x0f) : -1; + dst_hs = (c->cfg & 0x2000) ? ((c->cfg >> 9) & 0x0f) : -1; + 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) { + + 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) = *(uint8_t *)src_ptr; + break; + case 16: + for (i = 0; i < len; i += 2) + *(uint16_t *)(buf + i) = *(uint16_t *)src_ptr; + break; + case 32: + for (i = 0; i < len; i += 4) + *(uint32_t *)(buf + i) = *(uint32_t *)src_ptr; + break; + default: + for (i = 0; i < len; i += 8) + *(uint64_t *)(buf + i) = *(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(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 & (1 << 31))) { + s->tcsr |= (1 << c->id); + if (!(c->cfg & 0x01)) + 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)); + 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(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_state *)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 & 0x01) { + ++jobs; + ftdmac020_chan_start(c); + if (!(c->ccr & 0x01)) + ++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_state *)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 int size) +{ + ftdmac020_state *s = opaque; + ftdmac020_chan *c = NULL; + uint32_t i, ret = 0; + + if (addr < 0x100) { + switch(addr) { + case REG_ISR: + return ftdmac020_get_isr(s); + 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 int size) +{ + ftdmac020_state *s = 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 & 0x01) && (val & 0x01)) { + c->llp_cnt = 0; + } + c->ccr = (uint32_t)val & 0x87FFBFFF; + if (c->ccr & 0x01) { + 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_mem_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 *d) +{ + ftdmac020_state *s = DO_UPCAST(ftdmac020_state, busdev.qdev, d); + ftdmac020_chip_reset(s); +} + +static int ftdmac020_init(SysBusDevice *dev) +{ + ftdmac020_state *s = FROM_SYSBUS(typeof(*s), dev); + int i; + + memory_region_init_io(&s->iomem, &ftdmac020_mem_ops, s, "ftdmac020", 0x1000); + sysbus_init_mmio(dev, &s->iomem); + sysbus_init_irq(dev, &s->irq); + 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 = "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 *sdc = SYS_BUS_DEVICE_CLASS(klass); + DeviceClass *k = DEVICE_CLASS(klass); + + sdc->init = ftdmac020_init; + k->vmsd = &vmstate_ftdmac020; + k->reset = ftdmac020_reset; + k->no_user = 1; +} + +static TypeInfo ftdmac020_info = { + .name = "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..75df3bc --- /dev/null +++ b/hw/ftdmac020.h @@ -0,0 +1,117 @@ +/* + * arch/arm/mach-faraday/drivers/ftdmac020.h + * + * Faraday FTDMAC020 DMA controller + * + * Copyright (C) 2010 Faraday Technology + * Copyright (C) 2011 Dante Su + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#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_ENABLE (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 tc interrupt */ +#define CFG_MASK_EI (1 << 1) /* mask error interrupt */ +#define CFG_MASK_AI (1 << 2) /* mask 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 */